From e190f54dea78786939da4c3140775bc15b01a715 Mon Sep 17 00:00:00 2001 From: DigiWarfare Date: Tue, 26 Sep 2017 11:24:06 -0400 Subject: [PATCH] Base Commit SourceCode --- COPYING | 23 + INSTALL | 9 + MSVC/NovacoinSolution.sln | 62 + MSVC/NovacoinSolution.v11.suo | Bin 0 -> 135188 bytes MSVC/build-helpers/buildboost.bat | 19 + MSVC/build-helpers/buildopenssl.bat | 47 + MSVC/build-helpers/buildqrcode.bat | 25 + MSVC/build-helpers/buildqt32.bat | 21 + MSVC/build-helpers/buildqt64.bat | 21 + MSVC/build-helpers/libqrcode.vcxproj | 190 + MSVC/build-helpers/libqrencode.vcxproj | 190 + MSVC/include/inttypes.h | 308 ++ MSVC/include/stdint.h | 247 + MSVC/leveldb/leveldb.vcxproj | 221 + MSVC/leveldb/leveldb.vcxproj.filters | 135 + MSVC/leveldb/leveldb.vcxproj.user | 4 + MSVC/leveldb/leveldb.vcxproj.vspscc | 10 + MSVC/libcommon/libcommon.vcxproj | 256 + MSVC/libcommon/libcommon.vcxproj.filters | 239 + MSVC/libcommon/libcommon.vcxproj.user | 4 + MSVC/libcommon/libcommon.vcxproj.vspscc | 10 + MSVC/mynovacoin/mynovacoin.vcxproj | 196 + MSVC/mynovacoin/mynovacoin.vcxproj.filters | 14 + MSVC/mynovacoin/mynovacoin.vcxproj.user | 4 + MSVC/mynovacoin/mynovacoin.vcxproj.vspscc | 10 + MSVC/mynovacoinqt/icon.rc | 1 + MSVC/mynovacoinqt/mynovacoinqt.vcxproj | 1638 ++++++ .../mynovacoinqt/mynovacoinqt.vcxproj.filters | 897 ++++ MSVC/mynovacoinqt/mynovacoinqt.vcxproj.user | 8 + .../mynovacoinqt_plugin_import.cpp | 16 + MSVC/mynovacoinqt/mynovacoinqt_resource.rc | 43 + MSVC/mynovacoinqt/novacoin.ico | Bin 0 -> 15086 bytes README | 1 + README.md | 0 contrib/bitrpc/bitrpc.py | 324 ++ contrib/clang/nomacro.pl | 47 + contrib/debian/bin/novacoin-qt | 16 + contrib/debian/bin/novacoind | 14 + contrib/debian/changelog | 6 + contrib/debian/compat | 1 + contrib/debian/control | 57 + contrib/debian/copyright | 169 + contrib/debian/examples/novacoin.conf | 75 + contrib/debian/gbp.conf | 5 + contrib/debian/manpages/novacoin.conf.5 | 91 + contrib/debian/manpages/novacoind.1 | 206 + contrib/debian/novacoin-qt.desktop | 13 + contrib/debian/novacoin-qt.install | 5 + contrib/debian/novacoin-qt.lintian-overrides | 2 + contrib/debian/novacoin-qt.protocol | 11 + contrib/debian/novacoind.examples | 1 + contrib/debian/novacoind.install | 2 + contrib/debian/novacoind.lintian-overrides | 2 + contrib/debian/novacoind.manpages | 2 + contrib/debian/patches/README | 3 + contrib/debian/patches/series | 1 + contrib/debian/rules | 25 + contrib/debian/source/format | 1 + contrib/debian/watch | 4 + contrib/gentoo/novacoin-0.5.0.ebuild | 107 + contrib/initscripts/bsd/novacoin | 68 + contrib/initscripts/lsb/novacoind | 147 + contrib/initscripts/systemd/novacoind.service | 11 + contrib/macdeploy/LICENSE | 674 +++ contrib/macdeploy/background.png | Bin 0 -> 16606 bytes contrib/macdeploy/background.psd | Bin 0 -> 66096 bytes contrib/macdeploy/fancy.plist | 32 + contrib/macdeploy/macdeployqtplus | 808 +++ contrib/macdeploy/notes.txt | 26 + contrib/qt_translations.py | 22 + contrib/seeds/makeseeds.py | 29 + contrib/wallettools/walletchangepass.py | 5 + contrib/wallettools/walletdefrag.py | 20 + contrib/wallettools/walletunlock.py | 5 + doc/Doxyfile | 1752 +++++++ doc/README | 18 + doc/README_windows.txt | 18 + doc/assets-attribution.txt | 52 + doc/bitcoin_logo_doxygen.png | Bin 0 -> 5735 bytes doc/build-msw.txt | 71 + doc/build-osx.txt | 136 + doc/build-unix.txt | 137 + ... novacoin-qt for android under Windows.txt | 284 ++ ...g novacoind and novacoinqt under Linux.txt | 51 + ...and novacoinqt under Windows with MSVC.txt | 125 + ...nd novacoinqt under Windows with MinGW.txt | 506 ++ doc/coding.txt | 99 + ...ing_building Windows binary under Unix.txt | 330 ++ doc/readme-qt.rst | 163 + doc/tor.md | 99 + share/genbuild.sh | 35 + share/pixmaps/addressbook16.bmp | Bin 0 -> 1334 bytes share/pixmaps/addressbook16mask.bmp | Bin 0 -> 126 bytes share/pixmaps/addressbook20.bmp | Bin 0 -> 1478 bytes share/pixmaps/addressbook20mask.bmp | Bin 0 -> 142 bytes share/pixmaps/check.ico | Bin 0 -> 766 bytes share/pixmaps/favicon.ico | Bin 0 -> 2550 bytes share/pixmaps/novacoin.ico | Bin 0 -> 15086 bytes share/pixmaps/novacoin32.xpm | 210 + share/pixmaps/novacoin80.xpm | 339 ++ share/pixmaps/nsis-header.bmp | Bin 0 -> 34254 bytes share/pixmaps/nsis-wizard.bmp | Bin 0 -> 206038 bytes share/pixmaps/send16.bmp | Bin 0 -> 1334 bytes share/pixmaps/send16mask.bmp | Bin 0 -> 126 bytes share/pixmaps/send16masknoshadow.bmp | Bin 0 -> 126 bytes share/pixmaps/send20.bmp | Bin 0 -> 1478 bytes share/pixmaps/send20mask.bmp | Bin 0 -> 142 bytes share/qt/extract_strings_qt.py | 70 + share/qt/img/reload.xcf | Bin 0 -> 25292 bytes share/qt/make_spinner.py | 43 + share/qt/make_windows_icon.sh | 5 + share/setup.nsi | 163 + share/ui.rc | 15 + src/addrman.cpp | 539 ++ src/addrman.h | 693 +++ src/alert.cpp | 247 + src/alert.h | 102 + src/allocators.h | 258 + src/base58.cpp | 373 ++ src/base58.h | 159 + src/bignum.h | 781 +++ src/bitcoinrpc.cpp | 1351 +++++ src/bitcoinrpc.h | 231 + src/checkpoints.cpp | 442 ++ src/checkpoints.h | 159 + src/checkqueue.h | 218 + src/clientversion.h | 19 + src/coincontrol.h | 57 + src/compat.h | 73 + src/crypter.cpp | 124 + src/crypter.h | 107 + src/crypto/scrypt/asm/asm-wrapper.cpp | 22 + src/crypto/scrypt/asm/obj/.gitignore | 2 + src/crypto/scrypt/asm/scrypt-arm.S | 393 ++ src/crypto/scrypt/asm/scrypt-x86.S | 819 +++ src/crypto/scrypt/asm/scrypt-x86_64.S | 758 +++ src/crypto/scrypt/generic/obj/.gitignore | 2 + src/crypto/scrypt/generic/scrypt-generic.cpp | 138 + src/crypto/scrypt/intrin/obj/.gitignore | 2 + src/crypto/scrypt/intrin/scrypt-sse2.cpp | 152 + src/crypto/sha2/asm/obj/.gitignore | 2 + src/crypto/sha2/asm/sha2-arm.S | 613 +++ src/crypto/sha2/asm/sha2-x86.S | 626 +++ src/crypto/sha2/asm/sha2-x86_64.S | 2056 ++++++++ src/db.cpp | 611 +++ src/db.h | 328 ++ src/hash.h | 139 + src/init.cpp | 1012 ++++ src/init.h | 17 + src/irc.cpp | 407 ++ src/irc.h | 12 + src/json/LICENSE.txt | 24 + src/json/json_spirit.h | 18 + src/json/json_spirit_error_position.h | 54 + src/json/json_spirit_reader.cpp | 137 + src/json/json_spirit_reader.h | 62 + src/json/json_spirit_reader_template.h | 612 +++ src/json/json_spirit_stream_reader.h | 70 + src/json/json_spirit_utils.h | 61 + src/json/json_spirit_value.cpp | 8 + src/json/json_spirit_value.h | 533 ++ src/json/json_spirit_writer.cpp | 95 + src/json/json_spirit_writer.h | 50 + src/json/json_spirit_writer_template.h | 247 + src/kernel.cpp | 523 ++ src/kernel.h | 62 + src/kernel_worker.cpp | 646 +++ src/kernel_worker.h | 46 + src/kernelrecord.cpp | 143 + src/kernelrecord.h | 54 + src/key.cpp | 534 ++ src/key.h | 169 + src/keystore.cpp | 301 ++ src/keystore.h | 213 + src/leveldb/.gitignore | 13 + src/leveldb/AUTHORS | 12 + src/leveldb/CONTRIBUTING.md | 36 + src/leveldb/LICENSE | 27 + src/leveldb/Makefile | 227 + src/leveldb/NEWS | 17 + src/leveldb/README | 51 + src/leveldb/README.md | 138 + src/leveldb/TODO | 14 + src/leveldb/WINDOWS.md | 39 + src/leveldb/build_detect_platform | 219 + src/leveldb/db/autocompact_test.cc | 118 + src/leveldb/db/builder.cc | 88 + src/leveldb/db/builder.h | 34 + src/leveldb/db/c.cc | 597 +++ src/leveldb/db/c_test.c | 390 ++ src/leveldb/db/corruption_test.cc | 374 ++ src/leveldb/db/db_bench.cc | 978 ++++ src/leveldb/db/db_impl.cc | 1513 ++++++ src/leveldb/db/db_impl.h | 211 + src/leveldb/db/db_iter.cc | 322 ++ src/leveldb/db/db_iter.h | 28 + src/leveldb/db/db_test.cc | 2128 ++++++++ src/leveldb/db/dbformat.cc | 140 + src/leveldb/db/dbformat.h | 230 + src/leveldb/db/dbformat_test.cc | 112 + src/leveldb/db/dumpfile.cc | 225 + src/leveldb/db/filename.cc | 148 + src/leveldb/db/filename.h | 85 + src/leveldb/db/filename_test.cc | 123 + src/leveldb/db/leveldb_main.cc | 64 + src/leveldb/db/log_format.h | 35 + src/leveldb/db/log_reader.cc | 266 + src/leveldb/db/log_reader.h | 108 + src/leveldb/db/log_test.cc | 530 ++ src/leveldb/db/log_writer.cc | 103 + src/leveldb/db/log_writer.h | 48 + src/leveldb/db/memtable.cc | 145 + src/leveldb/db/memtable.h | 91 + src/leveldb/db/repair.cc | 461 ++ src/leveldb/db/skiplist.h | 384 ++ src/leveldb/db/skiplist_test.cc | 378 ++ src/leveldb/db/snapshot.h | 66 + src/leveldb/db/table_cache.cc | 127 + src/leveldb/db/table_cache.h | 61 + src/leveldb/db/version_edit.cc | 266 + src/leveldb/db/version_edit.h | 107 + src/leveldb/db/version_edit_test.cc | 46 + src/leveldb/db/version_set.cc | 1484 ++++++ src/leveldb/db/version_set.h | 396 ++ src/leveldb/db/version_set_test.cc | 179 + src/leveldb/db/write_batch.cc | 147 + src/leveldb/db/write_batch_internal.h | 49 + src/leveldb/db/write_batch_test.cc | 120 + src/leveldb/doc/bench/db_bench_sqlite3.cc | 718 +++ src/leveldb/doc/bench/db_bench_tree_db.cc | 528 ++ src/leveldb/doc/benchmark.html | 459 ++ src/leveldb/doc/doc.css | 89 + src/leveldb/doc/impl.html | 213 + src/leveldb/doc/index.html | 549 ++ src/leveldb/doc/log_format.txt | 75 + src/leveldb/doc/table_format.txt | 104 + src/leveldb/helpers/memenv/memenv.cc | 385 ++ src/leveldb/helpers/memenv/memenv.h | 20 + src/leveldb/helpers/memenv/memenv_test.cc | 232 + src/leveldb/include/leveldb/c.h | 290 ++ src/leveldb/include/leveldb/cache.h | 99 + src/leveldb/include/leveldb/comparator.h | 63 + src/leveldb/include/leveldb/db.h | 161 + src/leveldb/include/leveldb/dumpfile.h | 25 + src/leveldb/include/leveldb/env.h | 333 ++ src/leveldb/include/leveldb/filter_policy.h | 70 + src/leveldb/include/leveldb/iterator.h | 100 + src/leveldb/include/leveldb/options.h | 195 + src/leveldb/include/leveldb/slice.h | 109 + src/leveldb/include/leveldb/status.h | 106 + src/leveldb/include/leveldb/table.h | 85 + src/leveldb/include/leveldb/table_builder.h | 92 + src/leveldb/include/leveldb/write_batch.h | 64 + src/leveldb/issues/issue178_test.cc | 92 + src/leveldb/issues/issue200_test.cc | 59 + src/leveldb/port/README | 10 + src/leveldb/port/atomic_pointer.h | 223 + src/leveldb/port/port.h | 21 + src/leveldb/port/port_example.h | 135 + src/leveldb/port/port_posix.cc | 54 + src/leveldb/port/port_posix.h | 158 + src/leveldb/port/port_win.cc | 147 + src/leveldb/port/port_win.h | 174 + src/leveldb/port/thread_annotations.h | 60 + src/leveldb/port/win/stdint.h | 24 + src/leveldb/table/block.cc | 268 + src/leveldb/table/block.h | 44 + src/leveldb/table/block_builder.cc | 109 + src/leveldb/table/block_builder.h | 57 + src/leveldb/table/filter_block.cc | 111 + src/leveldb/table/filter_block.h | 68 + src/leveldb/table/filter_block_test.cc | 128 + src/leveldb/table/format.cc | 145 + src/leveldb/table/format.h | 108 + src/leveldb/table/iterator.cc | 67 + src/leveldb/table/iterator_wrapper.h | 63 + src/leveldb/table/merger.cc | 197 + src/leveldb/table/merger.h | 26 + src/leveldb/table/table.cc | 285 ++ src/leveldb/table/table_builder.cc | 270 + src/leveldb/table/table_test.cc | 868 ++++ src/leveldb/table/two_level_iterator.cc | 182 + src/leveldb/table/two_level_iterator.h | 34 + src/leveldb/util/arena.cc | 68 + src/leveldb/util/arena.h | 68 + src/leveldb/util/arena_test.cc | 68 + src/leveldb/util/bloom.cc | 95 + src/leveldb/util/bloom_test.cc | 161 + src/leveldb/util/cache.cc | 325 ++ src/leveldb/util/cache_test.cc | 186 + src/leveldb/util/coding.cc | 194 + src/leveldb/util/coding.h | 104 + src/leveldb/util/coding_test.cc | 196 + src/leveldb/util/comparator.cc | 81 + src/leveldb/util/crc32c.cc | 332 ++ src/leveldb/util/crc32c.h | 45 + src/leveldb/util/crc32c_test.cc | 72 + src/leveldb/util/env.cc | 96 + src/leveldb/util/env_posix.cc | 608 +++ src/leveldb/util/env_test.cc | 104 + src/leveldb/util/env_win.cc | 1040 ++++ src/leveldb/util/filter_policy.cc | 11 + src/leveldb/util/hash.cc | 52 + src/leveldb/util/hash.h | 19 + src/leveldb/util/hash_test.cc | 54 + src/leveldb/util/histogram.cc | 139 + src/leveldb/util/histogram.h | 42 + src/leveldb/util/logging.cc | 72 + src/leveldb/util/logging.h | 43 + src/leveldb/util/mutexlock.h | 41 + src/leveldb/util/options.cc | 29 + src/leveldb/util/posix_logger.h | 98 + src/leveldb/util/random.h | 64 + src/leveldb/util/status.cc | 75 + src/leveldb/util/testharness.cc | 77 + src/leveldb/util/testharness.h | 138 + src/leveldb/util/testutil.cc | 51 + src/leveldb/util/testutil.h | 53 + src/main.cpp | 4046 +++++++++++++++ src/main.h | 1655 ++++++ src/makefile.bsd | 241 + src/makefile.linux-mingw | 200 + src/makefile.mingw | 168 + src/makefile.osx | 198 + src/makefile.unix | 243 + src/miner.cpp | 797 +++ src/miner.h | 33 + src/ministun.h | 108 + src/mruset.h | 64 + src/net.cpp | 1884 +++++++ src/net.h | 688 +++ src/netbase.cpp | 1187 +++++ src/netbase.h | 153 + src/noui.cpp | 28 + src/ntp.cpp | 535 ++ src/ntp.h | 13 + src/obj/.gitignore | 2 + src/protocol.cpp | 150 + src/protocol.h | 138 + src/qt/aboutdialog.cpp | 50 + src/qt/aboutdialog.h | 30 + src/qt/addressbookpage.cpp | 372 ++ src/qt/addressbookpage.h | 85 + src/qt/addresstablemodel.cpp | 426 ++ src/qt/addresstablemodel.h | 92 + src/qt/askpassphrasedialog.cpp | 269 + src/qt/askpassphrasedialog.h | 46 + src/qt/bitcoin.cpp | 282 ++ src/qt/bitcoin.qrc | 60 + src/qt/bitcoinaddressvalidator.cpp | 77 + src/qt/bitcoinaddressvalidator.h | 24 + src/qt/bitcoinamountfield.cpp | 168 + src/qt/bitcoinamountfield.h | 60 + src/qt/bitcoingui.cpp | 1235 +++++ src/qt/bitcoingui.h | 216 + src/qt/bitcoinstrings.cpp | 182 + src/qt/bitcoinunits.cpp | 191 + src/qt/bitcoinunits.h | 69 + src/qt/clientmodel.cpp | 217 + src/qt/clientmodel.h | 88 + src/qt/coincontroldialog.cpp | 760 +++ src/qt/coincontroldialog.h | 102 + src/qt/coincontroltreewidget.cpp | 33 + src/qt/coincontroltreewidget.h | 17 + src/qt/csvmodelwriter.cpp | 88 + src/qt/csvmodelwriter.h | 46 + src/qt/dialogwindowflags.h | 9 + src/qt/editaddressdialog.cpp | 137 + src/qt/editaddressdialog.h | 52 + src/qt/forms/aboutdialog.ui | 158 + src/qt/forms/addressbookpage.ui | 175 + src/qt/forms/askpassphrasedialog.ui | 151 + src/qt/forms/coincontroldialog.ui | 554 ++ src/qt/forms/editaddressdialog.ui | 105 + src/qt/forms/intro.ui | 266 + src/qt/forms/multisigaddressentry.ui | 170 + src/qt/forms/multisigdialog.ui | 793 +++ src/qt/forms/multisiginputentry.ui | 160 + src/qt/forms/optionsdialog.ui | 592 +++ src/qt/forms/overviewpage.ui | 434 ++ src/qt/forms/qrcodedialog.ui | 212 + src/qt/forms/rpcconsole.ui | 782 +++ src/qt/forms/secondauthdialog.ui | 230 + src/qt/forms/sendcoinsdialog.ui | 794 +++ src/qt/forms/sendcoinsentry.ui | 169 + src/qt/forms/signverifymessagedialog.ui | 386 ++ src/qt/forms/transactiondescdialog.ui | 74 + src/qt/guiconstants.h | 39 + src/qt/guiutil.cpp | 539 ++ src/qt/guiutil.h | 148 + src/qt/intro.cpp | 292 ++ src/qt/intro.h | 73 + src/qt/locale/bitcoin_en.qm | Bin 0 -> 109091 bytes src/qt/locale/bitcoin_en.ts | 4447 ++++++++++++++++ src/qt/locale/bitcoin_ru.qm | Bin 0 -> 119439 bytes src/qt/locale/bitcoin_ru.ts | 4453 ++++++++++++++++ src/qt/locale/bitcoin_uk.qm | Bin 0 -> 117895 bytes src/qt/locale/bitcoin_uk.ts | 4476 +++++++++++++++++ src/qt/locale/translations.pro | 17 + src/qt/macdockiconhandler.h | 54 + src/qt/macdockiconhandler.mm | 139 + src/qt/macnotificationhandler.h | 30 + src/qt/macnotificationhandler.mm | 91 + src/qt/mintingfilterproxy.cpp | 7 + src/qt/mintingfilterproxy.h | 13 + src/qt/mintingtablemodel.cpp | 503 ++ src/qt/mintingtablemodel.h | 64 + src/qt/mintingview.cpp | 250 + src/qt/mintingview.h | 55 + src/qt/monitoreddatamapper.cpp | 36 + src/qt/monitoreddatamapper.h | 31 + src/qt/multisigaddressentry.cpp | 120 + src/qt/multisigaddressentry.h | 44 + src/qt/multisigdialog.cpp | 660 +++ src/qt/multisigdialog.h | 61 + src/qt/multisiginputentry.cpp | 174 + src/qt/multisiginputentry.h | 53 + src/qt/notificator.cpp | 326 ++ src/qt/notificator.h | 80 + src/qt/optionsdialog.cpp | 348 ++ src/qt/optionsdialog.h | 68 + src/qt/optionsmodel.cpp | 345 ++ src/qt/optionsmodel.h | 73 + src/qt/overviewpage.cpp | 234 + src/qt/overviewpage.h | 54 + src/qt/qrcodedialog.cpp | 172 + src/qt/qrcodedialog.h | 41 + src/qt/qtipcserver.cpp | 164 + src/qt/qtipcserver.h | 10 + src/qt/qvalidatedlineedit.cpp | 45 + src/qt/qvalidatedlineedit.h | 29 + src/qt/qvaluecombobox.cpp | 27 + src/qt/qvaluecombobox.h | 33 + src/qt/res/bitcoin-qt.rc | 37 + src/qt/res/icons/add.png | Bin 0 -> 1279 bytes src/qt/res/icons/address-book.png | Bin 0 -> 1916 bytes src/qt/res/icons/bitcoin.icns | Bin 0 -> 57205 bytes src/qt/res/icons/bitcoin.ico | Bin 0 -> 135961 bytes src/qt/res/icons/bitcoin.png | Bin 0 -> 35605 bytes src/qt/res/icons/bitcoin_testnet.png | Bin 0 -> 28756 bytes 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/configure.png | Bin 0 -> 1055 bytes 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 -> 673 bytes src/qt/res/icons/debugwindow.png | Bin 0 -> 5402 bytes src/qt/res/icons/dump.png | Bin 0 -> 1058 bytes src/qt/res/icons/edit.png | Bin 0 -> 1627 bytes src/qt/res/icons/editcopy.png | Bin 0 -> 879 bytes src/qt/res/icons/editpaste.png | Bin 0 -> 1458 bytes src/qt/res/icons/export.png | Bin 0 -> 2148 bytes src/qt/res/icons/filesave.png | Bin 0 -> 1741 bytes src/qt/res/icons/history.png | Bin 0 -> 1432 bytes src/qt/res/icons/import.png | Bin 0 -> 1006 bytes src/qt/res/icons/key.png | Bin 0 -> 1727 bytes src/qt/res/icons/lock_closed.png | Bin 0 -> 1679 bytes src/qt/res/icons/lock_open.png | Bin 0 -> 1644 bytes src/qt/res/icons/mining_active.png | Bin 0 -> 1027 bytes src/qt/res/icons/mining_inactive.png | Bin 0 -> 1170 bytes src/qt/res/icons/notsynced.png | Bin 0 -> 1013 bytes src/qt/res/icons/overview.png | Bin 0 -> 7455 bytes src/qt/res/icons/qrcode.png | Bin 0 -> 237 bytes src/qt/res/icons/quit.png | Bin 0 -> 2163 bytes src/qt/res/icons/receive.png | Bin 0 -> 1437 bytes src/qt/res/icons/remove.png | Bin 0 -> 1224 bytes src/qt/res/icons/send.png | Bin 0 -> 1487 bytes src/qt/res/icons/synced.png | Bin 0 -> 781 bytes src/qt/res/icons/transaction0.png | Bin 0 -> 569 bytes src/qt/res/icons/transaction2.png | Bin 0 -> 413 bytes src/qt/res/icons/tx_inout.png | Bin 0 -> 2442 bytes src/qt/res/icons/tx_input.png | Bin 0 -> 2152 bytes src/qt/res/icons/tx_mined.png | Bin 0 -> 3287 bytes src/qt/res/icons/tx_output.png | Bin 0 -> 2129 bytes src/qt/res/images/about.png | Bin 0 -> 3488 bytes src/qt/res/images/splash2.jpg | Bin 0 -> 18568 bytes src/qt/res/movies/update_spinner.mng | Bin 0 -> 27817 bytes src/qt/res/src/bitcoin.svg | 115 + src/qt/res/src/clock1.svg | 261 + src/qt/res/src/clock2.svg | 262 + src/qt/res/src/clock3.svg | 261 + src/qt/res/src/clock4.svg | 261 + src/qt/res/src/clock5.svg | 262 + src/qt/res/src/clock_green.svg | 262 + src/qt/res/src/inout.svg | 122 + src/qt/res/src/questionmark.svg | 159 + src/qt/rpcconsole.cpp | 525 ++ src/qt/rpcconsole.h | 87 + src/qt/secondauthdialog.cpp | 170 + src/qt/secondauthdialog.h | 42 + src/qt/sendcoinsdialog.cpp | 524 ++ src/qt/sendcoinsdialog.h | 72 + src/qt/sendcoinsentry.cpp | 172 + src/qt/sendcoinsentry.h | 56 + src/qt/signverifymessagedialog.cpp | 291 ++ src/qt/signverifymessagedialog.h | 50 + src/qt/test/test_main.cpp | 16 + src/qt/test/uritests.cpp | 71 + src/qt/test/uritests.h | 15 + src/qt/trafficgraphwidget.cpp | 175 + src/qt/trafficgraphwidget.h | 48 + src/qt/transactiondesc.cpp | 309 ++ src/qt/transactiondesc.h | 24 + src/qt/transactiondescdialog.cpp | 43 + src/qt/transactiondescdialog.h | 32 + src/qt/transactionfilterproxy.cpp | 86 + src/qt/transactionfilterproxy.h | 52 + src/qt/transactionrecord.cpp | 241 + src/qt/transactionrecord.h | 129 + src/qt/transactiontablemodel.cpp | 657 +++ src/qt/transactiontablemodel.h | 90 + src/qt/transactionview.cpp | 504 ++ src/qt/transactionview.h | 87 + src/qt/walletmodel.cpp | 533 ++ src/qt/walletmodel.h | 203 + src/rpcblockchain.cpp | 413 ++ src/rpcdump.cpp | 241 + src/rpcmining.cpp | 664 +++ src/rpcnet.cpp | 408 ++ src/rpcrawtransaction.cpp | 667 +++ src/rpcwallet.cpp | 1857 +++++++ src/script.cpp | 1940 +++++++ src/script.h | 622 +++ src/scrypt.h | 12 + src/serialize.h | 1431 ++++++ src/stun.cpp | 552 ++ src/sync.cpp | 128 + src/sync.h | 212 + src/timestamps.h | 7 + src/txdb-bdb.cpp | 436 ++ src/txdb-bdb.h | 45 + src/txdb-leveldb.cpp | 583 +++ src/txdb-leveldb.h | 211 + src/txdb.h | 20 + src/ui_interface.h | 112 + src/uint256.h | 629 +++ src/util.cpp | 1497 ++++++ src/util.h | 588 +++ src/version.cpp | 92 + src/version.h | 56 + src/wallet.cpp | 2740 ++++++++++ src/wallet.h | 1028 ++++ src/walletdb.cpp | 984 ++++ src/walletdb.h | 214 + xp.pro | 499 ++ 550 files changed, 129214 insertions(+) create mode 100644 COPYING create mode 100644 INSTALL create mode 100644 MSVC/NovacoinSolution.sln create mode 100644 MSVC/NovacoinSolution.v11.suo create mode 100644 MSVC/build-helpers/buildboost.bat create mode 100644 MSVC/build-helpers/buildopenssl.bat create mode 100644 MSVC/build-helpers/buildqrcode.bat create mode 100644 MSVC/build-helpers/buildqt32.bat create mode 100644 MSVC/build-helpers/buildqt64.bat create mode 100644 MSVC/build-helpers/libqrcode.vcxproj create mode 100644 MSVC/build-helpers/libqrencode.vcxproj create mode 100644 MSVC/include/inttypes.h create mode 100644 MSVC/include/stdint.h create mode 100644 MSVC/leveldb/leveldb.vcxproj create mode 100644 MSVC/leveldb/leveldb.vcxproj.filters create mode 100644 MSVC/leveldb/leveldb.vcxproj.user create mode 100644 MSVC/leveldb/leveldb.vcxproj.vspscc create mode 100644 MSVC/libcommon/libcommon.vcxproj create mode 100644 MSVC/libcommon/libcommon.vcxproj.filters create mode 100644 MSVC/libcommon/libcommon.vcxproj.user create mode 100644 MSVC/libcommon/libcommon.vcxproj.vspscc create mode 100644 MSVC/mynovacoin/mynovacoin.vcxproj create mode 100644 MSVC/mynovacoin/mynovacoin.vcxproj.filters create mode 100644 MSVC/mynovacoin/mynovacoin.vcxproj.user create mode 100644 MSVC/mynovacoin/mynovacoin.vcxproj.vspscc create mode 100644 MSVC/mynovacoinqt/icon.rc create mode 100644 MSVC/mynovacoinqt/mynovacoinqt.vcxproj create mode 100644 MSVC/mynovacoinqt/mynovacoinqt.vcxproj.filters create mode 100644 MSVC/mynovacoinqt/mynovacoinqt.vcxproj.user create mode 100644 MSVC/mynovacoinqt/mynovacoinqt_plugin_import.cpp create mode 100644 MSVC/mynovacoinqt/mynovacoinqt_resource.rc create mode 100644 MSVC/mynovacoinqt/novacoin.ico create mode 100644 README create mode 100644 README.md create mode 100644 contrib/bitrpc/bitrpc.py create mode 100644 contrib/clang/nomacro.pl create mode 100644 contrib/debian/bin/novacoin-qt create mode 100644 contrib/debian/bin/novacoind create mode 100644 contrib/debian/changelog create mode 100644 contrib/debian/compat create mode 100644 contrib/debian/control create mode 100644 contrib/debian/copyright create mode 100644 contrib/debian/examples/novacoin.conf create mode 100644 contrib/debian/gbp.conf create mode 100644 contrib/debian/manpages/novacoin.conf.5 create mode 100644 contrib/debian/manpages/novacoind.1 create mode 100644 contrib/debian/novacoin-qt.desktop create mode 100644 contrib/debian/novacoin-qt.install create mode 100644 contrib/debian/novacoin-qt.lintian-overrides create mode 100644 contrib/debian/novacoin-qt.protocol create mode 100644 contrib/debian/novacoind.examples create mode 100644 contrib/debian/novacoind.install create mode 100644 contrib/debian/novacoind.lintian-overrides create mode 100644 contrib/debian/novacoind.manpages create mode 100644 contrib/debian/patches/README create mode 100644 contrib/debian/patches/series create mode 100644 contrib/debian/rules create mode 100644 contrib/debian/source/format create mode 100644 contrib/debian/watch create mode 100644 contrib/gentoo/novacoin-0.5.0.ebuild create mode 100644 contrib/initscripts/bsd/novacoin create mode 100644 contrib/initscripts/lsb/novacoind create mode 100644 contrib/initscripts/systemd/novacoind.service create mode 100644 contrib/macdeploy/LICENSE create mode 100644 contrib/macdeploy/background.png create mode 100644 contrib/macdeploy/background.psd create mode 100644 contrib/macdeploy/fancy.plist create mode 100644 contrib/macdeploy/macdeployqtplus create mode 100644 contrib/macdeploy/notes.txt create mode 100644 contrib/qt_translations.py create mode 100644 contrib/seeds/makeseeds.py create mode 100644 contrib/wallettools/walletchangepass.py create mode 100644 contrib/wallettools/walletdefrag.py create mode 100644 contrib/wallettools/walletunlock.py create mode 100644 doc/Doxyfile create mode 100644 doc/README create mode 100644 doc/README_windows.txt create mode 100644 doc/assets-attribution.txt create mode 100644 doc/bitcoin_logo_doxygen.png create mode 100644 doc/build-msw.txt create mode 100644 doc/build-osx.txt create mode 100644 doc/build-unix.txt create mode 100644 doc/building novacoin-qt for android under Windows.txt create mode 100644 doc/building novacoind and novacoinqt under Linux.txt create mode 100644 doc/building novacoind and novacoinqt under Windows with MSVC.txt create mode 100644 doc/building novacoind and novacoinqt under Windows with MinGW.txt create mode 100644 doc/coding.txt create mode 100644 doc/crosscompiling_building Windows binary under Unix.txt create mode 100644 doc/readme-qt.rst create mode 100644 doc/tor.md create mode 100644 share/genbuild.sh create mode 100644 share/pixmaps/addressbook16.bmp create mode 100644 share/pixmaps/addressbook16mask.bmp create mode 100644 share/pixmaps/addressbook20.bmp create mode 100644 share/pixmaps/addressbook20mask.bmp create mode 100644 share/pixmaps/check.ico create mode 100644 share/pixmaps/favicon.ico create mode 100644 share/pixmaps/novacoin.ico create mode 100644 share/pixmaps/novacoin32.xpm create mode 100644 share/pixmaps/novacoin80.xpm create mode 100644 share/pixmaps/nsis-header.bmp create mode 100644 share/pixmaps/nsis-wizard.bmp create mode 100644 share/pixmaps/send16.bmp create mode 100644 share/pixmaps/send16mask.bmp create mode 100644 share/pixmaps/send16masknoshadow.bmp create mode 100644 share/pixmaps/send20.bmp create mode 100644 share/pixmaps/send20mask.bmp create mode 100644 share/qt/extract_strings_qt.py create mode 100644 share/qt/img/reload.xcf create mode 100644 share/qt/make_spinner.py create mode 100644 share/qt/make_windows_icon.sh create mode 100644 share/setup.nsi create mode 100644 share/ui.rc create mode 100644 src/addrman.cpp create mode 100644 src/addrman.h create mode 100644 src/alert.cpp create mode 100644 src/alert.h create mode 100644 src/allocators.h create mode 100644 src/base58.cpp create mode 100644 src/base58.h create mode 100644 src/bignum.h create mode 100644 src/bitcoinrpc.cpp create mode 100644 src/bitcoinrpc.h create mode 100644 src/checkpoints.cpp create mode 100644 src/checkpoints.h create mode 100644 src/checkqueue.h create mode 100644 src/clientversion.h create mode 100644 src/coincontrol.h create mode 100644 src/compat.h create mode 100644 src/crypter.cpp create mode 100644 src/crypter.h create mode 100644 src/crypto/scrypt/asm/asm-wrapper.cpp create mode 100644 src/crypto/scrypt/asm/obj/.gitignore create mode 100644 src/crypto/scrypt/asm/scrypt-arm.S create mode 100644 src/crypto/scrypt/asm/scrypt-x86.S create mode 100644 src/crypto/scrypt/asm/scrypt-x86_64.S create mode 100644 src/crypto/scrypt/generic/obj/.gitignore create mode 100644 src/crypto/scrypt/generic/scrypt-generic.cpp create mode 100644 src/crypto/scrypt/intrin/obj/.gitignore create mode 100644 src/crypto/scrypt/intrin/scrypt-sse2.cpp create mode 100644 src/crypto/sha2/asm/obj/.gitignore create mode 100644 src/crypto/sha2/asm/sha2-arm.S create mode 100644 src/crypto/sha2/asm/sha2-x86.S create mode 100644 src/crypto/sha2/asm/sha2-x86_64.S create mode 100644 src/db.cpp create mode 100644 src/db.h create mode 100644 src/hash.h create mode 100644 src/init.cpp create mode 100644 src/init.h create mode 100644 src/irc.cpp create mode 100644 src/irc.h create mode 100644 src/json/LICENSE.txt create mode 100644 src/json/json_spirit.h create mode 100644 src/json/json_spirit_error_position.h create mode 100644 src/json/json_spirit_reader.cpp create mode 100644 src/json/json_spirit_reader.h create mode 100644 src/json/json_spirit_reader_template.h create mode 100644 src/json/json_spirit_stream_reader.h create mode 100644 src/json/json_spirit_utils.h create mode 100644 src/json/json_spirit_value.cpp create mode 100644 src/json/json_spirit_value.h create mode 100644 src/json/json_spirit_writer.cpp create mode 100644 src/json/json_spirit_writer.h create mode 100644 src/json/json_spirit_writer_template.h create mode 100644 src/kernel.cpp create mode 100644 src/kernel.h create mode 100644 src/kernel_worker.cpp create mode 100644 src/kernel_worker.h create mode 100644 src/kernelrecord.cpp create mode 100644 src/kernelrecord.h create mode 100644 src/key.cpp create mode 100644 src/key.h create mode 100644 src/keystore.cpp create mode 100644 src/keystore.h create mode 100644 src/leveldb/.gitignore create mode 100644 src/leveldb/AUTHORS create mode 100644 src/leveldb/CONTRIBUTING.md create mode 100644 src/leveldb/LICENSE create mode 100644 src/leveldb/Makefile create mode 100644 src/leveldb/NEWS create mode 100644 src/leveldb/README create mode 100644 src/leveldb/README.md create mode 100644 src/leveldb/TODO create mode 100644 src/leveldb/WINDOWS.md create mode 100644 src/leveldb/build_detect_platform create mode 100644 src/leveldb/db/autocompact_test.cc create mode 100644 src/leveldb/db/builder.cc create mode 100644 src/leveldb/db/builder.h create mode 100644 src/leveldb/db/c.cc create mode 100644 src/leveldb/db/c_test.c create mode 100644 src/leveldb/db/corruption_test.cc create mode 100644 src/leveldb/db/db_bench.cc create mode 100644 src/leveldb/db/db_impl.cc create mode 100644 src/leveldb/db/db_impl.h create mode 100644 src/leveldb/db/db_iter.cc create mode 100644 src/leveldb/db/db_iter.h create mode 100644 src/leveldb/db/db_test.cc create mode 100644 src/leveldb/db/dbformat.cc create mode 100644 src/leveldb/db/dbformat.h create mode 100644 src/leveldb/db/dbformat_test.cc create mode 100644 src/leveldb/db/dumpfile.cc create mode 100644 src/leveldb/db/filename.cc create mode 100644 src/leveldb/db/filename.h create mode 100644 src/leveldb/db/filename_test.cc create mode 100644 src/leveldb/db/leveldb_main.cc create mode 100644 src/leveldb/db/log_format.h create mode 100644 src/leveldb/db/log_reader.cc create mode 100644 src/leveldb/db/log_reader.h create mode 100644 src/leveldb/db/log_test.cc create mode 100644 src/leveldb/db/log_writer.cc create mode 100644 src/leveldb/db/log_writer.h create mode 100644 src/leveldb/db/memtable.cc create mode 100644 src/leveldb/db/memtable.h create mode 100644 src/leveldb/db/repair.cc create mode 100644 src/leveldb/db/skiplist.h create mode 100644 src/leveldb/db/skiplist_test.cc create mode 100644 src/leveldb/db/snapshot.h create mode 100644 src/leveldb/db/table_cache.cc create mode 100644 src/leveldb/db/table_cache.h create mode 100644 src/leveldb/db/version_edit.cc create mode 100644 src/leveldb/db/version_edit.h create mode 100644 src/leveldb/db/version_edit_test.cc create mode 100644 src/leveldb/db/version_set.cc create mode 100644 src/leveldb/db/version_set.h create mode 100644 src/leveldb/db/version_set_test.cc create mode 100644 src/leveldb/db/write_batch.cc create mode 100644 src/leveldb/db/write_batch_internal.h create mode 100644 src/leveldb/db/write_batch_test.cc create mode 100644 src/leveldb/doc/bench/db_bench_sqlite3.cc create mode 100644 src/leveldb/doc/bench/db_bench_tree_db.cc create mode 100644 src/leveldb/doc/benchmark.html create mode 100644 src/leveldb/doc/doc.css create mode 100644 src/leveldb/doc/impl.html create mode 100644 src/leveldb/doc/index.html create mode 100644 src/leveldb/doc/log_format.txt create mode 100644 src/leveldb/doc/table_format.txt create mode 100644 src/leveldb/helpers/memenv/memenv.cc create mode 100644 src/leveldb/helpers/memenv/memenv.h create mode 100644 src/leveldb/helpers/memenv/memenv_test.cc create mode 100644 src/leveldb/include/leveldb/c.h create mode 100644 src/leveldb/include/leveldb/cache.h create mode 100644 src/leveldb/include/leveldb/comparator.h create mode 100644 src/leveldb/include/leveldb/db.h create mode 100644 src/leveldb/include/leveldb/dumpfile.h create mode 100644 src/leveldb/include/leveldb/env.h create mode 100644 src/leveldb/include/leveldb/filter_policy.h create mode 100644 src/leveldb/include/leveldb/iterator.h create mode 100644 src/leveldb/include/leveldb/options.h create mode 100644 src/leveldb/include/leveldb/slice.h create mode 100644 src/leveldb/include/leveldb/status.h create mode 100644 src/leveldb/include/leveldb/table.h create mode 100644 src/leveldb/include/leveldb/table_builder.h create mode 100644 src/leveldb/include/leveldb/write_batch.h create mode 100644 src/leveldb/issues/issue178_test.cc create mode 100644 src/leveldb/issues/issue200_test.cc create mode 100644 src/leveldb/port/README create mode 100644 src/leveldb/port/atomic_pointer.h create mode 100644 src/leveldb/port/port.h create mode 100644 src/leveldb/port/port_example.h create mode 100644 src/leveldb/port/port_posix.cc create mode 100644 src/leveldb/port/port_posix.h create mode 100644 src/leveldb/port/port_win.cc create mode 100644 src/leveldb/port/port_win.h create mode 100644 src/leveldb/port/thread_annotations.h create mode 100644 src/leveldb/port/win/stdint.h create mode 100644 src/leveldb/table/block.cc create mode 100644 src/leveldb/table/block.h create mode 100644 src/leveldb/table/block_builder.cc create mode 100644 src/leveldb/table/block_builder.h create mode 100644 src/leveldb/table/filter_block.cc create mode 100644 src/leveldb/table/filter_block.h create mode 100644 src/leveldb/table/filter_block_test.cc create mode 100644 src/leveldb/table/format.cc create mode 100644 src/leveldb/table/format.h create mode 100644 src/leveldb/table/iterator.cc create mode 100644 src/leveldb/table/iterator_wrapper.h create mode 100644 src/leveldb/table/merger.cc create mode 100644 src/leveldb/table/merger.h create mode 100644 src/leveldb/table/table.cc create mode 100644 src/leveldb/table/table_builder.cc create mode 100644 src/leveldb/table/table_test.cc create mode 100644 src/leveldb/table/two_level_iterator.cc create mode 100644 src/leveldb/table/two_level_iterator.h create mode 100644 src/leveldb/util/arena.cc create mode 100644 src/leveldb/util/arena.h create mode 100644 src/leveldb/util/arena_test.cc create mode 100644 src/leveldb/util/bloom.cc create mode 100644 src/leveldb/util/bloom_test.cc create mode 100644 src/leveldb/util/cache.cc create mode 100644 src/leveldb/util/cache_test.cc create mode 100644 src/leveldb/util/coding.cc create mode 100644 src/leveldb/util/coding.h create mode 100644 src/leveldb/util/coding_test.cc create mode 100644 src/leveldb/util/comparator.cc create mode 100644 src/leveldb/util/crc32c.cc create mode 100644 src/leveldb/util/crc32c.h create mode 100644 src/leveldb/util/crc32c_test.cc create mode 100644 src/leveldb/util/env.cc create mode 100644 src/leveldb/util/env_posix.cc create mode 100644 src/leveldb/util/env_test.cc create mode 100644 src/leveldb/util/env_win.cc create mode 100644 src/leveldb/util/filter_policy.cc create mode 100644 src/leveldb/util/hash.cc create mode 100644 src/leveldb/util/hash.h create mode 100644 src/leveldb/util/hash_test.cc create mode 100644 src/leveldb/util/histogram.cc create mode 100644 src/leveldb/util/histogram.h create mode 100644 src/leveldb/util/logging.cc create mode 100644 src/leveldb/util/logging.h create mode 100644 src/leveldb/util/mutexlock.h create mode 100644 src/leveldb/util/options.cc create mode 100644 src/leveldb/util/posix_logger.h create mode 100644 src/leveldb/util/random.h create mode 100644 src/leveldb/util/status.cc create mode 100644 src/leveldb/util/testharness.cc create mode 100644 src/leveldb/util/testharness.h create mode 100644 src/leveldb/util/testutil.cc create mode 100644 src/leveldb/util/testutil.h create mode 100644 src/main.cpp create mode 100644 src/main.h create mode 100644 src/makefile.bsd create mode 100644 src/makefile.linux-mingw create mode 100644 src/makefile.mingw create mode 100644 src/makefile.osx create mode 100644 src/makefile.unix create mode 100644 src/miner.cpp create mode 100644 src/miner.h create mode 100644 src/ministun.h create mode 100644 src/mruset.h create mode 100644 src/net.cpp create mode 100644 src/net.h create mode 100644 src/netbase.cpp create mode 100644 src/netbase.h create mode 100644 src/noui.cpp create mode 100644 src/ntp.cpp create mode 100644 src/ntp.h create mode 100644 src/obj/.gitignore create mode 100644 src/protocol.cpp create mode 100644 src/protocol.h create mode 100644 src/qt/aboutdialog.cpp create mode 100644 src/qt/aboutdialog.h create mode 100644 src/qt/addressbookpage.cpp create mode 100644 src/qt/addressbookpage.h create mode 100644 src/qt/addresstablemodel.cpp create mode 100644 src/qt/addresstablemodel.h create mode 100644 src/qt/askpassphrasedialog.cpp create mode 100644 src/qt/askpassphrasedialog.h create mode 100644 src/qt/bitcoin.cpp create mode 100644 src/qt/bitcoin.qrc create mode 100644 src/qt/bitcoinaddressvalidator.cpp create mode 100644 src/qt/bitcoinaddressvalidator.h create mode 100644 src/qt/bitcoinamountfield.cpp create mode 100644 src/qt/bitcoinamountfield.h create mode 100644 src/qt/bitcoingui.cpp create mode 100644 src/qt/bitcoingui.h create mode 100644 src/qt/bitcoinstrings.cpp create mode 100644 src/qt/bitcoinunits.cpp create mode 100644 src/qt/bitcoinunits.h create mode 100644 src/qt/clientmodel.cpp create mode 100644 src/qt/clientmodel.h create mode 100644 src/qt/coincontroldialog.cpp create mode 100644 src/qt/coincontroldialog.h create mode 100644 src/qt/coincontroltreewidget.cpp create mode 100644 src/qt/coincontroltreewidget.h create mode 100644 src/qt/csvmodelwriter.cpp create mode 100644 src/qt/csvmodelwriter.h create mode 100644 src/qt/dialogwindowflags.h create mode 100644 src/qt/editaddressdialog.cpp create mode 100644 src/qt/editaddressdialog.h create mode 100644 src/qt/forms/aboutdialog.ui create mode 100644 src/qt/forms/addressbookpage.ui create mode 100644 src/qt/forms/askpassphrasedialog.ui create mode 100644 src/qt/forms/coincontroldialog.ui create mode 100644 src/qt/forms/editaddressdialog.ui create mode 100644 src/qt/forms/intro.ui create mode 100644 src/qt/forms/multisigaddressentry.ui create mode 100644 src/qt/forms/multisigdialog.ui create mode 100644 src/qt/forms/multisiginputentry.ui create mode 100644 src/qt/forms/optionsdialog.ui create mode 100644 src/qt/forms/overviewpage.ui create mode 100644 src/qt/forms/qrcodedialog.ui create mode 100644 src/qt/forms/rpcconsole.ui create mode 100644 src/qt/forms/secondauthdialog.ui create mode 100644 src/qt/forms/sendcoinsdialog.ui create mode 100644 src/qt/forms/sendcoinsentry.ui create mode 100644 src/qt/forms/signverifymessagedialog.ui create mode 100644 src/qt/forms/transactiondescdialog.ui create mode 100644 src/qt/guiconstants.h create mode 100644 src/qt/guiutil.cpp create mode 100644 src/qt/guiutil.h create mode 100644 src/qt/intro.cpp create mode 100644 src/qt/intro.h create mode 100644 src/qt/locale/bitcoin_en.qm create mode 100644 src/qt/locale/bitcoin_en.ts create mode 100644 src/qt/locale/bitcoin_ru.qm create mode 100644 src/qt/locale/bitcoin_ru.ts create mode 100644 src/qt/locale/bitcoin_uk.qm create mode 100644 src/qt/locale/bitcoin_uk.ts create mode 100644 src/qt/locale/translations.pro create mode 100644 src/qt/macdockiconhandler.h create mode 100644 src/qt/macdockiconhandler.mm create mode 100644 src/qt/macnotificationhandler.h create mode 100644 src/qt/macnotificationhandler.mm create mode 100644 src/qt/mintingfilterproxy.cpp create mode 100644 src/qt/mintingfilterproxy.h create mode 100644 src/qt/mintingtablemodel.cpp create mode 100644 src/qt/mintingtablemodel.h create mode 100644 src/qt/mintingview.cpp create mode 100644 src/qt/mintingview.h create mode 100644 src/qt/monitoreddatamapper.cpp create mode 100644 src/qt/monitoreddatamapper.h create mode 100644 src/qt/multisigaddressentry.cpp create mode 100644 src/qt/multisigaddressentry.h create mode 100644 src/qt/multisigdialog.cpp create mode 100644 src/qt/multisigdialog.h create mode 100644 src/qt/multisiginputentry.cpp create mode 100644 src/qt/multisiginputentry.h create mode 100644 src/qt/notificator.cpp create mode 100644 src/qt/notificator.h create mode 100644 src/qt/optionsdialog.cpp create mode 100644 src/qt/optionsdialog.h create mode 100644 src/qt/optionsmodel.cpp create mode 100644 src/qt/optionsmodel.h create mode 100644 src/qt/overviewpage.cpp create mode 100644 src/qt/overviewpage.h create mode 100644 src/qt/qrcodedialog.cpp create mode 100644 src/qt/qrcodedialog.h create mode 100644 src/qt/qtipcserver.cpp create mode 100644 src/qt/qtipcserver.h create mode 100644 src/qt/qvalidatedlineedit.cpp create mode 100644 src/qt/qvalidatedlineedit.h create mode 100644 src/qt/qvaluecombobox.cpp create mode 100644 src/qt/qvaluecombobox.h create mode 100644 src/qt/res/bitcoin-qt.rc create mode 100644 src/qt/res/icons/add.png create mode 100644 src/qt/res/icons/address-book.png create mode 100644 src/qt/res/icons/bitcoin.icns create mode 100644 src/qt/res/icons/bitcoin.ico create mode 100644 src/qt/res/icons/bitcoin.png create mode 100644 src/qt/res/icons/bitcoin_testnet.png 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 create mode 100644 src/qt/res/icons/configure.png 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 create mode 100644 src/qt/res/icons/debugwindow.png create mode 100644 src/qt/res/icons/dump.png create mode 100644 src/qt/res/icons/edit.png create mode 100644 src/qt/res/icons/editcopy.png create mode 100644 src/qt/res/icons/editpaste.png create mode 100644 src/qt/res/icons/export.png create mode 100644 src/qt/res/icons/filesave.png create mode 100644 src/qt/res/icons/history.png create mode 100644 src/qt/res/icons/import.png create mode 100644 src/qt/res/icons/key.png create mode 100644 src/qt/res/icons/lock_closed.png create mode 100644 src/qt/res/icons/lock_open.png create mode 100644 src/qt/res/icons/mining_active.png create mode 100644 src/qt/res/icons/mining_inactive.png create mode 100644 src/qt/res/icons/notsynced.png create mode 100644 src/qt/res/icons/overview.png create mode 100644 src/qt/res/icons/qrcode.png create mode 100644 src/qt/res/icons/quit.png create mode 100644 src/qt/res/icons/receive.png create mode 100644 src/qt/res/icons/remove.png create mode 100644 src/qt/res/icons/send.png create mode 100644 src/qt/res/icons/synced.png create mode 100644 src/qt/res/icons/transaction0.png create mode 100644 src/qt/res/icons/transaction2.png create mode 100644 src/qt/res/icons/tx_inout.png create mode 100644 src/qt/res/icons/tx_input.png create mode 100644 src/qt/res/icons/tx_mined.png create mode 100644 src/qt/res/icons/tx_output.png create mode 100644 src/qt/res/images/about.png create mode 100644 src/qt/res/images/splash2.jpg create mode 100644 src/qt/res/movies/update_spinner.mng create mode 100644 src/qt/res/src/bitcoin.svg create mode 100644 src/qt/res/src/clock1.svg create mode 100644 src/qt/res/src/clock2.svg create mode 100644 src/qt/res/src/clock3.svg create mode 100644 src/qt/res/src/clock4.svg create mode 100644 src/qt/res/src/clock5.svg create mode 100644 src/qt/res/src/clock_green.svg create mode 100644 src/qt/res/src/inout.svg create mode 100644 src/qt/res/src/questionmark.svg create mode 100644 src/qt/rpcconsole.cpp create mode 100644 src/qt/rpcconsole.h create mode 100644 src/qt/secondauthdialog.cpp create mode 100644 src/qt/secondauthdialog.h create mode 100644 src/qt/sendcoinsdialog.cpp create mode 100644 src/qt/sendcoinsdialog.h create mode 100644 src/qt/sendcoinsentry.cpp create mode 100644 src/qt/sendcoinsentry.h create mode 100644 src/qt/signverifymessagedialog.cpp create mode 100644 src/qt/signverifymessagedialog.h create mode 100644 src/qt/test/test_main.cpp create mode 100644 src/qt/test/uritests.cpp create mode 100644 src/qt/test/uritests.h create mode 100644 src/qt/trafficgraphwidget.cpp create mode 100644 src/qt/trafficgraphwidget.h create mode 100644 src/qt/transactiondesc.cpp create mode 100644 src/qt/transactiondesc.h create mode 100644 src/qt/transactiondescdialog.cpp create mode 100644 src/qt/transactiondescdialog.h create mode 100644 src/qt/transactionfilterproxy.cpp create mode 100644 src/qt/transactionfilterproxy.h create mode 100644 src/qt/transactionrecord.cpp create mode 100644 src/qt/transactionrecord.h create mode 100644 src/qt/transactiontablemodel.cpp create mode 100644 src/qt/transactiontablemodel.h create mode 100644 src/qt/transactionview.cpp create mode 100644 src/qt/transactionview.h create mode 100644 src/qt/walletmodel.cpp create mode 100644 src/qt/walletmodel.h create mode 100644 src/rpcblockchain.cpp create mode 100644 src/rpcdump.cpp create mode 100644 src/rpcmining.cpp create mode 100644 src/rpcnet.cpp create mode 100644 src/rpcrawtransaction.cpp create mode 100644 src/rpcwallet.cpp create mode 100644 src/script.cpp create mode 100644 src/script.h create mode 100644 src/scrypt.h create mode 100644 src/serialize.h create mode 100644 src/stun.cpp create mode 100644 src/sync.cpp create mode 100644 src/sync.h create mode 100644 src/timestamps.h create mode 100644 src/txdb-bdb.cpp create mode 100644 src/txdb-bdb.h create mode 100644 src/txdb-leveldb.cpp create mode 100644 src/txdb-leveldb.h create mode 100644 src/txdb.h create mode 100644 src/ui_interface.h create mode 100644 src/uint256.h create mode 100644 src/util.cpp create mode 100644 src/util.h create mode 100644 src/version.cpp create mode 100644 src/version.h create mode 100644 src/wallet.cpp create mode 100644 src/wallet.h create mode 100644 src/walletdb.cpp create mode 100644 src/walletdb.h create mode 100644 xp.pro diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..d9cad955 --- /dev/null +++ b/COPYING @@ -0,0 +1,23 @@ +Copyright © 2009-2015 The Bitcoin developers +Copyright © 2011-2012 The PPCoin Developers +Copyright © 2014 The Peerunity Developers +Copyright © 2014 The EmerCoin Developers +Copyright © 2012-2015 The XP 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. diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..8857a93c --- /dev/null +++ b/INSTALL @@ -0,0 +1,9 @@ +Building XP + +See doc/readme-qt.rst for instructions on building XP Qt, +the intended-for-end-users, nice-graphical-interface, reference +implementation of XP. + +See doc/build-*.txt for instructions on building XPd, +the intended-for-services, no-graphical-interface, reference +implementation of XP. diff --git a/MSVC/NovacoinSolution.sln b/MSVC/NovacoinSolution.sln new file mode 100644 index 00000000..d22bc60d --- /dev/null +++ b/MSVC/NovacoinSolution.sln @@ -0,0 +1,62 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Express 2012 for Windows Desktop +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "XPD", "myXP\myXP.vcxproj", "{00225DF0-9DDB-41A2-972B-56DC24E2C995}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "leveldb", "leveldb\leveldb.vcxproj", "{002CC16A-B4EF-4737-B751-DAD3A8D14133}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "XPQT", "myXPqt\myXPqt.vcxproj", "{9191918D-7DE3-4BE1-8A32-2F2CF4EE6840}" + ProjectSection(ProjectDependencies) = postProject + {3703B138-B8DA-460E-9DD1-41BDC7588E80} = {3703B138-B8DA-460E-9DD1-41BDC7588E80} + {002CC16A-B4EF-4737-B751-DAD3A8D14133} = {002CC16A-B4EF-4737-B751-DAD3A8D14133} + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FF475423-FFFE-FFD1-FF41-FF790D534E02}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcommon", "libcommon\libcommon.vcxproj", "{3703B138-B8DA-460E-9DD1-41BDC7588E80}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {00225DF0-9DDB-41A2-972B-56DC24E2C995}.Debug|Win32.ActiveCfg = Debug|Win32 + {00225DF0-9DDB-41A2-972B-56DC24E2C995}.Debug|Win32.Build.0 = Debug|Win32 + {00225DF0-9DDB-41A2-972B-56DC24E2C995}.Debug|x64.ActiveCfg = Debug|x64 + {00225DF0-9DDB-41A2-972B-56DC24E2C995}.Debug|x64.Build.0 = Debug|x64 + {00225DF0-9DDB-41A2-972B-56DC24E2C995}.Release|Win32.ActiveCfg = Release|Win32 + {00225DF0-9DDB-41A2-972B-56DC24E2C995}.Release|Win32.Build.0 = Release|Win32 + {00225DF0-9DDB-41A2-972B-56DC24E2C995}.Release|x64.ActiveCfg = Release|x64 + {00225DF0-9DDB-41A2-972B-56DC24E2C995}.Release|x64.Build.0 = Release|x64 + {002CC16A-B4EF-4737-B751-DAD3A8D14133}.Debug|Win32.ActiveCfg = Debug|Win32 + {002CC16A-B4EF-4737-B751-DAD3A8D14133}.Debug|Win32.Build.0 = Debug|Win32 + {002CC16A-B4EF-4737-B751-DAD3A8D14133}.Debug|x64.ActiveCfg = Debug|x64 + {002CC16A-B4EF-4737-B751-DAD3A8D14133}.Debug|x64.Build.0 = Debug|x64 + {002CC16A-B4EF-4737-B751-DAD3A8D14133}.Release|Win32.ActiveCfg = Release|Win32 + {002CC16A-B4EF-4737-B751-DAD3A8D14133}.Release|Win32.Build.0 = Release|Win32 + {002CC16A-B4EF-4737-B751-DAD3A8D14133}.Release|x64.ActiveCfg = Release|x64 + {002CC16A-B4EF-4737-B751-DAD3A8D14133}.Release|x64.Build.0 = Release|x64 + {9191918D-7DE3-4BE1-8A32-2F2CF4EE6840}.Debug|Win32.ActiveCfg = Debug|Win32 + {9191918D-7DE3-4BE1-8A32-2F2CF4EE6840}.Debug|Win32.Build.0 = Debug|Win32 + {9191918D-7DE3-4BE1-8A32-2F2CF4EE6840}.Debug|x64.ActiveCfg = Debug|x64 + {9191918D-7DE3-4BE1-8A32-2F2CF4EE6840}.Release|Win32.ActiveCfg = Release|Win32 + {9191918D-7DE3-4BE1-8A32-2F2CF4EE6840}.Release|Win32.Build.0 = Release|Win32 + {9191918D-7DE3-4BE1-8A32-2F2CF4EE6840}.Release|x64.ActiveCfg = Release|x64 + {3703B138-B8DA-460E-9DD1-41BDC7588E80}.Debug|Win32.ActiveCfg = Debug|Win32 + {3703B138-B8DA-460E-9DD1-41BDC7588E80}.Debug|Win32.Build.0 = Debug|Win32 + {3703B138-B8DA-460E-9DD1-41BDC7588E80}.Debug|x64.ActiveCfg = Debug|x64 + {3703B138-B8DA-460E-9DD1-41BDC7588E80}.Debug|x64.Build.0 = Debug|x64 + {3703B138-B8DA-460E-9DD1-41BDC7588E80}.Release|Win32.ActiveCfg = Release|Win32 + {3703B138-B8DA-460E-9DD1-41BDC7588E80}.Release|Win32.Build.0 = Release|Win32 + {3703B138-B8DA-460E-9DD1-41BDC7588E80}.Release|x64.ActiveCfg = Release|x64 + {3703B138-B8DA-460E-9DD1-41BDC7588E80}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(DPCodeReviewSolutionGUID) = preSolution + DPCodeReviewSolutionGUID = {00000000-0000-0000-0000-000000000000} + EndGlobalSection +EndGlobal diff --git a/MSVC/NovacoinSolution.v11.suo b/MSVC/NovacoinSolution.v11.suo new file mode 100644 index 0000000000000000000000000000000000000000..5579796c3a0376e87bcbd3930ba3cc8bf342f2ca GIT binary patch literal 135188 zcmeHQ2YeJo+uzV_sEA@WDhdb*B%uTZ1QI$3LIlBJNG_KGmt0IPK%^sf#e!W_P_Qd@ z6!p{B-n(K$vA^nT@2K$o|2s3cdw1JzL-c+1=6=u3-0t2yQ=fU-%)a=-uA5ffw)gv5 zTo|Y|*S2ikPHSbl-W=&>>h~_1c0AH80b90i-O9_nUj_IbfJ>?HKhOdbv@kvqEnOR` zRU@v|8qj;*!yXs`s+#@X{&srTx3B;Hg|rLt+(Va(hsOhBSev1Z1okqdhH7)%a*4UW z2S%jA&p76$&5t?^VhmqB6lld-ois|6yuC&gH2xFJ5Ne8G!u&odNp;cs(8Q$ueDocrf4~`MU?=Y`|i`9)P8Q0|5LV z!%U=e0Q~?H0c`=h1C9gmem>$#z;1xU0mXoW0RsVt0FD403K#;|0k8$YQQHAf2$%ra z8^FBAARY_Y6R;N`7jPJW?KKEM`ZXTV7qBaUV|W;Veag02j(8}5{o5UI4B&G>FTiL( zC&00QFdz?*1z_(~044!40o8!{fDwQ?z`1|}0cC*g0p9^w*3S_41~gC8v?=mhH^g7c zbSdH=Wcp~toGAhMoByAM^ihBaU;v;9&=XJ(F!%8^{O0ovf9m`nchNhX{|kXXHUH5F z2!Hkb7o%Y7*7hiid5m!E&&4&)e?DWL|NG!tI)L+^^H$COU69@vU^o9e;@(aGtNBkH zod10QcJrUlaQ-tk&;QN1W;Op?&S6ZZG3KWV|Qhy<~bH#QVzh{)i8d z=>riT1UMMb5pW3LP(UX@XFwOgVSuiHZh+x{Oh9)4+awEdwoLa#+zZfK{?0|*SEdg~ z++U^#B0fT<2g^7QalT9slko_|BLSlTqXA<8V*y73#sQ`RScVCRC(88Eh$qSPF^H$g z^s$Hw0n-4-0g3?A0mXn4Kmbq*2m;Cg<$wx6C13_%CLjbLJ_R-SeSF-tdied@IorYMS@S*4e}>fj7jr5#|0#w2Gv@yyjQiC5=j?Z;zm&zZ zbdE$kYxR97=r4Jc9DrB)a}J&*zr)}}{{?<>g!X~>lcoRXAv^E*DH+-a;7^+Vo{#Lv zuQIsRKUZ5}|X25XPdLO8EL#LxAd ze7#rv1$b7iKk@j7>0Cf5)?w;HYW1?aW1)sp|P$KL>+pai7|+2ZC6 zKmAYsO=TL7e=OR*O6LF?w8^q9!rE+GKA@W%|GOhYul1))#6Dnf8-H1l=6Z@pEx$Ya z82^Q6d+JLVyx`~9whO?Yb`ox$3GiC~Il%vaGD9!;DU;a+;wRoS@PJqRbAkUJnV}c_ zP83k#CvFD2a+x+c&3@7T1T@!u2psRLketA8)Y(WlS9H0r#lJMdcn-bvwi z8hrvk@lH+x|Ic`~62RbIf2YyskN&&>yk6TsSJ&p~MYa4=^p|bGVEIto#W(T$8hOpF z{<$93zZ&EvuzSP*4xaZq|8m{Of79Br zzy74we`+DffBZAn{~#?qT%&pYOY`^+#u2I_c!APMh=gL=F=*w^W<~ zEp0wH#T;0M7NrGX$0>x3r6Ep>REMiWkT*3MVV#rVSgXNIDb)F@Fj5iND6#;(@F@bN zG2bb~-3B}##2s3_iuJts?@FC3WNB;gnRfWX7hZp8)2nyiK7YgWGxqIaRpOzjUoL9c z19q59lwI+>^R)Atu>3VBHET@W8hPqEe5l(g)LTSYTghLtMwPGzsTyUhnG(r3yZ41F z-#+urV6zi9S=DF=>XQ$v+mU$=&3`1&%|gSe4k$zc$WpUu=U|YRXc=zww@(w$pJJ@P zK~@UE9lsOzDb`=ga$L{6(%;|ktXKK3kH__|ED8KU)c-_0&){BvSNd{Lf{Gz73UK`^?e`3kML49VTlN*p#L??$q zlp&y#@=w{PZ_Y+dt=@^xb5Z+FJJM%F3rEU#BbC%HZ`y@ z_$#zy+{RzFJN-}NlA8-)W5t%v`q#y{>{b5H_P~FuPV$QXQsBQx7R|l>uIo?CV6m=o z?9~JWw#;-QNN+%L z?z2#;Qjn%vcu)ng5|75W(i7X2f$Q(CxOo7;Yx|q^pJpBBSgb77k_V^-c^r@ORO1sw zsfM9c^phhOZ4SMGpStAcN#L)B!g@!U$t2`g0BS@&oZ8+wpzLe`wgq{1w{Q^f?;&w} zt$!8pw~!eZ08b5SN}Tlc_NXITc@2TeE3?e9MR zTGa%+F$&q_oay+ZvrdFf>O|KGr`=${w-P7(m| z-wgb>$}&U?=8*Ut_`UKUtbdL}UVhqt=OpUi*AcsrzfVB<76QD^|GvO~r>v7(`}cL{ z|Dt{G6@P!=SF4CSes}suyo-{ke+c-!@?U*D*x%_-NPfy|{b~EuWh?y0t^Qf)e*XZ`x0>d58!qwz-#}HL;bzucN+cv>~G(r ztX{`oZ+H1CtqA#*BvJpasK58|m+d$LIQ}S4UzG&@`+@&%JkQ`h|Fa!iK+S*3v|juF zLExth#^43N(+Kc0{~rPV>jZjF_}%E=<;ZIlz-#><2mX5`e!~_b{C|c(4&jcZnUG3^ z)rLN(W+_enQARy|_Zs9cv(i(LO?1h}uQ&gmRO>{P@38_4^pwfPr|4 zr4}+5^_uiQ3gLHMv}D33QtiH?Y-aY##r`Y3-P!gGB3I|{k+{wI%8-v5)6@&*E9gzV!Wn7 zJRkp^j{0-Y%U%Gl{XZGcvd}w>BMi+-_vn< z8Nh4(PetF~F0=B2-;MuKW6vx8Gk|}UXZ+N^*az1CEZjaD;1&Ne;OFxU?)A@dm4B11 z|GB{be*(QLep=0)0{O36JTMF3wf>*tS+DX(uH)+OkN^4{_-6wc+}c0aQTh?`FJ(E^ z2YRM1U*F=sSN%_}JO2wxrD^8?yteNww`|*F#f#i|A^m3{?g^Yl}Xh9 zP2l(1{*=Gm=KqzzePa^%UjTl}fDCTqkNDm6Ppgs78i3dSf60U&j0pWelr1#piN#&6 zxnkf$7o4>7%!hYe+?7RCLtDl0I>`W4=#6#(_ay9(f{iN&8$~63dUUC-L%K6qEKZ{? zFqfLy4Bi{9vp00=$we9H05lQ{e*la}y>5yq)LElKDh4CM>@wZ7I6B3}xs3Mq*^M3z>%dE8OUhv-VxAzr#_zQL@um zQ@4=v|M@TS|0%)H;6_u5y@ePU)u0PSptYQ#TonS^g5?*bXI_2ol7_wV3p?NatH~^3iC~YkL;_BsAgQXQ;{4(JAwDvslr5{(o=4y$xf&{>L0N&_CG~&vjDMot$%TBc9aeP z<1lPppc!*=&D}K%oseS-XYFg+SOz*lNIyk-*&LyD!s}&yM zyz+|Q-2PkHz*M@`>5JFy=e%@I+jDZ?$Xn6u{pYrfeuc=HtvT>oH1rqae>e&s!APyw zXJa*TwDvpZhW>y~SGY``e8}PzQ%<~a)KLpczFoU=jov0^Oh!72FFxVNe_X89MJOW2 zo)}?dh{%kvSf~8^%Df#5j=L^6R@AFXV-Z@2o6kqw|Mo33hP`Q7-x z9Ps;2f%+dc7Z^J|tM08s;sW`@9lyK&X(00A+UynodOS;=9fLdm-r_-`JEoKjQgH0E1iodqL`R z3atMmJdh0jfoaL$chY|O(?1RZey{qU-j3Q|p?^FY^`9-P~g9^rU2Wp%JwCaETcF)aiZ@vG!v+9aBJTi(p_=Hi0Kbg;;qHWks0+{MlelP8BCB2H4 z-n?+?y3YO1BClf3?kI>i#%fKK|iKg0irLxl@Gmv|+Co>BE|`*Tm9 z-R#x1Syx|l%J+w`sK$&FDOvm`-9JxOv0P_KgmS40G=jcLq#azT4Sj)7kgD>3TmD(| z&3AscW>UAqp4@BY!#b-GU;ZAjpk-@$jh3IC=pU^uWb&vEBa205gr^vY9skvcSr4Vy zWtufI{-f%%cHDgfKKt?Rg8d&qY4A~FJ8ZV@kGZhZXGMXh16W(ka$&!vO*sPqO}DWR zwb^cO-TXuAoF`7n9yGLM+=i>I(Ws7>@d$tPOYDFxL35BtV?}$uvEuZRc~>3s%65ND zc)xt7_bss}?*H;>Kd`3XRalNV)RZ%x;=$Lh`uBh~k39YIx}Togy!y4RZ<@+B%BlW# z{8xPK#qQ^a&pzzsjy=DAs&x0Gx?cGEjg;zbrycLR*vS`=tkK>gh0@18CkFy-&D zC<*N$oMWO?&fkCFwpaPL7v`x`ApY@qU;@A^{tT1+>sEi-xSay=6Ynue;O`FnUiBZ& zyMO%YAF_b|3{@#1`H?V{=z=YeV4)i>{Va-*uGa|`iI-4O?zZyWHxIqNaMefS-+k<} zd+u#uK5PJUP-ue$}m~2&m}0ub79=0_ndGn5&QK? zWzFs-q`_j+*J*tZyl?M)-x+p)phK^sFAILz$Re7n#k4GTG&(uWX#yrPkjBXYRq(In z%3wB>9FO|215~5a@r7Mcq(8?l;J#E^iE7Yc^w?G;YR#P|9dzD#fj?F(-~3Ux>-*{D zvnxCO^-hvCa2x;Jf99ltrT^nvG+8t+^>@;M`la$ z?r{>)D#PGb|6E7!L!tkigM5;~&jZVq{?iSAUk~dKX(0FWc9@aaFMJXVN*{qyfU_rVb#AMxdyufARR)^E=ckHV{BOWeHZQ~oEL zP9t3GHW@{Xw_AVC%>zbl&TrYU>pN++AN)>4sw67rH3Xmdt@WS6vL4X7Et%6X^3j*q zUpxKhHG3Sk=VL^q@T!>C7?NB6=?_#<*7l;=|M_C_t!q{nj!#?bn7hCc_iffc`P83V z*WXw;1-HV|PWp7&s4qWS-*xG-wk>XbaLYoLMYVy78{z-qsDDjd{?+`6FTWbkLZl`0 zLIIcKDWFY!{k+|#2L|Ql&%bZqes$liv#NorNdkWo*Yx^NbNjo|ANpTAao}8k&PT2* z04#(RzW8gmfd1SgFM7f6L;?M*e|+~uGWgy6uP;GeE(3V2|DC}9WD@mvvcQx7h<3im z_P+=C-;wx_#y}&xPu(ju5rPj_Zvv>3PuWNCe@Ys^dHjvQP^S@!j)xObl4|HA=#?V` zj;6;y1T`lar~g+Nd^gYj93$Jq$?r|I2>73*>%SfTyc7C>A-25>P>On8$2Afq;rsAv zAptn~z;XZIZpddBfY7_RBGutZiY8*sf8w~O z3qBZg`^OjEHN0>ANz0)}vY@KGXcbCPcV++ktK~nz_y|K`;e(s6e(~9Be|Yx0DVu9r zANvi;_&A1#^8@u!r->FOo+JJ9KAYM}jh%~l_sQe%9YtF#Z%}_kusi z9z8Y}B~O1}1Ag!Nj~rM12Q}r_0oddYMUCEi2l&sHzr56cTkAjG1Ae9dbHm>o%Yai* z@_#WN@>>6V;IHynf9^7K3dBEE;`WMvU*JF9BYy55a|&Glr{IBU0I&E*06)hIgM0fs zjeep3Al~W7%PaoTz_0Z0{EsPsup7Lem-D;-8vg-utmUBj0W3j+Eus2?i>2WO9hld% z{eEMLdaddH*nY>)dCq#VENaUAV>RI3tk3^WEWbE`kSiD$`pxHW|6zxVE~%Qhf2;c+ zy8O+1IZ{l$q$hQTH89H$|6$_lL{`k-Ur_$jf7SY*Xd`ZSZ^5n`zusq3=V!`uE`55- zCe|yV?V^=EHA(w}*%QLs2G-aOJjLi;o~c1wFok{is6&Fg^M*ak zB>pGA%lz>liT+oEb#{mcq1EepFR?3CS=4_0`T4h=esklaE5BZO-;2vWI?=L(UfZ8% zWh(jKasPz9{U%-m)B4P2r7Z?6{&aKMCr?-0_0?mR<&#aO`%2s`e>mcg)gK!Aw|M`t z3T@DhW3JifkFp~kIO2k>tpnfR?uh?R++`c+f&Kq|m*aN(V9|qLR%IW&@%5XQ-p3mW z{oepSCDuqh2D{@&3HgK0EEIQQ#m9K}7NZ8y_YWacS|BO-WWV{~O&f+?)AN{u+cRE& z=B4*6OQDz9^fk%f_%rawTH@4r`|znq=%EDqSn-!kI_Y_Z- z6klz>E&+>9WdnB<(H3H;7_Pss*OO;%|F6#%w!gkp*-s~(ZdF_t|Nj|HSAI@cb7|L? z@3Hyo_xCt()y6TMURa%_YZdh~FPcFgM4J$yr77J8=^X&u13r@1X}4-C)4L+xQ>MQ} z+#awyU=KhCfXZ`kr1z1(_mlAfh|^{IAQ^X*@u4#AEaSsu+zoLqAXEPCA>(YsIWpZ# z#(iYWGW0|GaQSWDhc&LnrBOVJF2^b}>jgc|)JPPUY^7jP969EN)qXClu z#{eQY(BLV=Qvt^U_`hi~4j`T`(}q)=@aDd6A{ms>4k_F$@CH#FO~7hh)(;uSJ|1>!4ZdKKcUWO_B?H8OoI;_Cp{%ilL5z6o%%{Jj?OtulQ(;yYye zF2r{O*2&-ZB3=);PyT)Y@&C#6Lx}$ccv${^6!Bv+{RHACW%_Bv&j6kUJO_9l@B!dO z0NeOw8NVvy*Ac%BctievOU8V5Bhv56-FVi0)PRDrRGoK*+sl5J~jK4tqADRA2 z#@`_RR;Is4{DVyYg!pHf{#C}m$@mY(G7Z(WtNu9){O`8*KWq*B=aN<;xXB;H@8W-% z_5$*G5rC%iB!5gsy%qoE1-}yqPW#tO61P|U?NI+~@E-y_yaOC(wiknGhQm=lVyA=V)`lb9_@vU={L=o_DjZ`$BdWD z_yQSUB;!kDO#i4=fXn6YD`k9@jG4!3ndaQ!Z`#Nh-+*{L%FHoyv%Izz@vSmVY3B}^ zz6&wuHve}IVwz5vrc86cO!MqpUV8}fzhs(Y=uw$|95MTp&pah#dX@4w+nI55%puYo zraAu@b56Y`(;E=KA=7Umep{wDB7Rq3<{sOs2n(@t26dlId?` zOg5SLe~|G{GX6!zn`QjFjJL>GLml`(LUUYe0cZ(m1!xUu1K_>w5$^!l5wMfIwlm^g z0J{R(0d@nl2kZ_|dG3kyUVyy;`vCR@><8E%Z~!13a3J6yz`=lyfI|R>0y+UY1G)eX z19Sy+17rX)0o?&T0Gu1y09r|V0(t?sUU1FI1@r|_H+49mKVSf0Am9kVAi!Y25I`Ov zA21X!3@{v^>M_cc9xK!15RV5O1(*Ps2vE;YGNq>=Jr!^)pb#((a2%irFda|~C;F9L3&b-J&jj#(72;}{u0edfOxGcf$n-44vt_yg@d+|LPsR&mya=%p zLFZJj|7`cN58iq0o7a|Hb>jRNaVxr}0IeQ)Ek5{?Kb5zuLcX-s@l_99`q-DvTK)9o z+TGJ`Boc*G#qo&c?+O0o|5#N~?rG1M`|;brZI~-T7nYo*3e9{s^6& z@ILE#W^K1GHe5HiVd~<7FK+2f3hlKeT*j~6{NV;b>KnOnoysFoj*q9#IP|NZKDfI5 zg5R<)+ViJ7t;(V7-%$;!uhL(JAf7dU#z9qDRq5q`Y z0?Qux)6So!E{Izu>Fk`XPqY7wyR}ajl!0e!Q#r@iUy?F&_W{dC7n@96snl}M-}KQA zkZ)V#~j8?KvlfvIjj&##Epy7FLUssX7aR4LB0FWV8Y_5N9o%z&2L`pktZTK#(YqWT|xySC@3>sea0s;Ss_4Qd-(eld3r zc(C)j?OqAr_2n1SzWna`0rP4=6aB)S(>5(RN|99*1u!rgzX)|KR-g2Vwsue?D4Pl~r~N zmv{V>Fzf^UPbpJSp5SD*f6};f8K}+#1(B=scZX)i{tqwsDd*V*;(rh~9|EupcF&ow?+yGX z$_rlbJM{qZuSZ@_0lebh5BP6L0zc(iyFmQZTO`x|JA(IA__xverzAS=U*rkT+&!dr zqn`hB-PV`4TRLQGyPtArE&E6%)eu z@3a2mX78V7E+q{NXF($}7@Ye+#HLa^%D?vON0k55m#@KxU#mU#-5y%^#i6$KZQnV? zjN63Esxf@-Um<>Scmdt|uFiiXS8v{QCis`iam^!{pXTlL$@k;W{^W_{b;_wqrDFLu z4!#wHCzfzaSr+suJ&+o#-{U)Dc;?bzKpwO*J)mXb$EW;mZ@;h59{cgI)3oY4-hAcx z%6D)0jLHt9$D-v`Z7Jo)rej~|(ZX~4&HOi6mNA#+p1jYHsgKS&czy9HzqLEkvXs_l zZS%15IsT&Ou*>%sxFcfu3$ampA`Yt0L#oQt@gPHYu9@I8>G?qZiU+}-e13~>s}J1gx_#G;+H`$E=SNr%H4s(o_x%5wfW!Vn^FR^( zr`X4+{NmGpOB_@~CQ@v?;1*tPcHlnISou+K#@E$OZTHTb&93NrTAT1ER}9xV3RQI# z6{`8OCJ}elz=a^rmOLUagMv=`K$Kz7v--80vRda2{7=`b+KlxAK zhd?yl-~SWE59N@=@nD>vR-t{}@3y0tUHqR;&%O8EiUrF~_{6dm#YuSV>-h6`-T43L zV6pQbtk4$rNgK9c-GK-08@{vo^M^cAA5((y{^~FEr$QJN#a4r~@bfeFTyuO*?;V5v zn}2x1p_^i`$-cui#sB;4KkN2qNha#ULiqiNx!3Sm+sh~AR!)B8vXzTg{ZUFwfkA$B z{^R2%=YFzq{#mWxJacsI>5&&+j(I|rxtLcI!XSU`r*z*HTIj{{(pBGXSv>ptK2L02 z_3o*}5FO$&!F}gJj4Jcy8LpwrzC0gXjB5FHAHO>Bur((wFR6TQ!t=dWx3tO$!zEV! zZ*2P?7cdc{c>hOx`V*zUpq9q@)$!+-RA~3y{Md{;U)=ij&7XYnz#$J0p@k};b@tEy zVB^QLGTMGVz5T5#_<&oW9Z5aIrWJe7%xdl;6iew=xE9X6x@zp~&OfcY>z-F9Zo2!| zF#jFjN#eK4*D3xHH!I}>*QGf%V5|a|du5W@K<`EWZC8JB5`?)8UR`*`xgDMve)7Nf zxU}ShpK5<*1rpmpR?9IO1aYEVKGE3oi=ECNRYDm_aszl5!|jtxbrISU9ci5U#47Vq z9t0~W$F#FP?@=A@cyLwq+8@!MaOri z1=ylikNa!*) zVmJIwx)P&_6}n-Yfnd z(0^SmYvfjc;_q!+d-{kZk3R?apG*ROA7}h&599vx0I&7$1N=HikN3i>RMtI6+#K0n%gN}6} zzI84Xu`m4GJ}ovDkp7YvCO4SB=E*4;mpnUp{g%SajUCUV$59mj%0=SHg}IYk4WF!+=|?9Lyo(E46{+46I*db;YKobL}^UNd?JA~E*> z)3`K~*Ad*upVRF3XMZvGzdL>>?Ypo)u}Ym$7g;sEUQSrYJgid>z)N&8j*`_vyQ`Av-bx_PHA`=v$ItC^c8q@DT9Cm#_# z8^at-MWPQ}eVVx7sQG|0yHB3=+KTmu z&KO#fbGc=yqE*rNrv;FOTPSV%@>ghO&-Fd8>arFWZ^<~fxM2SSHdvJ%*I0VD^76h} z{&%B4S-~8>} ztVL6L&&%)n+1xwk0((OJwtkF@)RD9N|3*wMn&~fO{3YVAWcnM#jk4dM{LH%<#sgzQ zOH(?v|MTy^{}ba@`G1;ictR=6)6LauCaBx{?3cZ?AMV|{>r3k|sL6dzqao9${#@wq zVmXdPoMPoZ>WV?*RxdsLzz^Se=+*o4$8m-j^TtSF@G|hOHZ;($aC~jJytcS1eOP5E zSeM>;PHyimg<~rNwc)yOStNZ@WnF!7D1GA6wB!gtvIcOH2)@6=&b?Mt@!PF+58OVy z&w#?$?w`N9%e}gCKvc*0#w$;2GslOD8$y+Jk^JIFadT9(t0Vtx&%Z|Xr|#(6<(eIX#){PvIQcUFSNkQOs|!SAMjG1|x*|8q6)>-?Wb{BHUeb{F+|Uh97i@IQ^` z8NAfLuXxn*Tdn`94J;puyLbceQ(wh!Rl9vpZ$0XS4Yyt$sNIbhR7NW1HHYBy{5diH zl&=x}&%O8(kOp1x#mRS9eLgYkgnl<19_sS7F2<>{su=A5L^M1ihpWL=9xZS|F#UD%Kx@bvW<%aYwAbqV0!A6<_t_L1#aICJU&_u41bz78N)3qnn{L>>+ZW^WYhTD}yW^5)t?C!8 zC=+;qItqrS^#7>S)eziPM_&)}L-B8+ZScT2D?Ww=qaeMRiGiLU?=YtDtp59F#ZLn7Qvm8}o&j)Se-7{pz^V+Cp~$DcVY>e&(r*FY2D}5<2zVFp9$*uI zx_qm=+^!vo|BVBD3;15-KoVQ+M7wHgghiCsdf*YOLNrewCgXOw{Lax znjhW{cHZ<3$6P|Y^P1ZABL21RmhQi_`+UX8&)kxE%`P2A{>ScLysn2K{^)m<{-dP* z^FFy`Soy;J;;2}f1myrLfnZM((dQ2FFtnudmpx# zdSAz&@Pyg(e~Hh>-YHV zl3!*Y`|{lFtZ)Zw>ng)hQsRgHL4O|dH~$3v!2h{s5zv*X^WV@0Ljl78!vU&o$Oo$Q zSefS9J05TpU;X z{L#lv{)-`0D7tINXeQ2reEx&LebPgpcD}REFQ2ttk@H082}6{oUxlA`f2iw@R}9ZuI|d)cyaGsDB&aKMBt>cxnG_P5*ZQ{>1{liNx}7 zz%mm(@pk^jyyW;#zwT2)(p3sEMXyJExuZ}0MO6NQcLw)gtDLzVnCIf~*ivE%Xz6!P zFTeYu8((cV_x1X|r%a2m-OK}y>E!hnQTo5l%TKaT16;8z|Ca9ymv8>zKkYhoIQF~s z-wzGNmA|q6V;C^XI;wglDgXc4!tQ9J;U4)CczDUprK*E6$(%~ni* zYxvMjLtdJ{qQ`Cb%%t>|P(sV|zXv(BU#$Ax(4L#MSM$DlxaitTKK`Ta#${b9r$0g6 zcp_#){$m>*hewqB=U9I`{A#4L9Ax%zQtDUp1`fZn&8(v)o_P3?m+i9NTnf9>%eF4U zQ|{|ePscZ4bN#;q`P>EIkU-=bHm4=C|Dvbc`zMm#|NJ}fC$s+caHtXMe<$wW1Mph^ zt-!xn=IP%4ytySR;fma|Sh^O7m8 zuC2he%K=x)duuG7;j`D7uHPWjHzK|Xa5LZ*z*+z)_iX@L^*9FE4;+W&$HY zHYgc^ygGTc2LTTO{snj#@Ce{hz+-^N0nYmUypl!1Af0Bcg-pv zUi$#geF&gs^<%&%fKLJc1}Hnv7fAmHFd0DmPJCXo<+Yzp_X%YVB61C3JaLhH%B}sKczse;@If?YA2LhvEsZ?LP<4dc~h>i&@+NejP7yGq^mi z-=^+Gt-o&dchdbZGuFQ&;tYUS{KQ=eU~t2qrJrmdp0)bs`O{pFG67!kv+YllnYrV4 z;=aWB3uazLX6qQypJk?J{LU2E&-<6p1n!_`{LUkg?Vp9bI8VH`|1vzwG01St3)_o? z&+m>nO_l1BnbOyECKT%FI4_l}Mixee^X_epY=~}mEJHk3|5r9N*?IVHZ=4mJf!uq- zCN~VJo~UCEfY7%6-ne2IJOV_9HgPCfAC z5d9yydalfaID6vB9>AIb$s=FSm)ElJY(7dx+`0G+1@!QMi7k8ySHgFEbH&09Mi>vN+>WeZeU|lj$)_ml_^O$=g@1eM#fl?2~72?cB zdB`u101o3sRquxw8e*_etcX)kj`5kB(4E*HX(nuTQ+?*{zTrT~E2Ql(kuO|1Aedcw=d?4Q(*m1!~=_yz|XU9s${0_?eEM1srs|-3{GWL_l^U8 z${-AM7b^s9Fl#20^;6|>JRDKgfcPB-G!u){eYp`*9fZ!aD2@M6fkg|ZcvIc^xMx2< z8dp-c9WAp^gAkNzoW<1O8YP>7()p#6oU`V`f`kNrjidvA{mJEdsI{HtSZHmBInu1y zJ~7m!)O7PD>TFN8q~KC!Ip$^WGA1ajdCPDzTW*Es=)5Fx`!!d^(q-D>X;$J9#>!RJ zSQDtlIxfj_IhjGs$Dpcp`9k0*v&7*>e|meMe}eLeZy<6F!$5zoluxb%&=YA z9ks<$oChei9Bb~l;?1(g;n-^43tB?Mtnh0;(kCWs2Owdqa;A3nEu}VBag0>O{KW@k5^T;7tzcxec^(pW< z?!_t}OKmcx1zqeq+E%p1aQR4lWtDjUb#0^SpeR-{hnoo7)(oF`t*NJwLF>?83H=-!-VBqpgc@-5Ofjn4l zzr0yunWyU70OJieg1S>5MgI_cna6&`iSKcP(^QU3eBCQOPw24A<1oe6EUwen0xEot zG%-t);c}_-Jjbz?u~I~`GH4CjkP;@(AmGbv*`gKt=m;VeLH&8Z5Lb$IJ)mg&aVQma zZT4*+TPj1g;Q@VXk6MWghd51ZgSO+V3hiloV%w>5C$8Ppk#X#Z zF+=Tfs0qhRiH^%W5-8s|uT=K6QLQe@zAJKarm(T&-B|My?LCxDPEN9oy^K$TU*U@; zy_}#aP7fH9WLZ>y&(_yYj#gtks?ez{_>(A;^$X6#e#B)>IMV1QEncO;?Odclq&y+; zHbuG*H01{MT0}*aC~pq(+0xYLh;c}dzL3vc#40EDI;rxJ`*S*eS)38>Jo4@5kl=<4 zZlDuNuU4(5ST*=MO1`Pi{8l!xMPIB8^}yehtCsy5#qAO07!{&~#{1KZRg0FKrnKTD zuBfE{9LeN|c@~+Fy*aCEW7bo8eJp9h*<@#5Vr!YLN#jutzZ{k@R#6h4L;Az4T<$yN#`e`5+<>R;rIuLk zJXvWpeuZ+M|EcpIdZJJ40{8zsgPUytUhOacP3J#w4WWgC8UbpLIZF+m&dhxlVMOz_ z(&l&KigU&qw~jd-VkPW^mp0*iNQTd4Tam{!%jNVtaF!`z=c_?w4XIpFSNd@K)qq%O zYw5YL!~+jtrcPd@^W^roiv3ys+9(2zS?mgwhdTMy!rE(f5?`7wPUrD!`w<+P(5wlM zpIF_i>hT*;o2HbM5-kE8f7C+@+gxGF{~I=y9Pn85D*q??f0Hi}K6;$hh78|;k?Cwf zwWDi>mXMs$&H!hvoLxS#Qk=mdEkcgfNm4bX=d-eoxWD#D>@ro>OYeN|wzckY-j-r@ zu8eBXrlBS7X~MOUuQ8!Doavz2UZp4K=cF85E=&4H z-N#E%-qijtJAIMW%B1Yx{`Y^S_Ww}(A=;4^&(!`OQj%JoVsmVy_W#g_gkA^@`if@O z<*FHJ=(Y`C1+!FP&}QBzCud%}{@VRN?sQq{U^4WzoU11NNNL4aT8tqWC4OyYiQGjV z+9=Vpm$Kw&z*5Im-kxtr#N(HXVM!JM{I1+yOCr1^N`c9*7gIcsAncKWC|9sM@^qI7!u$pT{Q4|JU(E-4Ewr2lrQ*qZ&YnwllC!x#j%h?~6JPu%&VTnyO~mYsQed6 zJz-hr$R>wwXjkW;WZTOB$NA_^O=}BWUOYjpD4Ou7r{+;;_>7svJ+~2`w8MHuF>_4h zvZ(14{(Qu3U|t5y&bk(Rs>)m`>(hsTbY?riw⪻>UoEu)iivHgrt8G@Tf8E#aqzO zC@8w4)|4K|X*x!ly?=mx?XCHNc>g4}%P>Gw&KxgqrsRv2$Y~bodJN@zy0D2$W^WG` z8b8|3C?j(B4R=5qbNVQh%&)yLzGo8Wv>2~xz;UMReXn}Yu_!0kSnA}(3Ez#|Wh8X( zLJvwGJ!52xJ8UPeyNSLpM`334!ga+j#qtolASb^RCBFR(iOZ}FOWX%1wB0R$*)NZ6 zMa$HS&^eRN2PhwMFEiiJQ;JobwAbk!y~-Ds{^Rt@SmdCm)cgs6L0YZkR=ex)Nb$D-=65Ar)X$yK1S z)-NHq2>S*#SYqW9@3!)5KOnUt?`-gP%}h0G3The8vB%0yTpf*Gy9#AdwqEP9B#vz_ z-cts@Mf+}j7rd4SaZe@Yt0ZvR5~ z`^R1pJl$Wr|DSV@7XSSL&T@R>Xjgok(741)Dw)Vp?TELYKaHcb@t(}2BxYNVS^B2@ zsHpKglBTV`iG;5D?*_zW0LnPj5#r2BA!&|1r*_*YL_qV@3o*bBY}n52<|Ksz6}2M z$69Hf44&J{%E3Jk%7dyYMuU*s#L6V-Tk8A=^8KmvAN-AQwN@IuNjJ2n-<49#m(=+W zsq-IF=Rc?&Cb6$sOP&8<=zFNMqHJW=;iS%gu#=LMRJGm28@KV(6Y>V;1c-sYh8N>Wcx zFI;*u@B|9M>nWL(78vvHn$-CZY%L-2CY~cox|=%x!Cv1Ys2_O*?nDsQHoxaTSXuqe zt6{8`{zUZ5>Lhh1sr~%VpXH`kBU{$Gx0?q^lHcfPYk>{+9QF17zRwf|T6@ul|v zs{H~@C0(ZW|EBi;I=zj?+S5C=|JSg-nmrzsb-y{*Fu(h9Q~SSScd-ktuzy?Nf7kvm zzutSXzPqXYKVi+Rd$rfyr1t+Puc6fbAAg<+@m_a{dj*V>CP=GN`+p3bo7tO|ZIjyn zLvBIsU#E|l@JUSV|4FoaQBy+iIo?U_|4Hrt;h8|(KjPcdo)=E-|M=_oe<%$>%KyJgOUJ%v+COOvNY^^!?BQIz@vaM~{NLCApJ(0C zU!@N|+3;tfPYX}ep99deI{-7b=`MfzY|&RCg1uI1hqqz1sXs9BtWlE(VuO+c=GX@kLia}4aw;MZozdp1yuDFpQB>K$X6GFMeKD>*ciCCzUua zDM?Mzb9D0Uu;c#iWcLBc(XaQOXa&2y(=6|4fO)cwn|hbCI6sW%Q1U#SY$=K5qXY|* z>}9`WPUsyIPxn}hGC1p8?0U`qjNHk{(^=)a+zGwF3;4t>pDw5ZQ0iFTW>4ZzzM%zHfNp%lGwFB;k^O=X&)dnC{c!&KqfgK7-tC94Dh8>1*m{ddnu^jA7YE^uRzje=?%K zoKxoiFs%o^2ay(Zp1;T)Y&?arM!uPH2A&w8aR-j39j?!zxjL$B-Ihvuwg+Z+U)XW- z@wS)@JduTyAG7sq^vU2g+FWVp$;REG_|C)geQ|ZZwkvWS3v7Pzd!?|(=~OBDZ}Gds~Q6IZW3UlN0KR(Z;dJE5k4v zMT-28NRcZNsXiFTJur5A;d$z>df=a4c_o3B99R$hdkFH(!MG%c#?eV$ttWW49y)%Z zLnBt=9)kOM_+KwPnTz|mc*+abwCG&emRK7z7n-sh-uqw7g>6|DzjL9f%HX{%#Hgf} zmLtE9KB_tHhUjU|iY$EcG2${XMoFnTQmHx^450S5hnzQg7|&itCFO?g!LJY2<>gA< zb0!Z`CoTE|v^Kg8!Fr4x4sA{uBvb3Re$uOeUi!LLT-Sqf@10jakg=$X{a!tPD*iEe zOVN?qXq1Ze{)f0n}NM&_-U4C(-xF!D4vH!@rVU?kv8vlAW@@pm@999_& zl^)a||71l5m!Sqj>MKK~17{W$4Ji&pAJL6rTM<-7J96X)$5sYv!*vdglGqa6((~#= zk^0);fa+jmox#!74m4!2aR>Rkihb;i{_Q>e7PXoXD8Ux`>V{CI1=PfNigrGjlk} zau?f@<9E|aq~t$qy?sjlGiFCh{%efXY0aB^(a_I@gs|ayAh!r1b6s1_QZBSrSsvx-CY!J;Cq4f9uFJ5GjO`NgX{HQzy3 ze87D?O)r72)!2@=cI6i}iaTw;1N3gC(05VCwK^?XjoUR(EeQ#{3Pjx5u{!~Ff<*g$6-dzuM{K^}_QbMmXFr;nfY_~m{6 zc(Kn5KlMhNYt%5ALsS*9NVdPV4$obK+-OSB*L}E~G(i~R4I$`GsD|T!>Iya7;82J< zwmNlhVlWg8M1l#bwx(9)#7t$!@o!QEElG0x3wfaw+T+>Sqmm9iy?TROt{m$l;W-+B z?zkRvEQk^BeuiDo)^}DN`qh3T_jznd%ZKLWmskxLWNK`WGHsL{;&1$_t-IX%v(G(O zyfAH_!{1##zWl9&j+uU&6?$Xr@~$bY$h>2G$GzvT8$S7GUNxQ*DQDq|ba zE;BHr>I#n;UszWgC_Fw=SR1S>tPF&!>k5lYOEYV#X$Fg8V_NIu{?|}AFtaES3PY=E znZ?k6O`oXJrwfZgx-c1}(Z zw=*yY6*p>Q!RD6z%Lc5JY!;&L?kynt@*^J<<-o>=gBE^f*TA}+=vS_$&O7gKVc zF8lKQqqhsZvZeiVr@p(>zsEkv8pO{xUW*IdkKd6mC_xzIG+PoE8?{K3>wDpNU`qCp`v%LhZD33lkiV8+#Mq=n1Gt=@>bxYFikxUfV))qG~BkQWk70&CH|L9o363k;lYGT-e z6o(HM)p>9Gwtio6io;Ki=*DjYO>y{9V{Ob-PI36)6ztb*gU3yA_%&LJ!?)yG8qJFT zmvZ=!mMNBpbr&-puuN4Kmp$^-JOD$y=xNO7^WXY?O3kzF?tNx%@9y8UArlhM!(x(o z)y*h&xmt^gYhx!u9x8+%RZe!{1n5PI>w=Kt@-u^T66GRi0Yn$}j;{@bOM~%UDT^GV zB2ecmi1QK5C+SjoAWj1b=nPUoD@$&HQq`n*MMpXF0oKNAI1>*W8+E{yvwzv`?iUX2 z)%JjCznt=iWos8lJ<=#qOxC+_CeHbwPR0EGDb`QvFjMjz4zCb0srf}d_G`o6MxNsk z(lQeS7=6}-DoY?uRfVey>l&&9nc)&1L=h`v zpp76T+$ihPM^{3JlM5ZmNU0#HucE+L6^PV^GHZFBO>FUOB{M_&qpNVBI;xJR3Cyg~ znwC&y6^hK9VnnmU+?p+rHD^x3zdR_UK;x>R=;~RN)mzmkC9(hWB=*$m{ZCrGV@S&p zZlooaBF`>{e|UtnJ&_uZu%X6LjlGNZx;RuGhLWzLDuI#>Z)DB^YWYIJL=rhHxs2x} z$G62R7GiBPf}5ksN*ratS38nXEW&T{(&|tYU8e>f`pBO{pVXwUZPTHu;N_RvjZ(lKo=37XGT>7&-oPRk3~?Sp*SUmC#~>3S9W2bzw(RMBUNZ`Y?=7US5uBV zdg8FIdw%f5fR#iOrN&BMV27L66OFmBQUz<1Fq;Y!lx{JzH$K^j646z@&{CA}U>2Vl zY)~RZ3=tY;$6&y=9>uCldyzS%B^e=_=1WVgIgEY@c}eTMta{zd8Z=r>MJ=>G_D#?= zAs1bIt!hAt#*932G%`gMZqk}zfDGAs45nXrn) zjbcsnpgV=Ov8=Kj8@4Scstspi=!PxV=8S~0Mi+BF zQ+(LpgAb!amt0=By41iS(aN36gB1}jpMjZKmZLs#FeW&hmIh1e%V&#|A*>uv8@nuF zR@Vg+&CdFeb zHQ7Ib$4+rtF_iUxp3_RL%u>hwH(Z&iyA8^{l74)XjQ{X0H$#zIslMt{8_e_1=#<8Q zW45fAsea$&ZKvOldXz=^dwtY7u(pTovd{kcA8h=1Rz};;r?V7@>kC({rV>)w4 literal 0 HcmV?d00001 diff --git a/MSVC/build-helpers/buildboost.bat b/MSVC/build-helpers/buildboost.bat new file mode 100644 index 00000000..64eebdb6 --- /dev/null +++ b/MSVC/build-helpers/buildboost.bat @@ -0,0 +1,19 @@ +@ECHO ON +cd C:\MyProjects\Deps\boost_1_57_0 +if %errorlevel% NEQ 0 goto ERRORCLEANUP +md stage\lib\x64 +call "C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat" x86_amd64 +call bootstrap.bat +bjam --toolset=msvc-11.0 address-model=64 -a link=static runtime-link=static cxxflags="-Zc:wchar_t- +move /Y stage\lib\* stage\lib\x64\ +call "C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\bin\vcvars32.bat" +call bootstrap.bat +bjam --toolset=msvc-11.0 -a link=static runtime-link=static cxxflags="-Zc:wchar_t- +echo All finished! +pause +goto EOF +:ERRORCLEANUP +echo Something went wrong, please check the directories in this batch file! +pause +:EOF + diff --git a/MSVC/build-helpers/buildopenssl.bat b/MSVC/build-helpers/buildopenssl.bat new file mode 100644 index 00000000..a0c826c2 --- /dev/null +++ b/MSVC/build-helpers/buildopenssl.bat @@ -0,0 +1,47 @@ +@ECHO ON +SET OLDPATH=%PATH% +cd C:\MyProjects\Deps\openssl-1.0.2 +if %errorlevel% NEQ 0 goto ERRORCLEANUP +REM first change the debug compiler options +perl -pi.bak -e "s#/Zi#/Z7#g;" util/pl/VC-32.pl +perl -pi.bak -e "s#-Zi#-Z7#g;" configure +REM Now do 64 bit by setting environment to MSVC 64 bit +call "C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat" x86_amd64 +REM compile debug mode first +call perl Configure no-asm debug-VC-WIN64A +call ms\do_win64a +nmake -f ms\nt.mak clean +nmake -f ms\nt.mak +REM Now do release mode +call perl Configure no-asm VC-WIN64A +call ms\do_win64a +nmake -f ms\nt.mak clean +nmake -f ms\nt.mak +REM clean up and move files +set PATH=%OLDPATH% +md out64 +md out64.dbg +move /Y out32\* out64\ +move /Y out32.dbg\* out64.dbg\ +REM +REM Now do 32 bit by setting environment to MSVC 32 bit +call "C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\bin\vcvars32.bat" +REM debug 32 first +call perl Configure debug-VC-WIN32 +call ms\do_nasm +nmake -f ms\nt.mak clean +nmake -f ms\nt.mak +REM now do release mode +call perl Configure VC-WIN32 +call ms\do_nasm +nmake -f ms\nt.mak clean +nmake -f ms\nt.mak +REM put back the path +set PATH=%OLDPATH% +echo All finished! +pause +goto EOF +:ERRORCLEANUP +echo Something went wrong, please check the directories in this batch file! +pause +:EOF diff --git a/MSVC/build-helpers/buildqrcode.bat b/MSVC/build-helpers/buildqrcode.bat new file mode 100644 index 00000000..a92eb566 --- /dev/null +++ b/MSVC/build-helpers/buildqrcode.bat @@ -0,0 +1,25 @@ +@ECHO ON +cd C:\MyProjects\Deps\qrencode-win32\vc8 +if %errorlevel% NEQ 0 goto ERRORCLEANUP +copy C:\MyProjects\Deps\build-helpers\libqrcode.vcxproj . /Y +if %errorlevel% NEQ 0 goto ERRORCLEANUP +set VisualStudioVersion=11.0 +call "C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\bin\vcvars32.bat" +msbuild.exe libqrcode.vcxproj /t:clean /p:Configuration="Release" /p:Platform="Win32" +msbuild.exe libqrcode.vcxproj /t:clean /p:Configuration="Debug" /p:Platform="Win32" +msbuild.exe libqrcode.vcxproj /p:Configuration="Release" /p:Platform="Win32" +msbuild.exe libqrcode.vcxproj /p:Configuration="Debug" /p:Platform="Win32" +REM +REM now clean and build 64 bit +call "C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat" x86_amd64 +msbuild.exe libqrcode.vcxproj /t:clean /p:Configuration="Release" /p:Platform="x64" +msbuild.exe libqrcode.vcxproj /t:clean /p:Configuration="Debug" /p:Platform="x64" +msbuild.exe libqrcode.vcxproj /p:Configuration="Release" /p:Platform="x64" +msbuild.exe libqrcode.vcxproj /p:Configuration="Debug" /p:Platform="x64" +echo All finished! +pause +goto EOF +:ERRORCLEANUP +echo Something went wrong, please check the directories in this batch file! +pause +:EOF \ No newline at end of file diff --git a/MSVC/build-helpers/buildqt32.bat b/MSVC/build-helpers/buildqt32.bat new file mode 100644 index 00000000..32ae0906 --- /dev/null +++ b/MSVC/build-helpers/buildqt32.bat @@ -0,0 +1,21 @@ +@ECHO ON +cd C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2 +if %errorlevel% NEQ 0 goto ERRORCLEANUP +SET OLDPATH=%PATH% +set VisualStudioVersion=11.0 +REM first change the debug compiler options +perl -pi.bak -e "s#QMAKE_CFLAGS_RELEASE = -O2 -MD#QMAKE_CFLAGS_RELEASE = -O2 -MT#g;" qtbase\mkspecs\win32-msvc2012\qmake.conf +perl -pi.bak -e "s#QMAKE_CFLAGS_DEBUG = -Zi -MDd#QMAKE_CFLAGS_DEBUG = -Z7 -MTd#g;" qtbase\mkspecs\win32-msvc2012\qmake.conf +REM Now do 32 bit by setting environment to MSVC 32 bit +call "C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\bin\vcvars32.bat" +call configure -debug-and-release -openssl-linked -opensource -confirm-license -platform win32-msvc2012 -nomake examples -nomake tests -static -I \MyProjects\Deps\openssl-1.0.2\inc32 -L \MyProjects\Deps\openssl-1.0.2\out32.dbg -L \MyProjects\Deps\openssl-1.0.2\out32 -l gdi32 -no-opengl -qt-zlib -qt-libpng -qt-libjpeg +nmake +REM put back the path +set PATH=%OLDPATH% +echo All finished! +pause +goto EOF +:ERRORCLEANUP +echo Something went wrong, please check the directories in this batch file! +pause +:EOF \ No newline at end of file diff --git a/MSVC/build-helpers/buildqt64.bat b/MSVC/build-helpers/buildqt64.bat new file mode 100644 index 00000000..2cf14a93 --- /dev/null +++ b/MSVC/build-helpers/buildqt64.bat @@ -0,0 +1,21 @@ +@ECHO ON +cd C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64 +if %errorlevel% NEQ 0 goto ERRORCLEANUP +SET OLDPATH=%PATH% +set VisualStudioVersion=11.0 +REM first change the debug compiler options +perl -pi.bak -e "s#QMAKE_CFLAGS_RELEASE = -O2 -MD#QMAKE_CFLAGS_RELEASE = -O2 -MT#g;" qtbase\mkspecs\win32-msvc2012\qmake.conf +perl -pi.bak -e "s#QMAKE_CFLAGS_DEBUG = -Zi -MDd#QMAKE_CFLAGS_DEBUG = -Z7 -MTd#g;" qtbase\mkspecs\win32-msvc2012\qmake.conf +REM Now do 64 bit by setting environment to MSVC 64 bit +call "C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat" x86_amd64 +call configure -debug-and-release -openssl-linked -opensource -confirm-license -platform win32-msvc2012 -nomake examples -nomake tests -static -I \MyProjects\Deps\openssl-1.0.2\inc32 -L \MyProjects\Deps\openssl-1.0.2\out64.dbg -L \MyProjects\Deps\openssl-1.0.2\out64 -l gdi32 -no-opengl -qt-zlib -qt-libpng -qt-libjpeg +nmake +REM put back the path +set PATH=%OLDPATH% +echo All finished! +pause +goto EOF +:ERRORCLEANUP +echo Something went wrong, please check the directories in this batch file! +pause +:EOF \ No newline at end of file diff --git a/MSVC/build-helpers/libqrcode.vcxproj b/MSVC/build-helpers/libqrcode.vcxproj new file mode 100644 index 00000000..14a1c1ee --- /dev/null +++ b/MSVC/build-helpers/libqrcode.vcxproj @@ -0,0 +1,190 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + {04A35EED-5E35-4F7D-B92D-BE81559EAF0C} + SAK + SAK + SAK + SAK + Win32Proj + libqrcode + lib-qrcode + + + + StaticLibrary + true + v110 + Unicode + + + StaticLibrary + true + v110 + Unicode + + + StaticLibrary + false + v110 + true + Unicode + + + StaticLibrary + false + v110 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + Level1 + Disabled + _USRDLL;QRCODELIB_EXPORTS;HAVE_CONFIG_H;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + ..\vc8\qrcode;%(AdditionalIncludeDirectories) + OldStyle + true + false + MultiThreadedDebug + false + + + Windows + true + + + + + + + Level1 + Disabled + _USRDLL;QRCODELIB_EXPORTS;HAVE_CONFIG_H;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + ..\vc8\qrcode;%(AdditionalIncludeDirectories) + OldStyle + true + false + MultiThreadedDebug + false + + + Windows + true + + + + + Level1 + + + MaxSpeed + true + true + _USRDLL;QRCODELIB_EXPORTS;HAVE_CONFIG_H;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + MultiThreaded + false + ..\vc8\qrcode;%(AdditionalIncludeDirectories) + false + + + Windows + true + true + true + + + %(AdditionalDependencies) + %(AdditionalLibraryDirectories) + + + + + Level1 + + + MaxSpeed + true + true + _USRDLL;QRCODELIB_EXPORTS;HAVE_CONFIG_H;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + MultiThreaded + false + ..\vc8\qrcode;%(AdditionalIncludeDirectories) + false + + + Windows + true + true + true + + + %(AdditionalDependencies) + %(AdditionalLibraryDirectories) + + + + + + \ No newline at end of file diff --git a/MSVC/build-helpers/libqrencode.vcxproj b/MSVC/build-helpers/libqrencode.vcxproj new file mode 100644 index 00000000..14a1c1ee --- /dev/null +++ b/MSVC/build-helpers/libqrencode.vcxproj @@ -0,0 +1,190 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + {04A35EED-5E35-4F7D-B92D-BE81559EAF0C} + SAK + SAK + SAK + SAK + Win32Proj + libqrcode + lib-qrcode + + + + StaticLibrary + true + v110 + Unicode + + + StaticLibrary + true + v110 + Unicode + + + StaticLibrary + false + v110 + true + Unicode + + + StaticLibrary + false + v110 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + Level1 + Disabled + _USRDLL;QRCODELIB_EXPORTS;HAVE_CONFIG_H;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + ..\vc8\qrcode;%(AdditionalIncludeDirectories) + OldStyle + true + false + MultiThreadedDebug + false + + + Windows + true + + + + + + + Level1 + Disabled + _USRDLL;QRCODELIB_EXPORTS;HAVE_CONFIG_H;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + ..\vc8\qrcode;%(AdditionalIncludeDirectories) + OldStyle + true + false + MultiThreadedDebug + false + + + Windows + true + + + + + Level1 + + + MaxSpeed + true + true + _USRDLL;QRCODELIB_EXPORTS;HAVE_CONFIG_H;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + MultiThreaded + false + ..\vc8\qrcode;%(AdditionalIncludeDirectories) + false + + + Windows + true + true + true + + + %(AdditionalDependencies) + %(AdditionalLibraryDirectories) + + + + + Level1 + + + MaxSpeed + true + true + _USRDLL;QRCODELIB_EXPORTS;HAVE_CONFIG_H;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + MultiThreaded + false + ..\vc8\qrcode;%(AdditionalIncludeDirectories) + false + + + Windows + true + true + true + + + %(AdditionalDependencies) + %(AdditionalLibraryDirectories) + + + + + + \ No newline at end of file diff --git a/MSVC/include/inttypes.h b/MSVC/include/inttypes.h new file mode 100644 index 00000000..9b5d57cd --- /dev/null +++ b/MSVC/include/inttypes.h @@ -0,0 +1,308 @@ +// ISO C9x compliant inttypes.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. The name of the author may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_INTTYPES_H_ // [ +#define _MSC_INTTYPES_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#if _MSC_VER > 1500 +#include +#else +#include "stdint.h" +#endif +// 7.8 Format conversion of integer types + +typedef struct { + intmax_t quot; + intmax_t rem; +} imaxdiv_t; + +// 7.8.1 Macros for format specifiers + +#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198 + +// The fprintf macros for signed integers are: +#define PRId8 "d" +#define PRIi8 "i" +#define PRIdLEAST8 "d" +#define PRIiLEAST8 "i" +#define PRIdFAST8 "d" +#define PRIiFAST8 "i" + +#define PRId16 "hd" +#define PRIi16 "hi" +#define PRIdLEAST16 "hd" +#define PRIiLEAST16 "hi" +#define PRIdFAST16 "hd" +#define PRIiFAST16 "hi" + +#define PRId32 "I32d" +#define PRIi32 "I32i" +#define PRIdLEAST32 "I32d" +#define PRIiLEAST32 "I32i" +#define PRIdFAST32 "I32d" +#define PRIiFAST32 "I32i" + +#define PRId64 "I64d" +#define PRIi64 "I64i" +#define PRIdLEAST64 "I64d" +#define PRIiLEAST64 "I64i" +#define PRIdFAST64 "I64d" +#define PRIiFAST64 "I64i" + +#define PRIdMAX "I64d" +#define PRIiMAX "I64i" + +#define PRIdPTR "Id" +#define PRIiPTR "Ii" + +// The fprintf macros for unsigned integers are: +#define PRIo8 "o" +#define PRIu8 "u" +#define PRIx8 "x" +#define PRIX8 "X" +#define PRIoLEAST8 "o" +#define PRIuLEAST8 "u" +#define PRIxLEAST8 "x" +#define PRIXLEAST8 "X" +#define PRIoFAST8 "o" +#define PRIuFAST8 "u" +#define PRIxFAST8 "x" +#define PRIXFAST8 "X" + +#define PRIo16 "ho" +#define PRIu16 "hu" +#define PRIx16 "hx" +#define PRIX16 "hX" +#define PRIoLEAST16 "ho" +#define PRIuLEAST16 "hu" +#define PRIxLEAST16 "hx" +#define PRIXLEAST16 "hX" +#define PRIoFAST16 "ho" +#define PRIuFAST16 "hu" +#define PRIxFAST16 "hx" +#define PRIXFAST16 "hX" + +#define PRIo32 "I32o" +#define PRIu32 "I32u" +#define PRIx32 "I32x" +#define PRIX32 "I32X" +#define PRIoLEAST32 "I32o" +#define PRIuLEAST32 "I32u" +#define PRIxLEAST32 "I32x" +#define PRIXLEAST32 "I32X" +#define PRIoFAST32 "I32o" +#define PRIuFAST32 "I32u" +#define PRIxFAST32 "I32x" +#define PRIXFAST32 "I32X" + +#define PRIo64 "I64o" +#define PRIu64 "I64u" +#define PRIx64 "I64x" +#define PRIX64 "I64X" +#define PRIoLEAST64 "I64o" +#define PRIuLEAST64 "I64u" +#define PRIxLEAST64 "I64x" +#define PRIXLEAST64 "I64X" +#define PRIoFAST64 "I64o" +#define PRIuFAST64 "I64u" +#define PRIxFAST64 "I64x" +#define PRIXFAST64 "I64X" + +#define PRIoMAX "I64o" +#define PRIuMAX "I64u" +#define PRIxMAX "I64x" +#define PRIXMAX "I64X" + +#define PRIoPTR "Io" +#define PRIuPTR "Iu" +#define PRIxPTR "Ix" +#define PRIXPTR "IX" + +// The fscanf macros for signed integers are: +#define SCNd8 "d" +#define SCNi8 "i" +#define SCNdLEAST8 "d" +#define SCNiLEAST8 "i" +#define SCNdFAST8 "d" +#define SCNiFAST8 "i" + +#define SCNd16 "hd" +#define SCNi16 "hi" +#define SCNdLEAST16 "hd" +#define SCNiLEAST16 "hi" +#define SCNdFAST16 "hd" +#define SCNiFAST16 "hi" + +#define SCNd32 "ld" +#define SCNi32 "li" +#define SCNdLEAST32 "ld" +#define SCNiLEAST32 "li" +#define SCNdFAST32 "ld" +#define SCNiFAST32 "li" + +#define SCNd64 "I64d" +#define SCNi64 "I64i" +#define SCNdLEAST64 "I64d" +#define SCNiLEAST64 "I64i" +#define SCNdFAST64 "I64d" +#define SCNiFAST64 "I64i" + +#define SCNdMAX "I64d" +#define SCNiMAX "I64i" + +#ifdef _WIN64 // [ +# define SCNdPTR "I64d" +# define SCNiPTR "I64i" +#else // _WIN64 ][ +# define SCNdPTR "ld" +# define SCNiPTR "li" +#endif // _WIN64 ] + +// The fscanf macros for unsigned integers are: +#define SCNo8 "o" +#define SCNu8 "u" +#define SCNx8 "x" +#define SCNX8 "X" +#define SCNoLEAST8 "o" +#define SCNuLEAST8 "u" +#define SCNxLEAST8 "x" +#define SCNXLEAST8 "X" +#define SCNoFAST8 "o" +#define SCNuFAST8 "u" +#define SCNxFAST8 "x" +#define SCNXFAST8 "X" + +#define SCNo16 "ho" +#define SCNu16 "hu" +#define SCNx16 "hx" +#define SCNX16 "hX" +#define SCNoLEAST16 "ho" +#define SCNuLEAST16 "hu" +#define SCNxLEAST16 "hx" +#define SCNXLEAST16 "hX" +#define SCNoFAST16 "ho" +#define SCNuFAST16 "hu" +#define SCNxFAST16 "hx" +#define SCNXFAST16 "hX" + +#define SCNo32 "lo" +#define SCNu32 "lu" +#define SCNx32 "lx" +#define SCNX32 "lX" +#define SCNoLEAST32 "lo" +#define SCNuLEAST32 "lu" +#define SCNxLEAST32 "lx" +#define SCNXLEAST32 "lX" +#define SCNoFAST32 "lo" +#define SCNuFAST32 "lu" +#define SCNxFAST32 "lx" +#define SCNXFAST32 "lX" + +#define SCNo64 "I64o" +#define SCNu64 "I64u" +#define SCNx64 "I64x" +#define SCNX64 "I64X" +#define SCNoLEAST64 "I64o" +#define SCNuLEAST64 "I64u" +#define SCNxLEAST64 "I64x" +#define SCNXLEAST64 "I64X" +#define SCNoFAST64 "I64o" +#define SCNuFAST64 "I64u" +#define SCNxFAST64 "I64x" +#define SCNXFAST64 "I64X" + +#define SCNoMAX "I64o" +#define SCNuMAX "I64u" +#define SCNxMAX "I64x" +#define SCNXMAX "I64X" + +#ifdef _WIN64 // [ +# define SCNoPTR "I64o" +# define SCNuPTR "I64u" +# define SCNxPTR "I64x" +# define SCNXPTR "I64X" +#else // _WIN64 ][ +# define SCNoPTR "lo" +# define SCNuPTR "lu" +# define SCNxPTR "lx" +# define SCNXPTR "lX" +#endif // _WIN64 ] + +#endif // __STDC_FORMAT_MACROS ] + +// 7.8.2 Functions for greatest-width integer types + +// 7.8.2.1 The imaxabs function +#define imaxabs _abs64 + +// 7.8.2.2 The imaxdiv function + +// This is modified version of div() function from Microsoft's div.c found +// in %MSVC.NET%\crt\src\div.c +#ifdef STATIC_IMAXDIV // [ +static +#else // STATIC_IMAXDIV ][ +_inline +#endif // STATIC_IMAXDIV ] +imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) +{ + imaxdiv_t result; + + result.quot = numer / denom; + result.rem = numer % denom; + + if (numer < 0 && result.rem > 0) { + // did division wrong; must fix up + ++result.quot; + result.rem -= denom; + } + + return result; +} + +// 7.8.2.3 The strtoimax and strtoumax functions +#define strtoimax _strtoi64 +#define strtoumax _strtoui64 + +// 7.8.2.4 The wcstoimax and wcstoumax functions +#define wcstoimax _wcstoi64 +#define wcstoumax _wcstoui64 + + +#endif // _MSC_INTTYPES_H_ ] diff --git a/MSVC/include/stdint.h b/MSVC/include/stdint.h new file mode 100644 index 00000000..d02608a5 --- /dev/null +++ b/MSVC/include/stdint.h @@ -0,0 +1,247 @@ +// ISO C9x compliant stdint.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2008 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. The name of the author may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_STDINT_H_ // [ +#define _MSC_STDINT_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include + +// For Visual Studio 6 in C++ mode and for many Visual Studio versions when +// compiling for ARM we should wrap include with 'extern "C++" {}' +// or compiler give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#ifdef __cplusplus +extern "C" { +#endif +# include +#ifdef __cplusplus +} +#endif + +// Define _W64 macros to mark types changing their size, like intptr_t. +#ifndef _W64 +# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +# define _W64 __w64 +# else +# define _W64 +# endif +#endif + + +// 7.18.1 Integer types + +// 7.18.1.1 Exact-width integer types + +// Visual Studio 6 and Embedded Visual C++ 4 doesn't +// realize that, e.g. char has the same size as __int8 +// so we give up on __intX for them. +#if (_MSC_VER < 1300) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; +#else + typedef signed __int8 int8_t; + typedef signed __int16 int16_t; + typedef signed __int32 int32_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; +#endif +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + + +// 7.18.1.2 Minimum-width integer types +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +// 7.18.1.3 Fastest minimum-width integer types +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +// 7.18.1.4 Integer types capable of holding object pointers +#ifdef _WIN64 // [ + typedef signed __int64 intptr_t; + typedef unsigned __int64 uintptr_t; +#else // _WIN64 ][ + typedef _W64 signed int intptr_t; + typedef _W64 unsigned int uintptr_t; +#endif // _WIN64 ] + +// 7.18.1.5 Greatest-width integer types +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + + +// 7.18.2 Limits of specified-width integer types + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 + +// 7.18.2.1 Limits of exact-width integer types +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +// 7.18.2.2 Limits of minimum-width integer types +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +// 7.18.2.3 Limits of fastest minimum-width integer types +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +// 7.18.2.4 Limits of integer types capable of holding object pointers +#ifdef _WIN64 // [ +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else // _WIN64 ][ +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif // _WIN64 ] + +// 7.18.2.5 Limits of greatest-width integer types +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +// 7.18.3 Limits of other integer types + +#ifdef _WIN64 // [ +# define PTRDIFF_MIN _I64_MIN +# define PTRDIFF_MAX _I64_MAX +#else // _WIN64 ][ +# define PTRDIFF_MIN _I32_MIN +# define PTRDIFF_MAX _I32_MAX +#endif // _WIN64 ] + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX // [ +# ifdef _WIN64 // [ +# define SIZE_MAX _UI64_MAX +# else // _WIN64 ][ +# define SIZE_MAX _UI32_MAX +# endif // _WIN64 ] +#endif // SIZE_MAX ] + +// WCHAR_MIN and WCHAR_MAX are also defined in +#ifndef WCHAR_MIN // [ +# define WCHAR_MIN 0 +#endif // WCHAR_MIN ] +#ifndef WCHAR_MAX // [ +# define WCHAR_MAX _UI16_MAX +#endif // WCHAR_MAX ] + +#define WINT_MIN 0 +#define WINT_MAX _UI16_MAX + +#endif // __STDC_LIMIT_MACROS ] + + +// 7.18.4 Limits of other integer types + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +#define INTMAX_C INT64_C +#define UINTMAX_C UINT64_C + +#endif // __STDC_CONSTANT_MACROS ] + + +#endif // _MSC_STDINT_H_ ] diff --git a/MSVC/leveldb/leveldb.vcxproj b/MSVC/leveldb/leveldb.vcxproj new file mode 100644 index 00000000..14276e18 --- /dev/null +++ b/MSVC/leveldb/leveldb.vcxproj @@ -0,0 +1,221 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {002cc16a-b4ef-4737-b751-dad3a8d14133} + Win32Proj + leveldb + SAK + SAK + SAK + SAK + + + + StaticLibrary + true + v110 + Unicode + + + StaticLibrary + true + v110 + Unicode + + + StaticLibrary + false + v110 + true + Unicode + + + StaticLibrary + false + v110 + true + Unicode + + + + + + + + + + + + + + + + + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + + + + NotUsing + Level1 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions);LEVELDB_PLATFORM_WINDOWS + ..\..\src\leveldb\include;..\..\src\leveldb;%(AdditionalIncludeDirectories) + false + MultiThreadedDebug + ProgramDatabase + true + false + 4100 + + + Windows + true + + + + + + + Level1 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions);LEVELDB_PLATFORM_WINDOWS + ..\..\src\leveldb\include;..\..\src\leveldb;%(AdditionalIncludeDirectories) + false + MultiThreadedDebug + ProgramDatabase + true + false + 4100 + + + Windows + true + + + + + Level1 + + + MaxSpeed + true + true + WIN32;LEVELDB_PLATFORM_WINDOWS;_LIB;%(PreprocessorDefinitions) + ..\..\src\leveldb\include;..\..\src\leveldb;%(AdditionalIncludeDirectories) + MultiThreaded + false + false + true + 4100 + + + Windows + true + true + true + + + false + + + + + Level1 + + + MaxSpeed + true + true + WIN32;LEVELDB_PLATFORM_WINDOWS;_LIB;%(PreprocessorDefinitions) + ..\..\src\leveldb\include;..\..\src\leveldb;%(AdditionalIncludeDirectories) + MultiThreaded + false + false + true + 4100 + + + Windows + true + true + true + + + false + + + + + + \ No newline at end of file diff --git a/MSVC/leveldb/leveldb.vcxproj.filters b/MSVC/leveldb/leveldb.vcxproj.filters new file mode 100644 index 00000000..0476c887 --- /dev/null +++ b/MSVC/leveldb/leveldb.vcxproj.filters @@ -0,0 +1,135 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + + + \ No newline at end of file diff --git a/MSVC/leveldb/leveldb.vcxproj.user b/MSVC/leveldb/leveldb.vcxproj.user new file mode 100644 index 00000000..a375ae35 --- /dev/null +++ b/MSVC/leveldb/leveldb.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/MSVC/leveldb/leveldb.vcxproj.vspscc b/MSVC/leveldb/leveldb.vcxproj.vspscc new file mode 100644 index 00000000..b6d32892 --- /dev/null +++ b/MSVC/leveldb/leveldb.vcxproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/MSVC/libcommon/libcommon.vcxproj b/MSVC/libcommon/libcommon.vcxproj new file mode 100644 index 00000000..5eed114a --- /dev/null +++ b/MSVC/libcommon/libcommon.vcxproj @@ -0,0 +1,256 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ..\..\src;%(AdditionalIncludeDirectories) + ..\..\src;%(AdditionalIncludeDirectories) + ..\..\src;%(AdditionalIncludeDirectories) + ..\..\src;%(AdditionalIncludeDirectories) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {3703B138-B8DA-460E-9DD1-41BDC7588E80} + Win32Proj + libcommon + SAK + SAK + SAK + SAK + + + + StaticLibrary + true + v110 + Unicode + + + StaticLibrary + true + v110 + Unicode + + + StaticLibrary + false + v110 + true + Unicode + + + StaticLibrary + false + v110 + true + Unicode + + + + + + + + + + + + + + + + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + + + + NotUsing + Level1 + Disabled + STATICLIB;_CRT_SECURE_NO_WARNINGS;UNICODE;WIN32;_SCL_SECURE_NO_WARNINGS;_WIN32_WINNT=0x0501;HAVE_WORKING_BOOST_SLEEP_FOR;NOMINMAX;USE_LEVELDB;USE_IPV6=1;BOOST_SPIRIT_THREADSAFE;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;USE_SSE2;%(PreprocessorDefinitions) + ..\include;..\..\..\Deps;..\..\..\deps\openssl-1.0.2\inc32;..\..\..\src\leveldb\helpers\memenv;..\..\src\leveldb\helpers;..\..\src\leveldb\include;..\..\..\deps\db-6.0.20\build_windows;..\..\..\deps\boost_1_57_0;..\..\..\deps\boost_1_57_0\boost;.\GeneratedFiles;.\GeneratedFiles\$(ConfigurationName);.\;%(AdditionalIncludeDirectories) + ProgramDatabase + true + false + MultiThreadedDebug + false + + Default + 4100 + + + Windows + true + + + + + + + Level1 + Disabled + STATICLIB;_CRT_SECURE_NO_WARNINGS;UNICODE;WIN32;_WIN32;_SCL_SECURE_NO_WARNINGS;_WIN32_WINNT=0x0501;HAVE_WORKING_BOOST_SLEEP_FOR;NOMINMAX;USE_LEVELDB;USE_IPV6=1;BOOST_SPIRIT_THREADSAFE;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;USE_SSE2;%(PreprocessorDefinitions) + ..\include;..\..\..\Deps;..\..\..\deps\openssl-1.0.2\inc32;..\..\..\src\leveldb\helpers\memenv;..\..\src\leveldb\helpers;..\..\src\leveldb\include;..\..\..\deps\db-6.0.20\build_windows;..\..\..\deps\boost_1_57_0;..\..\..\deps\boost_1_57_0\boost;.\GeneratedFiles;.\GeneratedFiles\$(ConfigurationName);.\;%(AdditionalIncludeDirectories) + ProgramDatabase + true + false + MultiThreadedDebug + false + 4100 + + + Windows + true + + + + + Level1 + + + MaxSpeed + true + true + STATICLIB;_CRT_SECURE_NO_WARNINGS;UNICODE;WIN32;_SCL_SECURE_NO_WARNINGS;_WIN32_WINNT=0x0501;HAVE_WORKING_BOOST_SLEEP_FOR;NOMINMAX;USE_LEVELDB;USE_IPV6=1;BOOST_SPIRIT_THREADSAFE;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;USE_SSE2;%(PreprocessorDefinitions) + ..\include;..\..\..\deps\;..\..\..\deps\openssl-1.0.2\inc32;..\..\..\src\leveldb\helpers\memenv;..\..\src\leveldb\helpers;..\..\src\leveldb\include;..\..\..\deps\db-6.0.20\build_windows;..\..\..\deps\boost_1_57_0;..\..\..\deps\boost_1_57_0\boost;.\GeneratedFiles;.\GeneratedFiles\$(ConfigurationName);.\;%(AdditionalIncludeDirectories) + MultiThreaded + false + Default + true + false + 4100 + + + Windows + true + true + true + + + + + Level1 + + + MaxSpeed + true + true + STATICLIB;_CRT_SECURE_NO_WARNINGS;UNICODE;WIN32;_SCL_SECURE_NO_WARNINGS;_WIN32_WINNT=0x0501;HAVE_WORKING_BOOST_SLEEP_FOR;NOMINMAX;USE_LEVELDB;USE_IPV6=1;BOOST_SPIRIT_THREADSAFE;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;USE_SSE2;%(PreprocessorDefinitions) + ..\include;..\..\..\Deps;..\..\..\deps\openssl-1.0.2\inc32;..\..\..\src\leveldb\helpers\memenv;..\..\src\leveldb\helpers;..\..\src\leveldb\include;..\..\..\deps\db-6.0.20\build_windows;..\..\..\deps\boost_1_57_0;..\..\..\deps\boost_1_57_0\boost;.\GeneratedFiles;.\GeneratedFiles\$(ConfigurationName);.\;%(AdditionalIncludeDirectories) + MultiThreaded + false + Default + true + false + 4100 + + + Windows + true + true + true + + + + + \ No newline at end of file diff --git a/MSVC/libcommon/libcommon.vcxproj.filters b/MSVC/libcommon/libcommon.vcxproj.filters new file mode 100644 index 00000000..399d2b38 --- /dev/null +++ b/MSVC/libcommon/libcommon.vcxproj.filters @@ -0,0 +1,239 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/MSVC/libcommon/libcommon.vcxproj.user b/MSVC/libcommon/libcommon.vcxproj.user new file mode 100644 index 00000000..a375ae35 --- /dev/null +++ b/MSVC/libcommon/libcommon.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/MSVC/libcommon/libcommon.vcxproj.vspscc b/MSVC/libcommon/libcommon.vcxproj.vspscc new file mode 100644 index 00000000..b6d32892 --- /dev/null +++ b/MSVC/libcommon/libcommon.vcxproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/MSVC/mynovacoin/mynovacoin.vcxproj b/MSVC/mynovacoin/mynovacoin.vcxproj new file mode 100644 index 00000000..47def119 --- /dev/null +++ b/MSVC/mynovacoin/mynovacoin.vcxproj @@ -0,0 +1,196 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {00225DF0-9DDB-41A2-972B-56DC24E2C995} + XPD + SAK + SAK + SAK + SAK + + + + Application + v110 + + + Application + v110 + + + Application + v110_xp + true + + + Application + v110_xp + true + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + AllRules.ruleset + AllRules.ruleset + + + + + AllRules.ruleset + AllRules.ruleset + + + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(SolutionDir)$(Platform)\$(Configuration)\ + + + $(Platform)\$(Configuration)\ + + + $(Platform)\$(Configuration)\ + + + + UNICODE;WIN32;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;USE_LEVELDB;USE_IPV6=1;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;USE_SSE2;%(PreprocessorDefinitions) + ..\include;..\..\src\leveldb\include;..\..\..\deps\openssl-1.0.2\inc32;..\..\..\deps\db-6.0.20\build_windows;..\..\..\deps\boost_1_57_0;..\..\..\deps\boost_1_57_0\boost;.\GeneratedFiles;.\GeneratedFiles\$(ConfigurationName);.\;%(AdditionalIncludeDirectories) + Disabled + ProgramDatabase + MultiThreadedDebug + false + false + true + true + 4100 + + + Console + $(OutDir)\$(ProjectName).exe + ..\..\..\Deps\db-6.0.20\build_windows\Win32\Static Debug;..\..\..\deps\boost_1_57_0\stage\lib;..\..\..\deps\openssl-1.0.2\out32.dbg;$(Platform)\$(Configuration)\;%(AdditionalLibraryDirectories) + iphlpapi.lib;kernel32.lib;user32.lib;shell32.lib;uuid.lib;ole32.lib;advapi32.lib;ws2_32.lib;gdi32.lib;comdlg32.lib;oleaut32.lib;imm32.lib;winmm.lib;winspool.lib;ssleay32.lib;libeay32.lib;libdb60sd.lib;Shlwapi.lib;%(AdditionalDependencies) + true + + + + + UNICODE;WIN32;_WIN32;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;USE_LEVELDB;USE_IPV6=1;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;USE_SSE2;%(PreprocessorDefinitions) + ..\include;..\..\src\leveldb\include;..\..\..\Deps\openssl-1.0.2\inc32;..\..\..\Deps\db-6.0.20\build_windows;..\..\..\Deps\boost_1_57_0;..\..\..\Deps\boost_1_57_0\boost;.\GeneratedFiles;.\GeneratedFiles\$(ConfigurationName);.\;%(AdditionalIncludeDirectories) + Disabled + ProgramDatabase + MultiThreadedDebug + false + false + true + true + 4100 + + + Console + $(OutDir)\$(ProjectName).exe + ..\..\..\Deps\db-6.0.20\build_windows\x64\Static Debug;..\..\..\Deps\boost_1_57_0\stage\lib\x64;..\..\..\Deps\openssl-1.0.2\out64.dbg;$(Platform)\$(Configuration)\;%(AdditionalLibraryDirectories) + iphlpapi.lib;kernel32.lib;user32.lib;shell32.lib;uuid.lib;ole32.lib;advapi32.lib;ws2_32.lib;gdi32.lib;comdlg32.lib;oleaut32.lib;imm32.lib;winmm.lib;winspool.lib;ssleay32.lib;libeay32.lib;libdb60sd.lib;Shlwapi.lib;%(AdditionalDependencies) + true + + + + + UNICODE;WIN32;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;USE_LEVELDB;USE_IPV6=1;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;USE_SSE2;%(PreprocessorDefinitions) + ..\include;..\..\src\leveldb\include;..\..\..\deps\openssl-1.0.2\inc32;..\..\..\deps\db-6.0.20\build_windows;..\..\..\deps\boost_1_57_0;..\..\..\deps\boost_1_57_0\boost;.\GeneratedFiles;.\GeneratedFiles\$(ConfigurationName);.\;%(AdditionalIncludeDirectories) + ProgramDatabase + MultiThreaded + false + true + Full + AnySuitable + true + Speed + true + true + 4100 + + + Console + $(OutDir)\$(ProjectName).exe + ..\..\..\deps\db-6.0.20\build_windows\Win32\Static Release;..\..\..\deps\boost_1_57_0\stage\lib;..\..\..\deps\openssl-1.0.2\out32;%(AdditionalLibraryDirectories) + kernel32.lib;user32.lib;shell32.lib;uuid.lib;ole32.lib;advapi32.lib;ws2_32.lib;gdi32.lib;comdlg32.lib;oleaut32.lib;imm32.lib;winmm.lib;winspool.lib;ssleay32.lib;libeay32.lib;libdb60s.lib;Shlwapi.lib;iphlpapi.lib;%(AdditionalDependencies) + true + + + + + UNICODE;WIN32;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;USE_LEVELDB;USE_IPV6=1;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;USE_SSE2;%(PreprocessorDefinitions) + ..\include;..\..\src\leveldb\include;..\..\..\Deps\openssl-1.0.2\inc32;..\..\..\Deps\db-6.0.20\build_windows;..\..\..\Deps\boost_1_57_0;..\..\..\Deps\boost_1_57_0\boost;.\GeneratedFiles;.\GeneratedFiles\$(ConfigurationName);.\;%(AdditionalIncludeDirectories) + ProgramDatabase + MultiThreaded + false + true + Full + AnySuitable + true + Speed + true + true + 4100 + + + Console + $(OutDir)\$(ProjectName).exe + ..\..\..\Deps\db-6.0.20\build_windows\x64\Static Release;..\..\..\Deps\boost_1_57_0\stage\lib\x64;..\..\..\Deps\openssl-1.0.2\out64;%(AdditionalLibraryDirectories) + iphlpapi.lib;kernel32.lib;user32.lib;shell32.lib;uuid.lib;ole32.lib;advapi32.lib;ws2_32.lib;gdi32.lib;comdlg32.lib;oleaut32.lib;imm32.lib;winmm.lib;winspool.lib;ssleay32.lib;libeay32.lib;libdb60s.lib;Shlwapi.lib;%(AdditionalDependencies) + true + + + + + {002cc16a-b4ef-4737-b751-dad3a8d14133} + + + {3703b138-b8da-460e-9dd1-41bdc7588e80} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MSVC/mynovacoin/mynovacoin.vcxproj.filters b/MSVC/mynovacoin/mynovacoin.vcxproj.filters new file mode 100644 index 00000000..a6f552c7 --- /dev/null +++ b/MSVC/mynovacoin/mynovacoin.vcxproj.filters @@ -0,0 +1,14 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;cxx;c;def + + + + + Source Files + + + \ No newline at end of file diff --git a/MSVC/mynovacoin/mynovacoin.vcxproj.user b/MSVC/mynovacoin/mynovacoin.vcxproj.user new file mode 100644 index 00000000..a375ae35 --- /dev/null +++ b/MSVC/mynovacoin/mynovacoin.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/MSVC/mynovacoin/mynovacoin.vcxproj.vspscc b/MSVC/mynovacoin/mynovacoin.vcxproj.vspscc new file mode 100644 index 00000000..b6d32892 --- /dev/null +++ b/MSVC/mynovacoin/mynovacoin.vcxproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/MSVC/mynovacoinqt/icon.rc b/MSVC/mynovacoinqt/icon.rc new file mode 100644 index 00000000..598ce1f8 --- /dev/null +++ b/MSVC/mynovacoinqt/icon.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON "XP.ico" \ No newline at end of file diff --git a/MSVC/mynovacoinqt/mynovacoinqt.vcxproj b/MSVC/mynovacoinqt/mynovacoinqt.vcxproj new file mode 100644 index 00000000..944a58db --- /dev/null +++ b/MSVC/mynovacoinqt/mynovacoinqt.vcxproj @@ -0,0 +1,1638 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {9191918D-7DE3-4BE1-8A32-2F2CF4EE6840} + myXPqt + Qt4VSv1.0 + XPQT + + + + v110 + debug\ + false + Application + build\ + myXPqt + + + v110 + debug\ + false + Application + build\ + myXPqt + + + v110_xp + release\ + false + Application + build\ + myXPqt + true + + + v110_xp + release\ + false + Application + build\ + myXPqt + true + + + + + + + + + + + + + + + + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Configuration)\ + $(ProjectName) + $(ProjectName) + false + false + $(SolutionDir)$(Platform)\$(Configuration)\ + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Configuration)\ + $(ProjectName) + $(ProjectName) + true + true + false + false + + + + ..\include;..\..\..\deps\qrencode-win32;..\..\..\deps\openssl-1.0.2\inc32;..\src\leveldb\helpers;..\..\src\leveldb\include;..\..\..\deps\db-6.0.20\build_windows;..\..\..\deps\boost_1_57_0;..\..\..\deps\boost_1_57_0\boost;..\..\src\qt\forms;.\GeneratedFiles;.\GeneratedFiles\$(ConfigurationName);.\;%(AdditionalIncludeDirectories);..\..\src;..\..\src\json;..\..\src\qt;..\..\..\deps\qt-everywhere-opensource-src-5.3.2\qtbase\include;..\..\..\deps\qt-everywhere-opensource-src-5.3.2\qtbase\include\QtWidgets;..\..\..\deps\qt-everywhere-opensource-src-5.3.2\qtbase\include\QtNetwork;..\..\..\deps\qt-everywhere-opensource-src-5.3.2\qtbase\include\QtGui;..\..\..\deps\qt-everywhere-opensource-src-5.3.2\qtbase\include\QtCore;.\build + + + $(IntDir) + false + ProgramDatabase + Sync + $(IntDir) + Disabled + UNICODE;WIN32;QT_CORE_LIB;QT_GUI_LIB;QT_WIDGETS_LIB;QT_GUI;_SCL_SECURE_NO_WARNINGS;USE_LEVELDB;USE_IPV6=1;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;USE_SSE2;%(PreprocessorDefinitions) + false + MultiThreadedDebug + true + false + Level1 + true + 4100 + + + libcommon.lib;leveldb.lib;iphlpapi.lib;ssleay32.lib;libeay32.lib;lib-qrcode.lib;libdb60sd.lib;Shlwapi.lib;%(AdditionalDependencies);ws2_32.lib;imm32.lib;winmm.lib;qtmaind.lib;Qt5Cored.lib;Qt5Guid.lib;Qt5Widgetsd.lib;Qt5Networkd.lib;qwindowsd.lib;Qt5PlatformSupportD.lib;qtaccessiblewidgetsd.lib;qgenericbearerd.lib;qnativewifibearerd.lib;qddsd.lib;qicnsd.lib;qicod.lib;qjp2d.lib;qmngd.lib;qsvgd.lib;qtgad.lib;qtiffd.lib;qwbmpd.lib;qwebpd.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib + ..\..\..\deps\boost_1_57_0\stage\lib;..\..\..\Deps\qrencode-win32\vc8\Debug;..\..\..\Deps\db-6.0.20\build_windows\Win32\Static Debug;..\..\..\deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;..\..\..\deps\openssl-1.0.2\out32.dbg;..\..\..\deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins\accessible;..\..\..\deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins\bearer;..\..\..\deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins\platforms;..\..\..\deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins\imageformats;$(SolutionDir)$(Platform)\$(Configuration)\;%(AdditionalLibraryDirectories) + + + true + true + true + $(OutDir)\$(ProjectName).exe + true + Windows + true + + + LinkVerbose + + + Unsigned + None + 0 + + + + + + + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\lrelease C:\MyProjects\XP\src\qt\locale\bitcoin_en.ts -qm C:\MyProjects\XP\src\qt\locale\bitcoin_en.qm +C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\lrelease C:\MyProjects\XP\src\qt\locale\bitcoin_ru.ts -qm C:\MyProjects\XP\src\qt\locale\bitcoin_ru.qm +C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\lrelease C:\MyProjects\XP\src\qt\locale\bitcoin_uk.ts -qm C:\MyProjects\XP\src\qt\locale\bitcoin_uk.qm +C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe C:\MyProjects\XP\src\qt\intro.cpp -o C:\MyProjects\XP\MSVC\myXPqt\build\intro.moc +C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe C:\MyProjects\XP\src\qt\overviewpage.cpp -o C:\MyProjects\XP\MSVC\myXPqt\build\overviewpage.moc +C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe C:\MyProjects\XP\src\qt\rpcconsole.cpp -o C:\MyProjects\XP\MSVC\myXPqt\build\rpcconsole.moc + + + + + ..\include;..\..\..\deps\qrencode-win32;..\..\..\deps\openssl-1.0.2\inc32;..\src\leveldb\helpers;..\..\src\leveldb\include;..\..\..\deps\db-6.0.20\build_windows;..\..\..\deps\boost_1_57_0;..\..\..\deps\boost_1_57_0\boost;..\..\src\qt\forms;.\GeneratedFiles;.\GeneratedFiles\$(ConfigurationName);.\;..\..\src;..\..\src\qt;..\..\..\deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\include;..\..\..\deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\include\QtWidgets;..\..\..\deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\include\QtNetwork;..\..\..\deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\include\QtGui;..\..\..\deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\include\QtCore;.\build;%(AdditionalIncludeDirectories) + + + $(IntDir) + false + ProgramDatabase + Sync + $(IntDir) + Disabled + UNICODE;WIN32;_WIN32;QT_CORE_LIB;QT_GUI_LIB;QT_WIDGETS_LIB;QT_GUI;_SCL_SECURE_NO_WARNINGS;USE_LEVELDB;USE_IPV6=1;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;USE_SSE2;%(PreprocessorDefinitions) + false + MultiThreadedDebug + true + false + Level1 + true + 4100 + + + libcommon.lib;leveldb.lib;iphlpapi.lib;ssleay32.lib;libeay32.lib;lib-qrcode.lib;libdb60sd.lib;Shlwapi.lib;%(AdditionalDependencies);ws2_32.lib;imm32.lib;winmm.lib;qtmaind.lib;Qt5Cored.lib;Qt5Guid.lib;Qt5Widgetsd.lib;Qt5Networkd.lib;qwindowsd.lib;Qt5PlatformSupportD.lib;qtaccessiblewidgetsd.lib;qgenericbearerd.lib;qnativewifibearerd.lib;qddsd.lib;qicnsd.lib;qicod.lib;qjp2d.lib;qmngd.lib;qsvgd.lib;qtgad.lib;qtiffd.lib;qwbmpd.lib;qwebpd.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib + ..\..\..\deps\boost_1_57_0\stage\lib\x64;..\..\..\Deps\qrencode-win32\vc8\x64\Debug;..\..\..\Deps\db-6.0.20\build_windows\x64\Static Debug;..\..\..\deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;..\..\..\deps\openssl-1.0.2\out64.dbg;..\..\..\deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins\accessible;..\..\..\deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins\bearer;..\..\..\deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins\platforms;..\..\..\deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins\imageformats;$(SolutionDir)$(Platform)\$(Configuration)\;%(AdditionalLibraryDirectories) + + + true + true + true + $(OutDir)\$(ProjectName).exe + true + Windows + true + + + LinkVerbose + + + Unsigned + None + 0 + + + + + + + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\lrelease C:\MyProjects\XP\src\qt\locale\bitcoin_en.ts -qm C:\MyProjects\XP\src\qt\locale\bitcoin_en.qm +C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\lrelease C:\MyProjects\XP\src\qt\locale\bitcoin_ru.ts -qm C:\MyProjects\XP\src\qt\locale\bitcoin_ru.qm +C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\lrelease C:\MyProjects\XP\src\qt\locale\bitcoin_uk.ts -qm C:\MyProjects\XP\src\qt\locale\bitcoin_uk.qm +C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe C:\MyProjects\XP\src\qt\intro.cpp -o C:\MyProjects\XP\MSVC\myXPqt\build\intro.moc +C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe C:\MyProjects\XP\src\qt\overviewpage.cpp -o C:\MyProjects\XP\MSVC\myXPqt\build\overviewpage.moc +C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe C:\MyProjects\XP\src\qt\rpcconsole.cpp -o C:\MyProjects\XP\MSVC\myXPqt\build\rpcconsole.moc + + + + + ..\include;..\..\..\deps\qrencode-win32;..\..\..\deps\openssl-1.0.2\inc32;..\src\leveldb\helpers;..\..\src\leveldb\include;..\..\..\deps\db-6.0.20\build_windows;..\..\..\deps\boost_1_57_0;..\..\..\deps\boost_1_57_0\boost;..\..\src\qt\forms;.\GeneratedFiles;.\GeneratedFiles\$(ConfigurationName);.\;..\..\src;..\..\src\qt;..\..\..\deps\qt-everywhere-opensource-src-5.3.2\qtbase\include;..\..\..\deps\qt-everywhere-opensource-src-5.3.2\qtbase\include\QtWidgets;..\..\..\deps\qt-everywhere-opensource-src-5.3.2\qtbase\include\QtNetwork;..\..\..\deps\qt-everywhere-opensource-src-5.3.2\qtbase\include\QtGui;..\..\..\deps\qt-everywhere-opensource-src-5.3.2\qtbase\include\QtCore;.\build;%(AdditionalIncludeDirectories) + + + $(IntDir) + false + None + Sync + $(IntDir) + Full + UNICODE;WIN32;QT_CORE_LIB;QT_GUI_LIB;QT_WIDGETS_LIB;QT_GUI;_SCL_SECURE_NO_WARNINGS;USE_LEVELDB;USE_IPV6=1;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;USE_SSE2;%(PreprocessorDefinitions) + false + $(IntDir)vc$(PlatformToolsetVersion).pdb + MultiThreaded + true + false + Level3 + true + AnySuitable + true + Speed + true + true + true + 4100 + + + iphlpapi.lib;kernel32.lib;user32.lib;shell32.lib;uuid.lib;ssleay32.lib;libeay32.lib;lib-qrcode.lib;libdb60s.lib;Shlwapi.lib;%(AdditionalDependencies);ole32.lib;advapi32.lib;ws2_32.lib;gdi32.lib;comdlg32.lib;oleaut32.lib;imm32.lib;winmm.lib;qtmain.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;Qt5Network.lib;qwindows.lib;Qt5PlatformSupport.lib;qtaccessiblewidgets.lib;qgenericbearer.lib;qnativewifibearer.lib;qdds.lib;qicns.lib;qico.lib;qjp2.lib;qmng.lib;qsvg.lib;qtga.lib;qtiff.lib;qwbmp.lib;qwebp.lib;libcommon.lib;leveldb.lib + ..\..\..\deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;..\..\..\deps\openssl-1.0.2\out32.dbg;..\..\..\deps\openssl-1.0.2\out32;..\..\..\deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins\accessible;..\..\..\deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins\bearer;..\..\..\deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins\platforms;..\..\..\deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins\imageformats;..\..\..\deps\boost_1_57_0\stage\lib;..\..\..\Deps\qrencode-win32\vc8\Release\;..\..\..\Deps\db-6.0.20\build_windows\Win32\Static Release;$(SolutionDir)$(Platform)\$(Configuration)\;%(AdditionalLibraryDirectories) + "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) + true + false + true + false + $(OutDir)\$(ProjectName).exe + true + Windows + true + 0.75 + + + Unsigned + None + 0 + + + _WINDOWS;UNICODE;WIN32;QT_GUI;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;BOOST_THREAD_USE_LIB;BOOST_SPIRIT_THREADSAFE;QT_NO_DEBUG;QT_WIDGETS_LIB;QT_NETWORK_LIB;QT_GUI_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + + + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\lrelease C:\MyProjects\XP\src\qt\locale\bitcoin_en.ts -qm C:\MyProjects\XP\src\qt\locale\bitcoin_en.qm +C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\lrelease C:\MyProjects\XP\src\qt\locale\bitcoin_ru.ts -qm C:\MyProjects\XP\src\qt\locale\bitcoin_ru.qm +C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\lrelease C:\MyProjects\XP\src\qt\locale\bitcoin_uk.ts -qm C:\MyProjects\XP\src\qt\locale\bitcoin_uk.qm +C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe C:\MyProjects\XP\src\qt\intro.cpp -o C:\MyProjects\XP\MSVC\myXPqt\build\intro.moc +C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe C:\MyProjects\XP\src\qt\overviewpage.cpp -o C:\MyProjects\XP\MSVC\myXPqt\build\overviewpage.moc +C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe C:\MyProjects\XP\src\qt\rpcconsole.cpp -o C:\MyProjects\XP\MSVC\myXPqt\build\rpcconsole.moc + + + + + ..\include;..\..\..\deps\qrencode-win32;..\..\..\deps\openssl-1.0.2\inc32;..\src\leveldb\helpers;..\..\src\leveldb\include;..\..\..\deps\db-6.0.20\build_windows;..\..\..\deps\boost_1_57_0;..\..\..\deps\boost_1_57_0\boost;..\..\src\qt\forms;.\GeneratedFiles;.\GeneratedFiles\$(ConfigurationName);.\;..\..\src;..\..\src\qt;..\..\..\deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\include;..\..\..\deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\include\QtWidgets;..\..\..\deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\include\QtNetwork;..\..\..\deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\include\QtGui;..\..\..\deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\include\QtCore;.\build;%(AdditionalIncludeDirectories) + + + $(IntDir) + false + None + Sync + $(IntDir) + Full + UNICODE;WIN32;QT_CORE_LIB;QT_GUI_LIB;QT_WIDGETS_LIB;QT_GUI;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;USE_LEVELDB;USE_IPV6=1;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;USE_SSE2;%(PreprocessorDefinitions) + false + $(IntDir)vc$(PlatformToolsetVersion).pdb + MultiThreaded + true + false + Level3 + true + AnySuitable + true + Speed + true + true + 4100 + + + iphlpapi.lib;kernel32.lib;user32.lib;shell32.lib;uuid.lib;ssleay32.lib;libeay32.lib;lib-qrcode.lib;libdb60s.lib;Shlwapi.lib;%(AdditionalDependencies);ole32.lib;advapi32.lib;ws2_32.lib;gdi32.lib;comdlg32.lib;oleaut32.lib;imm32.lib;winmm.lib;qtmain.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;Qt5Network.lib;qwindows.lib;Qt5PlatformSupport.lib;qtaccessiblewidgets.lib;qgenericbearer.lib;qnativewifibearer.lib;qdds.lib;qicns.lib;qico.lib;qjp2.lib;qmng.lib;qsvg.lib;qtga.lib;qtiff.lib;qwbmp.lib;qwebp.lib;libcommon.lib;leveldb.lib + ..\..\..\deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;..\..\..\deps\openssl-1.0.2\out64;..\..\..\deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins\accessible;..\..\..\deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins\bearer;..\..\..\deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins\platforms;..\..\..\deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins\imageformats;..\..\..\deps\boost_1_57_0\stage\lib\x64;..\..\..\Deps\qrencode-win32\vc8\x64\Release\;..\..\..\Deps\db-6.0.20\build_windows\x64\Static Release;$(SolutionDir)$(Platform)\$(Configuration)\;%(AdditionalLibraryDirectories) + "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) + true + false + true + false + $(OutDir)\$(ProjectName).exe + true + Windows + true + 0.75 + + + Unsigned + None + 0 + + + _WINDOWS;UNICODE;WIN32;QT_GUI;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;BOOST_THREAD_USE_LIB;BOOST_SPIRIT_THREADSAFE;QT_NO_DEBUG;QT_WIDGETS_LIB;QT_NETWORK_LIB;QT_GUI_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + + + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\lrelease C:\MyProjects\XP\src\qt\locale\bitcoin_en.ts -qm C:\MyProjects\XP\src\qt\locale\bitcoin_en.qm +C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\lrelease C:\MyProjects\XP\src\qt\locale\bitcoin_ru.ts -qm C:\MyProjects\XP\src\qt\locale\bitcoin_ru.qm +C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\lrelease C:\MyProjects\XP\src\qt\locale\bitcoin_uk.ts -qm C:\MyProjects\XP\src\qt\locale\bitcoin_uk.qm +C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe C:\MyProjects\XP\src\qt\intro.cpp -o C:\MyProjects\XP\MSVC\myXPqt\build\intro.moc +C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe C:\MyProjects\XP\src\qt\overviewpage.cpp -o C:\MyProjects\XP\MSVC\myXPqt\build\overviewpage.moc +C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe C:\MyProjects\XP\src\qt\rpcconsole.cpp -o C:\MyProjects\XP\MSVC\myXPqt\build\rpcconsole.moc + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ..\..\src\qt\aboutdialog.h;%(AdditionalInputs) + ..\..\src\qt\aboutdialog.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\aboutdialog.h -o build\moc_aboutdialog.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\aboutdialog.h -o build\moc_aboutdialog.cpp + MOC ..\..\src\qt\aboutdialog.h + MOC ..\..\src\qt\aboutdialog.h + build\moc_aboutdialog.cpp;%(Outputs) + build\moc_aboutdialog.cpp;%(Outputs) + ..\..\src\qt\aboutdialog.h;%(AdditionalInputs) + ..\..\src\qt\aboutdialog.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\aboutdialog.h -o build\moc_aboutdialog.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\aboutdialog.h -o build\moc_aboutdialog.cpp + MOC ..\..\src\qt\aboutdialog.h + MOC ..\..\src\qt\aboutdialog.h + build\moc_aboutdialog.cpp;%(Outputs) + build\moc_aboutdialog.cpp;%(Outputs) + + + ..\..\src\qt\addressbookpage.h;%(AdditionalInputs) + ..\..\src\qt\addressbookpage.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\addressbookpage.h -o build\moc_addressbookpage.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\addressbookpage.h -o build\moc_addressbookpage.cpp + MOC ..\..\src\qt\addressbookpage.h + MOC ..\..\src\qt\addressbookpage.h + build\moc_addressbookpage.cpp;%(Outputs) + build\moc_addressbookpage.cpp;%(Outputs) + ..\..\src\qt\addressbookpage.h;%(AdditionalInputs) + ..\..\src\qt\addressbookpage.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\addressbookpage.h -o build\moc_addressbookpage.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\addressbookpage.h -o build\moc_addressbookpage.cpp + MOC ..\..\src\qt\addressbookpage.h + MOC ..\..\src\qt\addressbookpage.h + build\moc_addressbookpage.cpp;%(Outputs) + build\moc_addressbookpage.cpp;%(Outputs) + + + ..\..\src\qt\addresstablemodel.h;%(AdditionalInputs) + ..\..\src\qt\addresstablemodel.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\addresstablemodel.h -o build\moc_addresstablemodel.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\addresstablemodel.h -o build\moc_addresstablemodel.cpp + MOC ..\..\src\qt\addresstablemodel.h + MOC ..\..\src\qt\addresstablemodel.h + build\moc_addresstablemodel.cpp;%(Outputs) + build\moc_addresstablemodel.cpp;%(Outputs) + ..\..\src\qt\addresstablemodel.h;%(AdditionalInputs) + ..\..\src\qt\addresstablemodel.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\addresstablemodel.h -o build\moc_addresstablemodel.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\addresstablemodel.h -o build\moc_addresstablemodel.cpp + MOC ..\..\src\qt\addresstablemodel.h + MOC ..\..\src\qt\addresstablemodel.h + build\moc_addresstablemodel.cpp;%(Outputs) + build\moc_addresstablemodel.cpp;%(Outputs) + + + + + + ..\..\src\qt\askpassphrasedialog.h;%(AdditionalInputs) + ..\..\src\qt\askpassphrasedialog.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\askpassphrasedialog.h -o build\moc_askpassphrasedialog.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\askpassphrasedialog.h -o build\moc_askpassphrasedialog.cpp + MOC ..\..\src\qt\askpassphrasedialog.h + MOC ..\..\src\qt\askpassphrasedialog.h + build\moc_askpassphrasedialog.cpp;%(Outputs) + build\moc_askpassphrasedialog.cpp;%(Outputs) + ..\..\src\qt\askpassphrasedialog.h;%(AdditionalInputs) + ..\..\src\qt\askpassphrasedialog.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\askpassphrasedialog.h -o build\moc_askpassphrasedialog.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\askpassphrasedialog.h -o build\moc_askpassphrasedialog.cpp + MOC ..\..\src\qt\askpassphrasedialog.h + MOC ..\..\src\qt\askpassphrasedialog.h + build\moc_askpassphrasedialog.cpp;%(Outputs) + build\moc_askpassphrasedialog.cpp;%(Outputs) + + + + + ..\..\src\qt\bitcoinaddressvalidator.h;%(AdditionalInputs) + ..\..\src\qt\bitcoinaddressvalidator.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\bitcoinaddressvalidator.h -o build\moc_bitcoinaddressvalidator.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\bitcoinaddressvalidator.h -o build\moc_bitcoinaddressvalidator.cpp + MOC ..\..\src\qt\bitcoinaddressvalidator.h + MOC ..\..\src\qt\bitcoinaddressvalidator.h + build\moc_bitcoinaddressvalidator.cpp;%(Outputs) + build\moc_bitcoinaddressvalidator.cpp;%(Outputs) + ..\..\src\qt\bitcoinaddressvalidator.h;%(AdditionalInputs) + ..\..\src\qt\bitcoinaddressvalidator.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\bitcoinaddressvalidator.h -o build\moc_bitcoinaddressvalidator.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\bitcoinaddressvalidator.h -o build\moc_bitcoinaddressvalidator.cpp + MOC ..\..\src\qt\bitcoinaddressvalidator.h + MOC ..\..\src\qt\bitcoinaddressvalidator.h + build\moc_bitcoinaddressvalidator.cpp;%(Outputs) + build\moc_bitcoinaddressvalidator.cpp;%(Outputs) + + + ..\..\src\qt\trafficgraphwidget.h;%(AdditionalInputs) + ..\..\src\qt\trafficgraphwidget.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\trafficgraphwidget.h -o build\moc_trafficgraphwidget.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\trafficgraphwidget.h -o build\moc_trafficgraphwidget.cpp + MOC ..\..\src\qt\trafficgraphwidget.h + MOC ..\..\src\qt\trafficgraphwidget.h + build\moc_trafficgraphwidget.cpp;%(Outputs) + build\moc_trafficgraphwidget.cpp;%(Outputs) + ..\..\src\qt\trafficgraphwidget.h;%(AdditionalInputs) + ..\..\src\qt\trafficgraphwidget.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\trafficgraphwidget.h -o build\moc_trafficgraphwidget.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\trafficgraphwidget.h -o build\moc_trafficgraphwidget.cpp + MOC ..\..\src\qt\trafficgraphwidget.h + MOC ..\..\src\qt\trafficgraphwidget.h + build\moc_trafficgraphwidget.cpp;%(Outputs) + build\moc_trafficgraphwidget.cpp;%(Outputs) + + + ..\..\src\qt\bitcoinamountfield.h;%(AdditionalInputs) + ..\..\src\qt\bitcoinamountfield.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\bitcoinamountfield.h -o build\moc_bitcoinamountfield.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\bitcoinamountfield.h -o build\moc_bitcoinamountfield.cpp + MOC ..\..\src\qt\bitcoinamountfield.h + MOC ..\..\src\qt\bitcoinamountfield.h + build\moc_bitcoinamountfield.cpp;%(Outputs) + build\moc_bitcoinamountfield.cpp;%(Outputs) + ..\..\src\qt\bitcoinamountfield.h;%(AdditionalInputs) + ..\..\src\qt\bitcoinamountfield.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\bitcoinamountfield.h -o build\moc_bitcoinamountfield.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\bitcoinamountfield.h -o build\moc_bitcoinamountfield.cpp + MOC ..\..\src\qt\bitcoinamountfield.h + MOC ..\..\src\qt\bitcoinamountfield.h + build\moc_bitcoinamountfield.cpp;%(Outputs) + build\moc_bitcoinamountfield.cpp;%(Outputs) + + + ..\..\src\qt\bitcoingui.h;%(AdditionalInputs) + ..\..\src\qt\bitcoingui.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\bitcoingui.h -o build\moc_bitcoingui.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\bitcoingui.h -o build\moc_bitcoingui.cpp + MOC ..\..\src\qt\bitcoingui.h + MOC ..\..\src\qt\bitcoingui.h + build\moc_bitcoingui.cpp;%(Outputs) + build\moc_bitcoingui.cpp;%(Outputs) + ..\..\src\qt\bitcoingui.h;%(AdditionalInputs) + ..\..\src\qt\bitcoingui.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\bitcoingui.h -o build\moc_bitcoingui.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\bitcoingui.h -o build\moc_bitcoingui.cpp + MOC ..\..\src\qt\bitcoingui.h + MOC ..\..\src\qt\bitcoingui.h + build\moc_bitcoingui.cpp;%(Outputs) + build\moc_bitcoingui.cpp;%(Outputs) + + + + + + ..\..\src\qt\clientmodel.h;%(AdditionalInputs) + ..\..\src\qt\clientmodel.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\clientmodel.h -o build\moc_clientmodel.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\clientmodel.h -o build\moc_clientmodel.cpp + MOC ..\..\src\qt\clientmodel.h + MOC ..\..\src\qt\clientmodel.h + build\moc_clientmodel.cpp;%(Outputs) + build\moc_clientmodel.cpp;%(Outputs) + ..\..\src\qt\clientmodel.h;%(AdditionalInputs) + ..\..\src\qt\clientmodel.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\clientmodel.h -o build\moc_clientmodel.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\clientmodel.h -o build\moc_clientmodel.cpp + MOC ..\..\src\qt\clientmodel.h + MOC ..\..\src\qt\clientmodel.h + build\moc_clientmodel.cpp;%(Outputs) + build\moc_clientmodel.cpp;%(Outputs) + + + + + ..\..\src\qt\coincontroldialog.h;%(AdditionalInputs) + ..\..\src\qt\coincontroldialog.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\coincontroldialog.h -o build\moc_coincontroldialog.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\coincontroldialog.h -o build\moc_coincontroldialog.cpp + MOC ..\..\src\qt\coincontroldialog.h + MOC ..\..\src\qt\coincontroldialog.h + build\moc_coincontroldialog.cpp;%(Outputs) + build\moc_coincontroldialog.cpp;%(Outputs) + ..\..\src\qt\coincontroldialog.h;%(AdditionalInputs) + ..\..\src\qt\coincontroldialog.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\coincontroldialog.h -o build\moc_coincontroldialog.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\coincontroldialog.h -o build\moc_coincontroldialog.cpp + MOC ..\..\src\qt\coincontroldialog.h + MOC ..\..\src\qt\coincontroldialog.h + build\moc_coincontroldialog.cpp;%(Outputs) + build\moc_coincontroldialog.cpp;%(Outputs) + + + ..\..\src\qt\coincontroltreewidget.h;%(AdditionalInputs) + ..\..\src\qt\coincontroltreewidget.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\coincontroltreewidget.h -o build\moc_coincontroltreewidget.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\coincontroltreewidget.h -o build\moc_coincontroltreewidget.cpp + MOC ..\..\src\qt\coincontroltreewidget.h + MOC ..\..\src\qt\coincontroltreewidget.h + build\moc_coincontroltreewidget.cpp;%(Outputs) + build\moc_coincontroltreewidget.cpp;%(Outputs) + ..\..\src\qt\coincontroltreewidget.h;%(AdditionalInputs) + ..\..\src\qt\coincontroltreewidget.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\coincontroltreewidget.h -o build\moc_coincontroltreewidget.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\coincontroltreewidget.h -o build\moc_coincontroltreewidget.cpp + MOC ..\..\src\qt\coincontroltreewidget.h + MOC ..\..\src\qt\coincontroltreewidget.h + build\moc_coincontroltreewidget.cpp;%(Outputs) + build\moc_coincontroltreewidget.cpp;%(Outputs) + + + + + ..\..\src\qt\csvmodelwriter.h;%(AdditionalInputs) + ..\..\src\qt\csvmodelwriter.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\csvmodelwriter.h -o build\moc_csvmodelwriter.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\csvmodelwriter.h -o build\moc_csvmodelwriter.cpp + MOC ..\..\src\qt\csvmodelwriter.h + MOC ..\..\src\qt\csvmodelwriter.h + build\moc_csvmodelwriter.cpp;%(Outputs) + build\moc_csvmodelwriter.cpp;%(Outputs) + ..\..\src\qt\csvmodelwriter.h;%(AdditionalInputs) + ..\..\src\qt\csvmodelwriter.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\csvmodelwriter.h -o build\moc_csvmodelwriter.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\csvmodelwriter.h -o build\moc_csvmodelwriter.cpp + MOC ..\..\src\qt\csvmodelwriter.h + MOC ..\..\src\qt\csvmodelwriter.h + build\moc_csvmodelwriter.cpp;%(Outputs) + build\moc_csvmodelwriter.cpp;%(Outputs) + + + + ..\..\src\qt\editaddressdialog.h;%(AdditionalInputs) + ..\..\src\qt\editaddressdialog.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\editaddressdialog.h -o build\moc_editaddressdialog.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\editaddressdialog.h -o build\moc_editaddressdialog.cpp + MOC ..\..\src\qt\editaddressdialog.h + MOC ..\..\src\qt\editaddressdialog.h + build\moc_editaddressdialog.cpp;%(Outputs) + build\moc_editaddressdialog.cpp;%(Outputs) + ..\..\src\qt\editaddressdialog.h;%(AdditionalInputs) + ..\..\src\qt\editaddressdialog.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\editaddressdialog.h -o build\moc_editaddressdialog.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\editaddressdialog.h -o build\moc_editaddressdialog.cpp + MOC ..\..\src\qt\editaddressdialog.h + MOC ..\..\src\qt\editaddressdialog.h + build\moc_editaddressdialog.cpp;%(Outputs) + build\moc_editaddressdialog.cpp;%(Outputs) + + + + ..\..\src\qt\guiutil.h;%(AdditionalInputs) + ..\..\src\qt\guiutil.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\guiutil.h -o build\moc_guiutil.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\guiutil.h -o build\moc_guiutil.cpp + MOC ..\..\src\qt\guiutil.h + MOC ..\..\src\qt\guiutil.h + build\moc_guiutil.cpp;%(Outputs) + build\moc_guiutil.cpp;%(Outputs) + ..\..\src\qt\guiutil.h;%(AdditionalInputs) + ..\..\src\qt\guiutil.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\guiutil.h -o build\moc_guiutil.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\guiutil.h -o build\moc_guiutil.cpp + MOC ..\..\src\qt\guiutil.h + MOC ..\..\src\qt\guiutil.h + build\moc_guiutil.cpp;%(Outputs) + build\moc_guiutil.cpp;%(Outputs) + + + + + + + + + + + + + + + + + + + + ..\..\src\qt\monitoreddatamapper.h;%(AdditionalInputs) + ..\..\src\qt\monitoreddatamapper.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\monitoreddatamapper.h -o build\moc_monitoreddatamapper.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\monitoreddatamapper.h -o build\moc_monitoreddatamapper.cpp + MOC ..\..\src\qt\monitoreddatamapper.h + MOC ..\..\src\qt\monitoreddatamapper.h + build\moc_monitoreddatamapper.cpp;%(Outputs) + build\moc_monitoreddatamapper.cpp;%(Outputs) + ..\..\src\qt\monitoreddatamapper.h;%(AdditionalInputs) + ..\..\src\qt\monitoreddatamapper.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\monitoreddatamapper.h -o build\moc_monitoreddatamapper.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\monitoreddatamapper.h -o build\moc_monitoreddatamapper.cpp + MOC ..\..\src\qt\monitoreddatamapper.h + MOC ..\..\src\qt\monitoreddatamapper.h + build\moc_monitoreddatamapper.cpp;%(Outputs) + build\moc_monitoreddatamapper.cpp;%(Outputs) + + + + + + ..\..\src\qt\notificator.h;%(AdditionalInputs) + ..\..\src\qt\notificator.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\notificator.h -o build\moc_notificator.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\notificator.h -o build\moc_notificator.cpp + MOC ..\..\src\qt\notificator.h + MOC ..\..\src\qt\notificator.h + build\moc_notificator.cpp;%(Outputs) + build\moc_notificator.cpp;%(Outputs) + ..\..\src\qt\notificator.h;%(AdditionalInputs) + ..\..\src\qt\notificator.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\notificator.h -o build\moc_notificator.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\notificator.h -o build\moc_notificator.cpp + MOC ..\..\src\qt\notificator.h + MOC ..\..\src\qt\notificator.h + build\moc_notificator.cpp;%(Outputs) + build\moc_notificator.cpp;%(Outputs) + + + ..\..\src\qt\optionsdialog.h;%(AdditionalInputs) + ..\..\src\qt\optionsdialog.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\optionsdialog.h -o build\moc_optionsdialog.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\optionsdialog.h -o build\moc_optionsdialog.cpp + MOC ..\..\src\qt\optionsdialog.h + MOC ..\..\src\qt\optionsdialog.h + build\moc_optionsdialog.cpp;%(Outputs) + build\moc_optionsdialog.cpp;%(Outputs) + ..\..\src\qt\optionsdialog.h;%(AdditionalInputs) + ..\..\src\qt\optionsdialog.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\optionsdialog.h -o build\moc_optionsdialog.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\optionsdialog.h -o build\moc_optionsdialog.cpp + MOC ..\..\src\qt\optionsdialog.h + MOC ..\..\src\qt\optionsdialog.h + build\moc_optionsdialog.cpp;%(Outputs) + build\moc_optionsdialog.cpp;%(Outputs) + + + ..\..\src\qt\optionsmodel.h;%(AdditionalInputs) + ..\..\src\qt\optionsmodel.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\optionsmodel.h -o build\moc_optionsmodel.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\optionsmodel.h -o build\moc_optionsmodel.cpp + MOC ..\..\src\qt\optionsmodel.h + MOC ..\..\src\qt\optionsmodel.h + build\moc_optionsmodel.cpp;%(Outputs) + build\moc_optionsmodel.cpp;%(Outputs) + ..\..\src\qt\optionsmodel.h;%(AdditionalInputs) + ..\..\src\qt\optionsmodel.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\optionsmodel.h -o build\moc_optionsmodel.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\optionsmodel.h -o build\moc_optionsmodel.cpp + MOC ..\..\src\qt\optionsmodel.h + MOC ..\..\src\qt\optionsmodel.h + build\moc_optionsmodel.cpp;%(Outputs) + build\moc_optionsmodel.cpp;%(Outputs) + + + ..\..\src\qt\overviewpage.h;%(AdditionalInputs) + ..\..\src\qt\overviewpage.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\overviewpage.h -o build\moc_overviewpage.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\overviewpage.h -o build\moc_overviewpage.cpp + MOC ..\..\src\qt\overviewpage.h + MOC ..\..\src\qt\overviewpage.h + build\moc_overviewpage.cpp;%(Outputs) + build\moc_overviewpage.cpp;%(Outputs) + ..\..\src\qt\overviewpage.h;%(AdditionalInputs) + ..\..\src\qt\overviewpage.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\overviewpage.h -o build\moc_overviewpage.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\overviewpage.h -o build\moc_overviewpage.cpp + MOC ..\..\src\qt\overviewpage.h + MOC ..\..\src\qt\overviewpage.h + build\moc_overviewpage.cpp;%(Outputs) + build\moc_overviewpage.cpp;%(Outputs) + + + + ..\..\src\qt\qrcodedialog.h;%(AdditionalInputs) + ..\..\src\qt\qrcodedialog.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\qrcodedialog.h -o build\moc_qrcodedialog.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\qrcodedialog.h -o build\moc_qrcodedialog.cpp + MOC ..\..\src\qt\qrcodedialog.h + MOC ..\..\src\qt\qrcodedialog.h + build\moc_qrcodedialog.cpp;%(Outputs) + build\moc_qrcodedialog.cpp;%(Outputs) + ..\..\src\qt\qrcodedialog.h;%(AdditionalInputs) + ..\..\src\qt\qrcodedialog.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\qrcodedialog.h -o build\moc_qrcodedialog.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\qrcodedialog.h -o build\moc_qrcodedialog.cpp + MOC ..\..\src\qt\qrcodedialog.h + MOC ..\..\src\qt\qrcodedialog.h + build\moc_qrcodedialog.cpp;%(Outputs) + build\moc_qrcodedialog.cpp;%(Outputs) + + + + ..\..\src\qt\mintingfilterproxy.h;%(AdditionalInputs) + ..\..\src\qt\mintingfilterproxy.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\mintingfilterproxy.h -o build\moc_mintingfilterproxy.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\mintingfilterproxy.h -o build\moc_mintingfilterproxy.cpp + MOC ..\..\src\qt\mintingfilterproxy.h + MOC ..\..\src\qt\mintingfilterproxy.h + build\moc_mintingfilterproxy.cpp;%(Outputs) + build\moc_mintingfilterproxy.cpp;%(Outputs) + ..\..\src\qt\mintingfilterproxy.h;%(AdditionalInputs) + ..\..\src\qt\mintingfilterproxy.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\mintingfilterproxy.h -o build\moc_mintingfilterproxy.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\mintingfilterproxy.h -o build\moc_mintingfilterproxy.cpp + MOC ..\..\src\qt\mintingfilterproxy.h + MOC ..\..\src\qt\mintingfilterproxy.h + build\moc_mintingfilterproxy.cpp;%(Outputs) + build\moc_mintingfilterproxy.cpp;%(Outputs) + + + ..\..\src\qt\mintingtablemodel.h;%(AdditionalInputs) + ..\..\src\qt\mintingtablemodel.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\mintingtablemodel.h -o build\moc_mintingtablemodel.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\mintingtablemodel.h -o build\moc_mintingtablemodel.cpp + MOC ..\..\src\qt\mintingtablemodel.h + MOC ..\..\src\qt\mintingtablemodel.h + build\moc_mintingtablemodel.cpp;%(Outputs) + build\moc_mintingtablemodel.cpp;%(Outputs) + ..\..\src\qt\mintingtablemodel.h;%(AdditionalInputs) + ..\..\src\qt\mintingtablemodel.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\mintingtablemodel.h -o build\moc_mintingtablemodel.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\mintingtablemodel.h -o build\moc_mintingtablemodel.cpp + MOC ..\..\src\qt\mintingtablemodel.h + MOC ..\..\src\qt\mintingtablemodel.h + build\moc_mintingtablemodel.cpp;%(Outputs) + build\moc_mintingtablemodel.cpp;%(Outputs) + + + ..\..\src\qt\mintingview.h;%(AdditionalInputs) + ..\..\src\qt\mintingview.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\mintingview.h -o build\moc_mintingview.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\mintingview.h -o build\moc_mintingview.cpp + MOC ..\..\src\qt\mintingview.h + MOC ..\..\src\qt\mintingview.h + build\moc_mintingview.cpp;%(Outputs) + build\moc_mintingview.cpp;%(Outputs) + ..\..\src\qt\mintingview.h;%(AdditionalInputs) + ..\..\src\qt\mintingview.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\mintingview.h -o build\moc_mintingview.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\mintingview.h -o build\moc_mintingview.cpp + MOC ..\..\src\qt\mintingview.h + MOC ..\..\src\qt\mintingview.h + build\moc_mintingview.cpp;%(Outputs) + build\moc_mintingview.cpp;%(Outputs) + + + ..\..\src\qt\multisigaddressentry.h;%(AdditionalInputs) + ..\..\src\qt\multisigaddressentry.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\multisigaddressentry.h -o build\moc_multisigaddressentry.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\multisigaddressentry.h -o build\moc_multisigaddressentry.cpp + MOC ..\..\src\qt\multisigaddressentry.h + MOC ..\..\src\qt\multisigaddressentry.h + build\moc_multisigaddressentry.cpp;%(Outputs) + build\moc_multisigaddressentry.cpp;%(Outputs) + ..\..\src\qt\multisigaddressentry.h;%(AdditionalInputs) + ..\..\src\qt\multisigaddressentry.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\multisigaddressentry.h -o build\moc_multisigaddressentry.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\multisigaddressentry.h -o build\moc_multisigaddressentry.cpp + MOC ..\..\src\qt\multisigaddressentry.h + MOC ..\..\src\qt\multisigaddressentry.h + build\moc_multisigaddressentry.cpp;%(Outputs) + build\moc_multisigaddressentry.cpp;%(Outputs) + + + ..\..\src\qt\multisigdialog.h;%(AdditionalInputs) + ..\..\src\qt\multisigdialog.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\multisigdialog.h -o build\moc_multisigdialog.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\multisigdialog.h -o build\moc_multisigdialog.cpp + MOC ..\..\src\qt\multisigdialog.h + MOC ..\..\src\qt\multisigdialog.h + build\moc_multisigdialog.cpp;%(Outputs) + build\moc_multisigdialog.cpp;%(Outputs) + ..\..\src\qt\multisigdialog.h;%(AdditionalInputs) + ..\..\src\qt\multisigdialog.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\multisigdialog.h -o build\moc_multisigdialog.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\multisigdialog.h -o build\moc_multisigdialog.cpp + MOC ..\..\src\qt\multisigdialog.h + MOC ..\..\src\qt\multisigdialog.h + build\moc_multisigdialog.cpp;%(Outputs) + build\moc_multisigdialog.cpp;%(Outputs) + + + ..\..\src\qt\multisiginputentry.h;%(AdditionalInputs) + ..\..\src\qt\multisiginputentry.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\multisiginputentry.h -o build\moc_multisiginputentry.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\multisiginputentry.h -o build\moc_multisiginputentry.cpp + MOC ..\..\src\qt\multisiginputentry.h + MOC ..\..\src\qt\multisiginputentry.h + build\moc_multisiginputentry.cpp;%(Outputs) + build\moc_multisiginputentry.cpp;%(Outputs) + ..\..\src\qt\multisiginputentry.h;%(AdditionalInputs) + ..\..\src\qt\multisiginputentry.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\multisiginputentry.h -o build\moc_multisiginputentry.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\multisiginputentry.h -o build\moc_multisiginputentry.cpp + MOC ..\..\src\qt\multisiginputentry.h + MOC ..\..\src\qt\multisiginputentry.h + build\moc_multisiginputentry.cpp;%(Outputs) + build\moc_multisiginputentry.cpp;%(Outputs) + + + ..\..\src\qt\intro.h;%(AdditionalInputs) + ..\..\src\qt\intro.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\intro.h -o build\moc_intro.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\intro.h -o build\moc_intro.cpp + MOC ..\..\src\qt\intro.h + MOC ..\..\src\qt\intro.h + build\moc_intro.cpp;%(Outputs) + build\moc_intro.cpp;%(Outputs) + ..\..\src\qt\intro.h;%(AdditionalInputs) + ..\..\src\qt\intro.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\intro.h -o build\moc_intro.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\intro.h -o build\moc_intro.cpp + MOC ..\..\src\qt\intro.h + MOC ..\..\src\qt\intro.h + build\moc_intro.cpp;%(Outputs) + build\moc_intro.cpp;%(Outputs) + + + + ..\..\src\qt\qvalidatedlineedit.h;%(AdditionalInputs) + ..\..\src\qt\qvalidatedlineedit.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\qvalidatedlineedit.h -o build\moc_qvalidatedlineedit.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\qvalidatedlineedit.h -o build\moc_qvalidatedlineedit.cpp + MOC ..\..\src\qt\qvalidatedlineedit.h + MOC ..\..\src\qt\qvalidatedlineedit.h + build\moc_qvalidatedlineedit.cpp;%(Outputs) + build\moc_qvalidatedlineedit.cpp;%(Outputs) + ..\..\src\qt\qvalidatedlineedit.h;%(AdditionalInputs) + ..\..\src\qt\qvalidatedlineedit.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\qvalidatedlineedit.h -o build\moc_qvalidatedlineedit.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\qvalidatedlineedit.h -o build\moc_qvalidatedlineedit.cpp + MOC ..\..\src\qt\qvalidatedlineedit.h + MOC ..\..\src\qt\qvalidatedlineedit.h + build\moc_qvalidatedlineedit.cpp;%(Outputs) + build\moc_qvalidatedlineedit.cpp;%(Outputs) + + + ..\..\src\qt\qvaluecombobox.h;%(AdditionalInputs) + ..\..\src\qt\qvaluecombobox.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\qvaluecombobox.h -o build\moc_qvaluecombobox.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\qvaluecombobox.h -o build\moc_qvaluecombobox.cpp + MOC ..\..\src\qt\qvaluecombobox.h + MOC ..\..\src\qt\qvaluecombobox.h + build\moc_qvaluecombobox.cpp;%(Outputs) + build\moc_qvaluecombobox.cpp;%(Outputs) + ..\..\src\qt\qvaluecombobox.h;%(AdditionalInputs) + ..\..\src\qt\qvaluecombobox.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\qvaluecombobox.h -o build\moc_qvaluecombobox.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\qvaluecombobox.h -o build\moc_qvaluecombobox.cpp + MOC ..\..\src\qt\qvaluecombobox.h + MOC ..\..\src\qt\qvaluecombobox.h + build\moc_qvaluecombobox.cpp;%(Outputs) + build\moc_qvaluecombobox.cpp;%(Outputs) + + + ..\..\src\qt\rpcconsole.h;%(AdditionalInputs) + ..\..\src\qt\rpcconsole.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\rpcconsole.h -o build\moc_rpcconsole.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\rpcconsole.h -o build\moc_rpcconsole.cpp + MOC ..\..\src\qt\rpcconsole.h + MOC ..\..\src\qt\rpcconsole.h + build\moc_rpcconsole.cpp;%(Outputs) + build\moc_rpcconsole.cpp;%(Outputs) + ..\..\src\qt\rpcconsole.h;%(AdditionalInputs) + ..\..\src\qt\rpcconsole.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\rpcconsole.h -o build\moc_rpcconsole.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\rpcconsole.h -o build\moc_rpcconsole.cpp + MOC ..\..\src\qt\rpcconsole.h + MOC ..\..\src\qt\rpcconsole.h + build\moc_rpcconsole.cpp;%(Outputs) + build\moc_rpcconsole.cpp;%(Outputs) + + + + + ..\..\src\qt\secondauthdialog.h;%(AdditionalInputs) + ..\..\src\qt\secondauthdialog.h;%(AdditionalInputs) + build\moc_secondauthdialog.cpp;%(Outputs) + build\moc_secondauthdialog.cpp;%(Outputs) + MOC ..\..\src\qt\secondauthdialog.h + MOC ..\..\src\qt\secondauthdialog.h + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\secondauthdialog.h -o build\moc_secondauthdialog.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\secondauthdialog.h -o build\moc_secondauthdialog.cpp + ..\..\src\qt\secondauthdialog.h;%(AdditionalInputs) + ..\..\src\qt\secondauthdialog.h;%(AdditionalInputs) + build\moc_secondauthdialog.cpp;%(Outputs) + build\moc_secondauthdialog.cpp;%(Outputs) + MOC ..\..\src\qt\secondauthdialog.h + MOC ..\..\src\qt\secondauthdialog.h + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\secondauthdialog.h -o build\moc_secondauthdialog.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\secondauthdialog.h -o build\moc_secondauthdialog.cpp + + + ..\..\src\qt\sendcoinsdialog.h;%(AdditionalInputs) + ..\..\src\qt\sendcoinsdialog.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\sendcoinsdialog.h -o build\moc_sendcoinsdialog.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\sendcoinsdialog.h -o build\moc_sendcoinsdialog.cpp + MOC ..\..\src\qt\sendcoinsdialog.h + MOC ..\..\src\qt\sendcoinsdialog.h + build\moc_sendcoinsdialog.cpp;%(Outputs) + build\moc_sendcoinsdialog.cpp;%(Outputs) + ..\..\src\qt\sendcoinsdialog.h;%(AdditionalInputs) + ..\..\src\qt\sendcoinsdialog.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\sendcoinsdialog.h -o build\moc_sendcoinsdialog.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\sendcoinsdialog.h -o build\moc_sendcoinsdialog.cpp + MOC ..\..\src\qt\sendcoinsdialog.h + MOC ..\..\src\qt\sendcoinsdialog.h + build\moc_sendcoinsdialog.cpp;%(Outputs) + build\moc_sendcoinsdialog.cpp;%(Outputs) + + + ..\..\src\qt\sendcoinsentry.h;%(AdditionalInputs) + ..\..\src\qt\sendcoinsentry.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\sendcoinsentry.h -o build\moc_sendcoinsentry.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\sendcoinsentry.h -o build\moc_sendcoinsentry.cpp + MOC ..\..\src\qt\sendcoinsentry.h + MOC ..\..\src\qt\sendcoinsentry.h + build\moc_sendcoinsentry.cpp;%(Outputs) + build\moc_sendcoinsentry.cpp;%(Outputs) + ..\..\src\qt\sendcoinsentry.h;%(AdditionalInputs) + ..\..\src\qt\sendcoinsentry.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\sendcoinsentry.h -o build\moc_sendcoinsentry.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\sendcoinsentry.h -o build\moc_sendcoinsentry.cpp + MOC ..\..\src\qt\sendcoinsentry.h + MOC ..\..\src\qt\sendcoinsentry.h + build\moc_sendcoinsentry.cpp;%(Outputs) + build\moc_sendcoinsentry.cpp;%(Outputs) + + + + ..\..\src\qt\signverifymessagedialog.h;%(AdditionalInputs) + ..\..\src\qt\signverifymessagedialog.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\signverifymessagedialog.h -o build\moc_signverifymessagedialog.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\signverifymessagedialog.h -o build\moc_signverifymessagedialog.cpp + MOC ..\..\src\qt\signverifymessagedialog.h + MOC ..\..\src\qt\signverifymessagedialog.h + build\moc_signverifymessagedialog.cpp;%(Outputs) + build\moc_signverifymessagedialog.cpp;%(Outputs) + ..\..\src\qt\signverifymessagedialog.h;%(AdditionalInputs) + ..\..\src\qt\signverifymessagedialog.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\signverifymessagedialog.h -o build\moc_signverifymessagedialog.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\signverifymessagedialog.h -o build\moc_signverifymessagedialog.cpp + MOC ..\..\src\qt\signverifymessagedialog.h + MOC ..\..\src\qt\signverifymessagedialog.h + build\moc_signverifymessagedialog.cpp;%(Outputs) + build\moc_signverifymessagedialog.cpp;%(Outputs) + + + + + ..\..\src\qt\transactiondesc.h;%(AdditionalInputs) + ..\..\src\qt\transactiondesc.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\transactiondesc.h -o build\moc_transactiondesc.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\transactiondesc.h -o build\moc_transactiondesc.cpp + MOC ..\..\src\qt\transactiondesc.h + MOC ..\..\src\qt\transactiondesc.h + build\moc_transactiondesc.cpp;%(Outputs) + build\moc_transactiondesc.cpp;%(Outputs) + ..\..\src\qt\transactiondesc.h;%(AdditionalInputs) + ..\..\src\qt\transactiondesc.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\transactiondesc.h -o build\moc_transactiondesc.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\transactiondesc.h -o build\moc_transactiondesc.cpp + MOC ..\..\src\qt\transactiondesc.h + MOC ..\..\src\qt\transactiondesc.h + build\moc_transactiondesc.cpp;%(Outputs) + build\moc_transactiondesc.cpp;%(Outputs) + + + ..\..\src\qt\transactiondescdialog.h;%(AdditionalInputs) + ..\..\src\qt\transactiondescdialog.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\transactiondescdialog.h -o build\moc_transactiondescdialog.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\transactiondescdialog.h -o build\moc_transactiondescdialog.cpp + MOC ..\..\src\qt\transactiondescdialog.h + MOC ..\..\src\qt\transactiondescdialog.h + build\moc_transactiondescdialog.cpp;%(Outputs) + build\moc_transactiondescdialog.cpp;%(Outputs) + ..\..\src\qt\transactiondescdialog.h;%(AdditionalInputs) + ..\..\src\qt\transactiondescdialog.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\transactiondescdialog.h -o build\moc_transactiondescdialog.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\transactiondescdialog.h -o build\moc_transactiondescdialog.cpp + MOC ..\..\src\qt\transactiondescdialog.h + MOC ..\..\src\qt\transactiondescdialog.h + build\moc_transactiondescdialog.cpp;%(Outputs) + build\moc_transactiondescdialog.cpp;%(Outputs) + + + ..\..\src\qt\transactionfilterproxy.h;%(AdditionalInputs) + ..\..\src\qt\transactionfilterproxy.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\transactionfilterproxy.h -o build\moc_transactionfilterproxy.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\transactionfilterproxy.h -o build\moc_transactionfilterproxy.cpp + MOC ..\..\src\qt\transactionfilterproxy.h + MOC ..\..\src\qt\transactionfilterproxy.h + build\moc_transactionfilterproxy.cpp;%(Outputs) + build\moc_transactionfilterproxy.cpp;%(Outputs) + ..\..\src\qt\transactionfilterproxy.h;%(AdditionalInputs) + ..\..\src\qt\transactionfilterproxy.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\transactionfilterproxy.h -o build\moc_transactionfilterproxy.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\transactionfilterproxy.h -o build\moc_transactionfilterproxy.cpp + MOC ..\..\src\qt\transactionfilterproxy.h + MOC ..\..\src\qt\transactionfilterproxy.h + build\moc_transactionfilterproxy.cpp;%(Outputs) + build\moc_transactionfilterproxy.cpp;%(Outputs) + + + + ..\..\src\qt\transactiontablemodel.h;%(AdditionalInputs) + ..\..\src\qt\transactiontablemodel.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\transactiontablemodel.h -o build\moc_transactiontablemodel.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\transactiontablemodel.h -o build\moc_transactiontablemodel.cpp + MOC ..\..\src\qt\transactiontablemodel.h + MOC ..\..\src\qt\transactiontablemodel.h + build\moc_transactiontablemodel.cpp;%(Outputs) + build\moc_transactiontablemodel.cpp;%(Outputs) + ..\..\src\qt\transactiontablemodel.h;%(AdditionalInputs) + ..\..\src\qt\transactiontablemodel.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\transactiontablemodel.h -o build\moc_transactiontablemodel.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\transactiontablemodel.h -o build\moc_transactiontablemodel.cpp + MOC ..\..\src\qt\transactiontablemodel.h + MOC ..\..\src\qt\transactiontablemodel.h + build\moc_transactiontablemodel.cpp;%(Outputs) + build\moc_transactiontablemodel.cpp;%(Outputs) + + + ..\..\src\qt\transactionview.h;%(AdditionalInputs) + ..\..\src\qt\transactionview.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\transactionview.h -o build\moc_transactionview.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\transactionview.h -o build\moc_transactionview.cpp + MOC ..\..\src\qt\transactionview.h + MOC ..\..\src\qt\transactionview.h + build\moc_transactionview.cpp;%(Outputs) + build\moc_transactionview.cpp;%(Outputs) + ..\..\src\qt\transactionview.h;%(AdditionalInputs) + ..\..\src\qt\transactionview.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\transactionview.h -o build\moc_transactionview.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\transactionview.h -o build\moc_transactionview.cpp + MOC ..\..\src\qt\transactionview.h + MOC ..\..\src\qt\transactionview.h + build\moc_transactionview.cpp;%(Outputs) + build\moc_transactionview.cpp;%(Outputs) + + + + + + + + + + + ..\..\src\qt\walletmodel.h;%(AdditionalInputs) + ..\..\src\qt\walletmodel.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\walletmodel.h -o build\moc_walletmodel.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\walletmodel.h -o build\moc_walletmodel.cpp + MOC ..\..\src\qt\walletmodel.h + MOC ..\..\src\qt\walletmodel.h + build\moc_walletmodel.cpp;%(Outputs) + build\moc_walletmodel.cpp;%(Outputs) + ..\..\src\qt\walletmodel.h;%(AdditionalInputs) + ..\..\src\qt\walletmodel.h;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2/qtbase/include/QtCore ..\..\src\qt\walletmodel.h -o build\moc_walletmodel.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\moc.exe -DUNICODE -DWIN32 -DQT_GUI -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_NETWORK_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1700 -D_WIN32 -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/mkspecs/win32-msvc2012 -IC:/MyProjects/XP/MSVC/myXPqt -IC:/MyProjects/XP/src -IC:/MyProjects/XP/src/json -IC:/MyProjects/XP/src/qt -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtWidgets -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtNetwork -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtGui -IC:/MyProjects/Deps/qt-everywhere-opensource-src-5.3.2-64/qtbase/include/QtCore ..\..\src\qt\walletmodel.h -o build\moc_walletmodel.cpp + MOC ..\..\src\qt\walletmodel.h + MOC ..\..\src\qt\walletmodel.h + build\moc_walletmodel.cpp;%(Outputs) + build\moc_walletmodel.cpp;%(Outputs) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + + + true + true + + + + + + + + + + + + + + + + + + + + + + + + + Document + ..\..\src\qt\forms\aboutdialog.ui;%(AdditionalInputs) + ..\..\src\qt\forms\aboutdialog.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\aboutdialog.ui -o build\ui_aboutdialog.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\aboutdialog.ui -o build\ui_aboutdialog.h + UIC ..\..\src\qt\forms\aboutdialog.ui + UIC ..\..\src\qt\forms\aboutdialog.ui + build\ui_aboutdialog.h;%(Outputs) + build\ui_aboutdialog.h;%(Outputs) + ..\..\src\qt\forms\aboutdialog.ui;%(AdditionalInputs) + ..\..\src\qt\forms\aboutdialog.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\aboutdialog.ui -o build\ui_aboutdialog.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\aboutdialog.ui -o build\ui_aboutdialog.h + UIC ..\..\src\qt\forms\aboutdialog.ui + UIC ..\..\src\qt\forms\aboutdialog.ui + build\ui_aboutdialog.h;%(Outputs) + build\ui_aboutdialog.h;%(Outputs) + + + Document + ..\..\src\qt\forms\addressbookpage.ui;%(AdditionalInputs) + ..\..\src\qt\forms\addressbookpage.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\addressbookpage.ui -o build\ui_addressbookpage.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\addressbookpage.ui -o build\ui_addressbookpage.h + UIC ..\..\src\qt\forms\addressbookpage.ui + UIC ..\..\src\qt\forms\addressbookpage.ui + build\ui_addressbookpage.h;%(Outputs) + build\ui_addressbookpage.h;%(Outputs) + ..\..\src\qt\forms\addressbookpage.ui;%(AdditionalInputs) + ..\..\src\qt\forms\addressbookpage.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\addressbookpage.ui -o build\ui_addressbookpage.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\addressbookpage.ui -o build\ui_addressbookpage.h + UIC ..\..\src\qt\forms\addressbookpage.ui + UIC ..\..\src\qt\forms\addressbookpage.ui + build\ui_addressbookpage.h;%(Outputs) + build\ui_addressbookpage.h;%(Outputs) + + + Document + ..\..\src\qt\forms\askpassphrasedialog.ui;%(AdditionalInputs) + ..\..\src\qt\forms\askpassphrasedialog.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\askpassphrasedialog.ui -o build\ui_askpassphrasedialog.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\askpassphrasedialog.ui -o build\ui_askpassphrasedialog.h + UIC ..\..\src\qt\forms\askpassphrasedialog.ui + UIC ..\..\src\qt\forms\askpassphrasedialog.ui + build\ui_askpassphrasedialog.h;%(Outputs) + build\ui_askpassphrasedialog.h;%(Outputs) + ..\..\src\qt\forms\askpassphrasedialog.ui;%(AdditionalInputs) + ..\..\src\qt\forms\askpassphrasedialog.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\askpassphrasedialog.ui -o build\ui_askpassphrasedialog.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\askpassphrasedialog.ui -o build\ui_askpassphrasedialog.h + UIC ..\..\src\qt\forms\askpassphrasedialog.ui + UIC ..\..\src\qt\forms\askpassphrasedialog.ui + build\ui_askpassphrasedialog.h;%(Outputs) + build\ui_askpassphrasedialog.h;%(Outputs) + + + Document + ..\..\src\qt\forms\coincontroldialog.ui;%(AdditionalInputs) + ..\..\src\qt\forms\coincontroldialog.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\coincontroldialog.ui -o build\ui_coincontroldialog.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\coincontroldialog.ui -o build\ui_coincontroldialog.h + UIC ..\..\src\qt\forms\coincontroldialog.ui + UIC ..\..\src\qt\forms\coincontroldialog.ui + build\ui_coincontroldialog.h;%(Outputs) + build\ui_coincontroldialog.h;%(Outputs) + ..\..\src\qt\forms\coincontroldialog.ui;%(AdditionalInputs) + ..\..\src\qt\forms\coincontroldialog.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\coincontroldialog.ui -o build\ui_coincontroldialog.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\coincontroldialog.ui -o build\ui_coincontroldialog.h + UIC ..\..\src\qt\forms\coincontroldialog.ui + UIC ..\..\src\qt\forms\coincontroldialog.ui + build\ui_coincontroldialog.h;%(Outputs) + build\ui_coincontroldialog.h;%(Outputs) + + + Document + ..\..\src\qt\forms\editaddressdialog.ui;%(AdditionalInputs) + ..\..\src\qt\forms\editaddressdialog.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\editaddressdialog.ui -o build\ui_editaddressdialog.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\editaddressdialog.ui -o build\ui_editaddressdialog.h + UIC ..\..\src\qt\forms\editaddressdialog.ui + UIC ..\..\src\qt\forms\editaddressdialog.ui + build\ui_editaddressdialog.h;%(Outputs) + build\ui_editaddressdialog.h;%(Outputs) + ..\..\src\qt\forms\editaddressdialog.ui;%(AdditionalInputs) + ..\..\src\qt\forms\editaddressdialog.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\editaddressdialog.ui -o build\ui_editaddressdialog.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\editaddressdialog.ui -o build\ui_editaddressdialog.h + UIC ..\..\src\qt\forms\editaddressdialog.ui + UIC ..\..\src\qt\forms\editaddressdialog.ui + build\ui_editaddressdialog.h;%(Outputs) + build\ui_editaddressdialog.h;%(Outputs) + + + Document + ..\..\src\qt\forms\intro.ui;%(AdditionalInputs) + ..\..\src\qt\forms\intro.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\intro.ui -o build\ui_intro.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\intro.ui -o build\ui_intro.h + UIC ..\..\src\qt\forms\intro.ui + UIC ..\..\src\qt\forms\intro.ui + build\ui_intro.h;%(Outputs) + build\ui_intro.h;%(Outputs) + ..\..\src\qt\forms\intro.ui;%(AdditionalInputs) + ..\..\src\qt\forms\intro.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\intro.ui -o build\ui_intro.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\intro.ui -o build\ui_intro.h + UIC ..\..\src\qt\forms\intro.ui + UIC ..\..\src\qt\forms\intro.ui + build\ui_intro.h;%(Outputs) + build\ui_intro.h;%(Outputs) + + + Document + ..\..\src\qt\forms\multisigaddressentry.ui;%(AdditionalInputs) + ..\..\src\qt\forms\multisigaddressentry.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\multisigaddressentry.ui -o build\ui_multisigaddressentry.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\multisigaddressentry.ui -o build\ui_multisigaddressentry.h + UIC ..\..\src\qt\forms\multisigaddressentry.ui + UIC ..\..\src\qt\forms\multisigaddressentry.ui + build\ui_multisigaddressentry.h;%(Outputs) + build\ui_multisigaddressentry.h;%(Outputs) + ..\..\src\qt\forms\multisigaddressentry.ui;%(AdditionalInputs) + ..\..\src\qt\forms\multisigaddressentry.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\multisigaddressentry.ui -o build\ui_multisigaddressentry.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\multisigaddressentry.ui -o build\ui_multisigaddressentry.h + UIC ..\..\src\qt\forms\multisigaddressentry.ui + UIC ..\..\src\qt\forms\multisigaddressentry.ui + build\ui_multisigaddressentry.h;%(Outputs) + build\ui_multisigaddressentry.h;%(Outputs) + + + Document + ..\..\src\qt\forms\multisigdialog.ui;%(AdditionalInputs) + ..\..\src\qt\forms\multisigdialog.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\multisigdialog.ui -o build\ui_multisigdialog.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\multisigdialog.ui -o build\ui_multisigdialog.h + UIC ..\..\src\qt\forms\multisigdialog.ui + UIC ..\..\src\qt\forms\multisigdialog.ui + build\ui_multisigdialog.h;%(Outputs) + build\ui_multisigdialog.h;%(Outputs) + ..\..\src\qt\forms\multisigdialog.ui;%(AdditionalInputs) + ..\..\src\qt\forms\multisigdialog.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\multisigdialog.ui -o build\ui_multisigdialog.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\multisigdialog.ui -o build\ui_multisigdialog.h + UIC ..\..\src\qt\forms\multisigdialog.ui + UIC ..\..\src\qt\forms\multisigdialog.ui + build\ui_multisigdialog.h;%(Outputs) + build\ui_multisigdialog.h;%(Outputs) + + + Document + ..\..\src\qt\forms\multisiginputentry.ui;%(AdditionalInputs) + ..\..\src\qt\forms\multisiginputentry.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\multisiginputentry.ui -o build\ui_multisiginputentry.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\multisiginputentry.ui -o build\ui_multisiginputentry.h + UIC ..\..\src\qt\forms\multisiginputentry.ui + UIC ..\..\src\qt\forms\multisiginputentry.ui + build\ui_multisiginputentry.h;%(Outputs) + build\ui_multisiginputentry.h;%(Outputs) + ..\..\src\qt\forms\multisiginputentry.ui;%(AdditionalInputs) + ..\..\src\qt\forms\multisiginputentry.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\multisiginputentry.ui -o build\ui_multisiginputentry.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\multisiginputentry.ui -o build\ui_multisiginputentry.h + UIC ..\..\src\qt\forms\multisiginputentry.ui + UIC ..\..\src\qt\forms\multisiginputentry.ui + build\ui_multisiginputentry.h;%(Outputs) + build\ui_multisiginputentry.h;%(Outputs) + + + Document + ..\..\src\qt\forms\optionsdialog.ui;%(AdditionalInputs) + ..\..\src\qt\forms\optionsdialog.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\optionsdialog.ui -o build\ui_optionsdialog.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\optionsdialog.ui -o build\ui_optionsdialog.h + UIC ..\..\src\qt\forms\optionsdialog.ui + UIC ..\..\src\qt\forms\optionsdialog.ui + build\ui_optionsdialog.h;%(Outputs) + build\ui_optionsdialog.h;%(Outputs) + ..\..\src\qt\forms\optionsdialog.ui;%(AdditionalInputs) + ..\..\src\qt\forms\optionsdialog.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\optionsdialog.ui -o build\ui_optionsdialog.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\optionsdialog.ui -o build\ui_optionsdialog.h + UIC ..\..\src\qt\forms\optionsdialog.ui + UIC ..\..\src\qt\forms\optionsdialog.ui + build\ui_optionsdialog.h;%(Outputs) + build\ui_optionsdialog.h;%(Outputs) + + + Document + ..\..\src\qt\forms\overviewpage.ui;%(AdditionalInputs) + ..\..\src\qt\forms\overviewpage.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\overviewpage.ui -o build\ui_overviewpage.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\overviewpage.ui -o build\ui_overviewpage.h + UIC ..\..\src\qt\forms\overviewpage.ui + UIC ..\..\src\qt\forms\overviewpage.ui + build\ui_overviewpage.h;%(Outputs) + build\ui_overviewpage.h;%(Outputs) + ..\..\src\qt\forms\overviewpage.ui;%(AdditionalInputs) + ..\..\src\qt\forms\overviewpage.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\overviewpage.ui -o build\ui_overviewpage.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\overviewpage.ui -o build\ui_overviewpage.h + UIC ..\..\src\qt\forms\overviewpage.ui + UIC ..\..\src\qt\forms\overviewpage.ui + build\ui_overviewpage.h;%(Outputs) + build\ui_overviewpage.h;%(Outputs) + + + Document + ..\..\src\qt\forms\qrcodedialog.ui;%(AdditionalInputs) + ..\..\src\qt\forms\qrcodedialog.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\qrcodedialog.ui -o build\ui_qrcodedialog.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\qrcodedialog.ui -o build\ui_qrcodedialog.h + UIC ..\..\src\qt\forms\qrcodedialog.ui + UIC ..\..\src\qt\forms\qrcodedialog.ui + build\ui_qrcodedialog.h;%(Outputs) + build\ui_qrcodedialog.h;%(Outputs) + ..\..\src\qt\forms\qrcodedialog.ui;%(AdditionalInputs) + ..\..\src\qt\forms\qrcodedialog.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\qrcodedialog.ui -o build\ui_qrcodedialog.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\qrcodedialog.ui -o build\ui_qrcodedialog.h + UIC ..\..\src\qt\forms\qrcodedialog.ui + UIC ..\..\src\qt\forms\qrcodedialog.ui + build\ui_qrcodedialog.h;%(Outputs) + build\ui_qrcodedialog.h;%(Outputs) + + + Document + ..\..\src\qt\forms\rpcconsole.ui;%(AdditionalInputs) + ..\..\src\qt\forms\rpcconsole.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\rpcconsole.ui -o build\ui_rpcconsole.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\rpcconsole.ui -o build\ui_rpcconsole.h + UIC ..\..\src\qt\forms\rpcconsole.ui + UIC ..\..\src\qt\forms\rpcconsole.ui + build\ui_rpcconsole.h;%(Outputs) + build\ui_rpcconsole.h;%(Outputs) + ..\..\src\qt\forms\rpcconsole.ui;%(AdditionalInputs) + ..\..\src\qt\forms\rpcconsole.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\rpcconsole.ui -o build\ui_rpcconsole.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\rpcconsole.ui -o build\ui_rpcconsole.h + UIC ..\..\src\qt\forms\rpcconsole.ui + UIC ..\..\src\qt\forms\rpcconsole.ui + build\ui_rpcconsole.h;%(Outputs) + build\ui_rpcconsole.h;%(Outputs) + + + Document + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\secondauthdialog.ui -o build\ui_secondauthdialog.h + build\ui_secondauthdialog.h;%(Outputs) + ..\..\src\qt\forms\secondauthdialog.ui;%(AdditionalInputs) + UIC ..\..\src\qt\forms\secondauthdialog.ui + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\secondauthdialog.ui -o build\ui_secondauthdialog.h + UIC ..\..\src\qt\forms\secondauthdialog.ui + build\ui_secondauthdialog.h;%(Outputs) + ..\..\src\qt\forms\secondauthdialog.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\secondauthdialog.ui -o build\ui_secondauthdialog.h + UIC ..\..\src\qt\forms\secondauthdialog.ui + build\ui_secondauthdialog.h;%(Outputs) + ..\..\src\qt\forms\secondauthdialog.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\secondauthdialog.ui -o build\ui_secondauthdialog.h + UIC ..\..\src\qt\forms\secondauthdialog.ui + build\ui_secondauthdialog.h;%(Outputs) + ..\..\src\qt\forms\secondauthdialog.ui;%(AdditionalInputs) + + + Document + ..\..\src\qt\forms\sendcoinsdialog.ui;%(AdditionalInputs) + ..\..\src\qt\forms\sendcoinsdialog.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\sendcoinsdialog.ui -o build\ui_sendcoinsdialog.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\sendcoinsdialog.ui -o build\ui_sendcoinsdialog.h + UIC ..\..\src\qt\forms\sendcoinsdialog.ui + UIC ..\..\src\qt\forms\sendcoinsdialog.ui + build\ui_sendcoinsdialog.h;%(Outputs) + build\ui_sendcoinsdialog.h;%(Outputs) + ..\..\src\qt\forms\sendcoinsdialog.ui;%(AdditionalInputs) + ..\..\src\qt\forms\sendcoinsdialog.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\sendcoinsdialog.ui -o build\ui_sendcoinsdialog.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\sendcoinsdialog.ui -o build\ui_sendcoinsdialog.h + UIC ..\..\src\qt\forms\sendcoinsdialog.ui + UIC ..\..\src\qt\forms\sendcoinsdialog.ui + build\ui_sendcoinsdialog.h;%(Outputs) + build\ui_sendcoinsdialog.h;%(Outputs) + + + Document + ..\..\src\qt\forms\sendcoinsentry.ui;%(AdditionalInputs) + ..\..\src\qt\forms\sendcoinsentry.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\sendcoinsentry.ui -o build\ui_sendcoinsentry.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\sendcoinsentry.ui -o build\ui_sendcoinsentry.h + UIC ..\..\src\qt\forms\sendcoinsentry.ui + UIC ..\..\src\qt\forms\sendcoinsentry.ui + build\ui_sendcoinsentry.h;%(Outputs) + build\ui_sendcoinsentry.h;%(Outputs) + ..\..\src\qt\forms\sendcoinsentry.ui;%(AdditionalInputs) + ..\..\src\qt\forms\sendcoinsentry.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\sendcoinsentry.ui -o build\ui_sendcoinsentry.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\sendcoinsentry.ui -o build\ui_sendcoinsentry.h + UIC ..\..\src\qt\forms\sendcoinsentry.ui + UIC ..\..\src\qt\forms\sendcoinsentry.ui + build\ui_sendcoinsentry.h;%(Outputs) + build\ui_sendcoinsentry.h;%(Outputs) + + + Document + ..\..\src\qt\forms\signverifymessagedialog.ui;%(AdditionalInputs) + ..\..\src\qt\forms\signverifymessagedialog.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\signverifymessagedialog.ui -o build\ui_signverifymessagedialog.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\signverifymessagedialog.ui -o build\ui_signverifymessagedialog.h + UIC ..\..\src\qt\forms\signverifymessagedialog.ui + UIC ..\..\src\qt\forms\signverifymessagedialog.ui + build\ui_signverifymessagedialog.h;%(Outputs) + build\ui_signverifymessagedialog.h;%(Outputs) + ..\..\src\qt\forms\signverifymessagedialog.ui;%(AdditionalInputs) + ..\..\src\qt\forms\signverifymessagedialog.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\signverifymessagedialog.ui -o build\ui_signverifymessagedialog.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\signverifymessagedialog.ui -o build\ui_signverifymessagedialog.h + UIC ..\..\src\qt\forms\signverifymessagedialog.ui + UIC ..\..\src\qt\forms\signverifymessagedialog.ui + build\ui_signverifymessagedialog.h;%(Outputs) + build\ui_signverifymessagedialog.h;%(Outputs) + + + Document + ..\..\src\qt\forms\transactiondescdialog.ui;%(AdditionalInputs) + ..\..\src\qt\forms\transactiondescdialog.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\transactiondescdialog.ui -o build\ui_transactiondescdialog.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\transactiondescdialog.ui -o build\ui_transactiondescdialog.h + UIC ..\..\src\qt\forms\transactiondescdialog.ui + UIC ..\..\src\qt\forms\transactiondescdialog.ui + build\ui_transactiondescdialog.h;%(Outputs) + build\ui_transactiondescdialog.h;%(Outputs) + ..\..\src\qt\forms\transactiondescdialog.ui;%(AdditionalInputs) + ..\..\src\qt\forms\transactiondescdialog.ui;%(AdditionalInputs) + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\uic.exe ..\..\src\qt\forms\transactiondescdialog.ui -o build\ui_transactiondescdialog.h + (set QT_PLUGIN_PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\plugins) & (set PATH=C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\lib;%PATH:)=^)%) & C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\uic.exe ..\..\src\qt\forms\transactiondescdialog.ui -o build\ui_transactiondescdialog.h + UIC ..\..\src\qt\forms\transactiondescdialog.ui + UIC ..\..\src\qt\forms\transactiondescdialog.ui + build\ui_transactiondescdialog.h;%(Outputs) + build\ui_transactiondescdialog.h;%(Outputs) + + + + + + + + Document + ..\..\src\qt\bitcoin.qrc;..\..\src\qt\res\movies\update_spinner.mng;..\..\src\qt\res\images\about.png;..\..\src\qt\res\images\splash2.jpg;..\..\src\qt\locale\bitcoin_en.qm;..\..\src\qt\locale\bitcoin_ru.qm;..\..\src\qt\res\icons\quit.png;..\..\src\qt\res\icons\import.png;..\..\src\qt\res\icons\editcopy.png;..\..\src\qt\res\icons\add.png;..\..\src\qt\res\icons\transaction2.png;..\..\src\qt\res\icons\tx_inout.png;..\..\src\qt\res\icons\configure.png;..\..\src\qt\res\icons\receive.png;..\..\src\qt\res\icons\history.png;..\..\src\qt\res\icons\tx_output.png;..\..\src\qt\res\icons\key.png;..\..\src\qt\res\icons\connect0_16.png;..\..\src\qt\res\icons\connect1_16.png;..\..\src\qt\res\icons\connect2_16.png;..\..\src\qt\res\icons\XP-128.png;..\..\src\qt\res\icons\connect3_16.png;..\..\src\qt\res\icons\connect4_16.png;..\..\src\qt\res\icons\remove.png;..\..\src\qt\res\icons\mining_inactive.png;..\..\src\qt\res\icons\debugwindow.png;..\..\src\qt\res\icons\dump.png;..\..\src\qt\res\icons\edit.png;..\..\src\qt\res\icons\transaction0.png;..\..\src\qt\res\icons\tx_input.png;..\..\src\qt\res\icons\clock1.png;..\..\src\qt\res\icons\lock_open.png;..\..\src\qt\res\icons\clock2.png;..\..\src\qt\res\icons\overview.png;..\..\src\qt\res\icons\qrcode.png;..\..\src\qt\res\icons\clock3.png;..\..\src\qt\res\icons\tx_mined.png;..\..\src\qt\res\icons\clock4.png;..\..\src\qt\res\icons\address-book.png;..\..\src\qt\res\icons\clock5.png;..\..\src\qt\res\icons\mining_active.png;..\..\src\qt\res\icons\synced.png;..\..\src\qt\res\icons\export.png;..\..\src\qt\res\icons\XP-16.png;..\..\src\qt\res\icons\filesave.png;..\..\src\qt\res\icons\send.png;..\..\src\qt\res\icons\editpaste.png;..\..\src\qt\res\icons\lock_closed.png;%(AdditionalInputs) + ..\..\src\qt\bitcoin.qrc;..\..\src\qt\res\movies\update_spinner.mng;..\..\src\qt\res\images\about.png;..\..\src\qt\res\images\splash2.jpg;..\..\src\qt\locale\bitcoin_en.qm;..\..\src\qt\locale\bitcoin_ru.qm;..\..\src\qt\res\icons\quit.png;..\..\src\qt\res\icons\import.png;..\..\src\qt\res\icons\editcopy.png;..\..\src\qt\res\icons\add.png;..\..\src\qt\res\icons\transaction2.png;..\..\src\qt\res\icons\tx_inout.png;..\..\src\qt\res\icons\configure.png;..\..\src\qt\res\icons\receive.png;..\..\src\qt\res\icons\history.png;..\..\src\qt\res\icons\tx_output.png;..\..\src\qt\res\icons\key.png;..\..\src\qt\res\icons\connect0_16.png;..\..\src\qt\res\icons\connect1_16.png;..\..\src\qt\res\icons\connect2_16.png;..\..\src\qt\res\icons\XP-128.png;..\..\src\qt\res\icons\connect3_16.png;..\..\src\qt\res\icons\connect4_16.png;..\..\src\qt\res\icons\remove.png;..\..\src\qt\res\icons\mining_inactive.png;..\..\src\qt\res\icons\debugwindow.png;..\..\src\qt\res\icons\dump.png;..\..\src\qt\res\icons\edit.png;..\..\src\qt\res\icons\transaction0.png;..\..\src\qt\res\icons\tx_input.png;..\..\src\qt\res\icons\clock1.png;..\..\src\qt\res\icons\lock_open.png;..\..\src\qt\res\icons\clock2.png;..\..\src\qt\res\icons\overview.png;..\..\src\qt\res\icons\qrcode.png;..\..\src\qt\res\icons\clock3.png;..\..\src\qt\res\icons\tx_mined.png;..\..\src\qt\res\icons\clock4.png;..\..\src\qt\res\icons\address-book.png;..\..\src\qt\res\icons\clock5.png;..\..\src\qt\res\icons\mining_active.png;..\..\src\qt\res\icons\synced.png;..\..\src\qt\res\icons\export.png;..\..\src\qt\res\icons\XP-16.png;..\..\src\qt\res\icons\filesave.png;..\..\src\qt\res\icons\send.png;..\..\src\qt\res\icons\editpaste.png;..\..\src\qt\res\icons\lock_closed.png;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\rcc.exe -name bitcoin ..\..\src\qt\bitcoin.qrc -o debug\qrc_bitcoin.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\rcc.exe -name bitcoin ..\..\src\qt\bitcoin.qrc -o debug\qrc_bitcoin.cpp + RCC ..\..\src\qt\bitcoin.qrc + RCC ..\..\src\qt\bitcoin.qrc + debug\qrc_bitcoin.cpp;%(Outputs) + debug\qrc_bitcoin.cpp;%(Outputs) + ..\..\src\qt\bitcoin.qrc;..\..\src\qt\res\movies\update_spinner.mng;..\..\src\qt\res\images\about.png;..\..\src\qt\res\images\splash2.jpg;..\..\src\qt\locale\bitcoin_en.qm;..\..\src\qt\locale\bitcoin_ru.qm;..\..\src\qt\res\icons\quit.png;..\..\src\qt\res\icons\import.png;..\..\src\qt\res\icons\editcopy.png;..\..\src\qt\res\icons\add.png;..\..\src\qt\res\icons\transaction2.png;..\..\src\qt\res\icons\tx_inout.png;..\..\src\qt\res\icons\configure.png;..\..\src\qt\res\icons\receive.png;..\..\src\qt\res\icons\history.png;..\..\src\qt\res\icons\tx_output.png;..\..\src\qt\res\icons\key.png;..\..\src\qt\res\icons\connect0_16.png;..\..\src\qt\res\icons\connect1_16.png;..\..\src\qt\res\icons\connect2_16.png;..\..\src\qt\res\icons\XP-128.png;..\..\src\qt\res\icons\connect3_16.png;..\..\src\qt\res\icons\connect4_16.png;..\..\src\qt\res\icons\remove.png;..\..\src\qt\res\icons\mining_inactive.png;..\..\src\qt\res\icons\debugwindow.png;..\..\src\qt\res\icons\dump.png;..\..\src\qt\res\icons\edit.png;..\..\src\qt\res\icons\transaction0.png;..\..\src\qt\res\icons\tx_input.png;..\..\src\qt\res\icons\clock1.png;..\..\src\qt\res\icons\lock_open.png;..\..\src\qt\res\icons\clock2.png;..\..\src\qt\res\icons\overview.png;..\..\src\qt\res\icons\qrcode.png;..\..\src\qt\res\icons\clock3.png;..\..\src\qt\res\icons\tx_mined.png;..\..\src\qt\res\icons\clock4.png;..\..\src\qt\res\icons\address-book.png;..\..\src\qt\res\icons\clock5.png;..\..\src\qt\res\icons\mining_active.png;..\..\src\qt\res\icons\synced.png;..\..\src\qt\res\icons\export.png;..\..\src\qt\res\icons\XP-16.png;..\..\src\qt\res\icons\filesave.png;..\..\src\qt\res\icons\send.png;..\..\src\qt\res\icons\editpaste.png;..\..\src\qt\res\icons\lock_closed.png;%(AdditionalInputs) + ..\..\src\qt\bitcoin.qrc;..\..\src\qt\res\movies\update_spinner.mng;..\..\src\qt\res\images\about.png;..\..\src\qt\res\images\splash2.jpg;..\..\src\qt\locale\bitcoin_en.qm;..\..\src\qt\locale\bitcoin_ru.qm;..\..\src\qt\res\icons\quit.png;..\..\src\qt\res\icons\import.png;..\..\src\qt\res\icons\editcopy.png;..\..\src\qt\res\icons\add.png;..\..\src\qt\res\icons\transaction2.png;..\..\src\qt\res\icons\tx_inout.png;..\..\src\qt\res\icons\configure.png;..\..\src\qt\res\icons\receive.png;..\..\src\qt\res\icons\history.png;..\..\src\qt\res\icons\tx_output.png;..\..\src\qt\res\icons\key.png;..\..\src\qt\res\icons\connect0_16.png;..\..\src\qt\res\icons\connect1_16.png;..\..\src\qt\res\icons\connect2_16.png;..\..\src\qt\res\icons\XP-128.png;..\..\src\qt\res\icons\connect3_16.png;..\..\src\qt\res\icons\connect4_16.png;..\..\src\qt\res\icons\remove.png;..\..\src\qt\res\icons\mining_inactive.png;..\..\src\qt\res\icons\debugwindow.png;..\..\src\qt\res\icons\dump.png;..\..\src\qt\res\icons\edit.png;..\..\src\qt\res\icons\transaction0.png;..\..\src\qt\res\icons\tx_input.png;..\..\src\qt\res\icons\clock1.png;..\..\src\qt\res\icons\lock_open.png;..\..\src\qt\res\icons\clock2.png;..\..\src\qt\res\icons\overview.png;..\..\src\qt\res\icons\qrcode.png;..\..\src\qt\res\icons\clock3.png;..\..\src\qt\res\icons\tx_mined.png;..\..\src\qt\res\icons\clock4.png;..\..\src\qt\res\icons\address-book.png;..\..\src\qt\res\icons\clock5.png;..\..\src\qt\res\icons\mining_active.png;..\..\src\qt\res\icons\synced.png;..\..\src\qt\res\icons\export.png;..\..\src\qt\res\icons\XP-16.png;..\..\src\qt\res\icons\filesave.png;..\..\src\qt\res\icons\send.png;..\..\src\qt\res\icons\editpaste.png;..\..\src\qt\res\icons\lock_closed.png;%(AdditionalInputs) + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2\qtbase\bin\rcc.exe -name bitcoin ..\..\src\qt\bitcoin.qrc -o release\qrc_bitcoin.cpp + C:\MyProjects\Deps\qt-everywhere-opensource-src-5.3.2-64\qtbase\bin\rcc.exe -name bitcoin ..\..\src\qt\bitcoin.qrc -o release\qrc_bitcoin.cpp + RCC ..\..\src\qt\bitcoin.qrc + RCC ..\..\src\qt\bitcoin.qrc + release\qrc_bitcoin.cpp;%(Outputs) + release\qrc_bitcoin.cpp;%(Outputs) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MSVC/mynovacoinqt/mynovacoinqt.vcxproj.filters b/MSVC/mynovacoinqt/mynovacoinqt.vcxproj.filters new file mode 100644 index 00000000..8cf6fc36 --- /dev/null +++ b/MSVC/mynovacoinqt/mynovacoinqt.vcxproj.filters @@ -0,0 +1,897 @@ + + + + + {99349809-55BA-4b9d-BF79-8FDBB0286EB3} + ui + false + + + {99349809-55BA-4b9d-BF79-8FDBB0286EB3} + ui + false + + + {71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11} + cpp;c;cxx;moc;h;def;odl;idl;res; + + + {71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11} + cpp;c;cxx;moc;h;def;odl;idl;res; + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} + qrc;* + false + + + {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} + qrc;* + false + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {639EADAA-A684-42e4-A9AD-28FC9BCB8F7C} + ts;xlf + false + + + {639EADAA-A684-42e4-A9AD-28FC9BCB8F7C} + ts;xlf + false + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Form Files + + + Form Files + + + Form Files + + + Header Files + + + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + + + Translation Files + + + Translation Files + + + Resource Files + + + + + Form Files + + + Form Files + + + Form Files + + + Form Files + + + Form Files + + + Form Files + + + Form Files + + + Form Files + + + Form Files + + + Form Files + + + Form Files + + + Form Files + + + Form Files + + + Form Files + + + Form Files + + + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + + + + Resource Files + + + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + + + Header Files + + + Header Files + + + Generated Files + + + \ No newline at end of file diff --git a/MSVC/mynovacoinqt/mynovacoinqt.vcxproj.user b/MSVC/mynovacoinqt/mynovacoinqt.vcxproj.user new file mode 100644 index 00000000..0b632739 --- /dev/null +++ b/MSVC/mynovacoinqt/mynovacoinqt.vcxproj.user @@ -0,0 +1,8 @@ + + + + + + WindowsLocalDebugger + + \ No newline at end of file diff --git a/MSVC/mynovacoinqt/mynovacoinqt_plugin_import.cpp b/MSVC/mynovacoinqt/mynovacoinqt_plugin_import.cpp new file mode 100644 index 00000000..d88e1c16 --- /dev/null +++ b/MSVC/mynovacoinqt/mynovacoinqt_plugin_import.cpp @@ -0,0 +1,16 @@ +// This file is autogenerated by qmake. It imports static plugin classes for +// static plugins specified using QTPLUGIN and QT_PLUGIN_CLASS. variables. +#include +Q_IMPORT_PLUGIN(AccessibleFactory) +Q_IMPORT_PLUGIN(QGenericEnginePlugin) +Q_IMPORT_PLUGIN(QNativeWifiEnginePlugin) +Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin) +Q_IMPORT_PLUGIN(QDDSPlugin) +Q_IMPORT_PLUGIN(QICNSPlugin) +Q_IMPORT_PLUGIN(QICOPlugin) +Q_IMPORT_PLUGIN(QJp2Plugin) +Q_IMPORT_PLUGIN(QMngPlugin) +Q_IMPORT_PLUGIN(QTgaPlugin) +Q_IMPORT_PLUGIN(QTiffPlugin) +Q_IMPORT_PLUGIN(QWbmpPlugin) +Q_IMPORT_PLUGIN(QWebpPlugin) diff --git a/MSVC/mynovacoinqt/mynovacoinqt_resource.rc b/MSVC/mynovacoinqt/mynovacoinqt_resource.rc new file mode 100644 index 00000000..c2b00607 --- /dev/null +++ b/MSVC/mynovacoinqt/mynovacoinqt_resource.rc @@ -0,0 +1,43 @@ +# if defined(UNDER_CE) +# include +# else +# include +# endif +#include "../../src/clientversion.h" // holds the needed client version information + +myXPqt_resource.rc += icon.rc + +#define VER_PRODUCTVERSION CLIENT_VERSION_MAJOR,CLIENT_VERSION_MINOR,CLIENT_VERSION_REVISION,CLIENT_VERSION_BUILD +#define VER_PRODUCTVERSION_STR STRINGIZE(CLIENT_VERSION_MAJOR) "." STRINGIZE(CLIENT_VERSION_MINOR) "." STRINGIZE(CLIENT_VERSION_REVISION) "." STRINGIZE(CLIENT_VERSION_BUILD) +#define VER_FILEVERSION VER_PRODUCTVERSION +#define VER_FILEVERSION_STR VER_PRODUCTVERSION_STR + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VER_FILEVERSION +PRODUCTVERSION VER_PRODUCTVERSION +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_APP +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" // U.S. English - multilingual (hex) + BEGIN + VALUE "CompanyName", "XP" + VALUE "FileDescription", "XP-Qt (OSS GUI client for XP)" + VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "InternalName", "XP-qt" + VALUE "LegalCopyright", "2009-2012 The Bitcoin developers, 2012-2014 The XP & PPCoin developers" + VALUE "LegalTrademarks1", "Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php." + VALUE "OriginalFilename", "XP-qt.exe" + VALUE "ProductName", "XP-Qt" + VALUE "ProductVersion", VER_PRODUCTVERSION_STR + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1252 // language neutral - multilingual (decimal) + END +END +/* End of Version info */ + diff --git a/MSVC/mynovacoinqt/novacoin.ico b/MSVC/mynovacoinqt/novacoin.ico new file mode 100644 index 0000000000000000000000000000000000000000..ee62b47e7e7ec146ecc0fd773c487b6f960e507c GIT binary patch literal 15086 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 p+jZBr) { + rename $f, "$f.orig"; + open FIN, "$f.orig"; + open FOUT, ">$f"; + my $inmacro = 0; + my %macros = (); + while () { + if (m/^\.macro\s+([_0-9A-Z]+)(?:\s*)(.*)$/i) { + print FOUT "#define $1($2) \\\n"; + $macros{$1} = 1; + $inmacro = 1; + next; + } + if (m/^\.endm/) { + print FOUT "\n"; + $inmacro = 0; + next; + } + for my $m (keys %macros) { + s/^([ \t]*)($m)(?:[ \t]+([^#\n]*))?([;\n])/\1\2(\3)\4/; + } + if ($inmacro) { + if (m/^\s*#if/) { + $_ = while (!m/^\s*#endif/); + next; + } + next if (m/^\s*$/); + s/\\//g; + s/$/; \\/; + } + print FOUT; + } + close FOUT; + close FIN; +} diff --git a/contrib/debian/bin/novacoin-qt b/contrib/debian/bin/novacoin-qt new file mode 100644 index 00000000..1ed4a770 --- /dev/null +++ b/contrib/debian/bin/novacoin-qt @@ -0,0 +1,16 @@ +#!/bin/sh + +set -e + +umask 077 + +basedir=~/.XP +dbfile="$basedir/DB_CONFIG" +cfgfile="$basedir/XP.conf" + +[ -e "$basedir" ] || mkdir "$basedir" + +# XP does not clean up DB log files by default +[ -e "$dbfile" ] || echo 'set_flags DB_LOG_AUTOREMOVE' > "$dbfile" + +exec /usr/lib/XP/XP-qt "$@" diff --git a/contrib/debian/bin/novacoind b/contrib/debian/bin/novacoind new file mode 100644 index 00000000..ab552302 --- /dev/null +++ b/contrib/debian/bin/novacoind @@ -0,0 +1,14 @@ +#!/bin/sh + +set -e + +umask 077 + +basedir=~/.XP +cfgfile="$basedir/XP.conf" + +[ -e "$basedir" ] || mkdir "$basedir" + +[ -e "$cfgfile" ] || perl -le 'print"rpcpassword=",map{(a..z,A..Z,0..9)[rand 62]}0..9' > "$cfgfile" + +exec /usr/lib/XP/XPd "$@" diff --git a/contrib/debian/changelog b/contrib/debian/changelog new file mode 100644 index 00000000..7f1973eb --- /dev/null +++ b/contrib/debian/changelog @@ -0,0 +1,6 @@ +XP (0.4.0~dfsg-1) unstable; urgency=low + + [ Balthazar ] + * Initial release. + + -- Balthazar Mon, 15 Apr 2013 22:15:22 +0100 diff --git a/contrib/debian/compat b/contrib/debian/compat new file mode 100644 index 00000000..7f8f011e --- /dev/null +++ b/contrib/debian/compat @@ -0,0 +1 @@ +7 diff --git a/contrib/debian/control b/contrib/debian/control new file mode 100644 index 00000000..da1deda9 --- /dev/null +++ b/contrib/debian/control @@ -0,0 +1,57 @@ +Source: XP +Section: utils +Priority: optional +Maintainer: Alex D. +Uploaders: Alex D. +Build-Depends: debhelper, + devscripts, + bash-completion, + libboost-system-dev, + libdb++-dev, + libssl-dev, + pkg-config, + libboost-filesystem-dev, + libboost-program-options-dev, + libboost-thread-dev, + qt4-qmake, + libqt4-dev, + libqrencode-dev +Standards-Version: 3.9.2 +Homepage: http://www.novaco.in/ +Vcs-Git: git://github.com/XP-project/XP.git +Vcs-Browser: http://github.com/XP-project/XP + +Package: XPd +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: peer-to-peer network based digital currency - daemon + XP is a free open source peer-to-peer electronic cash system that + is completely decentralized, without the need for a central server or + trusted parties. Users hold the crypto keys to their own money and + transact directly with each other, with the help of a P2P network to + check for double-spending. + . + By default connects to an IRC network to discover other peers. + . + Full transaction history is stored locally at each client. This + requires 150+ MiB of space, slowly growing. + . + This package provides XPd, a combined daemon and CLI tool to + interact with the daemon. + +Package: XP-qt +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: peer-to-peer network based digital currency - Qt GUI + XP is a free open source peer-to-peer electronic cash system that + is completely decentralized, without the need for a central server or + trusted parties. Users hold the crypto keys to their own money and + transact directly with each other, with the help of a P2P network to + check for double-spending. + . + By default connects to an IRC network to discover other peers. + . + Full transaction history is stored locally at each client. This + requires 150+ MiB of space, slowly growing. + . + This package provides XP-Qt, a GUI for XP based on Qt. diff --git a/contrib/debian/copyright b/contrib/debian/copyright new file mode 100644 index 00000000..41a8ac94 --- /dev/null +++ b/contrib/debian/copyright @@ -0,0 +1,169 @@ +Format: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?rev=174 +Upstream-Name: XP +Upstream-Contact: Alex D. +Source: http://sourceforge.net/projects/XP/files/ + https://github.com/XP-project/XP + +Files: * +Copyright: 2012-2015, XP Developers +License: Expat + +Files: * +Copyright: 2009-2015, Bitcoin Developers +License: Expat +Comment: The Bitcoin Developers encompasses the current developers listed on bitcoin.org, + as well as the numerous contributors to the project. + +Files: src/json/* +Copyright: 2007-2009, John W. Wilkinson +License: Expat + +Files: src/strlcpy.h +Copyright: 1998, Todd C. Miller +License: ISC + +Files: debian/* +Copyright: 2010-2011, Jonas Smedegaard + 2011, Matt Corallo +License: GPL-2+ + +Files: debian/manpages/* +Copyright: Micah Anderson +License: GPL-3+ + +Files: src/qt/res/icons/clock*.png, src/qt/res/icons/tx*.png, + src/qt/res/src/*.svg +Copyright: Wladimir van der Laan +License: Expat + +Files: src/qt/res/icons/address-book.png, src/qt/res/icons/export.png, + src/qt/res/icons/history.png, src/qt/res/icons/key.png, + src/qt/res/icons/lock_*.png, src/qt/res/icons/overview.png, + src/qt/res/icons/receive.png, src/qt/res/icons/send.png, + src/qt/res/icons/synced.png, src/qt/res/icons/filesave.png +Copyright: David Vignoni (david@icon-king.com) + ICON KING - www.icon-king.com +License: LGPL +Comment: NUVOLA ICON THEME for KDE 3.x + Original icons: kaddressbook, klipper_dock, view-list-text, + key-password, encrypted/decrypted, go-home, go-down, + go-next, dialog-ok + Site: http://www.icon-king.com/projects/nuvola/ + +Files: src/qt/res/icons/connect*.png +Copyright: schollidesign +License: GPL-3+ +Comment: Icon Pack: Human-O2 + Site: http://findicons.com/icon/93743/blocks_gnome_netstatus_0 + +Files: src/qt/res/icons/transaction*.png +Copyright: md2k7 +License: Expat +Comment: Site: https://bitcointalk.org/index.php?topic=15276.0 + +Files: 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/remove.png +Copyright: http://www.everaldo.com +License: LGPL +Comment: Icon Pack: Crystal SVG + +Files: src/qt/res/icons/bitcoin.png, src/qt/res/icons/toolbar.png +Copyright: Bitboy (optimized for 16x16 by Wladimir van der Laan) +License: PUB-DOM +Comment: Site: https://bitcointalk.org/?topic=1756.0 + +Files: scripts/img/reload.xcf, src/qt/res/movies/update_spinner.mng +Copyright: Everaldo (Everaldo Coelho) +License: GPL-3+ +Comment: Icon Pack: Kids + Site: http://findicons.com/icon/17102/reload?id=17102 + +Files: src/qt/res/images/splash2.jpg +License: PUB-DOM +Copyright: Crobbo (forum) +Comment: Site: https://bitcointalk.org/index.php?topic=32273.0 + + +License: Expat + 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. + +License: ISC + 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. + +License: GPL-2+ + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + . + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. +Comment: + On Debian systems the GNU General Public License (GPL) version 2 is + located in '/usr/share/common-licenses/GPL-2'. + . + You should have received a copy of the GNU General Public License along + with this program. If not, see . + +License: GPL-3+ + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU General Public License, Version 3 or any + later version published by the Free Software Foundation. +Comment: + On Debian systems the GNU General Public License (GPL) version 3 is + located in '/usr/share/common-licenses/GPL-3'. + . + You should have received a copy of the GNU General Public License along + with this program. If not, see . + +License: LGPL + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + . + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +Comment: + On Debian systems the GNU Lesser General Public License (LGPL) is + located in '/usr/share/common-licenses/LGPL'. + . + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +License: PUB-DOM + This work is in the public domain. diff --git a/contrib/debian/examples/novacoin.conf b/contrib/debian/examples/novacoin.conf new file mode 100644 index 00000000..d51a2973 --- /dev/null +++ b/contrib/debian/examples/novacoin.conf @@ -0,0 +1,75 @@ +# XP.conf configuration file. Lines beginning with # are comments. + + +# Network-related settings: + +# Run on the test network instead of the real XP network. +#testnet=1 + +# Connect via a socks4 proxy +#proxy=127.0.0.1:9050 + +# Use as many addnode= settings as you like to connect to specific peers +#addnode=81.200.241.54 + +# ... or use as many connect= settings as you like to connect ONLY +# to specific peers: +#connect=81.200.241.54 + +# Do not use Internet Relay Chat (irc.lfnet.org #XP channel) to +# find other peers. +#noirc=1 + +# Maximum number of inbound+outbound connections. +#maxconnections= + + +# JSON-RPC options (for controlling a running process) + +# server=1 tells XP to accept JSON-RPC commands. +#server=1 + +# You must set rpcuser and rpcpassword to secure the JSON-RPC api +# Remember, DON'T USE default password +#rpcuser=Ulysseys +#rpcpassword=YourSuperGreatPasswordNumber_385593 + +# By default, only RPC connections from localhost are allowed. Specify +# as many rpcallowip= settings as you like to allow connections from +# other hosts (and you may use * as a wildcard character): +#rpcallowip=10.1.1.34 +#rpcallowip=192.168.1.* + +# Listen for RPC connections on this TCP port: +rpcport=8344 + +# You can use XP or XPd to send commands to XP/XPd +# running on another host using this option: +rpcconnect=127.0.0.1 + +# Use Secure Sockets Layer (also known as TLS or HTTPS) to communicate +# with XP -server or XPd +#rpcssl=1 + +# OpenSSL settings used when rpcssl=1 +rpcsslciphers=TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH +rpcsslcertificatechainfile=server.cert +rpcsslprivatekeyfile=server.pem + + +# Miscellaneous options + +# Pre-generate this many public/private key pairs, so wallet backups will be valid for +# both prior transactions and several dozen future transactions. +keypool=100 + +# Pay an optional transaction fee every time you send XPs. +paytxfee=0.01 + +# User interface options + +# Start XP minimized +#min=1 + +# Minimize to the system tray +#minimizetotray=1 diff --git a/contrib/debian/gbp.conf b/contrib/debian/gbp.conf new file mode 100644 index 00000000..a7281f94 --- /dev/null +++ b/contrib/debian/gbp.conf @@ -0,0 +1,5 @@ +# Configuration file for git-buildpackage and friends + +[DEFAULT] +pristine-tar = True +sign-tags = True diff --git a/contrib/debian/manpages/novacoin.conf.5 b/contrib/debian/manpages/novacoin.conf.5 new file mode 100644 index 00000000..8ba38826 --- /dev/null +++ b/contrib/debian/manpages/novacoin.conf.5 @@ -0,0 +1,91 @@ +.TH XP.CONF "8" "April 2013" "XP.conf 3.19" +.SH NAME +XP.conf \- XP configuration file +.SH SYNOPSIS +All command-line options (except for '-datadir' and '-conf') may be specified in a configuration file, and all configuration file options may also be specified on the command line. Command-line options override values set in the configuration file. +.TP +The configuration file is a list of 'setting=value' pairs, one per line, with optional comments starting with the '#' character. +.TP +The configuration file is not automatically created; you can create it using your favorite plain-text editor. By default, XPd(1) will look for a file named XP.conf(5) in the XP data directory, but both the data directory and the configuration file path may be changed using the '-datadir' and '-conf' command-line arguments. +.SH LOCATION +XP.conf should be located in $HOME/.XP +.SH NETWORK-RELATED SETTINGS +.TP +.TP +\fBtestnet=\fR[\fI'1'\fR|\fI'0'\fR] +Enable or disable run on the test network instead of the real *XP* network. +.TP +\fBproxy=\fR\fI'127.0.0.1:9050'\fR +Connect via a socks4 proxy. +.TP +\fBaddnode=\fR\fI'10.0.0.2:8333'\fR +Use as many *addnode=* settings as you like to connect to specific peers. +.TP +\fBconnect=\fR\fI'10.0.0.1:8333'\fR +Use as many *connect=* settings as you like to connect ONLY to specific peers. +.TP +\fBnoirc=\fR[\fI'1'\fR|\fI'0'\fR] +Use or Do not use Internet Relay Chat (irc.lfnet.org #XP channel) to find other peers. +.TP +\fRmaxconnections=\fR\fI'value'\fR +Maximum number of inbound+outbound connections. +.SH JSON-RPC OPTIONS +.TP +\fBserver=\fR[\fI'1'\fR|\fI'0'\fR] +Tells *XP* to accept or not accept JSON-RPC commands. +.TP +\fBrpcuser=\fR\fI'username'\fR +You must set *rpcuser* to secure the JSON-RPC api. +.TP +\fBrpcpassword=\fR\fI'password'\fR +You must set *rpcpassword* to secure the JSON-RPC api. +.TP +\fBrpctimeout=\fR\fI'30'\fR +How many seconds *XP* will wait for a complete RPC HTTP request, after the HTTP connection is established. +.TP +\fBrpcallowip=\fR\fI'192.168.1.*'\fR +By default, only RPC connections from localhost are allowed. Specify as many *rpcallowip=* settings as you like to allow connections from other hosts (and you may use * as a wildcard character). +.TP +\fBrpcport=\fR\fI'8332'\fR +Listen for RPC connections on this TCP port. +.TP +\fBrpcconnect=\fR\fI'127.0.0.1'\fR +You can use *XP* or *XPd(1)* to send commands to *XP*/*XPd(1)* running on another host using this option. +.TP +\fBrpcssl=\fR\fI'1'\fR +Use Secure Sockets Layer (also known as TLS or HTTPS) to communicate with *XP* '-server' or *XPd(1)*. Example of OpenSSL settings used when *rpcssl*='1': +.TP +\fBrpcsslciphers=\fR\fI'TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH'\fR +.TP +\fBrpcsslcertificatechainfile=\fR\fI'server.cert'\fR +.TP +\fBrpcsslprivatekeyfile=\fR\fI'server.pem'\fR +.TP +.SH MISCELLANEOUS OPTIONS +.TP +\fBgen=\fR[\fI'0'\fR|\fI'1'\fR] +Enable or disable attempt to generate XPs. +.TP +\fBkeypool=\fR\fI'100'\fR +Pre-generate this many public/private key pairs, so wallet backups will be valid for both prior transactions and several dozen future transactions. +.TP +\fBpaytxfee=\fR\fI'0.00'\fR +Pay an optional transaction fee every time you send XPs. Transactions with fees are more likely than free transactions to be included in generated blocks, so may be validated sooner. +.TP +\fBallowreceivebyip=\fR\fI'1'\fR +Allow direct connections for the 'pay via IP address' feature. +.TP +.SH USER INTERFACE OPTIONS +.TP +\fBmin=\fR[\fI'0'\fR|\fI'1'\fR] +Enable or disable start XPd minimized. +.TP +\fBminimizetotray=\fR[\fI'0'\fR|\fI'1'\fR] +Enable or disable minimize to the system tray. +.SH "SEE ALSO" +XPd(1) +.SH AUTHOR +This manual page was written by Micah Anderson for the Debian system (but may be used by others). Permission is granted to copy, distribute and/or modify this document under the terms of the GNU General Public License, Version 3 or any later version published by the Free Software Foundation. + +On Debian systems, the complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL. + diff --git a/contrib/debian/manpages/novacoind.1 b/contrib/debian/manpages/novacoind.1 new file mode 100644 index 00000000..762a9bdb --- /dev/null +++ b/contrib/debian/manpages/novacoind.1 @@ -0,0 +1,206 @@ +.TH XPD "7" "April 2013" "XPd 3.19" +.SH NAME +XPd \- peer-to-peer network based digital currency +.SH SYNOPSIS +XP [options] [params] +.TP +XP [options] help - Get help for a command +.SH DESCRIPTION +This manual page documents the XPd program. XP is a peer-to-peer digital currency. Peer-to-peer (P2P) means that there is no central authority to issue new money or keep track of transactions. Instead, these tasks are managed collectively by the nodes of the network. Advantages: + +XPs can be sent easily through the Internet, without having to trust middlemen. Transactions are designed to be irreversible. Be safe from instability caused by fractional reserve banking and central banks. The limited inflation of the XP system’s money supply is distributed evenly (by CPU power) throughout the network, not monopolized by banks. + +.SH OPTIONS +.TP +\fB\-conf=\fR +Specify configuration file (default: XP.conf) +.TP +\fB\-gen\fR +Generate coins +.TP +\fB\-gen\fR=\fI0\fR +Don't generate coins +.TP +\fB\-min\fR +Start minimized +.TP +\fB\-datadir=\fR +Specify data directory +.TP +\fB\-proxy=\fR +Connect through socks4 proxy +.TP +\fB\-addnode=\fR +Add a node to connect to +.TP +\fB\-connect=\fR +Connect only to the specified node +.TP +\fB\-paytxfee=\fR +Fee per KB to add to transactions you send +.TP +\fB\-server\fR +Accept command line and JSON\-RPC commands +.TP +\fB\-daemon\fR +Run in the background as a daemon and accept commands +.TP +\fB\-testnet\fR +Use the test network +.TP +\fB\-rpcuser=\fR +Username for JSON\-RPC connections +.TP +\fB\-rpcpassword=\fR +Password for JSON\-RPC connections +.TP +\fB\-rpcport=\fR +Listen for JSON\-RPC connections on +.TP +\fB\-rpcallowip=\fR +Allow JSON\-RPC connections from specified IP address +.TP +\fB\-rpcconnect=\fR +Send commands to node running on +.PP +SSL options: (see the Bitcoin Wiki for SSL setup instructions) +.TP +\fB\-rpcssl\fR=\fI1\fR +Use OpenSSL (https) for JSON\-RPC connections +.TP +\fB\-rpcsslcertificatchainfile=\fR +Server certificate file (default: server.cert) +.TP +\fB\-rpcsslprivatekeyfile=\fR +Server private key (default: server.pem) +.TP +\fB\-rpcsslciphers=\fR +Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) +.TP +\-? +This help message +.SH COMMANDS +.TP +\fBbackupwallet 'destination'\fR +Safely copies *wallet.dat* to 'destination', which can be a directory or a path with filename. +.TP +\fBgetaccount 'XPaddress'\fR +Returns the account associated with the given address. +.TP +\fBsetaccount 'XPaddress' ['account']\fR +Sets the ['account'] associated with the given address. ['account'] may be omitted to remove an address from ['account']. +.TP +\fBgetaccountaddress 'account'\fR +Returns a new XP address for 'account'. +.TP +\fBgetaddressesbyaccount 'account'\fR +Returns the list of addresses associated with the given 'account'. +.TP +\fBgetbalance 'account'\fR +Returns the server's available balance, or the balance for 'account'. +.TP +\fBgetblockcount\fR +Returns the number of blocks in the longest block chain. +.TP +\fBgetblocknumber\fR +Returns the block number of the latest block in the longest block chain. +.TP +\fBgetconnectioncount\fR +Returns the number of connections to other nodes. +.TP +\fBgetdifficulty\fR +Returns the proof-of-work difficulty as a multiple of the minimum difficulty. +.TP +\fBgetgenerate\fR +Returns boolean true if server is trying to generate XPs, false otherwise. +.TP +\fBsetgenerate 'generate' ['genproclimit']\fR +Generation is limited to ['genproclimit'] processors, -1 is unlimited. +.TP +\fBgethashespersec\fR +Returns a recent hashes per second performance measurement while generating. +.TP +\fBgetinfo\fR +Returns an object containing server information. +.TP +\fBgetnewaddress 'account'\fR +Returns a new XP 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'. +.TP +\fBgetreceivedbyaccount 'account' ['minconf=1']\fR +Returns the total amount received by addresses associated with 'account' in transactions with at least ['minconf'] confirmations. +.TP +\fBgetreceivedbyaddress 'XPaddress' ['minconf=1']\fR +Returns the total amount received by 'XPaddress' in transactions with at least ['minconf'] confirmations. +.TP +\fBgettransaction 'txid'\fR +Returns information about a specific transaction, given hexadecimal transaction ID. +.TP +\fBgetwork 'data'\fR +If 'data' is specified, tries to solve the block and returns true if it was successful. If 'data' is not specified, returns formatted hash 'data' to work on: + + "midstate" : precomputed hash state after hashing the first half of the data. + "data" : block data. + "hash1" : formatted hash buffer for second hash. + "target" : little endian hash target. +.TP +\fBhelp 'command'\fR +List commands, or get help for a command. +.TP +\fBlistaccounts ['minconf=1']\fR +List accounts and their current balances. +.TP +\fBlistreceivedbyaccount ['minconf=1'] ['includeempty=false']\fR +['minconf'] is the minimum number of confirmations before payments are included. ['includeempty'] whether to include addresses that haven't received any payments. Returns an array of objects containing: + + "account" : the account of the receiving address. + "amount" : total amount received by the address. + "confirmations" : number of confirmations of the most recent transaction included. +.TP +\fBlistreceivedbyaddress ['minconf=1'] ['includeempty=false']\fR +['minconf'] is the minimum number of confirmations before payments are included. ['includeempty'] whether to include addresses that haven't received any payments. Returns an array of objects containing: + + "address" : receiving address. + "account" : the account of the receiving address. + "amount" : total amount received by the address. + "confirmations" : number of confirmations of the most recent transaction included. +.TP +\fBlisttransactions 'account' ['count=10']\fR +Returns a list of the last ['count'] transactions for 'account' - for all accounts if 'account' is not specified or is "*". Each entry in the list may contain: + + "category" : will be generate, send, receive, or move. + "amount" : amount of transaction. + "fee" : Fee (if any) paid (only for send transactions). + "confirmations" : number of confirmations (only for generate/send/receive). + "txid" : transaction ID (only for generate/send/receive). + "otheraccount" : account funds were moved to or from (only for move). + "message" : message associated with transaction (only for send). + "to" : message-to associated with transaction (only for send). +.TP +\fBmove <'fromaccount'> <'toaccount'> <'amount'> ['minconf=1'] ['comment']\fR +Moves funds between accounts. +.TP +\fBsendfrom* <'account'> <'XPaddress'> <'amount'> ['minconf=1'] ['comment'] ['comment-to']\fR +Sends amount from account's balance to 'XPaddress'. This method will fail if there is less than amount XPs with ['minconf'] confirmations in the account's balance (unless account is the empty-string-named default account; it behaves like the *sendtoaddress* method). Returns transaction ID on success. +.TP +\fBsendtoaddress 'XPaddress' 'amount' ['comment'] ['comment-to']\fR +Sends amount from the server's available balance to 'XPaddress'. amount is a real and is rounded to the nearest 0.01. Returns transaction id on success. +.TP +\fBstop\fR +Stops the XP server. +.TP +\fBvalidateaddress 'XPaddress'\fR +Checks that 'XPaddress' looks like a proper XP address. Returns an object containing: + + "isvalid" : true or false. + "ismine" : true if the address is in the server's wallet. + "address" : XPaddress. + + *note: ismine and address are only returned if the address is valid. + +.SH "SEE ALSO" +XP.conf(5) +.SH AUTHOR +This manual page was written by Micah Anderson for the Debian system (but may be used by others). Permission is granted to copy, distribute and/or modify this document under the terms of the GNU General Public License, Version 3 or any later version published by the Free Software Foundation. + +On Debian systems, the complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL. + diff --git a/contrib/debian/novacoin-qt.desktop b/contrib/debian/novacoin-qt.desktop new file mode 100644 index 00000000..1c5cac85 --- /dev/null +++ b/contrib/debian/novacoin-qt.desktop @@ -0,0 +1,13 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=XP +Comment=XP P2P Cryptocurrency +Comment[ru]=XP, P2P криптовалюта +Comment[fr]=XP, monnaie virtuelle cryptographique pair à pair +Comment[tr]=XP, eşten eşe kriptografik sanal para birimi +Exec=/usr/bin/XP-qt +Terminal=false +Type=Application +Icon=/usr/share/pixmaps/XP80.xpm +MimeType=x-scheme-handler/XP; +Categories=Office; diff --git a/contrib/debian/novacoin-qt.install b/contrib/debian/novacoin-qt.install new file mode 100644 index 00000000..2f262da5 --- /dev/null +++ b/contrib/debian/novacoin-qt.install @@ -0,0 +1,5 @@ +XP-qt usr/bin +share/pixmaps/XP32.xpm usr/share/pixmaps +share/pixmaps/XP80.xpm usr/share/pixmaps +debian/XP-qt.desktop usr/share/applications +debian/XP-qt.protocol usr/share/kde4/services/ diff --git a/contrib/debian/novacoin-qt.lintian-overrides b/contrib/debian/novacoin-qt.lintian-overrides new file mode 100644 index 00000000..1aac8dce --- /dev/null +++ b/contrib/debian/novacoin-qt.lintian-overrides @@ -0,0 +1,2 @@ +# Linked code is Expat - only Debian packaging is GPL-2+ +XP-qt: possible-gpl-code-linked-with-openssl diff --git a/contrib/debian/novacoin-qt.protocol b/contrib/debian/novacoin-qt.protocol new file mode 100644 index 00000000..20d66a1f --- /dev/null +++ b/contrib/debian/novacoin-qt.protocol @@ -0,0 +1,11 @@ +[Protocol] +exec=XP-qt '%u' +protocol=XP +input=none +output=none +helper=true +listing= +reading=false +writing=false +makedir=false +deleting=false diff --git a/contrib/debian/novacoind.examples b/contrib/debian/novacoind.examples new file mode 100644 index 00000000..4cf1f3e7 --- /dev/null +++ b/contrib/debian/novacoind.examples @@ -0,0 +1 @@ +debian/examples/XP.conf diff --git a/contrib/debian/novacoind.install b/contrib/debian/novacoind.install new file mode 100644 index 00000000..dea9f171 --- /dev/null +++ b/contrib/debian/novacoind.install @@ -0,0 +1,2 @@ +debian/bin/XPd usr/bin +src/XPd usr/lib/XP diff --git a/contrib/debian/novacoind.lintian-overrides b/contrib/debian/novacoind.lintian-overrides new file mode 100644 index 00000000..36a2deef --- /dev/null +++ b/contrib/debian/novacoind.lintian-overrides @@ -0,0 +1,2 @@ +# Linked code is Expat - only Debian packaging is GPL-2+ +XPd: possible-gpl-code-linked-with-openssl diff --git a/contrib/debian/novacoind.manpages b/contrib/debian/novacoind.manpages new file mode 100644 index 00000000..8c9629f9 --- /dev/null +++ b/contrib/debian/novacoind.manpages @@ -0,0 +1,2 @@ +debian/manpages/XPd.1 +debian/manpages/XP.conf.5 diff --git a/contrib/debian/patches/README b/contrib/debian/patches/README new file mode 100644 index 00000000..80c15843 --- /dev/null +++ b/contrib/debian/patches/README @@ -0,0 +1,3 @@ +0xxx: Grabbed from upstream development. +1xxx: Possibly relevant for upstream adoption. +2xxx: Only relevant for official Debian release. diff --git a/contrib/debian/patches/series b/contrib/debian/patches/series new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/contrib/debian/patches/series @@ -0,0 +1 @@ + diff --git a/contrib/debian/rules b/contrib/debian/rules new file mode 100644 index 00000000..aaab9048 --- /dev/null +++ b/contrib/debian/rules @@ -0,0 +1,25 @@ +#!/usr/bin/make -f +# -*- mode: makefile; coding: utf-8 -*- + +DEB_INSTALL_EXAMPLES_XPd += debian/examples/* +DEB_INSTALL_MANPAGES_XPd += debian/manpages/* + +%: + dh --with bash-completion $@ + +override_dh_auto_build: + cd src; $(MAKE) -f makefile.unix XPd USE_ASM=1 USE_LEVELDB=1 + $(MAKE) + +override_dh_auto_clean: + if [ -f Makefile ]; then $(MAKE) clean; else rm -rf build/; rm -f XP-qt; fi + cd src; $(MAKE) -f makefile.unix clean + +override_dh_auto_configure: + qmake XP-qt.pro USE_ASM=1 USE_LEVELDB=1 + +# Ensure wrapper is set executable +binary-post-install/XPd: + chmod +x $(cdbs_curdestdir)usr/bin/XPd +binary-post-install/XP-qt: + chmod +x $(cdbs_curdestdir)usr/bin/XP-qt diff --git a/contrib/debian/source/format b/contrib/debian/source/format new file mode 100644 index 00000000..163aaf8d --- /dev/null +++ b/contrib/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/contrib/debian/watch b/contrib/debian/watch new file mode 100644 index 00000000..1868674e --- /dev/null +++ b/contrib/debian/watch @@ -0,0 +1,4 @@ +# Run the "uscan" command to check for upstream updates and more. +version=4 +opts=uversionmangle=s/(\d)(alpha|beta|rc)/$1~$2/,dversionmangle=s/~dfsg\d*// \ + http://githubredir.debian.net/github/CryptoManiac/XP v(.*).tar.gz diff --git a/contrib/gentoo/novacoin-0.5.0.ebuild b/contrib/gentoo/novacoin-0.5.0.ebuild new file mode 100644 index 00000000..3fcd409a --- /dev/null +++ b/contrib/gentoo/novacoin-0.5.0.ebuild @@ -0,0 +1,107 @@ +# Distributed under the terms of the GNU General Public License v2 +EAPI=5 +LANGS="en ru" + +inherit db-use eutils fdo-mime gnome2-utils kde4-functions qt4-r2 + +DB_VER="4.8" + +DESCRIPTION="XP - a hybrid PoW+PoS energy efficient p2p-cryptocurrency and electronic payment system." +HOMEPAGE="https://novaco.in/" +SRC_URI="https://github.com/${PN}-project/${PN}/archive/nvc-v${PV}.tar.gz -> ${PN}-${PV}.tar.gz" + +LICENSE="MIT" +SLOT="0" +KEYWORDS="~amd64 ~x86" +IUSE="+dbus -ipv6 kde +qrcode" + +RDEPEND=" + dev-libs/boost[threads(+)] + dev-libs/openssl:0[-bindist] + qrcode? ( + media-gfx/qrencode + ) + sys-libs/db:$(db_ver_to_slot "${DB_VER}")[cxx] + dev-qt/qtgui:4 + dbus? ( + dev-qt/qtdbus:4 + ) +" +DEPEND="${RDEPEND} + >=app-shells/bash-4.1 + >sys-devel/gcc-4.3.3 + >=dev-libs/openssl-0.9.8g + >=sys-libs/db-4.8.30 + >=dev-libs/boost-1.37 +" + +DOCS="doc/translation_process.md" + +S="${WORKDIR}/${PN}-nvc-v${PV}" + +src_prepare() { + cd src || die + + local filt= yeslang= nolang= + + for ts in $(ls qt/locale/*.ts) + do + x="${ts/*bitcoin_/}" + x="${x/.ts/}" + if ! use "linguas_$x"; then + nolang="$nolang $x" + rm "$ts" + filt="$filt\\|$x" + else + yeslang="$yeslang $x" + fi + done + + filt="bitcoin_\\(${filt:2}\\)\\.\(qm\|ts\)" + sed "/${filt}/d" -i 'qt/bitcoin.qrc' + einfo "Languages -- Enabled:$yeslang -- Disabled:$nolang" +} + +src_configure() { + OPTS=() + + use dbus && OPTS+=("USE_DBUS=1") + use ipv6 || OPTS+=("USE_IPV6=-") + + OPTS+=("BDB_INCLUDE_PATH=$(db_includedir "${DB_VER}")") + OPTS+=("BDB_LIB_SUFFIX=-${DB_VER}") + + if has_version '>=dev-libs/boost-1.52'; then + OPTS+=("LIBS+=-lboost_chrono\$\$BOOST_LIB_SUFFIX") + fi + + eqmake4 ${PN}-qt.pro "${OPTS[@]}" +} + +src_install() { + dobin ${PN}-qt + + insinto /usr/share/pixmaps + newins "src/qt/res/icons/XP-128.png" "${PN}.png" + + make_desktop_entry "${PN}-qt" "XP" "/usr/share/pixmaps/${PN}.png" "Network;P2P;Finance;" + + if use kde; then + insinto /usr/share/kde4/services + newins contrib/debian/XP-qt.protocol ${PN}.protocol + fi +} + +update_caches() { + gnome2_icon_cache_update + fdo-mime_desktop_database_update + buildsycoca +} + +pkg_postinst() { + update_caches +} + +pkg_postrm() { + update_caches +} \ No newline at end of file diff --git a/contrib/initscripts/bsd/novacoin b/contrib/initscripts/bsd/novacoin new file mode 100644 index 00000000..ea4470a0 --- /dev/null +++ b/contrib/initscripts/bsd/novacoin @@ -0,0 +1,68 @@ +#!/bin/sh + +# PROVIDE: XP +# REQUIRE: LOGIN +# KEYWORD: shutdown + +# +# Add the following lines to /etc/rc.conf.local or /etc/rc.conf +# to enable this service: +# +# XP_enable (bool): Set to NO by default. Set this variable to YES if you want to enable XP service. +# XP_config (path): Set to /usr/local/etc/XP.conf by default. +# XP_user: The user account XP daemon runs as. It uses 'root' user by default. +# XP_group: The group account XP daemon runs as. It uses 'wheel' group by default. +# XP_datadir (str): Default to "/var/db/XP". Base data directory. + +. /etc/rc.subr + +name=XP +rcvar=XP_enable + +: ${XP_enable:=NO} +: ${XP_config=/usr/local/etc/XP.conf} +: ${XP_datadir=/var/db/XP} +: ${XP_user="root"} +: ${XP_group="wheel"} + +required_files=${XP_config} +command=/usr/local/bin/XPd +XP_chdir=${XP_datadir} +pidfile="${XP_datadir}/XPd.pid" +stop_cmd=XP_stop +command_args="-conf=${XP_config} -datadir=${XP_datadir} -daemon -pid=${pidfile}" +start_precmd="${name}_prestart" + +XP_create_datadir() +{ + echo "Creating data directory" + eval mkdir -p ${XP_datadir} + [ $? -eq 0 ] && chown -R ${XP_user}:${XP_group} ${XP_datadir} +} + +XP_prestart() +{ + if [ ! -d "${XP_datadir}/." ]; then + XP_create_datadir || return 1 + fi +} + +XP_requirepidfile() +{ + if [ ! "0`check_pidfile ${pidfile} ${command}`" -gt 1 ]; then + echo "${name} not running? (check $pidfile)." + exit 1 + fi +} + +XP_stop() +{ + XP_requirepidfile + + echo "Stopping ${name}." + eval ${command} -conf=${XP_config} -datadir=${XP_datadir} stop + wait_for_pids ${rc_pid} +} + +load_rc_config $name +run_rc_command "$1" diff --git a/contrib/initscripts/lsb/novacoind b/contrib/initscripts/lsb/novacoind new file mode 100644 index 00000000..2360bdc3 --- /dev/null +++ b/contrib/initscripts/lsb/novacoind @@ -0,0 +1,147 @@ +#!/bin/sh +### BEGIN INIT INFO +# Provides: XPd +# Required-Start: $network +# Required-Stop: $network +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: XPd daemon startup script +# Description: XPd daemon startup script +### END INIT INFO + +# Do NOT "set -e" + +# PATH should only include /usr/* if it runs after the mountnfs.sh script +PATH=/sbin:/usr/sbin:/bin:/usr/bin +DESC="XP Daemon" +NAME=XPd +DAEMON=/opt/XP/$NAME +PIDFILE=/var/run/$NAME.pid +SCRIPTNAME=/etc/init.d/$NAME +CHUID=XP:XP +DAEMON_ARGS="-daemon -pid=$PIDFILE" + +# Exit if the package is not installed +[ -x "$DAEMON" ] || exit 0 + +# Read configuration variable file if it is present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +# Load the VERBOSE setting and other rcS variables +. /lib/init/vars.sh + +# Define LSB log_* functions. +# Depend on lsb-base (>= 3.0-6) to ensure that this file is present. +. /lib/lsb/init-functions + +# +# Function that starts the daemon/service +# +do_start() +{ + # Return + # 0 if daemon has been started + # 1 if daemon was already running + # 2 if daemon could not be started + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ + || return 1 + start-stop-daemon --start --quiet --chuid $CHUID --pidfile $PIDFILE --exec $DAEMON -- \ + $DAEMON_ARGS \ + || return 2 +} + +# +# Function that stops the daemon/service +# +do_stop() +{ + # Return + # 0 if daemon has been stopped + # 1 if daemon was already stopped + # 2 if daemon could not be stopped + # other if a failure occurred + start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME + RETVAL="$?" + [ "$RETVAL" = 2 ] && return 2 + # Wait for children to finish too if this is a daemon that forks + # and if the daemon is only ever run from this initscript. + # If the above conditions are not satisfied then add some other code + # that waits for the process to drop all resources that could be + # needed by services started subsequently. A last resort is to + # sleep for some time. + start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON + [ "$?" = 2 ] && return 2 + # Many daemons don't delete their pidfiles when they exit. + rm -f $PIDFILE + return "$RETVAL" +} + +# +# Function that sends a SIGHUP to the daemon/service +# +do_reload() { + # + # If the daemon can reload its configuration without + # restarting (for example, when it is sent a SIGHUP), + # then implement that here. + # + start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME + return 0 +} + +case "$1" in + start) + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" + do_start + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + stop) + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" + do_stop + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + #reload|force-reload) + # + # If do_reload() is not implemented then leave this commented out + # and leave 'force-reload' as an alias for 'restart'. + # + #log_daemon_msg "Reloading $DESC" "$NAME" + #do_reload + #log_end_msg $? + #;; + restart|force-reload) + # + # If the "reload" option is implemented then remove the + # 'force-reload' alias + # + log_daemon_msg "Restarting $DESC" "$NAME" + do_stop + case "$?" in + 0|1) + do_start + case "$?" in + 0) log_end_msg 0 ;; + 1) log_end_msg 1 ;; # Old process is still running + *) log_end_msg 1 ;; # Failed to start + esac + ;; + *) + # Failed to stop + log_end_msg 1 + ;; + esac + ;; + *) + #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 + echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2 + exit 3 + ;; +esac + +: diff --git a/contrib/initscripts/systemd/novacoind.service b/contrib/initscripts/systemd/novacoind.service new file mode 100644 index 00000000..6893e121 --- /dev/null +++ b/contrib/initscripts/systemd/novacoind.service @@ -0,0 +1,11 @@ +[Unit] +Description=XP daemon serivce +After=network.target + +[Service] +Type=simple +User=XP +ExecStart=/opt/XP/XPd + +[Install] +WantedBy=multi-user.target diff --git a/contrib/macdeploy/LICENSE b/contrib/macdeploy/LICENSE new file mode 100644 index 00000000..94a9ed02 --- /dev/null +++ b/contrib/macdeploy/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/contrib/macdeploy/background.png b/contrib/macdeploy/background.png new file mode 100644 index 0000000000000000000000000000000000000000..51dc01ac08f55952ddfcf115d0c3a829d8d2ac8d GIT binary patch literal 16606 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 Q6|rtySCcQ1HGTO105^iBbN~PV literal 0 HcmV?d00001 diff --git a/contrib/macdeploy/background.psd b/contrib/macdeploy/background.psd new file mode 100644 index 0000000000000000000000000000000000000000..e0a8c9ab9da81e052dfd9daa61e594462ef0e8fe GIT binary patch literal 66096 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%0D&E9M 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 SdW)#^7SZ=_%J{SE!2btPrJpkZ literal 0 HcmV?d00001 diff --git a/contrib/macdeploy/fancy.plist b/contrib/macdeploy/fancy.plist new file mode 100644 index 00000000..d0f5631d --- /dev/null +++ b/contrib/macdeploy/fancy.plist @@ -0,0 +1,32 @@ + + + + + window_bounds + + 300 + 300 + 800 + 620 + + background_picture + background.png + icon_size + 96 + applications_symlink + + items_position + + Applications + + 370 + 156 + + XP-Qt.app + + 128 + 156 + + + + diff --git a/contrib/macdeploy/macdeployqtplus b/contrib/macdeploy/macdeployqtplus new file mode 100644 index 00000000..c1955da4 --- /dev/null +++ b/contrib/macdeploy/macdeployqtplus @@ -0,0 +1,808 @@ +#!/usr/bin/env python + +# +# Copyright (C) 2011 Patrick "p2k" Schneider +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import subprocess, sys, re, os, shutil, stat, os.path +from string import Template +from time import sleep +from argparse import ArgumentParser + +# This is ported from the original macdeployqt with modifications + +class FrameworkInfo(object): + def __init__(self): + self.frameworkDirectory = "" + self.frameworkName = "" + self.frameworkPath = "" + self.binaryDirectory = "" + self.binaryName = "" + self.binaryPath = "" + self.version = "" + self.installName = "" + self.deployedInstallName = "" + self.sourceFilePath = "" + self.destinationDirectory = "" + self.sourceResourcesDirectory = "" + self.destinationResourcesDirectory = "" + + def __eq__(self, other): + if self.__class__ == other.__class__: + return self.__dict__ == other.__dict__ + else: + return False + + def __str__(self): + return """ Framework name: %s + Framework directory: %s + Framework path: %s + Binary name: %s + Binary directory: %s + Binary path: %s + Version: %s + Install name: %s + Deployed install name: %s + Source file Path: %s + Deployed Directory (relative to bundle): %s +""" % (self.frameworkName, + self.frameworkDirectory, + self.frameworkPath, + self.binaryName, + self.binaryDirectory, + self.binaryPath, + self.version, + self.installName, + self.deployedInstallName, + self.sourceFilePath, + self.destinationDirectory) + + def isDylib(self): + return self.frameworkName.endswith(".dylib") + + def isQtFramework(self): + if self.isDylib(): + return self.frameworkName.startswith("libQt") + else: + return self.frameworkName.startswith("Qt") + + reOLine = re.compile(r'^(.+) \(compatibility version [0-9.]+, current version [0-9.]+\)$') + bundleFrameworkDirectory = "Contents/Frameworks" + bundleBinaryDirectory = "Contents/MacOS" + + @classmethod + def fromOtoolLibraryLine(cls, line): + # Note: line must be trimmed + if line == "": + return None + + # Don't deploy system libraries (exception for libQtuitools and libQtlucene). + if line.startswith("/System/Library/") or line.startswith("@executable_path") or (line.startswith("/usr/lib/") and "libQt" not in line): + return None + + m = cls.reOLine.match(line) + if m is None: + raise RuntimeError("otool line could not be parsed: " + line) + + path = m.group(1) + + info = cls() + info.sourceFilePath = path + info.installName = path + + if path.endswith(".dylib"): + dirname, filename = os.path.split(path) + info.frameworkName = filename + info.frameworkDirectory = dirname + info.frameworkPath = path + + info.binaryDirectory = dirname + info.binaryName = filename + info.binaryPath = path + info.version = "-" + + info.installName = path + info.deployedInstallName = "@executable_path/../Frameworks/" + info.binaryName + info.sourceFilePath = path + info.destinationDirectory = cls.bundleFrameworkDirectory + else: + parts = path.split("/") + i = 0 + # Search for the .framework directory + for part in parts: + if part.endswith(".framework"): + break + i += 1 + if i == len(parts): + raise RuntimeError("Could not find .framework or .dylib in otool line: " + line) + + info.frameworkName = parts[i] + info.frameworkDirectory = "/".join(parts[:i]) + info.frameworkPath = os.path.join(info.frameworkDirectory, info.frameworkName) + + info.binaryName = parts[i+3] + info.binaryDirectory = "/".join(parts[i+1:i+3]) + info.binaryPath = os.path.join(info.binaryDirectory, info.binaryName) + info.version = parts[i+2] + + info.deployedInstallName = "@executable_path/../Frameworks/" + os.path.join(info.frameworkName, info.binaryPath) + info.destinationDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, info.binaryDirectory) + + info.sourceResourcesDirectory = os.path.join(info.frameworkPath, "Resources") + info.destinationResourcesDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, "Resources") + + return info + +class ApplicationBundleInfo(object): + def __init__(self, path): + self.path = path + appName = os.path.splitext(os.path.basename(path))[0] + self.binaryPath = os.path.join(path, "Contents", "MacOS", appName) + if not os.path.exists(self.binaryPath): + raise RuntimeError("Could not find bundle binary for " + path) + self.resourcesPath = os.path.join(path, "Contents", "Resources") + self.pluginPath = os.path.join(path, "Contents", "PlugIns") + +class DeploymentInfo(object): + def __init__(self): + self.qtPath = None + self.pluginPath = None + self.deployedFrameworks = [] + + def detectQtPath(self, frameworkDirectory): + parentDir = os.path.dirname(frameworkDirectory) + if os.path.exists(os.path.join(parentDir, "translations")): + # Classic layout, e.g. "/usr/local/Trolltech/Qt-4.x.x" + self.qtPath = parentDir + elif os.path.exists(os.path.join(parentDir, "share", "qt4", "translations")): + # MacPorts layout, e.g. "/opt/local/share/qt4" + self.qtPath = os.path.join(parentDir, "share", "qt4") + elif os.path.exists(os.path.join(os.path.dirname(parentDir), "share", "qt4", "translations")): + # Newer Macports layout + self.qtPath = os.path.join(os.path.dirname(parentDir), "share", "qt4") + else: + self.qtPath = os.getenv("QTDIR", None) + + if self.qtPath is not None: + pluginPath = os.path.join(self.qtPath, "plugins") + if os.path.exists(pluginPath): + self.pluginPath = pluginPath + + def usesFramework(self, name): + nameDot = "%s." % name + libNameDot = "lib%s." % name + for framework in self.deployedFrameworks: + if framework.endswith(".framework"): + if framework.startswith(nameDot): + return True + elif framework.endswith(".dylib"): + if framework.startswith(libNameDot): + return True + return False + +def getFrameworks(binaryPath, verbose): + if verbose >= 3: + print "Inspecting with otool: " + binaryPath + otool = subprocess.Popen(["otool", "-L", binaryPath], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + o_stdout, o_stderr = otool.communicate() + if otool.returncode != 0: + if verbose >= 1: + sys.stderr.write(o_stderr) + sys.stderr.flush() + raise RuntimeError("otool failed with return code %d" % otool.returncode) + + otoolLines = o_stdout.split("\n") + otoolLines.pop(0) # First line is the inspected binary + if ".framework" in binaryPath or binaryPath.endswith(".dylib"): + otoolLines.pop(0) # Frameworks and dylibs list themselves as a dependency. + + libraries = [] + for line in otoolLines: + info = FrameworkInfo.fromOtoolLibraryLine(line.strip()) + if info is not None: + if verbose >= 3: + print "Found framework:" + print info + libraries.append(info) + + return libraries + +def runInstallNameTool(action, *args): + subprocess.check_call(["install_name_tool", "-"+action] + list(args)) + +def changeInstallName(oldName, newName, binaryPath, verbose): + if verbose >= 3: + print "Using install_name_tool:" + print " in", binaryPath + print " change reference", oldName + print " to", newName + runInstallNameTool("change", oldName, newName, binaryPath) + +def changeIdentification(id, binaryPath, verbose): + if verbose >= 3: + print "Using install_name_tool:" + print " change identification in", binaryPath + print " to", id + runInstallNameTool("id", id, binaryPath) + +def runStrip(binaryPath, verbose): + if verbose >= 3: + print "Using strip:" + print " stripped", binaryPath + subprocess.check_call(["strip", "-x", binaryPath]) + +def copyFramework(framework, path, verbose): + if framework.sourceFilePath.startswith("Qt"): + #standard place for Nokia Qt installer's frameworks + fromPath = "/Library/Frameworks/" + framework.sourceFilePath + else: + fromPath = framework.sourceFilePath + toDir = os.path.join(path, framework.destinationDirectory) + toPath = os.path.join(toDir, framework.binaryName) + + if not os.path.exists(fromPath): + raise RuntimeError("No file at " + fromPath) + + if os.path.exists(toPath): + return None # Already there + + if not os.path.exists(toDir): + os.makedirs(toDir) + + shutil.copy2(fromPath, toPath) + if verbose >= 3: + print "Copied:", fromPath + print " to:", toPath + + permissions = os.stat(toPath) + if not permissions.st_mode & stat.S_IWRITE: + os.chmod(toPath, permissions.st_mode | stat.S_IWRITE) + + if not framework.isDylib(): # Copy resources for real frameworks + fromResourcesDir = framework.sourceResourcesDirectory + if os.path.exists(fromResourcesDir): + toResourcesDir = os.path.join(path, framework.destinationResourcesDirectory) + shutil.copytree(fromResourcesDir, toResourcesDir) + if verbose >= 3: + print "Copied resources:", fromResourcesDir + print " to:", toResourcesDir + elif framework.frameworkName.startswith("libQtGui"): # Copy qt_menu.nib (applies to non-framework layout) + qtMenuNibSourcePath = os.path.join(framework.frameworkDirectory, "Resources", "qt_menu.nib") + qtMenuNibDestinationPath = os.path.join(path, "Contents", "Resources", "qt_menu.nib") + if os.path.exists(qtMenuNibSourcePath) and not os.path.exists(qtMenuNibDestinationPath): + shutil.copytree(qtMenuNibSourcePath, qtMenuNibDestinationPath) + if verbose >= 3: + print "Copied for libQtGui:", qtMenuNibSourcePath + print " to:", qtMenuNibDestinationPath + + return toPath + +def deployFrameworks(frameworks, bundlePath, binaryPath, strip, verbose, deploymentInfo=None): + if deploymentInfo is None: + deploymentInfo = DeploymentInfo() + + while len(frameworks) > 0: + framework = frameworks.pop(0) + deploymentInfo.deployedFrameworks.append(framework.frameworkName) + + if verbose >= 2: + print "Processing", framework.frameworkName, "..." + + # Get the Qt path from one of the Qt frameworks + if deploymentInfo.qtPath is None and framework.isQtFramework(): + deploymentInfo.detectQtPath(framework.frameworkDirectory) + + if framework.installName.startswith("@executable_path"): + if verbose >= 2: + print framework.frameworkName, "already deployed, skipping." + continue + + # install_name_tool the new id into the binary + changeInstallName(framework.installName, framework.deployedInstallName, binaryPath, verbose) + + # Copy farmework to app bundle. + deployedBinaryPath = copyFramework(framework, bundlePath, verbose) + # Skip the rest if already was deployed. + if deployedBinaryPath is None: + continue + + if strip: + runStrip(deployedBinaryPath, verbose) + + # install_name_tool it a new id. + changeIdentification(framework.deployedInstallName, deployedBinaryPath, verbose) + # Check for framework dependencies + dependencies = getFrameworks(deployedBinaryPath, verbose) + + for dependency in dependencies: + changeInstallName(dependency.installName, dependency.deployedInstallName, deployedBinaryPath, verbose) + + # Deploy framework if necessary. + if dependency.frameworkName not in deploymentInfo.deployedFrameworks and dependency not in frameworks: + frameworks.append(dependency) + + return deploymentInfo + +def deployFrameworksForAppBundle(applicationBundle, strip, verbose): + frameworks = getFrameworks(applicationBundle.binaryPath, verbose) + if len(frameworks) == 0 and verbose >= 1: + print "Warning: Could not find any external frameworks to deploy in %s." % (applicationBundle.path) + return DeploymentInfo() + else: + return deployFrameworks(frameworks, applicationBundle.path, applicationBundle.binaryPath, strip, verbose) + +def deployPlugins(appBundleInfo, deploymentInfo, strip, verbose): + # Lookup available plugins, exclude unneeded + plugins = [] + for dirpath, dirnames, filenames in os.walk(deploymentInfo.pluginPath): + pluginDirectory = os.path.relpath(dirpath, deploymentInfo.pluginPath) + if pluginDirectory == "designer": + # Skip designer plugins + continue + elif pluginDirectory == "phonon" or pluginDirectory == "phonon_backend": + # Deploy the phonon plugins only if phonon is in use + if not deploymentInfo.usesFramework("phonon"): + continue + elif pluginDirectory == "sqldrivers": + # Deploy the sql plugins only if QtSql is in use + if not deploymentInfo.usesFramework("QtSql"): + continue + elif pluginDirectory == "script": + # Deploy the script plugins only if QtScript is in use + if not deploymentInfo.usesFramework("QtScript"): + continue + elif pluginDirectory == "qmltooling": + # Deploy the qml plugins only if QtDeclarative is in use + if not deploymentInfo.usesFramework("QtDeclarative"): + continue + elif pluginDirectory == "bearer": + # Deploy the bearer plugins only if QtNetwork is in use + if not deploymentInfo.usesFramework("QtNetwork"): + continue + + for pluginName in filenames: + pluginPath = os.path.join(pluginDirectory, pluginName) + if pluginName.endswith("_debug.dylib"): + # Skip debug plugins + continue + elif pluginPath == "imageformats/libqsvg.dylib" or pluginPath == "iconengines/libqsvgicon.dylib": + # Deploy the svg plugins only if QtSvg is in use + if not deploymentInfo.usesFramework("QtSvg"): + continue + elif pluginPath == "accessible/libqtaccessiblecompatwidgets.dylib": + # Deploy accessibility for Qt3Support only if the Qt3Support is in use + if not deploymentInfo.usesFramework("Qt3Support"): + continue + elif pluginPath == "graphicssystems/libqglgraphicssystem.dylib": + # Deploy the opengl graphicssystem plugin only if QtOpenGL is in use + if not deploymentInfo.usesFramework("QtOpenGL"): + continue + + plugins.append((pluginDirectory, pluginName)) + + for pluginDirectory, pluginName in plugins: + if verbose >= 2: + print "Processing plugin", os.path.join(pluginDirectory, pluginName), "..." + + sourcePath = os.path.join(deploymentInfo.pluginPath, pluginDirectory, pluginName) + destinationDirectory = os.path.join(appBundleInfo.pluginPath, pluginDirectory) + if not os.path.exists(destinationDirectory): + os.makedirs(destinationDirectory) + + destinationPath = os.path.join(destinationDirectory, pluginName) + shutil.copy2(sourcePath, destinationPath) + if verbose >= 3: + print "Copied:", sourcePath + print " to:", destinationPath + + if strip: + runStrip(destinationPath, verbose) + + dependencies = getFrameworks(destinationPath, verbose) + + for dependency in dependencies: + changeInstallName(dependency.installName, dependency.deployedInstallName, destinationPath, verbose) + + # Deploy framework if necessary. + if dependency.frameworkName not in deploymentInfo.deployedFrameworks: + deployFrameworks([dependency], appBundleInfo.path, destinationPath, strip, verbose, deploymentInfo) + +qt_conf="""[Paths] +translations=Resources +plugins=PlugIns +""" + +ap = ArgumentParser(description="""Improved version of macdeployqt. + +Outputs a ready-to-deploy app in a folder "dist" and optionally wraps it in a .dmg file. +Note, that the "dist" folder will be deleted before deploying on each run. + +Optionally, Qt translation files (.qm) and additional resources can be added to the bundle. + +Also optionally signs the .app bundle; set the CODESIGNARGS environment variable to pass arguments +to the codesign tool. +E.g. CODESIGNARGS='--sign "Developer ID Application: ..." --keychain /encrypted/foo.keychain'""") + +ap.add_argument("app_bundle", nargs=1, metavar="app-bundle", help="application bundle to be deployed") +ap.add_argument("-verbose", type=int, nargs=1, default=[1], metavar="<0-3>", help="0 = no output, 1 = error/warning (default), 2 = normal, 3 = debug") +ap.add_argument("-no-plugins", dest="plugins", action="store_false", default=True, help="skip plugin deployment") +ap.add_argument("-no-strip", dest="strip", action="store_false", default=True, help="don't run 'strip' on the binaries") +ap.add_argument("-sign", dest="sign", action="store_true", default=False, help="sign .app bundle with codesign tool") +ap.add_argument("-dmg", nargs="?", const="", metavar="basename", help="create a .dmg disk image; if basename is not specified, a camel-cased version of the app name is used") +ap.add_argument("-fancy", nargs=1, metavar="plist", default=[], help="make a fancy looking disk image using the given plist file with instructions; requires -dmg to work") +ap.add_argument("-add-qt-tr", nargs=1, metavar="languages", default=[], help="add Qt translation files to the bundle's ressources; the language list must be separated with commas, not with whitespace") +ap.add_argument("-add-resources", nargs="+", metavar="path", default=[], help="list of additional files or folders to be copied into the bundle's resources; must be the last argument") + +config = ap.parse_args() + +verbose = config.verbose[0] + +# ------------------------------------------------ + +app_bundle = config.app_bundle[0] + +if not os.path.exists(app_bundle): + if verbose >= 1: + sys.stderr.write("Error: Could not find app bundle \"%s\"\n" % (app_bundle)) + sys.exit(1) + +app_bundle_name = os.path.splitext(os.path.basename(app_bundle))[0] + +# ------------------------------------------------ + +for p in config.add_resources: + if verbose >= 3: + print "Checking for \"%s\"..." % p + if not os.path.exists(p): + if verbose >= 1: + sys.stderr.write("Error: Could not find additional resource file \"%s\"\n" % (p)) + sys.exit(1) + +# ------------------------------------------------ + +if len(config.fancy) == 1: + if verbose >= 3: + print "Fancy: Importing plistlib..." + try: + import plistlib + except ImportError: + if verbose >= 1: + sys.stderr.write("Error: Could not import plistlib which is required for fancy disk images.\n") + sys.exit(1) + + if verbose >= 3: + print "Fancy: Importing appscript..." + try: + import appscript + except ImportError: + if verbose >= 1: + sys.stderr.write("Error: Could not import appscript which is required for fancy disk images.\n") + sys.stderr.write("Please install it e.g. with \"sudo easy_install appscript\".\n") + sys.exit(1) + + p = config.fancy[0] + if verbose >= 3: + print "Fancy: Loading \"%s\"..." % p + if not os.path.exists(p): + if verbose >= 1: + sys.stderr.write("Error: Could not find fancy disk image plist at \"%s\"\n" % (p)) + sys.exit(1) + + try: + fancy = plistlib.readPlist(p) + except: + if verbose >= 1: + sys.stderr.write("Error: Could not parse fancy disk image plist at \"%s\"\n" % (p)) + sys.exit(1) + + try: + assert not fancy.has_key("window_bounds") or (isinstance(fancy["window_bounds"], list) and len(fancy["window_bounds"]) == 4) + assert not fancy.has_key("background_picture") or isinstance(fancy["background_picture"], str) + assert not fancy.has_key("icon_size") or isinstance(fancy["icon_size"], int) + assert not fancy.has_key("applications_symlink") or isinstance(fancy["applications_symlink"], bool) + if fancy.has_key("items_position"): + assert isinstance(fancy["items_position"], dict) + for key, value in fancy["items_position"].iteritems(): + assert isinstance(value, list) and len(value) == 2 and isinstance(value[0], int) and isinstance(value[1], int) + except: + if verbose >= 1: + sys.stderr.write("Error: Bad format of fancy disk image plist at \"%s\"\n" % (p)) + sys.exit(1) + + if fancy.has_key("background_picture"): + bp = fancy["background_picture"] + if verbose >= 3: + print "Fancy: Resolving background picture \"%s\"..." % bp + if not os.path.exists(bp): + bp = os.path.join(os.path.dirname(p), bp) + if not os.path.exists(bp): + if verbose >= 1: + sys.stderr.write("Error: Could not find background picture at \"%s\" or \"%s\"\n" % (fancy["background_picture"], bp)) + sys.exit(1) + else: + fancy["background_picture"] = bp +else: + fancy = None + +# ------------------------------------------------ + +if os.path.exists("dist"): + if verbose >= 2: + print "+ Removing old dist folder +" + + shutil.rmtree("dist") + +# ------------------------------------------------ + +target = os.path.join("dist", app_bundle) + +if verbose >= 2: + print "+ Copying source bundle +" +if verbose >= 3: + print app_bundle, "->", target + +os.mkdir("dist") +shutil.copytree(app_bundle, target) + +applicationBundle = ApplicationBundleInfo(target) + +# ------------------------------------------------ + +if verbose >= 2: + print "+ Deploying frameworks +" + +try: + deploymentInfo = deployFrameworksForAppBundle(applicationBundle, config.strip, verbose) + if deploymentInfo.qtPath is None: + deploymentInfo.qtPath = os.getenv("QTDIR", None) + if deploymentInfo.qtPath is None: + if verbose >= 1: + sys.stderr.write("Warning: Could not detect Qt's path, skipping plugin deployment!\n") + config.plugins = False +except RuntimeError as e: + if verbose >= 1: + sys.stderr.write("Error: %s\n" % str(e)) + sys.exit(ret) + +# ------------------------------------------------ + +if config.plugins: + if verbose >= 2: + print "+ Deploying plugins +" + + try: + deployPlugins(applicationBundle, deploymentInfo, config.strip, verbose) + except RuntimeError as e: + if verbose >= 1: + sys.stderr.write("Error: %s\n" % str(e)) + sys.exit(ret) + +# ------------------------------------------------ + +if len(config.add_qt_tr) == 0: + add_qt_tr = [] +else: + qt_tr_dir = os.path.join(deploymentInfo.qtPath, "translations") + add_qt_tr = ["qt_%s.qm" % lng for lng in config.add_qt_tr[0].split(",")] + for lng_file in add_qt_tr: + p = os.path.join(qt_tr_dir, lng_file) + if verbose >= 3: + print "Checking for \"%s\"..." % p + if not os.path.exists(p): + if verbose >= 1: + sys.stderr.write("Error: Could not find Qt translation file \"%s\"\n" % (lng_file)) + sys.exit(1) + +# ------------------------------------------------ + +if verbose >= 2: + print "+ Installing qt.conf +" + +f = open(os.path.join(applicationBundle.resourcesPath, "qt.conf"), "wb") +f.write(qt_conf) +f.close() + +# ------------------------------------------------ + +if len(add_qt_tr) > 0 and verbose >= 2: + print "+ Adding Qt translations +" + +for lng_file in add_qt_tr: + if verbose >= 3: + print os.path.join(qt_tr_dir, lng_file), "->", os.path.join(applicationBundle.resourcesPath, lng_file) + shutil.copy2(os.path.join(qt_tr_dir, lng_file), os.path.join(applicationBundle.resourcesPath, lng_file)) + +# ------------------------------------------------ + +if len(config.add_resources) > 0 and verbose >= 2: + print "+ Adding additional resources +" + +for p in config.add_resources: + t = os.path.join(applicationBundle.resourcesPath, os.path.basename(p)) + if verbose >= 3: + print p, "->", t + if os.path.isdir(p): + shutil.copytree(p, t) + else: + shutil.copy2(p, t) + +# ------------------------------------------------ + +if config.sign and 'CODESIGNARGS' not in os.environ: + print "You must set the CODESIGNARGS environment variable. Skipping signing." +elif config.sign: + if verbose >= 1: + print "Code-signing app bundle %s"%(target,) + subprocess.check_call("codesign --force %s %s"%(os.environ['CODESIGNARGS'], target), shell=True) + +# ------------------------------------------------ + +if config.dmg is not None: + def runHDIUtil(verb, image_basename, **kwargs): + hdiutil_args = ["hdiutil", verb, image_basename + ".dmg"] + if kwargs.has_key("capture_stdout"): + del kwargs["capture_stdout"] + run = subprocess.check_output + else: + if verbose < 2: + hdiutil_args.append("-quiet") + elif verbose >= 3: + hdiutil_args.append("-verbose") + run = subprocess.check_call + + for key, value in kwargs.iteritems(): + hdiutil_args.append("-" + key) + if not value is True: + hdiutil_args.append(str(value)) + + return run(hdiutil_args) + + if verbose >= 2: + if fancy is None: + print "+ Creating .dmg disk image +" + else: + print "+ Preparing .dmg disk image +" + + if config.dmg != "": + dmg_name = config.dmg + else: + spl = app_bundle_name.split(" ") + dmg_name = spl[0] + "".join(p.capitalize() for p in spl[1:]) + + if fancy is None: + try: + runHDIUtil("create", dmg_name, srcfolder="dist", format="UDBZ", volname=app_bundle_name, ov=True) + except subprocess.CalledProcessError as e: + sys.exit(e.returncode) + else: + if verbose >= 3: + print "Determining size of \"dist\"..." + size = 0 + for path, dirs, files in os.walk("dist"): + for file in files: + size += os.path.getsize(os.path.join(path, file)) + size += int(size * 0.1) + + if verbose >= 3: + print "Creating temp image for modification..." + try: + runHDIUtil("create", dmg_name + ".temp", srcfolder="dist", format="UDRW", size=size, volname=app_bundle_name, ov=True) + except subprocess.CalledProcessError as e: + sys.exit(e.returncode) + + if verbose >= 3: + print "Attaching temp image..." + try: + output = runHDIUtil("attach", dmg_name + ".temp", readwrite=True, noverify=True, noautoopen=True, capture_stdout=True) + except subprocess.CalledProcessError as e: + sys.exit(e.returncode) + + m = re.search("/Volumes/(.+$)", output) + disk_root = m.group(0) + disk_name = m.group(1) + + if verbose >= 2: + print "+ Applying fancy settings +" + + if fancy.has_key("background_picture"): + bg_path = os.path.join(disk_root, os.path.basename(fancy["background_picture"])) + if verbose >= 3: + print fancy["background_picture"], "->", bg_path + shutil.copy2(fancy["background_picture"], bg_path) + else: + bg_path = None + + if fancy.get("applications_symlink", False): + os.symlink("/Applications", os.path.join(disk_root, "Applications")) + + # The Python appscript package broke with OSX 10.8 and isn't being fixed. + # So we now build up an AppleScript string and use the osascript command + # to make the .dmg file pretty: + appscript = Template( """ + on run argv + tell application "Finder" + tell disk "$disk" + open + set current view of container window to icon view + set toolbar visible of container window to false + set statusbar visible of container window to false + set the bounds of container window to {$window_bounds} + set theViewOptions to the icon view options of container window + set arrangement of theViewOptions to not arranged + set icon size of theViewOptions to $icon_size + $background_commands + $items_positions + close -- close/reopen works around a bug... + open + update without registering applications + delay 5 + eject + end tell + end tell + end run + """) + + itemscript = Template('set position of item "${item}" of container window to {${position}}') + items_positions = [] + if fancy.has_key("items_position"): + for name, position in fancy["items_position"].iteritems(): + params = { "item" : name, "position" : ",".join([str(p) for p in position]) } + items_positions.append(itemscript.substitute(params)) + + params = { + "disk" : "XP-Qt", + "window_bounds" : "300,300,800,620", + "icon_size" : "96", + "background_commands" : "", + "items_positions" : "\n ".join(items_positions) + } + if fancy.has_key("window_bounds"): + params["window.bounds"] = ",".join([str(p) for p in fancy["window_bounds"]]) + if fancy.has_key("icon_size"): + params["icon_size"] = str(fancy["icon_size"]) + if bg_path is not None: + # Set background file, then call SetFile to make it invisible. + # (note: making it invisible first makes set background picture fail) + bgscript = Template("""set background picture of theViewOptions to file "$bgpic" + do shell script "SetFile -a V /Volumes/$disk/$bgpic" """) + params["background_commands"] = bgscript.substitute({"bgpic" : os.path.basename(bg_path), "disk" : params["disk"]}) + + s = appscript.substitute(params) + if verbose >= 2: + print("Running AppleScript:") + print(s) + + p = subprocess.Popen(['osascript', '-'], stdin=subprocess.PIPE) + p.communicate(input=s) + if p.returncode: + print("Error running osascript.") + + if verbose >= 2: + print "+ Finalizing .dmg disk image +" + + try: + runHDIUtil("convert", dmg_name + ".temp", format="UDBZ", o=dmg_name + ".dmg", ov=True) + except subprocess.CalledProcessError as e: + sys.exit(e.returncode) + + os.unlink(dmg_name + ".temp.dmg") + +# ------------------------------------------------ + +if verbose >= 2: + print "+ Done +" + +sys.exit(0) diff --git a/contrib/macdeploy/notes.txt b/contrib/macdeploy/notes.txt new file mode 100644 index 00000000..10b14ebe --- /dev/null +++ b/contrib/macdeploy/notes.txt @@ -0,0 +1,26 @@ + +macdeployqtplus works best on OS X Lion, for Snow Leopard you'd need to install +Python 2.7 and make it your default Python installation. + +You will need the appscript package for the fancy disk image creation to work. +Install it by invoking "sudo easy_install appscript". + +Ths script should be invoked in the target directory like this: +$source_dir/contrib/macdeploy/macdeployqtplus XP-Qt.app -add-qt-tr da,de,es,hu,ru,uk,zh_CN,zh_TW -dmg -fancy $source_dir/contrib/macdeploy/fancy.plist -verbose 2 + +During the process, the disk image window will pop up briefly where the fancy +settings are applied. This is normal, please do not interfere. + +You can also set up Qt Creator for invoking the script. For this, go to the +"Projects" tab on the left side, switch to "Run Settings" above and add a +deploy configuration. Next add a deploy step choosing "Custom Process Step". +Fill in the following. + +Enable custom process step: [x] +Command: %{sourceDir}/contrib/macdeploy/macdeployqtplus +Working directory: %{buildDir} +Command arguments: XP-Qt.app -add-qt-tr da,de,es,hu,ru,uk,zh_CN,zh_TW -dmg -fancy %{sourceDir}/contrib/macdeploy/fancy.plist -verbose 2 + +After that you can start the deployment process through the menu with +Build -> Deploy Project "XP-qt" + diff --git a/contrib/qt_translations.py b/contrib/qt_translations.py new file mode 100644 index 00000000..fd8a8b71 --- /dev/null +++ b/contrib/qt_translations.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +# Helpful little script that spits out a comma-separated list of +# language codes for Qt icons that should be included +# in binary bitcoin distributions + +import glob +import os +import re +import sys + +if len(sys.argv) != 3: + sys.exit("Usage: %s $QTDIR/translations $BITCOINDIR/src/qt/locale"%sys.argv[0]) + +d1 = sys.argv[1] +d2 = sys.argv[2] + +l1 = set([ re.search(r'qt_(.*).qm', f).group(1) for f in glob.glob(os.path.join(d1, 'qt_*.qm')) ]) +l2 = set([ re.search(r'bitcoin_(.*).qm', f).group(1) for f in glob.glob(os.path.join(d2, 'bitcoin_*.qm')) ]) + +print ",".join(sorted(l1.intersection(l2))) + diff --git a/contrib/seeds/makeseeds.py b/contrib/seeds/makeseeds.py new file mode 100644 index 00000000..ef1190aa --- /dev/null +++ b/contrib/seeds/makeseeds.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +NSEEDS=600 + +import re +import sys +from subprocess import check_output + +def main(): + lines = sys.stdin.readlines() + + ips = [] + pattern = re.compile(r"^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3}):7777") + for line in lines: + m = pattern.match(line) + if m is None: + continue + ip = 0 + for i in range(0,4): + ip = ip + (int(m.group(i+1)) << (8*(i))) + if ip == 0: + continue + ips.append(ip) + + for row in range(0, min(NSEEDS,len(ips)), 8): + print " " + ", ".join([ "0x%08x"%i for i in ips[row:row+8] ]) + "," + +if __name__ == '__main__': + main() diff --git a/contrib/wallettools/walletchangepass.py b/contrib/wallettools/walletchangepass.py new file mode 100644 index 00000000..0b5859f3 --- /dev/null +++ b/contrib/wallettools/walletchangepass.py @@ -0,0 +1,5 @@ +from jsonrpc import ServiceProxy +access = ServiceProxy("http://127.0.0.1:8344") +pwd = raw_input("Enter old wallet passphrase: ") +pwd2 = raw_input("Enter new wallet passphrase: ") +access.walletpassphrasechange(pwd, pwd2) \ No newline at end of file diff --git a/contrib/wallettools/walletdefrag.py b/contrib/wallettools/walletdefrag.py new file mode 100644 index 00000000..2dbef760 --- /dev/null +++ b/contrib/wallettools/walletdefrag.py @@ -0,0 +1,20 @@ +from jsonrpc import JSONRPCException, ServiceProxy + +MaxProcessSum = 200000 # Maximum amount of coins to merge +MaxOutputSum = 500 # Maximum transaction value +MinInputSum = 50 # Minimum input value, inputs with lower size will be ignored + +access = ServiceProxy("http://alexd:123456789@127.0.0.1:8344") # http://username:password@host:port/ + +try: + balance = access.getbalance() + print 'Balance = ', balance + + if balance > MaxProcessSum: + print 'Balance is above MaxProcessSum, setting amount to ', MaxProcessSum + balance = MaxProcessSum + + if balance > MaxOutputSum: + access.mergecoins(balance, MinInputSum, MaxOutputSum) +except JSONRPCException,e: + print 'Error: %s' % e.error diff --git a/contrib/wallettools/walletunlock.py b/contrib/wallettools/walletunlock.py new file mode 100644 index 00000000..17ad07a2 --- /dev/null +++ b/contrib/wallettools/walletunlock.py @@ -0,0 +1,5 @@ +from jsonrpc import ServiceProxy +access = ServiceProxy("http://127.0.0.1:8344") +pwd = raw_input("Enter wallet passphrase: ") +access.walletpassphrase(pwd, 60) + diff --git a/doc/Doxyfile b/doc/Doxyfile new file mode 100644 index 00000000..3c03f133 --- /dev/null +++ b/doc/Doxyfile @@ -0,0 +1,1752 @@ +# Doxyfile 1.7.4 + +# !!! Invoke doxygen from project root using: +# doxygen doc/Doxyfile + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = XP + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 0.5.0 + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "P2P Digital Currency" + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = doc/XP_logo_doxygen.png + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = doc/doxygen + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this +# tag. The format is ext=language, where ext is a file extension, and language +# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, +# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions +# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. The create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = src + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.d \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.idl \ + *.odl \ + *.cs \ + *.php \ + *.php3 \ + *.inc \ + *.m \ + *.mm \ + *.dox \ + *.py \ + *.f90 \ + *.f \ + *.for \ + *.vhd \ + *.vhdl + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is adviced to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the stylesheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list. + +USE_INLINE_TREES = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the +# mathjax.org site, so you can quickly see the result without installing +# MathJax, but it is strongly recommended to install a local copy of MathJax +# before deployment. + +MATHJAX_RELPATH = http://www.mathjax.org/mathjax + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvantages are that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will write a font called Helvetica to the output +# directory and reference it in all dot files that doxygen generates. +# When you want a differently looking font you can specify the font name +# using DOT_FONTNAME. You need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/doc/README b/doc/README new file mode 100644 index 00000000..cd4e924c --- /dev/null +++ b/doc/README @@ -0,0 +1,18 @@ +XP 0.5.0 BETA + +Copyright (c) 2013 XP Developers +Copyright (c) 2011-2012 PPCoin Developers +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/). This product includes +cryptographic software written by Eric Young (eay@cryptsoft.com). + + +Intro +----- +XP is a free open source project derived from Bitcoin, with +the goal of providing a long-term energy-efficient scrypt-based crypto-currency. +Built on the foundation of Bitcoin and PPCoin, innovations such as proof-of-stake +help further advance the field of crypto-currency. + diff --git a/doc/README_windows.txt b/doc/README_windows.txt new file mode 100644 index 00000000..68d3267f --- /dev/null +++ b/doc/README_windows.txt @@ -0,0 +1,18 @@ +XP 0.4.4 BETA + +Copyright (c) 2013 XP Developers +Copyright (c) 2011-2013 PPCoin Developers +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/). This product includes +cryptographic software written by Eric Young (eay@cryptsoft.com). + + +Intro +----- +XP is a free open source project derived from Bitcoin, with +the goal of providing a long-term energy-efficient scrypt-based crypto-currency. +Built on the foundation of Bitcoin and PPCoin, innovations such as proof-of-stake and scrypt +help further advance the field of crypto-currency. + diff --git a/doc/assets-attribution.txt b/doc/assets-attribution.txt new file mode 100644 index 00000000..fabcdeea --- /dev/null +++ b/doc/assets-attribution.txt @@ -0,0 +1,52 @@ +Icon: src/qt/res/icons/clock*.png, src/qt/res/icons/tx*.png, + src/qt/res/src/*.svg +Designer: Wladimir van der Laan +License: MIT + +Icon: src/qt/res/icons/address-book.png, src/qt/res/icons/export.png, + src/qt/res/icons/history.png, src/qt/res/icons/key.png, + src/qt/res/icons/lock_*.png, src/qt/res/icons/overview.png, + src/qt/res/icons/receive.png, src/qt/res/icons/send.png, + src/qt/res/icons/synced.png, src/qt/res/icons/filesave.png +Icon Pack: NUVOLA ICON THEME for KDE 3.x +Designer: David Vignoni (david@icon-king.com) + ICON KING - www.icon-king.com +License: LGPL +Site: http://www.icon-king.com/projects/nuvola/ + +Icon: src/qt/res/icons/connect*.png +Icon Pack: Human-O2 +Designer: schollidesign +License: GNU/GPL +Site: http://findicons.com/icon/93743/blocks_gnome_netstatus_0 + +Icon: src/qt/res/icons/transaction*.png +Designer: md2k7 +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, + 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/remove.png (edited) +Designer: http://www.everaldo.com +Icon Pack: Crystal SVG +License: LGPL + +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 + +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 + +Image: src/qt/res/images/splash2.jpg (Wallet image) +Designer: Crobbo (forum) +Site: https://bitcointalk.org/index.php?topic=32273.0 +License: Public domain + diff --git a/doc/bitcoin_logo_doxygen.png b/doc/bitcoin_logo_doxygen.png new file mode 100644 index 0000000000000000000000000000000000000000..5b41b02e18e57e42ba6ee790ca69e88439ea09c0 GIT binary patch literal 5735 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 literal 0 HcmV?d00001 diff --git a/doc/build-msw.txt b/doc/build-msw.txt new file mode 100644 index 00000000..6d33e8ed --- /dev/null +++ b/doc/build-msw.txt @@ -0,0 +1,71 @@ +Copyright (c) 2009-2012 Bitcoin Developers +Copyright (c) 2013 XP Developers +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/). This product includes +cryptographic software written by Eric Young (eay@cryptsoft.com). + + +See readme-qt.rst for instructions on building XP QT, the +graphical user interface. + +WINDOWS BUILD NOTES +=================== + +Compilers Supported +------------------- +TODO: What works? +Note: releases are cross-compiled using mingw running on Linux. + + +Dependencies +------------ +Libraries you need to download separately and build: + + default path download +OpenSSL \openssl-1.0.1j-mgw http://www.openssl.org/source/ +Berkeley DB \db-6.0.20.NC-mgw http://www.oracle.com/technology/software/products/berkeley-db/index.html +Boost \boost-1.57.0-mgw http://www.boost.org/users/download/ + +Their licenses: +OpenSSL Old BSD license with the problematic advertising requirement +Berkeley DB New BSD license with additional requirement that linked software must be free open source +Boost MIT-like license + +Versions used in this release: +OpenSSL 1.0.1j +Berkeley DB 6.0.20.NC +Boost 1.57.0 + + +OpenSSL +------- +MSYS shell: +un-tar sources with MSYS 'tar xfz' to avoid issue with symlinks (OpenSSL ticket 2377) +change 'MAKE' env. variable from 'C:\MinGW32\bin\mingw32-make.exe' to '/c/MinGW32/bin/mingw32-make.exe' + +cd /c/openssl-1.0.1j-mgw +./config +make + +Berkeley DB +----------- +MSYS shell: +cd /c/db-6.0.20.NC-mgw/build_unix +sh ../dist/configure --enable-mingw --enable-cxx +make + +Boost +----- +DOS prompt: +downloaded boost jam 3.1.18 +cd \boost-1.57.0-mgw +bjam toolset=gcc --build-type=complete stage + +XP +------- +DOS prompt: +cd \XP\src +mingw32-make -f makefile.mingw +strip XPd.exe diff --git a/doc/build-osx.txt b/doc/build-osx.txt new file mode 100644 index 00000000..2e4e8df4 --- /dev/null +++ b/doc/build-osx.txt @@ -0,0 +1,136 @@ +Mac OS X Build Instructions and Notes +==================================== +This guide will show you how to build XPd(headless client) for OSX. + +Notes +----- + +* Tested on OS X 10.7 through 10.10 on 64-bit Intel processors only. + +* All of the commands should be executed in a Terminal application. The +built-in one is located in `/Applications/Utilities`. + +Preparation +----------- + +You need to install XCode with all the options checked so that the compiler +and everything is available in /usr not just /Developer. XCode should be +available on your OS X installation media, but if not, you can get the +current version from https://developer.apple.com/xcode/. If you install +Xcode 4.3 or later, you'll need to install its command line tools. This can +be done in `Xcode > Preferences > Downloads > Components` and generally must +be re-done or updated every time Xcode is updated. + +There's also an assumption that you already have `git` installed. If +not, it's the path of least resistance to install [Github for Mac](https://mac.github.com/) +(OS X 10.7+) or +[Git for OS X](https://code.google.com/p/git-osx-installer/). It is also +available via Homebrew. + +You will also need to install [Homebrew](http://brew.sh) in order to install library +dependencies. + +The installation of the actual dependencies is covered in the Instructions +sections below. + +Instructions: Homebrew +---------------------- + +#### Install dependencies using Homebrew + + brew install autoconf automake libtool boost openssl pkg-config protobuf qt qrencode + +#### Installing berkeley-db4 using Homebrew + +The homebrew package for berkeley-db4 has been broken for some time. It will install without Java though. + +Running this command takes you into brew's interactive mode, which allows you to configure, make, and install by hand: +``` +$ brew install https://raw.github.com/mxcl/homebrew/master/Library/Formula/berkeley-db4.rb -–without-java +``` + +The rest of these commands are run inside brew interactive mode: +``` +/private/tmp/berkeley-db4-UGpd0O/db-4.8.30 $ cd .. +/private/tmp/berkeley-db4-UGpd0O $ db-4.8.30/dist/configure --prefix=/usr/local/Cellar/berkeley-db4/4.8.30 --mandir=/usr/local/Cellar/berkeley-db4/4.8.30/share/man --enable-cxx +/private/tmp/berkeley-db4-UGpd0O $ make +/private/tmp/berkeley-db4-UGpd0O $ make install +/private/tmp/berkeley-db4-UGpd0O $ exit +``` + +After exiting, you'll get a warning that the install is keg-only, which means it wasn't symlinked to `/usr/local`. You don't need it to link it to build XP, but if you want to, here's how: + + $ brew link --force berkeley-db4 + + +### Building `XPd` + +1. Clone the github tree to get the source code and go into the directory. + + git clone https://github.com/XP-project/XP.git + cd XP + +2. Build XP-Qt application: + qmake + make + +3. Build bitcoind: + cd src + make -f makefile.osx XPd + +Use Qt Creator as IDE +------------------------ +You can use Qt Creator as IDE, for debugging and for manipulating forms, etc. +Download Qt Creator from http://www.qt.io/download/. Download the "community edition" and only install Qt Creator (uncheck the rest during the installation process). + +1. Make sure you installed everything through homebrew mentioned above +2. In Qt Creator do "File" -> "Open Project" +3. Select XP-qt.pro as project file. +4. In the "Projects" tab select "Manage Kits..." +5. Select the default "Desktop" kit and select "Clang (x86 64bit in /usr/bin)" as compiler +6. Select LLDB as debugger (you might need to set the path to your installtion) +7. Start debugging with Qt Creator + +Creating a release build +------------------------ +You can ignore this section if you are building `XPd` for your own use. + +XPd binary isn't included in the XP-Qt.app bundle. + +If you are building `XPd` or `XP-Qt` for others, your build machine should be set up +as follows for maximum compatibility: + +All dependencies should be compiled with these flags: + + -mmacosx-version-min=10.7 + -arch x86_64 + -isysroot $(xcode-select --print-path)/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk + +Once dependencies are compiled, you can create the .dmg disk image: + +./contrib/macdeploy/macdeployqtplus XP-Qt.app -dmg -fancy ./contrib/macdeploy/fancy.plist + +Running +------- + +It's now available at `./XPd`, provided that you are still in the `src` +directory. We have to first create the RPC configuration file, though. + +Run `./XPd` to get the filename where it should be put, or just try these +commands: + + echo -e "rpcuser=XPrpc\nrpcpassword=$(xxd -l 16 -p /dev/urandom)" > "/Users/${USER}/Library/Application Support/XP/XP.conf" + chmod 600 "/Users/${USER}/Library/Application Support/XP/XP.conf" + +The next time you run it, it will start downloading the blockchain, but it won't +output anything while it's doing this. This process may take several hours; +you can monitor its process by looking at the debug.log file, like this: + + tail -f $HOME/Library/Application\ Support/XP/debug.log + +Other commands: +------- + + ./XPd -daemon # to start the bitcoin daemon. + ./XPd --help # for a list of command-line options. + ./XPd help # When the daemon is running, to get a list of RPC commands diff --git a/doc/build-unix.txt b/doc/build-unix.txt new file mode 100644 index 00000000..54964e5c --- /dev/null +++ b/doc/build-unix.txt @@ -0,0 +1,137 @@ +Copyright (c) 2009-2012 Bitcoin Developers +Copyright (c) 2013 XP Developers +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/). This product includes +cryptographic software written by Eric Young (eay@cryptsoft.com). + + +UNIX BUILD NOTES +================ + +To Build +-------- + +cd src/ +make -f makefile.unix # Headless XP + +See readme-qt.rst for instructions on building XP QT, +the graphical XP. + +Dependencies +------------ + + Library Purpose Description + ------- ------- ----------- + libssl SSL Support Secure communications + libdb4.8 Berkeley DB Blockchain & wallet storage + libboost Boost C++ Library + libqrencode QRCode generation Optional QRCode generation + +Note that libexecinfo should be installed, if you building under *BSD systems. +This library provides backtrace facility. + +libqrencode is used for QRCode image generation. It can be downloaded +from http://fukuchi.org/works/qrencode/index.html.en, or installed via +your package manager. + +Licenses of statically linked libraries: + Berkeley DB New BSD license with additional requirement that linked + software must be free open source + Boost MIT-like license + +Versions used in this release: + GCC 4.3.3 + OpenSSL 0.9.8g + Berkeley DB 4.8.30.NC + Boost 1.37 + +Dependency Build Instructions: Ubuntu & Debian +---------------------------------------------- +sudo apt-get install build-essential +sudo apt-get install libssl-dev +sudo apt-get install libdb4.8-dev +sudo apt-get install libdb4.8++-dev + Boost 1.40+: sudo apt-get install libboost-all-dev + or Boost 1.37: sudo apt-get install libboost1.37-dev +sudo apt-get install libqrencode-dev + +If using Boost 1.37, append -mt to the boost libraries in the makefile. + + +Dependency Build Instructions: Gentoo +------------------------------------- + +Note: If you just want to install XPd on Gentoo, you can add the XP + overlay and use your package manager: + layman -a XP && emerge XPd + +emerge -av1 --noreplace boost glib openssl sys-libs/db:4.8 + +Take the following steps to build: + cd ${XP_DIR}/src + make -f makefile.unix BDB_INCLUDE_PATH='/usr/include/db4.8' + strip XPd + + +Notes +----- +The release is built with GCC and then "strip XPd" to strip the debug +symbols, which reduces the executable size by about 90%. + +Berkeley DB +----------- +You need Berkeley DB 4.8. If you have to build Berkeley DB yourself: +../dist/configure --enable-cxx +make + + +Boost +----- +If you need to build Boost yourself: +sudo su +./bootstrap.sh +./bjam install + + +Security +-------- +To help make your XP installation more secure by making certain attacks impossible to +exploit even if a vulnerability is found, you can take the following measures: + +* Position Independent Executable + Build position independent code to take advantage of Address Space Layout Randomization + offered by some kernels. An attacker who is able to cause execution of code at an arbitrary + memory location is thwarted if he doesn't know where anything useful is located. + The stack and heap are randomly located by default but this allows the code section to be + randomly located as well. + + On an Amd64 processor where a library was not compiled with -fPIC, this will cause an error + such as: "relocation R_X86_64_32 against `......' can not be used when making a shared object;" + + To build with PIE, use: + make -f makefile.unix ... -e PIE=1 + + To test that you have built PIE executable, install scanelf, part of paxutils, and use: + scanelf -e ./XP + + The output should contain: + TYPE + ET_DYN + +* Non-executable Stack + If the stack is executable then trivial stack based buffer overflow exploits are possible if + vulnerable buffers are found. By default, XP should be built with a non-executable stack + but if one of the libraries it uses asks for an executable stack or someone makes a mistake + and uses a compiler extension which requires an executable stack, it will silently build an + executable without the non-executable stack protection. + + To verify that the stack is non-executable after compiling use: + scanelf -e ./XP + + the output should contain: + STK/REL/PTL + RW- R-- RW- + + The STK RW- means that the stack is readable and writeable but not executable. diff --git a/doc/building novacoin-qt for android under Windows.txt b/doc/building novacoin-qt for android under Windows.txt new file mode 100644 index 00000000..60ef8923 --- /dev/null +++ b/doc/building novacoin-qt for android under Windows.txt @@ -0,0 +1,284 @@ +ARMV7 +1.1 Установка архиватора 7z: http://www.7-zip.org/ +(при написании инструкции использовался 7-Zip 9.20 64 bit X64, но скорее всего подойдёт любая версия) + +1.2 Установка msys shell: +-Скачайте http://sourceforge.net/projects/mingw/files/Installer/mingw-get-setup.exe/download +-нажмите Install +-директория для установки C:\MinGW +-поставить галочку напротив "...also install support for the graphical user interface." +-убрать галочки напротив "..in the start menu, and/or .." "... on the desktop" +-нажмите continue +-нажмите continue +-нажмите All Packages, затем MSYS +-поставте галочки напротив: +msys-autoconf-bin +msys-automake-bin +msys-base-bin +msys-libtool-bin +-нажмите Installation, Apply Changes, Apply. После завершения установки нажмите Close и закройте MinGW Installation Manager. + +1.3 Установка qt-5.4.0 для Android +-Скачайте http://master.qt.io/archive/qt/5.4/5.4.0/qt-opensource-windows-x86-android-5.4.0.exe +-Запустите установщик +-Укажите каталог для установки(я при написании инструкции устанавливал в папку C:\Qt\Qt5.4.0) +-Выберите среди компонентов MinGW 4.9.1, остальные компоненты оставьте по умолчанию +(У меня окошко выглядит так: https://yadi.sk/i/e9W_pXTge43u5 ) +(можно так же выбрать Android X86, и Android armv5 если собираетесь собирать под эти CPU) + +1.4 Установка JAVA JDK +-Зайдите на сайт http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html +-Скачайте и установите JDK(при написании инструкции использовалась jdk-8u25-windows-i586.exe ) + +1.5 Установка Android SDK +-Скачайте http://dl.google.com/android/installer_r24.0.2-windows.exe +-Установите в папку C:\Android\sdk +-Запустите SDK Manager.exe +-Установите Android SDK Tools 24.0.2, Android SDK Platform-tools 21, Android SDK Build-tools 21.1.2, Google USB driver, +SDK Platform 21, Google API 21, ARM EABI v7a System Image + +1.6 Установка Android NDK +-На сайте https://developer.android.com/tools/sdk/ndk/index.html#Downloads +-Скачайте https://dl.google.com/android/ndk/android-ndk-r10d-windows-x86_64.exe +-Установите в папку C:\Android\android-ndk-r10d (файл android-ndk-r10d-windows-x86_64.exe это самораспоковывающийся архив, который по двойному клику распаковывается в текущую папку, поэтому лучше открыть его 7-zip и выбрать папку для распаковки самостоятельно) +-Добавьте путь C:\Android\android-ndk-r10d\toolchains\arm-linux-androideabi-4.9\prebuilt\windows-x86_64\bin в системную переменную PATH +Подробное описание как это сделать на Windows 8, Windows 7, Windows XP и Windows Vista +https://www.java.com/ru/download/help/path.xml +(только не нужно выполнять последний пункт из этой инструкции "Откройте заново окно командной строки и выполните код java." ) + +Проверка что вы добавили правильный путь в переменную PATH: +-откройте командную строку Windows(Нажмите кнопку Windows + R одновременно. Откроется окно "Выполнить", в поле открыть наберите cmd. Нажмите Ok). +-наберите arm-linux-androideabi-g++ -v +В командной строке выведется текст. В конце текста должно быть написано: +Thread model: posix +gcc version 4.9 20140827 (prerelease) (GCC) + +1.7 Установка Apache Ant +-Зайдите на сайт https://ant.apache.org/bindownload.cgi +-Скачайте архив с программой: http://apache-mirror.rbc.ru/pub/apache//ant/binaries/apache-ant-1.9.4-bin.zip +-Распакуйте в папку C:\Android\apache-ant-1.9.4 + +1.8 Добавьте новые системные переменные(Они нужны только для пункта 2. Построение зависимостей, после этого их можно убрать) +-Нажмите правой кнопкой мыши на "Компьютер", в открывшемся меню нажмите "Свойства", затем "Дополнительные параметры системы", потом "Переменные среды" +https://yadi.sk/i/GnG9HiGme46oi +-В "Системные переменные" нажмите "Cоздать"(ниже будет идти список "переменная и её значение") +ANDROID_DEV /c/Android/android-ndk-r10d/platforms/android-9/arch-arm/usr +AR /c/Android/android-ndk-r10d/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/bin/arm-linux-androideabi-ar +AS /c/Android/android-ndk-r10d/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/bin/arm-linux-androideabi-as +CC /c/Android/android-ndk-r10d/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/bin/arm-linux-androideabi-gcc +CFLAGS --sysroot=/c/Android/android-ndk-r10d/platforms/android-9/arch-arm/ +CPP /c/Android/android-ndk-r10d/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/bin/arm-linux-androideabi-cpp +CPPFLAGS --sysroot=/c/Android/android-ndk-r10d/platforms/android-9/arch-arm/ +CXX /c/Android/android-ndk-r10d/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/bin/arm-linux-androideabi-g++ +CXXFLAGS --sysroot=/c/Android/android-ndk-r10d/platforms/android-9/arch-arm/ -I/c/Android/android-ndk-r10d/sources/cxx-stl/gnu-libstdc++/4.9/include -I/c/Android/android-ndk-r10d/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/include +LD /c/Android/android-ndk-r10d/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/bin/arm-linux-androideabi-ld +NDK_PROJECT_PATH C:\Android\android-ndk-r10d +RANLIB /c/Android/android-ndk-r10d/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/bin/arm-linux-androideabi-ranlib + +2. Построение зависимостей. +В инструкции все зависимости сохраняются в папку C:\Android\deps + +2.1 OpenSSL +-Скачайте http://www.openssl.org/source/openssl-1.0.2.tar.gz +-Перейдите в папку C:\MinGW\msys\1.0 и запустите msys.bat +Из MinGw shell выполните следующий код: + +cd /c/deps/ +tar xvfz openssl-1.0.2.tar.gz +cd openssl-1.0.2 +Configure no-shared no-dso android-armv7 +make + +2.2 Berkeley DB +-Скачайте http://download.oracle.com/berkeley-db/db-6.0.20.tar.gz +-Из MinGw shell выполните следующий код: + +cd /c/deps/ +tar xvfz db-6.0.20.tar.gz +cd db-6.0.20/build_unix +../dist/configure --host=arm-none-linux --enable-cxx --disable-shared --disable-replication +make + +2.3 Boost +-Скачайте http://sourceforge.net/projects/boost/files/boost/1.57.0/boost_1_57_0.7z/download +-Распакуйте boost_1_57_0.7z в папку C:\Android\deps +-Откройте командную строку Windows, и выполните следующий код: + +cd C:\Android\deps\boost_1_57_0 +bootstrap.bat + +Не закрывайте командную строку. Откройте файл C:\Android\deps\boost_1_57_0\project-config.jam в текстовом редакторе, удалите его содержимое, вставьте следующий текст + +import option ; + +using gcc : arm : arm-linux-androideabi-g++.exe ; + +option.set keep-going : false ; + +и сохраните файл + +Вернитесь в командную строку и введите: + +b2 --build-type=complete --with-chrono --with-filesystem --with-program_options --with-system --with-thread toolset=gcc-arm variant=release link=static threading=multi threadapi=pthread target-os=android define=BOOST_MATH_DISABLE_FLOAT128 include=C:\Android\android-ndk-r10d\sources\cxx-stl\gnu-libstdc++\4.9\include include=C:\Android\android-ndk-r10d\sources\cxx-stl\gnu-libstdc++\4.9\libs\armeabi\include include=C:\Android\android-ndk-r10d\platforms\android-9\arch-arm\usr\include + +После выполнения этой команды в папке C:\Android\deps\boost_1_57_0\stage\lib должны появиться следующие файлы: +libboost_atomic-gcc-mt-s-1_57.a +libboost_chrono-gcc-mt-s-1_57.a +libboost_filesystem-gcc-mt-s-1_57.a +libboost_program_options-gcc-mt-s-1_57.a +libboost_system-gcc-mt-s-1_57.a +libboost_thread_pthread-gcc-mt-s-1_57.a + +2.4 LevelDB +-Зайдите на сайт https://github.com/XP-project/XP +-Нажмите Download ZIP +-Распакуйте XP-master.zip в C:\Android +-Откройте MinGW shell (C:\MinGW\msys\1.0\msys.bat) и выполните следующий код: + +cd /c/android/XP-master/src/leveldb +TARGET_OS=OS_ANDROID_CROSSCOMPILE make libleveldb.a libmemenv.a + +2.5 ifaddrs +-Зайдите на сайт https://github.com/kmackay/android-ifaddrs +-Нажмите Download ZIP +-Откройте архив android-ifaddrs-master.zip , выберите файлы ifaddrs.c и ifaddrs.h и извлеките их в C:\Android\XP-master\src + +3. Компиляция +3.1 Собираем XP QT + +Откройте файл XP-qt.pro +Вместо + +#BOOST_LIB_SUFFIX=-mgw49-mt-s-1_55 +#BOOST_INCLUDE_PATH=C:/deps/boost_1_55_0 +#BOOST_LIB_PATH=C:/deps/boost_1_55_0/stage/lib +#BDB_INCLUDE_PATH=C:/deps/db-6.0.20/build_unix +#BDB_LIB_PATH=C:/deps/db-6.0.20/build_unix +#OPENSSL_INCLUDE_PATH=C:/deps/openssl-1.0.1j/include +#OPENSSL_LIB_PATH=C:/deps/openssl-1.0.1j + +вставьте + +USE_IPV6=0 +USE_LEVELDB=1 +USE_ASM=1 + +BOOST_LIB_SUFFIX=-gcc-mt-s-1_57 +BOOST_INCLUDE_PATH=C:/Android/deps/boost_1_57_0 +BOOST_LIB_PATH=C:/Android/deps/boost_1_57_0/stage/lib +BDB_INCLUDE_PATH=C:/Android/deps/db-6.0.20/build_unix +BDB_LIB_PATH=C:/Android/deps/db-6.0.20/build_unix +OPENSSL_INCLUDE_PATH=C:/Android/deps/openssl-1.0.2/include +OPENSSL_LIB_PATH=C:/Android/deps/openssl-1.0.2 + +Вместо + +!windows|contains(USE_BUILD_INFO, 1) { + genbuild.depends = FORCE + genbuild.commands = cd $$PWD; /bin/sh share/genbuild.sh $$OUT_PWD/build/build.h + genbuild.target = $$OUT_PWD/build/build.h + PRE_TARGETDEPS += $$OUT_PWD/build/build.h + QMAKE_EXTRA_TARGETS += genbuild + DEFINES += HAVE_BUILD_INFO +} + +вставьте + +#!windows|contains(USE_BUILD_INFO, 1) { +# genbuild.depends = FORCE +# genbuild.commands = cd $$PWD; /bin/sh share/genbuild.sh $$OUT_PWD/build/build.h +# genbuild.target = $$OUT_PWD/build/build.h +# PRE_TARGETDEPS += $$OUT_PWD/build/build.h +# QMAKE_EXTRA_TARGETS += genbuild +# DEFINES += HAVE_BUILD_INFO +#} + +После строки src/irc.h \ вставьте src/ifaddrs.h \ +(то есть чтобы стало выглядеть так: +src/irc.h \ +src/ifaddrs.h \ +src/mruset.h \ +) + +После строки src/qt/transactiontablemodel.cpp \ вставьте src/ifaddrs.c \ + +Закоментируйте строку 434 LIBS += -lrt +(то есть чтобы стало выглядеть так: +!windows:!macx { + DEFINES += LINUX +# LIBS += -lrt +} +) + +Измените строку +LIBS += -lboost_system$$BOOST_LIB_SUFFIX -lboost_filesystem$$BOOST_LIB_SUFFIX -lboost_program_options$$BOOST_LIB_SUFFIX -lboost_thread$$BOOST_THREAD_LIB_SUFFIX +на +LIBS += -lboost_system$$BOOST_LIB_SUFFIX -lboost_filesystem$$BOOST_LIB_SUFFIX -lboost_program_options$$BOOST_LIB_SUFFIX -lboost_thread_pthread$$BOOST_THREAD_LIB_SUFFIX + +-Откройте файл C:\Qt\Qt5.4.0\Tools\QtCreator\bin\qtcreator.exe +-Нажмите "Инструменты"->"Параметры", затем выберите вкладку Android +-Вставьте в строку "Размещение JDK" C:\Program Files (x86)\Java\jdk1.8.0_25 +-Вставьте в строку "Размещение SDK для Android" C:\Android\sdk +-Вставьте в строку "Размещение NDK для Android" C:\Android\android-ndk-r10d +-Вставьте в строку "Программа Ant" C:\Android\apache-ant-1.9.4\bin\ant.bat +-Нажмите "OK" +-Нажмите "Файл"->"Открыть файл или проект..." +-Найдите "XP-qt.pro" и нажмите "открыть" +-Поставьте галочку напротив Android для armeabi-v7a(GCC 4.9, Qt 5.4.0), остальные галочки уберите +-Нажмите "Настроить проект" +-Нажмите "Проекты"->"Сборка"->"Создать шаблоны"(картинка чтобы лучше понять https://yadi.sk/i/M8mhG2tce4cEy) +-Нажмите "Завершить" +-В проекте XP-qt выберете Другие файлы->android->AndroidManifest.xml и задайте имя пакета, имя приложения, иконки приложения(Иконки находятся в папке C:\Android\XP-master\src\qt\res\icons) (Для Google Play Market иконка должна быть не менее 512x512)(https://yadi.sk/i/oY-OmnrWe4cTY) +-Возьмите ваш Android телефон/планшет(версия должна быть от 2.3 и выше) +-Перейдите в "Настройки"->"Приложения". +-Поставьте галочку напротив "Неизвестные источники" +-Перейдите в "Разработка" +-Поставьте галочки напротив "Отладка по USB" +-Подключите через USB ваш телефон/планшет к компьютеру. Не выбирайте опцию в телефоне/планшете "Включить USB-накопитель" +-Должно найтись Android устройство. https://yadi.sk/i/wFWZLxtie4DrN +Если Windows не может найти драйвер для устройства, то укажите самостоятельно в папке C:\Android\sdk\extras\google\usb_driver есть файл android_winusb.inf +-В QtCreator'е нажмите на зеленого робота и выберите Сборка: Выпуск +-Нажмите зеленую стрелку(Запустить)(https://yadi.sk/i/hKHKL_Phe4EFL) +-Выберите свой телефон/планшет, нажмите OK и ждите завершения компиляции. +-После завершения компиляции XP-Qt будет установлен на ваш телефон/планшет и запущен. А в папке C:\Android\build-XP-qt-Android_armeabi_v7a_GCC_4_9_Qt_5_4_0-Release\android-build\bin будет файл QtApp-debug.apk. Переименуйте его в XP-Qt-0.5.1-debug.apk и можно распростронять :) + +Изменения для Android X86 +1. Добавить путь C:\Android\android-ndk-r10d\toolchains\x86-4.9\prebuilt\windows-x86_64\bin в переменную PATH +2. Изменить системные переменные так: + +ANDROID_DEV /c/Android/android-ndk-r10d/platforms/android-9/arch-x86/usr +AR /c/Android/android-ndk-r10d/toolchains/x86-4.9/prebuilt/windows-x86_64/bin/i686-linux-android-ar +AS /c/Android/android-ndk-r10d/toolchains/x86-4.9/prebuilt/windows-x86_64/bin/i686-linux-android-as +CC /c/Android/android-ndk-r10d/toolchains/x86-4.9/prebuilt/windows-x86_64/bin/i686-linux-android-gcc +CFLAGS --sysroot=/c/Android/android-ndk-r10d/platforms/android-9/arch-x86/ +CPP /c/Android/android-ndk-r10d/toolchains/x86-4.9/prebuilt/windows-x86_64/bin/i686-linux-android-cpp +CPPFLAGS --sysroot=/c/Android/android-ndk-r10d/platforms/android-9/arch-x86/ +CXX /c/Android/android-ndk-r10d/toolchains/x86-4.9/prebuilt/windows-x86_64/bin/i686-linux-android-g++ +CXXFLAGS --sysroot=/c/Android/android-ndk-r10d/platforms/android-9/arch-x86/ -I/c/Android/android-ndk-r10d/sources/cxx-stl/gnu-libstdc++/4.9/include -I/c/Android/android-ndk-r10d/sources/cxx-stl/gnu-libstdc++/4.9/libs/x86/include +LD /c/Android/android-ndk-r10d/toolchains/x86-4.9/prebuilt/windows-x86_64/bin/i686-linux-android-ld +NDK_PROJECT_PATH C:\Android\android-ndk-r10d +RANLIB /c/Android/android-ndk-r10d/toolchains/x86-4.9/prebuilt/windows-x86_64/bin/i686-linux-android-ranlib + +3. При построении OpenSSL выполнить +Configure no-shared no-dso android-x86 +вместо +Configure no-shared no-dso android-armv7 + +4. При построении BerkeleyDB выполнить +../dist/configure --host=x86-none-linux --enable-cxx --disable-shared --disable-replication +вместо +../dist/configure --host=arm-none-linux --enable-cxx --disable-shared --disable-replication + +5. При построении Boost вставить +using gcc : x86 : i686-linux-android-g++.exe ; +вместо +using gcc : arm : arm-linux-androideabi-g++.exe ; + +и выполнить +b2 --build-type=complete --with-chrono --with-filesystem --with-program_options --with-system --with-thread toolset=gcc variant=release link=static threading=multi threadapi=pthread target-os=android define=BOOST_MATH_DISABLE_FLOAT128 include=C:\Android\android-ndk-r10d\sources\cxx-stl\gnu-libstdc++\4.9\include include=C:\Android\android-ndk-r10d\sources\cxx-stl\gnu-libstdc++\4.9\libs\x86\include include=C:\Android\android-ndk-r10d\platforms\android-9\arch-x86\usr\include + +вместо + +b2 --build-type=complete --with-chrono --with-filesystem --with-program_options --with-system --with-thread toolset=gcc-arm variant=release link=static threading=multi threadapi=pthread target-os=android define=BOOST_MATH_DISABLE_FLOAT128 include=C:\Android\android-ndk-r10d\sources\cxx-stl\gnu-libstdc++\4.9\include include=C:\Android\android-ndk-r10d\sources\cxx-stl\gnu-libstdc++\4.9\libs\armeabi\include include=C:\Android\android-ndk-r10d\platforms\android-9\arch-arm\usr\include + +6. В QtCreator выбирать Android для X86 вместо Android для armeabi-v7a diff --git a/doc/building novacoind and novacoinqt under Linux.txt b/doc/building novacoind and novacoinqt under Linux.txt new file mode 100644 index 00000000..bdbd1ac3 --- /dev/null +++ b/doc/building novacoind and novacoinqt under Linux.txt @@ -0,0 +1,51 @@ +Сборка для Linux +Если предпочитаете компилировать свои собственные бинарные файлы, тогда нужны пакеты разработчика: + +Ubuntu/Debian: + sudo apt-get install git qt4-qmake libqt4-dev build-essential libboost-dev libboost-system-dev + libboost-filesystem-dev libboost-program-options-dev libboost-thread-dev + libssl-dev libdb++-dev libqrencode-dev + +Если у вас возникла ошибка при установке qt4-qmake (Возникает на Ubuntu 14.04.2 и возможно в других версиях) +libcheese-gtk23 : Depends: libclutter-gtk-1.0-0 (>= 0.91.8) but it is not going to be installed + Depends: libcogl15 (>= 1.15.8) but it is not going to be installed +libcheese7 : Depends: libclutter-gst-2.0-0 (>= 0.10.0) but it is not going to be installed + Depends: gstreamer1.0-clutter but it is not going to be installed +libclutter-1.0-0 : Depends: libcogl-pango15 (>= 1.15.8) but it is not going to be installed + Depends: libcogl15 (>= 1.15.8) but it is not going to be installed + +То введите +sudo apt-get install libglew-dev libcheese7 libcheese-gtk23 libclutter-gst-2.0-0 libcogl15 libclutter-gtk-1.0-0 libclutter-1.0-0 xserver-xorg-input-all + +openSUSE(проверено на версии 13.2): + sudo zypper install git gcc gcc-c++ libqt4-devel boost-devel libopenssl-devel libdb-4_8-devel libqrencode3 + +После того, как установка завершалась, можно клонировать XP репозитарий + + git clone https://github.com/XP-project/XP.git + +и наконец, скомпилировать свой клиент + + cd XP + qmake USE_O3=1 USE_ASM=1 (если хотите LevelDB, то USE_LEVELDB=1) + make + cd src + make -f makefile.unix USE_O3=1 USE_ASM=1 (если хотите LevelDB, то USE_LEVELDB=1) + strip XPd + +Команды выше компилируют бинарные файлы с динамической линковкой, если вы хотите со статической линковкой и ваш дистрибутив содержит статические библиотеки(.a , а не .so), то добавьте: +-в команду qmake: + RELEASE=1 +-в команду make -f makefile.unix: + STATIC=1 + +Если вы хотите уменьшить размер бинарных файлов, то +1) Перейдите на сайт http://upx.sourceforge.net/#downloadupx +2) Скачайте программу в соответсвии с вашей системой +3) Распакуйте программу в папку с бинарными XP файлами. +4) Введите в терминале +./upx -9 XPd +затем +./upx -9 XP-qt + +И ваши XPd и XP-qt станут меньше ~ в 3 раза. diff --git a/doc/building novacoind and novacoinqt under Windows with MSVC.txt b/doc/building novacoind and novacoinqt under Windows with MSVC.txt new file mode 100644 index 00000000..f95db36f --- /dev/null +++ b/doc/building novacoind and novacoinqt under Windows with MSVC.txt @@ -0,0 +1,125 @@ +1. Подготовка системы. +Внимание: данная инструкция (включая сопутствуюшие файлы) расчитана на сборку в среде Windows 64-bit. + +1.1 Установка архиватора 7z: http://www.7-zip.org/ +(при написании инструкции использовался 7-Zip 9.20 64 bit X64, но скорее всего подойдёт любая версия) + +1.2 Установка Visual Studio 2012. (Подойдёт даже бесплатная версия) +http://www.microsoft.com/ru-ru/download/details.aspx?id=34673 +Установить обновление Visual Studio 2012 Update 4 +http://www.microsoft.com/ru-ru/download/details.aspx?id=39305 + +1.3 Установка Perl(при написании инструкции использовался ActivePerl-5.16.3.1604-MSWin32-x86-298023.msi) +http://www.activestate.com/activeperl/downloads +Убедитесь что после установки Perl в PATH есть пути(у меня Perl был установлен на диск C:\) C:\Perl\site\bin;C:\Perl\bin; + +1.4 Установка Python(подойдёт и 2 версия и 3 версия)(при написании инструкции использовался Python 3.3.3) +https://www.python.org/downloads/ +Убедитесь что после установки Python в PATH есть путь до python.exe + +1.5 Установка Nasm (при написании инструкции использовался nasm-2.07-installer.exe) +http://sourceforge.net/projects/nasm/files/latest/download +Убедитесь что после установки Nasm в PATH есть путь до Nasm.exe + +2. Построение зависимостей. +В инструкции все зависимости сохраняются в папку C:\MyProjects\Deps +Скопируйте папку build-helpers(находится в архиве с исходниками в папке MSVC) и вставьте в папку C:\MyProjects\Deps + +2.1 OpenSSL +-Скачайте http://www.openssl.org/source/openssl-1.0.2.tar.gz +-Распакуйте архив в папку C:\MyProjects\Deps +-Откройте командную строку Windows и выполните следующий код: + +cd C:\MyProjects\Deps\build-helpers +buildopenssl.bat + + +2.2 Berkeley DB +-Скачайте http://download.oracle.com/berkeley-db/db-6.0.20.tar.gz +-Распакуйте архив в папку C:\MyProjects\Deps +-Запустите Visual Studio 2012 +-Откройте C:\MyProjects\Deps\db-6.0.20\build_windows\Berkeley_DB_vs2010.sln +-Выберете проект db +-Измените конфигурацию с Debug на Static Debug +-Нажмите правой кнопкой мыши на проект db, затем в "Свойства" -> "Общие" убедитесь, что Набор символов "Использовать многобайтовую кодировку" +-Перейдите в "C/C++" -> "Препроцессор" -> "Определения препроцессора" и удалите _UNICODE и UNICODE, также убедитесь, что в Унаследованных значениях нет +_UNICODE и UNICODE +-Нажмите правой кнопкой мыши на проект db, затем "Только проект" -> "Построить только db" +-Постройте так все Static конфигурации(Static Debug 32 bit, Static Release 32 bit, Static Debug X64, Static Release X64) + +Проверка: +Если вы всё сделали правильно то у вас будут файлы: +C:\MyProjects\Deps\db-6.0.20\build_windows\Win32\Static Release\libdb60s.lib +C:\MyProjects\Deps\db-6.0.20\build_windows\Win32\Static Debug\libdb60sd.lib +C:\MyProjects\Deps\db-6.0.20\build_windows\x64\Static Release\libdb60s.lib +C:\MyProjects\Deps\db-6.0.20\build_windows\x64\Static Debug\libdb60sd.lib + +2.3 Boost +-Скачайте http://sourceforge.net/projects/boost/files/boost/1.57.0/boost_1_57_0.7z/download +-Распакуйте boost_1_57_0.7z в папку C:\MyProjects\Deps +-Откройте командную строку Windows и выполните следующий код: + +cd C:\MyProjects\Deps\build-helpers +buildboost.bat + +2.4 qrencode +-Скачайте http://qrencode-win32.googlecode.com/archive/681f2ea7a41f919486d9932b3352a2e6920e1cb9.zip +-Распакуйте +-Откройте командную строку Windows и выполните следующий код: + +cd C:\MyProjects\Deps\build-helpers +buildqrcode.bat + +2.5 Qt 5 +-Скачайте http://download.qt-project.org/official_releases/qt/5.3/5.3.2/single/qt-everywhere-opensource-src-5.3.2.7z +-Распакуйте в C:\MyProjects\Deps +-Если вам нужна поддержка 64 bit, то переименуйте папку qt-everywhere-opensource-src-5.3.2 в qt-everywhere-opensource-src-5.3.2-64 +(если вам нужна возможность строить и 32 bit и 64 bit бинарники, то у вас должны быть обе папки и qt-everywhere-opensource-src-5.3.2-64 и qt-everywhere-opensource-src-5.3.2 ) +-Откройте командную строку Windows и выполните следующий код: + +cd C:\MyProjects\Deps\build-helpers +buildqt32.bat + +или для 64 bit + +cd C:\MyProjects\Deps\build-helpers +buildqt64.bat + + +3. Компиляция +Будем хранить исходники в папке C:\MyProjects + +3.0 Подготовка Microsoft Visual Studio 2012 к работе +-Откройте Microsoft Visual Studio 2012 +-Нажмите СЕРВИС -> Параметры.. -> Текстовый редактор -> Все языки -> Табуляция и поставьте флажок "Вставлять пробелы" +(Если у вас английская версия, то это выглядит так: Tools->Options->Text Editor->All Languages->Tabs и поставьте флажок "Insert Spaces" вместо "Keep Tabs".) + +3.1 Скачиваем исходники +-Зайдите на сайт https://github.com/XP-project/XP +-Нажмите Download ZIP +-Распакуйте XP-master.zip в C:\MyProjects +-переименуйте XP-master в XP + +3.2 Компиляция +-Откройте Microsoft Visual Studio 2012 +-Откройте файл C:\MyProjects\XP\MSVC\XPSolution.sln +-Можете компилировать и XPD и XPQT +(по-умолчанию выставлена компиляция с LevelDB, если вы хотите компилировать с BerkleyDB, то +-Выберете проект libcommon, перейдите в свойства -> С\С++ -> Препроцессор, удалите USE_LEVELDB +-Удалите из проекта libcommon libcommon -> Source Files -> txdb-leveldb.cpp и добавьте в проект +txdb-bdb.cpp +-Удалите из проекта libcommon libcommon -> Header Files -> txdb-leveldb.h и добавьте в проект +txdb-bdb.h +-Выберете проект XPD, перейдите в свойства -> С\С++ -> Препроцессор, удалите USE_LEVELDB +-Выберете проект XPQT, перейдите в свойства -> С\С++ -> Препроцессор, удалите USE_LEVELDB + +Если вы хотите уменьшить размер бинарных файлов, то +1) Перейдите на сайт http://upx.sourceforge.net/#downloadupx +2) Скачайте программу в соответсвии с вашей системой +3) Распакуйте программу в папку с бинарными XP файлами. +4) Введите в консоли +upx -9 XPD.exe +затем +upx -9 XPQT.exe + +И ваши XPD и XPQT станут меньше ~ в 3 раза. diff --git a/doc/building novacoind and novacoinqt under Windows with MinGW.txt b/doc/building novacoind and novacoinqt under Windows with MinGW.txt new file mode 100644 index 00000000..57eb1569 --- /dev/null +++ b/doc/building novacoind and novacoinqt under Windows with MinGW.txt @@ -0,0 +1,506 @@ +32 bit: +1. Подготовка системы. + +1.1 Установка архиватора 7z: http://www.7-zip.org/ +(при написании инструкции использовался 7-Zip 9.20 64 bit X64, но скорее всего подойдёт любая версия) + +1.2 Установка msys shell: +-Скачайте http://sourceforge.net/projects/mingw/files/Installer/mingw-get-setup.exe/download +-нажмите Install +-директория для установки C:\MinGW +-поставить галочку напротив "...also install support for the graphical user interface." +-убрать галочки напротив "..in the start menu, and/or .." "... on the desktop" +-нажмите continue +-нажмите continue +-нажмите All Packages, затем MSYS +-поставте галочки напротив: +msys-autoconf-bin +msys-automake-bin +msys-base-bin +msys-libtool-bin +-нажмите Installation, Apply Changes, Apply. После завершения установки нажмите Close и закройте MinGW Installation Manager. + +1.3 Установка MinGW-builds project toolchain: +Скачайте http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/4.9.2/threads-posix/dwarf/i686-4.9.2-release-posix-dwarf-rt_v3-rev0.7z/download +и распакуйте на C:\ + +1.4 Добавьте в переменную PATH путь C:\mingw32\bin; +Подробное описание как это сделать на Windows 8, Windows 7, Windows XP и Windows Vista +https://www.java.com/ru/download/help/path.xml +(только не нужно выполнять последний пункт из этой инструкции "Откройте заново окно командной строки и выполните код java." ) + +Проверка что вы всё сделали правильно: +1)C:\MinGW\bin должен содержать только файл mingw-get.exe +2)Откройте Командную строку (Нажмите кнопку Windows + R одновременно. Откроется окно "Выполнить", в поле открыть наберите cmd. Нажмите Ok). Наберите gcc -v. В командной строке выведется текст. В конце текста должно быть написано: +Thread model: posix +gcc version 4.9.2 (i686-posix-dwarf-rev0, Built by MinGW-W64 project) + +2. Построение зависимостей. +В инструкции все зависимости сохраняются в папку c:\deps + +2.1 OpenSSL +-Скачайте https://openssl.org/source/openssl-1.0.2.tar.gz +-Перейдите в папку C:\MinGW\msys\1.0 и запустите msys.bat +Из MinGw shell выполните следующий код: + +cd /c/deps/ +tar xvfz openssl-1.0.2.tar.gz +cd openssl-1.0.2 +Configure no-shared no-dso mingw +make + +2.2 Berkeley DB +-Скачайте http://download.oracle.com/berkeley-db/db-6.0.20.tar.gz +-Из MinGw shell выполните следующий код: + +cd /c/deps/ +tar xvfz db-6.0.20.tar.gz +cd db-6.0.20/build_unix +../dist/configure --enable-mingw --enable-cxx --disable-shared --disable-replication +make + +2.3 Boost +-Скачайте http://sourceforge.net/projects/boost/files/boost/1.57.0/boost_1_57_0.7z/download +-Распакуйте boost_1_57_0.7z в папку C:\deps +-Откройте командную строку Windows, и выполните следующий код: + +cd C:\deps\boost_1_57_0\ +bootstrap.bat mingw +b2 --build-type=complete --with-chrono --with-filesystem --with-program_options --with-system --with-thread toolset=gcc variant=release link=static threading=multi runtime-link=static stage + +2.4 qrencode +-Скачайте http://download.sourceforge.net/libpng/libpng-1.6.15.tar.gz?download +-Распакуйте +-Откройте MinGw shell (C:\MinGW\msys\1.0\msys.bat) и выполните следующий код: + +cd /c/deps/libpng-1.6.15 +configure --disable-shared +make +cp .libs/libpng16.a .libs/libpng.a + +-Скачайте http://fukuchi.org/works/qrencode/qrencode-3.4.4.tar.gz +-Распакуйте +-Выполните следующий код в MinGW shell: + +cd /c/deps/qrencode-3.4.4 + +LIBS="../libpng-1.6.15/.libs/libpng.a ../../mingw32/i686-w64-mingw32/lib/libz.a" \ +png_CFLAGS="-I../libpng-1.6.15" \ +png_LIBS="-L../libpng-1.6.15/.libs" \ +configure --enable-static --disable-shared --without-tools + +make + +2.5 Qt 5 и Qt 4 +Библиотеки Qt будем хранить в папке C:\Qt +Для ускорения компиляции вместо +mingw32-make +используйте +mingw32-make -j n , где вместо n количество ядер вашего процессора +Qt 5: +-Скачайте http://download.qt-project.org/official_releases/qt/5.4/5.4.0/submodules/qtbase-opensource-src-5.4.0.7z +http://download.qt-project.org/official_releases/qt/5.4/5.4.0/submodules/qttools-opensource-src-5.4.0.7z +http://download.qt-project.org/official_releases/qt/5.4/5.4.0/submodules/qttranslations-opensource-src-5.4.0.7z + +-Распакуйте в C:\Qt +-Переименуйте папку qtbase-opensource-src-5.4.0 в 5.4.0 +-Откройте командную строку Windows и выполните следующий код: + +set INCLUDE=C:\deps\libpng-1.6.15;C:\deps\openssl-1.0.2\include +set LIB=C:\deps\libpng-1.6.15\.libs;C:\deps\openssl-1.0.2 + +cd C:\Qt\5.4.0 + +configure.bat -release -opensource -confirm-license -static -make libs -no-sql-sqlite -no-opengl -system-zlib -qt-pcre -no-icu -no-gif -system-libpng -no-libjpeg -no-freetype -no-angle -openssl -no-dbus -no-audio-backend -no-wmf-backend -no-qml-debug + +mingw32-make + +set PATH=%PATH%;C:\Qt\5.4.0\bin + +cd C:\Qt\qttools-opensource-src-5.4.0 +qmake qttools.pro +mingw32-make + +cd C:\Qt\qttranslations-opensource-src-5.4.0 +qmake qttranslations.pro +mingw32-make + + +Qt4: +-Скачайте http://download.qt-project.org/official_releases/qt/4.8/4.8.6/qt-everywhere-opensource-src-4.8.6.zip +-Распакуйте в C:\Qt +-Переименуйте папку qt-everywhere-opensource-src-4.8.6 в 4.8.6 +-Откройте командную строку Windows и выполните следующий код: + +cd C:\Qt\4.8.6 + +configure -release -opensource -confirm-license -static -no-sql-sqlite -no-qt3support -no-opengl -qt-zlib -no-gif -qt-libpng -qt-libmng -no-libtiff -qt-libjpeg -no-dsp -no-vcproj -no-openssl -no-dbus -no-phonon -no-phonon-backend -no-multimedia -no-audio-backend -no-webkit -no-script -no-scripttools -no-declarative -no-declarative-debug -qt-style-windows -qt-style-windowsxp -qt-style-windowsvista -no-style-plastique -no-style-cleanlooks -no-style-motif -no-style-cde -nomake demos -nomake examples + +mingw32-make + + +3. Компиляция +Будем хранить исходники в папке C:\MyProjects + +3.1 Скачиваем исходники +-Зайдите на сайт https://github.com/XP-project/XP +-Нажмите Download ZIP +-Распакуйте XP-master.zip в C:\MyProjects + +3.2 Собираем XPd +-Перейдите в папку C:\MyProjects\XP-master\src +-Откройте файл makefile.mingw в текстовом редакторе.(При написании инструкции использовался WordPad) +-Добавьте строчку USE_ASM:=1 +-Поменяйте текущие INCLUDEPATHS, LIBPATHS, LIBS на: + +BOOST_SUFFIX?=-mgw49-mt-s-1_57 + +INCLUDEPATHS= \ + -I"$(CURDIR)" \ + -I"/c/deps/boost_1_57_0" \ + -I"/c/deps" \ + -I"/c/deps/db-6.0.20/build_unix" \ + -I"/c/deps/openssl-1.0.2/include" + +LIBPATHS= \ + -L"$(CURDIR)/leveldb" \ + -L"/c/deps/boost_1_57_0/stage/lib" \ + -L"/c/deps/db-6.0.20/build_unix" \ + -L"/c/deps/openssl-1.0.2" + +LIBS= \ + -l leveldb \ + -l memenv \ + -l boost_system$(BOOST_SUFFIX) \ + -l boost_filesystem$(BOOST_SUFFIX) \ + -l boost_program_options$(BOOST_SUFFIX) \ + -l boost_thread$(BOOST_SUFFIX) \ + -l boost_chrono$(BOOST_SUFFIX) \ + -l db_cxx \ + -l ssl \ + -l crypto + +-Поменяйте LDFLAGS=-Wl,--dynamicbase -Wl,--nxcompat на +LDFLAGS=-Wl,--dynamicbase -Wl,--nxcompat -Wl,--large-address-aware -static + +-Если вы хотите использовать LevelDB как базу блоков то измените: +USE_LEVELDB:=0 +на +USE_LEVELDB:=1 +(выше USE_IPV6:=1) +Так же измените +cd leveldb; make; cd .. +на +cd leveldb; TARGET_OS=NATIVE_WINDOWS make libleveldb.a libmemenv.a; cd .. + +Ещё измените +obj/txdb-leveldb.o: leveldb/libleveldb.lib +на +obj/txdb-leveldb.o: leveldb/libleveldb.a + + + +Если вы хотите использовать BerkeleyDB как базу блоков, то просто удалите строчку USE_LEVELDB:=1 + +-Сохраните измененный файл makefile.mingw + +-Откройте MinGW shell (C:\MinGW\msys\1.0\msys.bat) и выполните следующий код: + +cd /c/MyProjects/XP-master/src +make -f makefile.mingw +strip XPd.exe + +Если у вас возникает ошибка "make: cc: Command not found", то поменяйте в файле makefile.mingw + + $(CC) -c $(xCXXFLAGS) -MMD -o $@ $< +на + gcc -c $(xCXXFLAGS) -MMD -o $@ $< + +Если всё сделано правильно, то файл XPd.exe будет находится в папке C:\MyProjects\XP-master\src + + +3.3 Собираем XP QT + +Внимание: Если вы хотите собирать XP Qt с LevelDB, но пропустили шаг со сборкой XPd.exe, то +-Откройте MinGW shell (C:\MinGW\msys\1.0\msys.bat) и выполните следующий код: + +cd /c/myprojects/XP-master/src/leveldb +TARGET_OS=NATIVE_WINDOWS make libleveldb.a libmemenv.a + +-Откройте файл C:\MyProjects\XP-master\XP-qt.pro в текстовом редакторе(при написании инструкции использовался WordPad) +-Ниже +# Dependency library locations can be customized with: +# BOOST_INCLUDE_PATH, BOOST_LIB_PATH, BDB_INCLUDE_PATH, +# BDB_LIB_PATH, OPENSSL_INCLUDE_PATH and OPENSSL_LIB_PATH respectively +замените прописанные пути к зависимостям на + +BOOST_LIB_SUFFIX=-mgw49-mt-s-1_57 +BOOST_INCLUDE_PATH=C:/deps/boost_1_57_0 +BOOST_LIB_PATH=C:/deps/boost_1_57_0/stage/lib +BDB_INCLUDE_PATH=C:/deps/db-6.0.20/build_unix +BDB_LIB_PATH=C:/deps/db-6.0.20/build_unix +OPENSSL_INCLUDE_PATH=C:/deps/openssl-1.0.2/include +OPENSSL_LIB_PATH=C:/deps/openssl-1.0.2 +QRENCODE_INCLUDE_PATH=C:/deps/qrencode-3.4.4 +QRENCODE_LIB_PATH=C:/deps/qrencode-3.4.4/.libs + +Так же измените(если ещё не изменено) +LIBS += -lshlwapi +genleveldb.commands = cd $$PWD/src/leveldb && CC=$$QMAKE_CC CXX=$$QMAKE_CXX TARGET_OS=OS_WINDOWS_CROSSCOMPILE $(MAKE) OPT=\"$$QMAKE_CXXFLAGS $$QMAKE_CXXFLAGS_RELEASE\" libleveldb.a libmemenv.a && $$QMAKE_RANLIB $$PWD/src/leveldb/libleveldb.a && $$QMAKE_RANLIB $$PWD/src/leveldb/libmemenv.a + +на + +LIBS += -lshlwapi +#genleveldb.commands = cd $$PWD/src/leveldb && CC=$$QMAKE_CC CXX=$$QMAKE_CXX TARGET_OS=OS_WINDOWS_CROSSCOMPILE $(MAKE) OPT=\"$$QMAKE_CXXFLAGS $$QMAKE_CXXFLAGS_RELEASE\" libleveldb.a libmemenv.a && $$QMAKE_RANLIB $$PWD/src/leveldb/libleveldb.a && $$QMAKE_RANLIB $$PWD/src/leveldb/libmemenv.a + +Если в файле нет такой строчки CONFIG += static , то добавьте её. + +Измените +win32:QMAKE_LFLAGS........................ +на +win32:QMAKE_LFLAGS *= -Wl,--large-address-aware -static + +Измените +windows:LIBS += -lboost_chrono$$BOOST_LIB_SUFFIX -Wl,-Bstatic -lpthread -Wl,-Bdynamic +на +windows:LIBS += -lboost_chrono$$BOOST_LIB_SUFFIX + +-Сохраните измененный файл XP-qt.pro +-Откройте командную строку Windows и выполните следующий код: + +Qt5 + транзакционный индекс BDB + +set PATH=%PATH%;C:\Qt\5.4.0\bin +cd C:\MyProjects\XP-master +qmake "USE_IPV6=1" "USE_ASM=1" XP-qt.pro +mingw32-make -f Makefile.Release + + +Qt5 + транзакционный индекс LevelDB + +set PATH=%PATH%;C:\Qt\5.4.0\bin +cd C:\MyProjects\XP-master +qmake "USE_IPV6=1" "USE_LEVELDB=1" "USE_ASM=1" XP-qt.pro +mingw32-make -f Makefile.Release + +Qt4 + транзакционный индекс BDB + +set PATH=%PATH%;C:\Qt\4.8.6\bin +cd C:\MyProjects\XP-master +qmake "USE_IPV6=1" "USE_ASM=1" XP-qt.pro +mingw32-make -f Makefile.Release + +Qt4 + транзакционный индекс LevelDB + +set PATH=%PATH%;C:\Qt\4.8.6\bin +cd C:\MyProjects\XP-master +qmake "USE_IPV6=1" "USE_LEVELDB=1" "USE_ASM=1" XP-qt.pro +mingw32-make -f Makefile.Release + + + +Если всё сделано правильно, то файл XP-qt.exe будет находится в папке C:\MyProjects\XP-master\release + +Если вы хотите уменьшить размер бинарных файлов, то +1) Перейдите на сайт http://upx.sourceforge.net/#downloadupx +2) Скачайте программу в соответсвии с вашей системой +3) Распакуйте программу в папку с бинарными XP файлами. +4) Введите в консоли +upx -9 XPd.exe +затем +upx -9 XP-qt.exe + +И ваши XPd и XP-qt станут меньше ~ в 3 раза. + + + +64 bit: +1.1 Так же как 32 bit + +1.2 Так же как 32 bit + +1.3 Установка MinGW-builds project toolchain: +Скачайте http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/4.9.2/threads-posix/seh/x86_64-4.9.2-release-posix-seh-rt_v3-rev0.7z/download +и распакуйте на C:\ + +1.4 Удалите из переменной PATH путь C:\mingw32\bin; и добавьте в PATH путь C:\mingw64\bin; + +Проверка что вы всё сделали правильно: +1)C:\MinGW\bin должен содержать только файл mingw-get.exe +2)Откройте Командную строку (Нажмите кнопку Windows + R одновременно. Откроется окно "Выполнить", в поле открыть наберите cmd. Нажмите Ok). Наберите gcc -v. В командной строке выведется текст. В конце текста должно быть написано: +Thread model: posix +gcc version 4.9.2 (x86_64-posix-seh-rev0, Built by MinGW-W64 project) + +2. Построение зависимостей. +В инструкции все зависимости сохраняются в папку c:\deps\x64 + +2.1 OpenSSL: http://www.openssl.org/source/openssl-1.0.2.tar.gz +-Перейдите в папку C:\MinGW\msys\1.0 и запустите msys.bat +Из MinGw shell выполните следующий код: + +cd /c/deps/x64/ +tar xvfz openssl-1.0.2.tar.gz +cd openssl-1.0.2 +Configure no-shared no-dso mingw64 +make + +2.2 Так же как 32 bit, только cd /c/deps/ меняется на cd /c/deps/x64/ + +2.3 Так же как 32 bit, только распаковка в C:\deps\x64 и команда cd C:\deps\boost_1_57_0\ меняется на cd C:\deps\x64\boost_1_57_0\ + +2.4 Часть с libpng так же как 32 bit, только распаковка в C:\deps\x64 +Затем +-Скачайте http://fukuchi.org/works/qrencode/qrencode-3.4.4.tar.gz +-Распакуйте в C:\deps\x64 +-Выполните следующий код в MinGW shell: + +cd /c/deps/x64/qrencode-3.4.4 + +LIBS="../libpng-1.6.15/.libs/libpng.a ../../../mingw64/x86_64-w64-mingw32/lib/libz.a" \ +png_CFLAGS="-I../libpng-1.6.15" \ +png_LIBS="-L../libpng-1.6.15/.libs" \ +configure --enable-static --disable-shared --without-tools + +make + + +2.5 Qt 5 и Qt 4 +Библиотеки Qt будем хранить в папке C:\Qt +Для ускорения компиляции вместо +mingw32-make +используйте +mingw32-make -j n , где вместо n количество ядер вашего процессора +Qt 5: +-Скачайте http://download.qt-project.org/official_releases/qt/5.4/5.4.0/submodules/qtbase-opensource-src-5.4.0.7z +http://download.qt-project.org/official_releases/qt/5.4/5.4.0/submodules/qttools-opensource-src-5.4.0.7z +http://download.qt-project.org/official_releases/qt/5.4/5.4.0/submodules/qttranslations-opensource-src-5.4.0.7z +-Распакуйте в C:\Qt +-Переименуйте папку qtbase-opensource-src-5.4.0 в 5.4.0-x64 +-Переименуйте папку qttools-opensource-src-5.4.0 в qttools-opensource-src-5.4.0-x64 +-Переименуйте папку qttranslations-opensource-src-5.4.0 в qttranslations-opensource-src-5.4.0-x64 +-Откройте командную строку Windows и выполните следующий код: + +set INCLUDE=C:\deps\x64\libpng-1.6.15;C:\deps\x64\openssl-1.0.2\include +set LIB=C:\deps\x64\libpng-1.6.15\.libs;C:\deps\x64\openssl-1.0.2 + +cd C:\Qt\5.4.0-x64 + +configure.bat -release -opensource -confirm-license -static -make libs -no-sql-sqlite -no-opengl -system-zlib -qt-pcre -no-icu -no-gif -system-libpng -no-libjpeg -no-freetype -no-angle -openssl -no-dbus -no-audio-backend -no-wmf-backend -no-qml-debug + +mingw32-make + +set PATH=%PATH%;C:\Qt\5.4.0-x64\bin + +cd C:\Qt\qttools-opensource-src-5.4.0-x64 +qmake qttools.pro +mingw32-make + +cd C:\Qt\qttranslations-opensource-src-5.4.0-x64 +qmake qttranslations.pro +mingw32-make + +Qt4: +-Скачайте http://download.qt-project.org/official_releases/qt/4.8/4.8.6/qt-everywhere-opensource-src-4.8.6.zip +-Распакуйте в C:\Qt +-Переименуйте папку qt-everywhere-opensource-src-4.8.6 в 4.8.6-x64 +-Откройте командную строку Windows и выполните следующий код: + +configure -release -opensource -confirm-license -static -no-sql-sqlite -no-qt3support -no-opengl -qt-zlib -no-gif -qt-libpng -qt-libmng -no-libtiff -qt-libjpeg -no-dsp -no-vcproj -no-openssl -no-dbus -no-phonon -no-phonon-backend -no-multimedia -no-audio-backend -no-webkit -no-script -no-scripttools -no-declarative -no-declarative-debug -qt-style-windows -qt-style-windowsxp -qt-style-windowsvista -no-style-plastique -no-style-cleanlooks -no-style-motif -no-style-cde -nomake demos -nomake examples + +mingw32-make + +3. Компиляция +Будем хранить исходники в папке C:\MyProjects + +3.1 Так же как 32 bit + +3.2 Собираем XPd +-Измените makefile.mingw так же как в 32 bit +-Откройте файл makefile.mingw в текстовом редакторе и сохраните его как makefile.ming64 +-Измените в INCLUDEPATHS и LIBPATHS /c/deps/ на /c/deps/x64/ +-Измените +LDFLAGS=-Wl,--dynamicbase -Wl,--nxcompat -Wl,--large-address-aware -static +на +LDFLAGS=-Wl,--dynamicbase -Wl,--nxcompat -Wl,-static, -static-libgcc +-Удалите (если они есть)obj(.o) файлы из C:\MyProjects\XP-master\src\obj , если остались после 32 bit +-Удалите (если они есть)libleveldb.a и libmemenv.a в папке C:\MyProjects\XP-master\src\leveldb +-Удалите (если они есть)obj(.o) файлы из +C:\MyProjects\XP-master\src\leveldb\db +C:\MyProjects\XP-master\src\leveldb\helpers\memenv +C:\MyProjects\XP-master\src\leveldb\port +C:\MyProjects\XP-master\src\leveldb\table +C:\MyProjects\XP-master\src\leveldb\util +-Откройте MinGW shell (C:\MinGW\msys\1.0\msys.bat) и выполните следующий код: + +cd /c/MyProjects/XP-master/src +make -f makefile.mingw64 +strip XPd.exe + +Если всё сделано правильно, то файл XPd.exe будет находится в папке C:\MyProjects\XP-master\src + + +3.3 Собираем XP QT +Внимание: Если вы хотите собирать XP Qt с LevelDB, но пропустили шаг со сборкой XPd.exe, то +-Удалите (если они есть)libleveldb.a и libmemenv.a в папке C:\MyProjects\XP-master\src\leveldb +-Удалите (если они есть)obj(.o) файлы из +C:\MyProjects\XP-master\src\leveldb\db +C:\MyProjects\XP-master\src\leveldb\helpers\memenv +C:\MyProjects\XP-master\src\leveldb\port +C:\MyProjects\XP-master\src\leveldb\table +C:\MyProjects\XP-master\src\leveldb\util +-Откройте MinGW shell (C:\MinGW\msys\1.0\msys.bat) и выполните следующий код: + +cd /c/myprojects/XP-master/src/leveldb +TARGET_OS=NATIVE_WINDOWS make libleveldb.a libmemenv.a + +-Измените файл XP-qt.pro так же как в 32 bit +-Откройте файл XP-qt.pro в текстовом редакторе и сохраните его как XP-qt64.pro +-Замените в INCLUDE и LIB путях текст C:/deps на текст C:/deps/x64 (то есть вместо C:/deps/boost_1_57_0 нужно C:/deps/x64/boost_1_57_0 и т.д.) +-Замените +win32:QMAKE_LFLAGS *= -Wl,--large-address-aware -static +на +win32:QMAKE_LFLAGS *= -Wl,-static + +-Откройте командную строку Windows и выполните следующий код: + +Qt5 + транзакционный индекс BDB + +set PATH=%PATH%;C:\Qt\5.4.0-x64\bin +cd C:\MyProjects\XP-master +qmake "USE_IPV6=1" "USE_ASM=1" XP-qt64.pro +mingw32-make -f Makefile.Release + + +Qt5 + транзакционный индекс LevelDB + +set PATH=%PATH%;C:\Qt\5.4.0-x64\bin +cd C:\MyProjects\XP-master +qmake "USE_IPV6=1" "USE_LEVELDB=1" "USE_ASM=1" XP-qt64.pro +mingw32-make -f Makefile.Release + +Qt4 + транзакционный индекс BDB + +set PATH=%PATH%;C:\Qt\4.8.6-x64\bin +cd C:\MyProjects\XP-master +qmake "USE_IPV6=1" "USE_ASM=1" XP-qt64.pro +mingw32-make -f Makefile.Release + +Qt4 + транзакционный индекс LevelDB + +set PATH=%PATH%;C:\Qt\4.8.6-x64\bin +cd C:\MyProjects\XP-master +qmake "USE_IPV6=1" "USE_LEVELDB=1" "USE_ASM=1" XP-qt64.pro +mingw32-make -f Makefile.Release + +Если всё сделано правильно, то файл XP-qt.exe будет находится в папке C:\MyProjects\XP-master\release + +Если вы хотите уменьшить размер бинарных файлов, то +1) Перейдите на сайт http://upx.sourceforge.net/#downloadupx +2) Скачайте программу в соответсвии с вашей системой +3) Распакуйте программу в папку с бинарными XP файлами. +4) Введите в консоли +upx -9 XPd.exe +затем +upx -9 XP-qt.exe + +И ваши XPd и XP-qt станут меньше ~ в 3 раза. diff --git a/doc/coding.txt b/doc/coding.txt new file mode 100644 index 00000000..46da6613 --- /dev/null +++ b/doc/coding.txt @@ -0,0 +1,99 @@ +Please be consistent with the existing coding style. + +Block style: + +bool Function(char* psz, int n) +{ + // Comment summarising what this section of code does + for (int i = 0; i < n; i++) + { + // When something fails, return early + if (!Something()) + return false; + ... + } + + // Success return is usually at the end + return true; +} + +- ANSI/Allman block style +- 4 space indenting, no tabs +- No extra spaces inside parenthesis; please don't do ( this ) +- No space after function names, one space after if, for and while + +Variable names begin with the type in lowercase, like nSomeVariable. +Please don't put the first word of the variable name in lowercase like +someVariable. + +Common types: +n integer number: short, unsigned short, int, unsigned int, + int64, uint64, sometimes char if used as a number +d double, float +f flag +hash uint256 +p pointer or array, one p for each level of indirection +psz pointer to null terminated string +str string object +v vector or similar list objects +map map or multimap +set set or multiset +bn CBigNum + +------------------------- +Locking/mutex usage notes + +The code is multi-threaded, and uses mutexes and the +CRITICAL_BLOCK/TRY_CRITICAL_BLOCK macros to protect data structures. + +Deadlocks due to inconsistent lock ordering (thread 1 locks cs_main +and then cs_wallet, while thread 2 locks them in the opposite order: +result, deadlock as each waits for the other to release its lock) are +a problem. Compile with -DDEBUG_LOCKORDER to get lock order +inconsistencies reported in the debug.log file. + +Re-architecting the core code so there are better-defined interfaces +between the various components is a goal, with any necessary locking +done by the components (e.g. see the self-contained CKeyStore class +and its cs_KeyStore lock for example). + +------- +Threads + +StartNode : Starts other threads. + +ThreadGetMyExternalIP : Determines outside-the-firewall IP address, +sends addr message to connected peers when it determines it. + +ThreadIRCSeed : Joins IRC bootstrapping channel, watching for new +peers and advertising this node's IP address. + +ThreadSocketHandler : Sends/Receives data from peers on port 8333. + +ThreadMessageHandler : Higher-level message handling (sending and +receiving). + +ThreadOpenConnections : Initiates new connections to peers. + +ThreadTopUpKeyPool : replenishes the keystore's keypool. + +ThreadCleanWalletPassphrase : re-locks an encrypted wallet after user +has unlocked it for a period of time. + +SendingDialogStartTransfer : used by pay-via-ip-address code (obsolete) + +ThreadDelayedRepaint : repaint the gui + +ThreadFlushWalletDB : Close the wallet.dat file if it hasn't been used +in 500ms. + +ThreadRPCServer : Remote procedure call handler, listens on port 8332 +for connections and services them. + +ThreadBitcoinMiner : Generates bitcoins + +ThreadMapPort : Universal plug-and-play startup/shutdown + +Shutdown : Does an orderly shutdown of everything + +ExitTimeout : Windows-only, sleeps 5 seconds then exits application diff --git a/doc/crosscompiling_building Windows binary under Unix.txt b/doc/crosscompiling_building Windows binary under Unix.txt new file mode 100644 index 00000000..bd33816c --- /dev/null +++ b/doc/crosscompiling_building Windows binary under Unix.txt @@ -0,0 +1,330 @@ +Данный способ должен подходить для любой Unix системы: Linux, FreeBSD, MacOS X +При написании этого руководства использовался Linux Mint 17.1 ( http://linuxmint.com/ ) +1) Подготовка системы для кросскомпиляции. + + sudo apt-get install git bison cmake flex g++ gperf ruby scons libghc-zlib-dev libghc-zlib-bindings-dev + +2) Установка MXE и зависимостей + cd /home/<ваше имя>/ + git clone https://github.com/mxe/mxe.git + + +2.1) gcc + cd /home/<ваше имя>/mxe + make gcc zlib + +Проверка того, что установка gcc прошла успешно: +В папке /home/<ваше имя>/mxe/usr/bin должен появиться файл i686-w64-mingw32.static-gcc и i686-w64-mingw32.static-g++ +И при запуске ./i686-w64-mingw32.static-gcc -v должно выдаваться +...... +Thread model: win32 +gcc version 4.9.2 (GCC) + +В папке /home/<ваше имя>/mxe/usr/i686-w64-mingw32.static/lib должен быть файл: +libz.a + +2.2) Openssl + + cd /home/<ваше имя>/mxe + make openssl + +Проверка что установка Openssl прошла успешно: +В папке /home/<ваше имя>/mxe/usr/i686-w64-mingw32.static/lib должны появиться два файла +-libssl.a +-libcrypto.a +В папке mxe/usr/i686-w64-mingw32.static/include должна появиться папка openssl + +2.3) Boost + + cd /home/<ваше имя>/mxe + make boost + +Проверка что установка Boost прошла успешно: +В /home/<ваше имя>/mxe/usr/i686-w64-mingw32.static/lib папке должны появиться файлы: +-libboost_имябиблиотеки_mt.a (libboost_atomic-mt.a и другие подобные файлы) +-libboost_имябиблиотеки_mt-d.a (libboost_atomic-mt-d.a и другие подобные файлы) + +В папке /home/<ваше имя>/mxe/usr/i686-w64-mingw32.static/include должна появиться папка boost + +2.4) Berkeley DB +-Скачайте http://download.oracle.com/berkeley-db/db-6.0.20.tar.gz +-Распакуйте в домашнюю папку +-Откройте файл /home/<ваше имя>/db-6.0.20/src/dbinc/win_db.h +-Измените +#include +на +#include +-Сохраните файл +-Откройте терминал + cd /home/<ваше имя>/db-6.0.20/build_unix + export PATH=/home/<ваше имя>/mxe/usr/bin:$PATH + ../dist/configure --host=i686-w64-mingw32.static --enable-mingw --enable-cxx --disable-shared --disable-replication + make + +Проверка, что установка Berkeley DB прошла успешно: +В папке /home/<ваше имя>/db-6.0.20/build_unix должны появиться файлы: +libdb.a +libdb_cxx.a + +2.5) qrencode +-Скачайте http://download.sourceforge.net/libpng/libpng-1.6.15.tar.gz?download +-Распакуйте в домашнюю папку +-Откройте терминал + + export PATH=/home/<ваше имя>/mxe/usr/bin:$PATH + cd /home/<ваше имя>/libpng-1.6.15 + ./configure --host=i686-w64-mingw32.static --disable-shared + make + cp .libs/libpng16.a .libs/libpng.a + +Проверка что libpng собралась успешно: +в папке /home/<ваше имя>/libpng-1.6.15/.libs должен быть файл: +libpng.a + +-Скачайте http://fukuchi.org/works/qrencode/qrencode-3.4.4.tar.gz +-Распакуйте в домашнюю папку +-Откройте терминал + export PATH=/home/<ваше имя>/mxe/usr/bin:$PATH + cd /home/<ваше имя>/qrencode-3.4.4 + + LIBS="../libpng-1.6.15/.libs/libpng.a ../mxe/usr/i686-w64-mingw32.static/lib/libz.a" \ + png_CFLAGS="-I../libpng-1.6.15" \ + png_LIBS="-L../libpng-1.6.15/.libs" \ + ./configure --host=i686-w64-mingw32.static --enable-static --disable-shared --without-tools + + make + +Проверка что qrencode успешно собралась: +в папке /home/<ваше имя>/qrencode-3.4.4/.libs должен быть файл: +libqrencode.a + +2.6 Qt 5 и Qt 4 + +Qt 4 +-Откройте терминал + + cd /home/<ваше имя>/mxe + make qt + +Проверка что Qt4 собрался правильно: +-Откройте терминал +-Введите следующие команды + export PATH=/home/<ваше имя>/mxe/usr/bin:$PATH + i686-w64-mingw32.static-qmake-qt4 -v + +-Должно появиться в ответ +QMake version 2.01a +Using Qt version 4.8.6 in /home/<ваше имя>/mxe/usr/i686-w64-mingw32.static/qt/lib + +Qt 5 +-Откройте терминал + + export PATH=/home/<ваше имя>/mxe/usr/bin:$PATH + cd /home/<ваше имя>/mxe + make qtbase qttools qttranslations + +Проверка что Qt5 собрался правильно: +-Откройте терминал +-Введите следующие команды + export PATH=/home/<ваше имя>/mxe/usr/bin:$PATH + i686-w64-mingw32.static-qmake-qt5 -v + +-Должно появиться в ответ +QMake version 3.0 +Using Qt version 5.4.0 in /home/<ваше имя>/mxe/usr/i686-w64-mingw32.static/qt5/lib + +3. Компиляция + +3.1 Скачиваем исходники +-Перейдите в домашнюю папку +-Откройте терминал + + git clone https://github.com/XP-project/XP.git + +3.2 Собираем XPd +-Откройте файл makefile.mingw в текстовом редакторе.(При написании инструкции использовался Pluma 1.8.1) +-Вставьте +CC=$(CROSS)gcc +CXX=$(CROSS)g++ +-Добавьте строчку USE_ASM:=1 +-Поменяйте текущие INCLUDEPATHS, LIBPATHS, LIBS на: + +BOOST_SUFFIX?=-mt + +INCLUDEPATHS= \ + -I"$(CURDIR)" \ + -I"/home/<ваше имя>/mxe/usr/i686-w64-mingw32.static/include" \ + -I"/home/<ваше имя>/" \ + -I"/home/<ваше имя>/db-6.0.20/build_unix" \ + +LIBPATHS= \ + -L"$(CURDIR)/leveldb" \ + -L"/home/<ваше имя>/mxe/usr/i686-w64-mingw32.static/lib" \ + -L"/home/<ваше имя>/db-6.0.20/build_unix" \ + +LIBS= \ + -l leveldb \ + -l memenv \ + -l boost_system$(BOOST_SUFFIX) \ + -l boost_filesystem$(BOOST_SUFFIX) \ + -l boost_program_options$(BOOST_SUFFIX) \ + -l boost_thread$(BOOST_SUFFIX) \ + -l boost_chrono$(BOOST_SUFFIX) \ + -l db_cxx \ + -l ssl \ + -l crypto \ + -l z +-Поменяйте LDFLAGS=-Wl,--dynamicbase -Wl,--nxcompat на +LDFLAGS=-Wl,--dynamicbase -Wl,--nxcompat -Wl,--large-address-aware -static + +-Поменяйте в последних строчках makefile.mingw +g++ -c $(CFLAGS) -o $@ $< +на +$(CXX) -c $(CFLAGS) -o $@ $< + +g++ $(CFLAGS) $(LDFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) +на +$(CXX) $(CFLAGS) $(LDFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) + +-Если вы хотите использовать LevelDB как базу блоков то измените: +USE_LEVELDB:=0 +на +USE_LEVELDB:=1 +(выше USE_IPV6:=1) +Откройте файл /home/<ваше имя>/XP/src/leveldb/Makefile +Вставьте ниже строчки CXXFLAGS += -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT) следующие строки: + +CC=$(CROSS)gcc +CXX=$(CROSS)g++ +Сохраните файл + +Снова возвращаемся в файл makefile.mingw, измените +cd leveldb; make; cd .. +на +cd leveldb; TARGET_OS=NATIVE_WINDOWS make CROSS=i686-w64-mingw32.static- libleveldb.a libmemenv.a; cd .. + +Ещё измените +obj/txdb-leveldb.o: leveldb/libleveldb.lib +на +obj/txdb-leveldb.o: leveldb/libleveldb.a + +Если вы хотите использовать BerkeleyDB как базу блоков, то просто удалите строчку USE_LEVELDB:=1 + +-Сохраните измененный файл makefile.mingw + +-Откройте терминал +cd /home/<ваше имя>/XP/src +export PATH=/home/<ваше имя>/mxe/usr/bin:$PATH +make CROSS=i686-w64-mingw32.static- -f makefile.mingw +strip XPd.exe + + +Во время линковки будут выдаваться подобные сообщения: +libboost_thread_win32-mt.a(thread.o): duplicate section `.rdata$_ZTVN5boost16exception_detail10clone_implINS0_19error_info_injectorINS_9gregorian9bad_monthEEEEE[__ZTVN5boost16exception_detail10clone_implINS0_19error_info_injectorINS_9gregorian9bad_monthEEEEE]' has different size +Игнорируйте их. На работоспособность программы это никак не влияет + + +3.3 Собираем XP QT + +Внимание: Если вы хотите собирать XP Qt с LevelDB, но пропустили шаг со сборкой XPd.exe, то +-Измените Makefile в папке leveldb также как в пункте 3.2 +-Откройте терминал и выполните следующие команды: +export PATH=/home/<ваше имя>/mxe/usr/bin:$PATH +cd /home/<ваше имя>/XP/src/leveldb +TARGET_OS=NATIVE_WINDOWS make CROSS=i686-w64-mingw32.static- libleveldb.a libmemenv.a; + +-Откройте файл /home/<ваше имя>/XP/XP-qt.pro в текстовом редакторе(при написании инструкции использовался Pluma 1.8.1) +-Ниже +# Dependency library locations can be customized with: +# BOOST_INCLUDE_PATH, BOOST_LIB_PATH, BDB_INCLUDE_PATH, +# BDB_LIB_PATH, OPENSSL_INCLUDE_PATH and OPENSSL_LIB_PATH respectively +замените прописанные пути к зависимостям на + +BOOST_LIB_SUFFIX=-mt +BOOST_INCLUDE_PATH=/home/<ваше имя>/mxe/usr/i686-w64-mingw32.static/include +BOOST_LIB_PATH=/home/<ваше имя>/mxe/usr/i686-w64-mingw32.static/lib +BDB_INCLUDE_PATH=/home/<ваше имя>/db-6.0.20/build_unix +BDB_LIB_PATH=//home/<ваше имя>/db-6.0.20/build_unix +OPENSSL_INCLUDE_PATH=/home/<ваше имя>/mxe/usr/i686-w64-mingw32.static/include +OPENSSL_LIB_PATH=/home/<ваше имя>/mxe/usr/i686-w64-mingw32.static/lib +QRENCODE_INCLUDE_PATH=/home/<ваше имя>/qrencode-3.4.4 +QRENCODE_LIB_PATH=/home/<ваше имя>/qrencode-3.4.4/.libs + +Так же измените(если ещё не изменено) +LIBS += -lshlwapi +genleveldb.commands = cd $$PWD/src/leveldb && CC=$$QMAKE_CC CXX=$$QMAKE_CXX TARGET_OS=OS_WINDOWS_CROSSCOMPILE $(MAKE) OPT=\"$$QMAKE_CXXFLAGS $$QMAKE_CXXFLAGS_RELEASE\" libleveldb.a libmemenv.a && $$QMAKE_RANLIB $$PWD/src/leveldb/libleveldb.a && $$QMAKE_RANLIB $$PWD/src/leveldb/libmemenv.a + +на + +LIBS += -lshlwapi +#genleveldb.commands = cd $$PWD/src/leveldb && CC=$$QMAKE_CC CXX=$$QMAKE_CXX TARGET_OS=OS_WINDOWS_CROSSCOMPILE $(MAKE) OPT=\"$$QMAKE_CXXFLAGS $$QMAKE_CXXFLAGS_RELEASE\" libleveldb.a libmemenv.a && $$QMAKE_RANLIB $$PWD/src/leveldb/libleveldb.a && $$QMAKE_RANLIB $$PWD/src/leveldb/libmemenv.a + +Если в файле нет такой строчки CONFIG += static , то добавьте её. + +Измените +win32:QMAKE_LFLAGS........................ +на +win32:QMAKE_LFLAGS *= -Wl,--large-address-aware -static + +Измените +win32:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]\\lrelease.exe +на +win32:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]/lrelease + +Измените +LIBS += -lboost_system$$BOOST_LIB_SUFFIX -lboost_filesystem$$BOOST_LIB_SUFFIX -lboost_program_options$$BOOST_LIB_SUFFIX -lboost_thread$$BOOST_THREAD_LIB_SUFFIX +на +LIBS += -lboost_system$$BOOST_LIB_SUFFIX -lboost_filesystem$$BOOST_LIB_SUFFIX -lboost_program_options$$BOOST_LIB_SUFFIX -lboost_thread_win32$$BOOST_THREAD_LIB_SUFFIX + +Измените +windows:LIBS += -lboost_chrono$$BOOST_LIB_SUFFIX -Wl,-Bstatic -lpthread -Wl,-Bdynamic +на +windows:LIBS += -lboost_chrono$$BOOST_LIB_SUFFIX + +-Сохраните измененный файл XP-qt.pro +-Откройте терминал и выполните следующие команды + +Qt4 + транзакционный индекс LevelDB + +export PATH=/home/<ваше имя>/mxe/usr/bin:$PATH +cd /home/<ваше имя>/XP +i686-w64-mingw32.static-qmake-qt4 "USE_IPV6=1" "USE_LEVELDB=1" "USE_ASM=1" XP-qt.pro +make -j n -f Makefile.Release (вместо n количество ядер вашего процессора, которые вы хотите выделить под сборку) + +Qt4 + транзакционный индекс BDB + +export PATH=/home/<ваше имя>/mxe/usr/bin:$PATH +cd /home/<ваше имя>/XP +i686-w64-mingw32.static-qmake-qt4 "USE_IPV6=1" "USE_ASM=1" XP-qt.pro +make -j n -f Makefile.Release (вместо n количество ядер вашего процессора, которые вы хотите выделить под сборку) + +Qt5 + транзакционный индекс LevelDB + +export PATH=/home/<ваше имя>/mxe/usr/bin:$PATH +cd /home/<ваше имя>/XP +i686-w64-mingw32.static-qmake-qt5 "USE_IPV6=1" "USE_LEVELDB=1" "USE_ASM=1" XP-qt.pro +make -j n -f Makefile.Release (вместо n количество ядер вашего процессора, которые вы хотите выделить под сборку) + +Qt5 + транзакционный индекс BDB + +export PATH=/home/<ваше имя>/mxe/usr/bin:$PATH +cd /home/<ваше имя>/XP +i686-w64-mingw32.static-qmake-qt5 "USE_IPV6=1" "USE_ASM=1" XP-qt.pro +make -j n -f Makefile.Release (вместо n количество ядер вашего процессора, которые вы хотите выделить под сборку) + + +Если всё сделано правильно, то файл XP-qt.exe будет находится в папке /home/<ваше имя>/XP/release +Во время линковки будут выдаваться подобные сообщения: +libboost_thread_win32-mt.a(thread.o): duplicate section `.rdata$_ZTVN5boost16exception_detail10clone_implINS0_19error_info_injectorINS_9gregorian9bad_monthEEEEE[__ZTVN5boost16exception_detail10clone_implINS0_19error_info_injectorINS_9gregorian9bad_monthEEEEE]' has different size +Игнорируйте их. На работоспособность программы это никак не влияет + +Если вы хотите уменьшить размер бинарных файлов, то +1) Перейдите на сайт http://upx.sourceforge.net/#downloadupx +2) Скачайте программу в соответсвии с вашей системой +3) Распакуйте программу в папку с бинарными XP файлами. +4) Введите в терминале +./upx -9 XPd.exe +затем +./upx -9 XP-qt.exe + +И ваши XPd и XP-qt станут меньше ~ в 3 раза. diff --git a/doc/readme-qt.rst b/doc/readme-qt.rst new file mode 100644 index 00000000..07065d91 --- /dev/null +++ b/doc/readme-qt.rst @@ -0,0 +1,163 @@ +XP-qt: Qt4 GUI for XP +=============================== + +Build instructions +=================== + +Debian +------- + +First, make sure that the required packages for Qt4 development of your +distribution are installed, these are + +:: + +for Debian and Ubuntu <= 11.10 : + +:: + + apt-get install qt4-qmake libqt4-dev build-essential libboost-dev libboost-system-dev \ + libboost-filesystem-dev libboost-program-options-dev libboost-thread-dev \ + libssl-dev libdb4.8++-dev + +for Ubuntu >= 12.04 (please read the 'Berkely DB version warning' below): + +:: + + apt-get install qt4-qmake libqt4-dev build-essential libboost-dev libboost-system-dev \ + libboost-filesystem-dev libboost-program-options-dev libboost-thread-dev \ + libssl-dev libdb++-dev + +then execute the following: + +:: + + qmake + make + +Alternatively, install Qt Creator and open the `XP-qt.pro` file. + +An executable named `XP-qt` will be built. + + +Windows +-------- + +Windows build instructions: + +- Download the `QT Windows SDK`_ and install it. You don't need the Symbian stuff, just the desktop Qt. + +- Download and extract the `dependencies archive`_ [#]_, or compile openssl, boost and dbcxx yourself. + +- Copy the contents of the folder "deps" to "X:\\QtSDK\\mingw", replace X:\\ with the location where you installed the Qt SDK. Make sure that the contents of "deps\\include" end up in the current "include" directory. + +- Open the .pro file in QT creator and build as normal (ctrl-B) + +.. _`QT Windows SDK`: http://qt.nokia.com/downloads/sdk-windows-cpp +.. _`dependencies archive`: https://download.visucore.com/bitcoin/qtgui_deps_1.zip +.. [#] PGP signature: https://download.visucore.com/bitcoin/qtgui_deps_1.zip.sig (signed with RSA key ID `610945D0`_) +.. _`610945D0`: http://pgp.mit.edu:11371/pks/lookup?op=get&search=0x610945D0 + + +Mac OS X +-------- + +- Download and install the `Qt Mac OS X SDK`_. It is recommended to also install Apple's Xcode with UNIX tools. + +- Download and install `MacPorts`_. + +- Execute the following commands in a terminal to get the dependencies: + +:: + + sudo port selfupdate + sudo port install boost db48 + +- Open the .pro file in Qt Creator and build as normal (cmd-B) + +.. _`Qt Mac OS X SDK`: http://qt.nokia.com/downloads/sdk-mac-os-cpp +.. _`MacPorts`: http://www.macports.org/install.php + + +Build configuration options +============================ + +LevelDB transaction index +-------------------------- + +To use LevelDB for transaction index, pass the following argument to qmake: + +:: + + qmake "USE_LEVELDB=1" + +No additional external dependencies are required. If you're running this on your current sources tree then don't forget to run + +:: + + make distclean + +prior to running qmake. + +Assembler implementation of scrypt hashing +------------------------------------------ + +To use optimized scrypt implementation instead of generic scrypt module, pass the following argument to qmake: + +:: + + qmake "USE_ASM=1" + + +If you're using clang compiler then you need to unroll macroses before compiling. Following commands will do this for you: + +:: + + cd src/ + ../contrib/clang/nomacro.pl + +No additional external dependencies required. Note that only x86, x86_64 and ARM processors are supported. + +Notification support for recent (k)ubuntu versions +--------------------------------------------------- + +To see desktop notifications on (k)ubuntu versions starting from 10.04, enable usage of the +FreeDesktop notification interface through DBUS using the following qmake option: + +:: + + qmake "USE_DBUS=1" + +Generation of QR codes +----------------------- + +libqrencode is used to generate QRCode images for payment requests. +It can be downloaded from http://fukuchi.org/works/qrencode/index.html.en, or installed via your package manager. + +Berkely DB version warning +========================== + +A warning for people using the *static binary* version of XP on a Linux/UNIX-ish system (tl;dr: **Berkely DB databases are not forward compatible**). + +The static binary version of XP is linked against libdb5.3. + +If the globally installed development package of Berkely DB installed on your system is 5.X, for example, 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! + +Ubuntu 11.10 warning +==================== + +Ubuntu 11.10 has a package called 'qt-at-spi' installed by default. At the time of writing, having that package +installed causes XP-qt to crash intermittently. The issue has been reported as `launchpad bug 857790`_, but +isn't yet fixed. + +Until the bug is fixed, you can remove the qt-at-spi package to work around the problem, though this will presumably +disable screen reader functionality for Qt apps: + +:: + + sudo apt-get remove qt-at-spi + +.. _`launchpad bug 857790`: https://bugs.launchpad.net/ubuntu/+source/qt-at-spi/+bug/857790 diff --git a/doc/tor.md b/doc/tor.md new file mode 100644 index 00000000..e125fe92 --- /dev/null +++ b/doc/tor.md @@ -0,0 +1,99 @@ +TOR SUPPORT IN XP +====================== + +It is possible to run XP as a Tor hidden service, and connect to such services. + +The following directions assume you have a Tor proxy running on port 9050. Many distributions default to having a SOCKS proxy listening on port 9050, but others may not. In particular, the Tor Browser Bundle defaults to listening on a random port. See [Tor Project FAQ:TBBSocksPort](https://www.torproject.org/docs/faq.html.en#TBBSocksPort) for how to properly +configure Tor. + + +1. Run XP behind a Tor proxy +--------------------------------- + +The first step is running XP behind a Tor proxy. This will already make all +outgoing connections be anonymized, but more is possible. + + -proxy=ip:port Set the proxy server. If SOCKS5 is selected (default), this proxy + server will be used to try to reach .onion addresses as well. + + -onion=ip:port Set the proxy server to use for tor hidden services. You do not + need to set this if it's the same as -proxy. You can use -noonion + to explicitly disable access to hidden service. + + -listen When using -proxy, listening is disabled by default. If you want + to run a hidden service (see next section), you'll need to enable + it explicitly. + + -connect=X When behind a Tor proxy, you can specify .onion addresses instead + -addnode=X of IP addresses or hostnames in these parameters. It requires + -seednode=X SOCKS5. In Tor mode, such addresses can also be exchanged with + other P2P nodes. + +In a typical situation, this suffices to run behind a Tor proxy: + + ./XP -proxy=127.0.0.1:9050 + + +2. Run a XP hidden server +------------------------------ + +If you configure your Tor system accordingly, it is possible to make your node also +reachable from the Tor network. Add these lines to your /etc/tor/torrc (or equivalent +config file): + + HiddenServiceDir /var/lib/tor/XP-service/ + HiddenServicePort 7777 127.0.0.1:7777 + HiddenServicePort 17777 127.0.0.1:17777 + +The directory can be different of course, but (both) port numbers should be equal to +your XPd's P2P listen port (7777 by default, 17777 by default for testnet). + + -externalip=X You can tell XP about its publicly reachable address using + this option, and this can be a .onion address. Given the above + configuration, you can find your onion address in + /var/lib/tor/XP-service/hostname. Onion addresses are given + preference for your node to advertize itself with, for connections + coming from unroutable addresses (such as 127.0.0.1, where the + Tor proxy typically runs). + + -listen You'll need to enable listening for incoming connections, as this + is off by default behind a proxy. + + -discover When -externalip is specified, no attempt is made to discover local + IPv4 or IPv6 addresses. If you want to run a dual stack, reachable + from both Tor and IPv4 (or IPv6), you'll need to either pass your + other addresses using -externalip, or explicitly enable -discover. + Note that both addresses of a dual-stack system may be easily + linkable using traffic analysis. + +In a typical situation, where you're only reachable via Tor, this should suffice: + + ./XPd -proxy=127.0.0.1:9050 -externalip=youraddress.onion -listen + +(obviously, replace the Onion address with your own). If you don't care too much +about hiding your node, and want to be reachable on IPv4 as well, additionally +specify: + + ./XPd ... -discover + +and open port 7777 on your firewall. + +If you only want to use Tor to reach onion addresses, but not use it as a proxy +for normal IPv4/IPv6 communication, use: + + ./XP -onion=127.0.0.1:9050 -externalip=youraddress.onion -discover + +Known addresses of XP nodes + + seedp4knqnoei57u.onion + seedr3hhlepyi7fd.onion + seed3uuomkclbiz4.onion + seedeh7qck3ouff5.onion + seedt3sraf53ajiy.onion + seedg4qyccsg42oq.onion + novaqrtoywpg7jly.onion + seed3d5wolqbgrcb.onion + seed24u5dwph3qw4.onion + mj26ulzbs2oskgym.onion + eqon4usunavt76m7.onion + 5rg3vq4jagckeckf.onion diff --git a/share/genbuild.sh b/share/genbuild.sh new file mode 100644 index 00000000..d959877d --- /dev/null +++ b/share/genbuild.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +if [ $# -gt 0 ]; then + FILE="$1" + shift + if [ -f "$FILE" ]; then + INFO="$(head -n 1 "$FILE")" + fi +else + echo "Usage: $0 " + exit 1 +fi + +if [ -e "$(which git)" ]; then + # clean 'dirty' status of touched files that haven't been modified + git diff >/dev/null 2>/dev/null + + # get a string like "v0.6.0-66-g59887e8-dirty" + DESC="$(git describe --dirty 2>/dev/null)" + + # get a string like "2012-04-10 16:27:19 +0200" + TIME="$(git log -n 1 --format="%ci")" +fi + +if [ -n "$DESC" ]; then + NEWINFO="#define BUILD_DESC \"$DESC\"" +else + NEWINFO="// No build information available" +fi + +# only update build.h if necessary +if [ "$INFO" != "$NEWINFO" ]; then + echo "$NEWINFO" >"$FILE" + echo "#define BUILD_DATE \"$TIME\"" >>"$FILE" +fi diff --git a/share/pixmaps/addressbook16.bmp b/share/pixmaps/addressbook16.bmp new file mode 100644 index 0000000000000000000000000000000000000000..c5576910b1b6b95c23ed7c53adec399a944e610a GIT binary patch literal 1334 zcmZ9MKWrOS9LK*+iDcA*T|jP9K#EfVb%3_kxoL?Sz~+CTKo0?umkcM}a-)X~w`8y~ zWwata$wP;F^b*OVM^AaW@iN6rqzqX?CP!j;d|n(O=-qwq_kQ2^d%y4Zy_2r_Y;|6j z*XnE1Jw~M~TvK5-acvg<`*zOBxvAXL<4n`D6HQM%GM!B(AlC_)PJEuJ;Aq!F>#NkLovPNIJ5$BCNMjXNzRgQ_+*rp!g; zA@kIOH0r69ChE4y+o3w`mbys@n%a#*r9QS=N;g8a!={p`qHa`Dn3U9Rt|@8O6xBjv zRneM91uViXss*BIg-Ct1pujo>E&wqwzyhb34*>)GZGZ*v0ek=-zz6UFd;rgan8}zy z9K*-(2F}14I0I)qN3Dgo@D|R(;UVVyV|>JTi1A?2a=ck4gU;+G$Cp8$Dx%MwXb<4< zWDqq77(5xY4U>i@25o~j0eEeA^bPt3eWn2Qp2vg1gW((~M&aj7mEptAkqMyg_a@XPLPt?d zkC;5hhr8_UYhqQ@*XHJ?wzjskv$LbUy*(Wq9B6-kUx$Z>IyySi@$s=vPfvAzey)p) z3;p%C)5{kxw7tE}>9ScKn#}@lhQO)#%Jtps`s3Lw7VG>=O7B&F`?+#Qdc9t(-w6Nw z{nu~qFaPlTdHBKC%zr1p5Vd?$#kAAwEFRk42>mO|ReC~Q-`SIO_!hG=g_r>}+ss?m65uRi?;YjfP( literal 0 HcmV?d00001 diff --git a/share/pixmaps/addressbook16mask.bmp b/share/pixmaps/addressbook16mask.bmp new file mode 100644 index 0000000000000000000000000000000000000000..d3a478d1ad1d4800008fda5be5d583f3d0423480 GIT binary patch literal 126 tcmZ?rtz&=yJ0PV2!~#&v$iN7eZ~&8-#Q*>Q!Giz)F))yd?S4Sa0{}e{AL0N2 literal 0 HcmV?d00001 diff --git a/share/pixmaps/addressbook20.bmp b/share/pixmaps/addressbook20.bmp new file mode 100644 index 0000000000000000000000000000000000000000..2b33b228aaec9ef51fb3dd46b3eab98f3d61d7df GIT binary patch literal 1478 zcma)+J!~6g9LAp(Xj4#-1LY%>FBd2^1%kPdf|c^&f`Ee-Fy_la3iQe$IKDk(@RGsG z9usXbe`4esg7wT_5VJWhTCq&7#TB`8C#7sMrJhB7@1L`@i5T{jfVq~s9}w4E! zw_*2z22GFXsne(_Y6L`^Q&Ln9)UH?6LQUklMn%-9Rn@6^>eWhWRy~zaBdDkql+^G% zxmCM%<*{$qXH@q}s-X&AKx|alBhM=mUG_yagLz^>b>!A&$jizrda9N1+@k8GJjy9> z3#t{1s-n7^Q?J+hqv|Z8#9m33I+p%fx*CFV4wui69xl=fgy&0 zHi&!}3=9Sa0|TuQJ`4s11A_s;Fh8c5q0o@z)5B-)!(r0iHpf?1ZLO}ZYHe*z8yg$i z+}zam_O`aRwzRXequt$I?d|Ppe}7*G2M0PlJk-Dc8U1tPhSt~DIbB|sAI&C#J4GgAUy7(iLxq5=0b7wD>|14iTJ13pEig)jYa`|ijuj|Xd{$44U-+JXM>s@~B ze0F~B8Rz8vXU<$GJsqUpEY+uS&Pl@Tw=exP@s8g$^ZbVs@0%yToOqAF{OrTj+PqKh z`{=0l_^GM)#Y3On@@AKo&)K~1UcWZ2U08f)$$4~Pap8&8SARLm(^2hh-qP}ew)Tg& zy<4?!7M$E7wco$@8{Qk2Q|5D5QkK+v>=D;`4}9?ACI0Kqe*VH!_CVjizOwS^rPG<` PK3-Y5_U;`Xahmo&>vaK4 literal 0 HcmV?d00001 diff --git a/share/pixmaps/addressbook20mask.bmp b/share/pixmaps/addressbook20mask.bmp new file mode 100644 index 0000000000000000000000000000000000000000..56ce6125dbb83abfac437bbbcdb611f45ee57cff GIT binary patch literal 142 zcmZ?r?PGudJ0PV2#3E44$iN7e2mq2o+z`wJ7J(4||Nn>c{{KI~KrXi54>yJZ01nMB AR{#J2 literal 0 HcmV?d00001 diff --git a/share/pixmaps/check.ico b/share/pixmaps/check.ico new file mode 100644 index 0000000000000000000000000000000000000000..0c4e6e81473d47761346fd71e171a807bda5ec58 GIT binary patch literal 766 zcmdUtAritc5Ji7?>Oo=IK{2@m+>AIRcZlpMaugJUL?RfLzb&Fm5tZVcTcTu78nlc+X79T)o9yH~Bu18~wK99d`Ux|y2#0TZ_)l%(v!!U*NL^S< zm%;~9QW>8Y{WOqh07L_>g8?ux2p5ho`>mS)zJ7m;2t| z?*#^6AV!UXd=JIirvV4xzx#Gr`xKy-W8*Bxr0k?D<9=xy;Jf^0W;_b9jOiwL?%b`lRW>msC-He%;SK*p@4U50HfO#30u;j=^ zDALcvGrI&Uq!d{f;dQhOo;elpJYEi;6H>Y5n44+FyII#T|5!cVIbH`JlUb?;9~c|3 zBJZF07~J7Mlv{u=&&# zgcjD|eNz(_n;Nm?WGfW%?J1?mYsS(q+u(b;5xxbD@F{GAqM!py3%-T-R~_&K(fxzMhgcsMtr|33T6?Y=w+zkX2wIJ|ZGXhK6;9qhJYtDa*)g`yFp|k^=<@b=% z7AP;aV9TXe1Yd5))`|{nueyaW^KFD)y^DxzcMyH_G~&LhM8fHEL|te=blDBWUur@^ zc@xxsZ-VM_GgK8VNUm%_qPZQqs#c^_-$d%QThQ138*#NiAgZj-ams3|E3F8Zh4oUNt|?V+7ps zB7!g$Nf?h@IH3}=(TRnijSuJ;1S;Y{Wg4jYD`+eO&0qf{Zzp*t$uE+;pX5U&ua^Ah zk~c}dQ1TZfFXzdij_pAhNv?Vx6F_8LnUwd57
&NeSIdbJk`}Pf$5i^rN9A~rZH!;sm^il^8K)7nF$NLBL zyAP!PaYCT(S9b5}Hmq2+N&oy5fBooLTGzpU(0MKY_!IrFU)h=R+NAiPfzCQluVqX0 zifH|qiEnQU8ni$+XVxq?{RoGo?Jggv7e5oG+xW(eD7|}xev$8Lt*t%$l7Iig)VTSv z(^s!aP|2iOsM_|s;h&D$_13CwI@?gWz-b8@pLbrL?VYG~eARjb@BK-mO-|N^yky<8 zd@P(l*=b(?Uhf#0XL4UfG6y3E^w* zzp*{-{J4lioi8i3Juj_GXntYU&SUlg(QZGNT%k7(oD!8DwkYG=_wueYsrdnuTS;we zALU)8kFkQbfr%X99hyHQL~Z-iK5NqQ20471c|>?2Jfpa&?-l<@7l)|q z_?YWm6s}QNOW`qvF%-5?IK_;nZXRjtp|F|G=~Wa~GD+@HTqW1AeXU z6sJ*`N?{(wUlczv@?TvHDr@H8W!vs8J%0Iz@*Z;i;s=U9taT#6^?Zz!WUgW_HKo^7W%jM3M-DZZdhK*GJ(*T>ph=D-xyiR{?AZtkqP zL-9OiJwb60WevgP%2tY>`Qe$H6mL<}W;ypM4qqM`WyxB$2@wZJ&PdL$h>_m$H+m^f zrFfgNPLStRL-8!dO;k5MpsWcv+;>Ojf~eGi{ptQsr(NkblXn}Ai(h3PKVV{W9}{JK zfAvw^Nv9;!cXk_8o805lgb4qCn-ZBmcWS&L(=ENYVNv#l9=Fd+zI#h!Dsl+@bI|y$ h`z=ea|Jgc68s-!YGR%n_&|iRI&M(!&ah8lF`yZ|pi&Ovr literal 0 HcmV?d00001 diff --git a/share/pixmaps/novacoin.ico b/share/pixmaps/novacoin.ico new file mode 100644 index 0000000000000000000000000000000000000000..ee62b47e7e7ec146ecc0fd773c487b6f960e507c GIT binary patch literal 15086 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 p+jZBr c #8D5D07", +", c #805610", +"< c #815817", +"1 c #925911", +"2 c #9A5B15", +"3 c #98660E", +"4 c #9E6B0E", +"5 c #936519", +"6 c #A0631B", +"7 c #B26B1B", +"8 c #AC7616", +"9 c #B57D1D", +"0 c #885F23", +"q c #825928", +"w c #8D652F", +"e c #926028", +"r c #886538", +"t c #8C6532", +"y c #966C33", +"u c #946D38", +"i c #99733E", +"p c #9E783F", +"a c #9D7136", +"s c #AA7327", +"d c #BB7727", +"f c #B07C22", +"g c #A07334", +"h c #AD7630", +"j c #AD7B36", +"k c #A37738", +"l c #A2793D", +"z c #AE7E3A", +"x c #B47E3A", +"c c #987540", +"v c #9D7943", +"b c #9C7B4E", +"n c #9C7E55", +"m c #AF7E41", +"M c #BD861E", +"N c #B68429", +"B c #BB862A", +"V c #AD8038", +"C c #B38139", +"Z c #C88F27", +"A c #C88229", +"S c #C98C28", +"D c #C3822A", +"F c #D1952C", +"G c #C18736", +"H c #C78837", +"J c #D7993C", +"K c #E49E38", +"L c #E6A637", +"P c #EAA936", +"I c #EFA63C", +"U c #A58245", +"Y c #A98243", +"T c #A3804F", +"R c #B98846", +"E c #B68E4B", +"W c #BC8A4A", +"Q c #BF974F", +"! c #BE9757", +"~ c #BF9B53", +"^ c #BD9A5E", +"/ c #CC9A44", +"( c #C0974C", +") c #C59447", +"_ c #D39B45", +"` c #C79C51", +"' c #CB9D52", +"] c #D5A248", +"[ c #DBA64B", +"{ c #DCA94D", +"} c #DAA144", +"| c #CFA754", +" . c #CCA458", +".. c #D4AA5B", +"X. c #DFAC52", +"o. c #DAB258", +"O. c #E0A74B", +"+. c #E7AF4C", +"@. c #EDB64C", +"#. c #F0B049", +"$. c #F6BA49", +"%. c #F7B848", +"&. c #E0AD53", +"*. c #E6B356", +"=. c #E5B45A", +"-. c #E8B75B", +";. c #E6BA5B", +":. c #EBBA5D", +">. c #EAB453", +",. c #F4BF59", +"<. c #C49F60", +"1. c #CAA562", +"2. c #D5A663", +"3. c #DDB76F", +"4. c #DEB96F", +"5. c #E1B761", +"6. c #E1B863", +"7. c #EBBC63", +"8. c #E2BA6F", +"9. c #F0BF62", +"0. c #E1BD7D", +"q. c #FBC355", +"w. c #FBC75B", +"e. c #E5C161", +"r. c #EFC765", +"t. c #EFC669", +"y. c #EACA68", +"u. c #F6CD67", +"i. c #FCCE62", +"p. c #FACD68", +"a. c #F3C567", +"s. c #FCD267", +"d. c #F7D26B", +"f. c #FED169", +"g. c #FED86E", +"h. c #EBC171", +"j. c #ECC77D", +"k. c #F3CB71", +"l. c #FCCF75", +"z. c #F3CC78", +"x. c #EED173", +"c. c #F5D572", +"v. c #F7D876", +"b. c #FCDB74", +"n. c #F5D17C", +"m. c #FAD57A", +"M. c #FCDE7B", +"N. c #F7E37E", +"B. c #FBE17E", +"V. c #F5D485", +"C. c #FAD885", +"Z. c #FBDC95", +"A. c #F7D998", +"S. c #FDE584", +"D. c #FFE987", +"F. c #FDE28A", +"G. c #FFEB8A", +"H. c #FCE294", +"J. c #FEED97", +"K. c #FDEE9B", +"L. c #FFF591", +"P. c #FDF19D", +"I. c #FAE4A4", +"U. c #FEF4A3", +"Y. c #F8E5B5", +"T. c #FCECB0", +"R. c #F9E4B8", +"E. c #FDF6B2", +"W. c #FFF0B8", +"Q. c #F6E8C7", +"!. c #FFF2CC", +"~. c None", +/* pixels */ +"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.] ] ] ] ] ] ] ~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.] ] { ;.r.u.s.s.s.s.c.r.>.{ ] ~.~.", +"~.~.~.~.~.~.~.~.~.~.~.~.~.~.] e.N.P.U.G.F.B.v.d.s.B.D.W.!.V.{ ~.", +"~.~.~.~.~.~.~.~.~.~.~.~.~.{ y.L.U.K.E.K.S.x.M.G.v.B.B.s.f.J.M./ ", +"~.~.~.~.~.~.~.~.~.~.~.~.~.&.m.U.P.U.K.H.J.S.S.S.D.d.s.g.b.w.w.z ", +"~.~.~.~.~.~.~.~.~.~.~.~.~.&.q.Z.T.K.G.G.N.x.c.M.v.B.d.m.l.@.F j ", +"~.~.~.~.~.~.~.~.~.~.~.~.~.&.q.p.C.Y.Q.R.A.A.H.z.6.4.1.Q N M Z z ", +"~.~.~.~.~.~.~.~.~.~.~.~.~.&.>.a.n.H.C.f.$.P F M 3 : > 4 8 f j C ", +"~.~.~.~.~.~.~.~.~.~.~.~.~.&.a.7.7.j.V.7.+.L F 9 5 O < e w k ] j ", +"~.~.~.~.~.~.~.~.~.~.~.~.~.&.q.Z.I.Z.y.6... .~ E Y v Y .x.@.F j ", +"~.~.~.~.~.~.~.~.~.~.~.~.~.O.$.p.C.Y.Q.Y.A.A.Z.z.6.4.1.~ N M Z z ", +"~.~.~.~.~.~.] ] ] ] ] ] ] K K I #.:.:.>.#.P F 9 3 : > 4 8 f h C ", +"~.~.] ] { ;.y.u.s.s.s.s.d.t.-.{ } [ _ H d D A 9 5 O # w w p ] j ", +"~.] e.N.P.U.G.S.B.v.d.d.M.G.T.!.V.{ J G h e & p Y v Y ` h.@.F j ", +"[ y.L.U.J.E.K.S.c.M.G.v.S.M.s.s.K.B.) 2.( ) W ' e.4.1.Q N M Z V ", +"&.C.U.P.P.K.H.J.S.S.G.S.c.s.g.b.w.q.j _ A 7 2 1 3 : > 4 8 9 j C ", +"&.q.Z.T.K.G.S.S.v.c.b.v.M.d.m.z.@.F C V.{ D 2 1 5 # # e w a / V ", +"&.q.s.C.R.Q.R.A.Z.H.z.6.4.1.Q N M Z j K.M./ % $ p v m <.h.@.F V ", +"&.*.7.n.H.C.p.$.P F 9 3 : > 4 8 9 j ) w.w.z ) C x 3. .~ N M Z j ", +"~.*.-.-.j.V.7.+.L F 9 5 , < 0 w l 1.l.@.F x 2 - o ; > 4 8 M k C ", +"~.~.~.*.,.9.5...E Y c b b v m 1.1.Q N M Z j { s . O # e w k / j ", +"~.~.~.&.*.9.z.H.C.p.%.P F 9 3 ; > 3 8 N s Q B./ % r V ^ 8.@.F j ", +"~.~.~.~.*.-.7.j.V.7.+.L F 9 5 , < e w i | i.w.j V R ! ~ N M Z V ", +"~.~.~.~.~.+.,.h.j.0. .E Y c c v U ~ o.v.c.@.F j o X ; 3 8 f h C ", +"~.~.~.~.] X.q.s.C.Y.Q.Y.A.A.Z.z.6.4.1.Q N M Z j o . @ e w a / j ", +"~.~.~.[ y.7.*.9.z.H.F.d.%.P F 9 3 : > 4 8 f h g $ & y ! 4.@.F j ", +"~.~.~.&.C.K.n.7.7.j.V.7.+.L F 9 5 # < 0 w y m x j R E Q N M Z V ", +"~.~.~.&.q.Z.T.K.M.z.t.6.' E Y c u T T V B z 2 = o X ; 4 8 9 k j ", +"~.~.~.&.q.s.C.Y.Q.R.A.A.H.t.4.4.1.~ N M Z j 2 = . . @ 0 w y a ~.", +"~.~.~.&.*.7.n.H.C.p.$.P F M 3 > > 4 8 f j z U i r * * r ~.~.~.~.", +"~.~.~.~.*.-.7.j.j.7.+.L F 9 5 O # e w y g ~.~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.,.-...` E U i r * * * ~.~.~.~.~.~.~.~.~.~.~.~.~.~." +}; diff --git a/share/pixmaps/novacoin80.xpm b/share/pixmaps/novacoin80.xpm new file mode 100644 index 00000000..d98b0804 --- /dev/null +++ b/share/pixmaps/novacoin80.xpm @@ -0,0 +1,339 @@ +/* XPM */ +static char *XP_80[] = { +/* columns rows colors chars-per-pixel */ +"80 80 253 2 ", +" c #7A4B09", +". c #794C19", +"X c #7D5418", +"o c #784D0F", +"O c #7B5628", +"+ c #7C5A32", +"@ c #6A4A26", +"# c #84550A", +"$ c #8C5C0A", +"% c #875807", +"& c #845413", +"* c #8A5513", +"= c #8D5C15", +"- c #87591D", +"; c #945B16", +": c #90570F", +"> c #9D6A15", +", c #94651B", +"< c #9B6C1C", +"1 c #976618", +"2 c #916314", +"3 c #A16E16", +"4 c #A46919", +"5 c #A6711C", +"6 c #AE781C", +"7 c #AA751C", +"8 c #B47C1A", +"9 c #865B25", +"0 c #8B5E2A", +"q c #8A632A", +"w c #9C6523", +"e c #9C6C23", +"r c #976927", +"t c #9E7229", +"y c #8B6532", +"u c #886537", +"i c #936B33", +"p c #986E34", +"a c #9C7337", +"s c #A46B23", +"d c #AA6D24", +"f c #A26824", +"g c #A57424", +"h c #AA7623", +"j c #AC7B25", +"k c #A2732C", +"l c #A67A2C", +"z c #AC7C2A", +"x c #AA7529", +"c c #B47222", +"v c #B17E25", +"b c #B77A2A", +"n c #A37533", +"m c #AB7D32", +"M c #A77A36", +"N c #B57D35", +"B c #B06F23", +"V c #C37D27", +"C c #997745", +"Z c #A47C40", +"A c #B8811D", +"S c #AF812E", +"D c #B58125", +"F c #BA8425", +"G c #B4832C", +"H c #BB852B", +"J c #BC882D", +"K c #BE8827", +"L c #AD8133", +"P c #AC823C", +"I c #B38433", +"U c #BA8634", +"Y c #BD8B34", +"T c #BC8639", +"R c #BD8A3C", +"E c #B58639", +"W c #C58D2D", +"Q c #C7872B", +"! c #CC912C", +"~ c #D2962A", +"^ c #D99B2B", +"/ c #C38D33", +"( c #C38D3C", +") c #C78835", +"_ c #C49136", +"` c #CA9436", +"' c #C5923B", +"] c #CB963A", +"[ c #CD993C", +"{ c #CE9837", +"} c #D29C3B", +"| c #D59839", +" . c #D28B2F", +".. c #DCA23B", +"X. c #E3A43A", +"o. c #E7A939", +"O. c #F1AF36", +"+. c #E29F3D", +"@. c #AB8545", +"#. c #B78A46", +"$. c #BB924A", +"%. c #BE9150", +"&. c #C38C47", +"*. c #C49343", +"=. c #CA9543", +"-. c #CD9B44", +";. c #C7984B", +":. c #D29D42", +">. c #D59D48", +",. c #C79B55", +"<. c #D29B52", +"1. c #D5A144", +"2. c #DBA344", +"3. c #D5A24A", +"4. c #DAA54A", +"5. c #DDA94C", +"6. c #DAA84B", +"7. c #CCA253", +"8. c #CCA659", +"9. c #D3A554", +"0. c #DCAB53", +"q. c #DBAC5B", +"w. c #D5A957", +"e. c #D8AB54", +"r. c #EAAE45", +"t. c #E3AC4B", +"y. c #E4A843", +"u. c #E6B14D", +"i. c #EBB34C", +"p. c #EEB145", +"a. c #F4BB4E", +"s. c #F8B947", +"d. c #E2AD52", +"f. c #E2AC5A", +"g. c #E5B253", +"h. c #EBB552", +"j. c #EEB954", +"k. c #E4B45B", +"l. c #E9B55A", +"z. c #ECBB5C", +"x. c #E6B95C", +"c. c #F2BC55", +"v. c #F2BD5A", +"b. c #F6BB55", +"n. c #CAA666", +"m. c #D6AB66", +"M. c #DDB368", +"N. c #DBB676", +"B. c #D1AE72", +"V. c #E3B563", +"C. c #E4BC63", +"Z. c #EBBC63", +"A. c #E4BA6B", +"S. c #EABE6B", +"D. c #E2B66A", +"F. c #F1BF64", +"G. c #E4BE75", +"H. c #E3BD7B", +"J. c #E9BD74", +"K. c #D09F61", +"L. c #FAC14E", +"P. c #F6C155", +"I. c #FBC454", +"U. c #F5C35C", +"Y. c #FAC55A", +"T. c #FECA5D", +"R. c #FEC856", +"E. c #EEC15E", +"W. c #ECC265", +"Q. c #ECC36C", +"!. c #EAC66C", +"~. c #F3C463", +"^. c #F5CA64", +"/. c #FDCC63", +"(. c #F3C56B", +"). c #F3CB6C", +"_. c #FBCC6A", +"`. c #F9C763", +"'. c #FFD165", +"]. c #FED36B", +"[. c #FBD66E", +"{. c #EBC474", +"}. c #ECCA74", +"|. c #EAC47B", +" X c #EDCA7B", +".X c #E6C279", +"XX c #F4CB74", +"oX c #FACD72", +"OX c #F4CC7B", +"+X c #F4C976", +"@X c #EFD079", +"#X c #F5D174", +"$X c #FDD373", +"%X c #FEDA74", +"&X c #F4D47A", +"*X c #FCD47A", +"=X c #F6DA7C", +"-X c #FDDC7B", +";X c #F7D977", +":X c #FEE27D", +">X c #FFE97F", +",X c #D9B982", +"XgXgX>X>X:X:X[._.P.P.h.i.E LXLXLXLXLXLXLXLX", +"LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLX2.5.0.x.!.7XeXhXkXhXkXhXhXhXfX=X&X!.&X%X%X%X].T.).:XvXcXVXDXDXNXeX*X`.c.6.LXLXLXLXLXLX", +"LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXt.6.6.}.wXlXzXzXMXzXxXkXhXxXjX&X}.&X&X-X-X%X$XU.W.W.].].].].%X9XkXDXKXKXGXiXY.c.5.LXLXLXLX", +"LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXt.4.w. XMXMXMXlXzXDXGXDXCXzXjXjX9X9XfX9X9X;X#X!.W.#X%X:X[.].].'.'.'./././.sXSXHXGX9X/.h.LXLXLX", +"LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLX4.0.wXNXMXMXzXzXMXVXzXDXSXcXxXgXjXfX:X-X!.!.}.;X*X=X;X=X-X-X-X%X[.]./././.T.T.-XVXDXgXT.j.LXLX", +"LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLX5.3.7XCXMXMXMXNXCXMXxXxXSXDXVXvXgXjXfXfX!.!.9XgXfX}.!.).).^.!.[.).).^.].].]./././.T.gXZX-XY.u.LX", +"LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXg.0.lXNXCXSXSXBXxXsXxXxXzXDXVXxX=X@X@X{.OX&X}.&X@X:XgX-X$X$X$X%X%X-X;X/.U.P.Y.T.[.%XT.gX:XY.5.LX", +"LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXz.d.sXGXCXBXxXxXzXzXzXVXzXsXwX7XwXxXhXeX9X9X-X&X=X}.gXgX=X:XgXfX#XE.U.T.T.'.'.'.%X[.s.T.T.P.*.LX", +"LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXt.W.W.6XzXMXMXCXCXCXzXwX7XeXkXlXxXNXwXhXhXfX9XfX=X@X:X@X:XfX).x.T.'.'.'.'.%X%X%XT.L.I.R.I.t.T LX", +"LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXt.g.XX{.0XCXSXCXxXhXsXkXxXxXhXhXfX0X@X@X X}.{.Q.!.}.@X=X}.x.^.].].].[.:X:X[./.P.L.I.I.b.g.| R LX", +"LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXi.h.l.OX5X4X6X0XxXlXxXxXxXxXkXhXhXhXeX9X9X9X-X-X-X-X=X&X}.:X%X:X-X-X#X).`.P.I.T.T.P.P.2.[ { Y LX", +"LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXh.c.j.(.A.qXpXuXyXuXsXhXhXkXhXkXhXeXhX9X9X9X9X-X*X%X$XoXW.W.^.E.U.~.U.T./._.*X+XU.3.:._ } } ( LX", +"LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXh.c.v._.Z.OXJ.dXAXnXAXFXpX0X4X&X-X8XfX9X9X9X9X-X-X*X$X$X].].'./._.$XoXiXsXpXN.-.=.F } ` } [ R LX", +"LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXi.c.v._.Z.oXQ.OX1X.XmXmXbXFXnXbXAXuXuXqX4XOX8X*XoX$X_._.oX8X8X|.3XN.m.n.m Y j G ] K -.` } | R LX", +"LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXt.j.c._.Z.*X!.*X7X}.lX3X|.2XN.{.|.M.J.|.D.{.G.M.m.D.9.*.3.P t E P = l S > Y D J ' K } ' | Y *.LX", +"LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXz.d.d.F.z.oXQ.*X7X XkX7X&X8XZ.(.F.4.b.r.| t.| Q _ *.5 g E , $ g L $ S S 3 ' D J ] K } W ( =.=.LX", +"LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXg.Z.d.g.k.(.S.OX1X XxX7XXX-XW.`.`.r.b.s...r...! { [ 8 h Y e $ z L % D I 3 Y G G Y F Y I 4.u.( LX", +"LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXt.Z.(.x.S.V.V.{.|.|.sX2XOX-XZ.`./.i.b.s.o.p.o.~ | } 8 j Y < $ j L % D S 3 Y D D L t -.g.Y.4.E LX", +"LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXu.h.F.OX X4XQ.W.W.A.3X.h.y.` 5.3._ =.*.> g E , # x L % S S 5 Y D J ] K } ` [ U -.LX", +"LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXz.g.d.Z.k.*XS.*X7X}.kX6X#X-XC.).`.t.b.p.| u...W ` =.7 h Y e $ j L % D I 3 ' G J _ H ] H R 5.=.LX", +"LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXt.S.g.k.k.C.D. X1X}.kX6XOX-X~.`.`.r.b.s.X.p...~ { } 8 6 Y < $ j L % D I 3 Y D H Y x m *.j.t.T LX", +"LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXr.l.J.S.Q.Q.C.S.G..XuX3XOX7XC.`.`.t.b.s.X.i.o.~ { [ A S Y < $ j S % l S 1 I k k m R h.c.j.[ R LX", +"LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXi.l.b.4X5X4X!.).(.C.{.G.J.|.D.S.F.y.b.p...u...! { ] 7 7 Y , % g t o e q q i N 3.h.P.Y.5.| { R LX", +"LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXp.p.i.b.l.4XdX|.4XXX).).).Z.V.q.9.9.4.>.[ -.' Y Y L t r a X O y i a P ;.0.^.$X(.U.3.:./ [ { R LX", +"LXLXLXLXLXLXLXLXLXLXLXLXLXP P u.u.u.i.a.a.a.a.a.c.a.a.a.a.j.i.u.r.p.r.s.y.b.l.F.qX{.4X4XS.W.~.W.).W.C.q.e.w.7.$.;.;.;.;.7.e.e.x.z.(._.qXuX0X3X-.=.H } _ ..} R LX", +"LXLXLXLXLXLXLXLXLXt.t.t.t.u.h.U.`.`._._.].].].].].'.'.[.%X%X].'.`.b.b.c.j.h.t.h.k.k.+XH.J.3XN.D.X>X:X:XgXcXcXCXMXkX$X].Y.b.h.h.F.f.d.f.>.>.<.&.<.K.m.J.J.D.f.A.q.;.9.#.k N P 2 S S 3 Y D J ` K } ] ../ R LX", +"LXLXLXLXt.4.6.!.rXiXlXxXxXxXkXhXhXjXgX=X}.XX&X-X-X%X[.U.P.E.].%X9XhXzXDXHXHXFXlX_.P.j.g.i.h.>.>.>.) V V B N ) V ( *.4 s U , # l L o D S 5 ' F J ] K } _ ( ' 4.LX", +"LXLXt.6.q. XzXzXzXzXCXGXSXVXzXjXxX9X X&X9X9X-X-X).W.W.).%X[.].].]./././.].eXFXKXHXNX$XP.h.b.+.+.+.V +.V B V c c ] :.8 h Y < $ j L $ D I 3 Y G J Y D Y S -.b.=.LX", +"LX1.6.M.sXMXzXMXzXzXSXNXDXSXcXjXgXhXfXfX>X}.#XW.XX;X-X:X:X:X%X%X].].'.T.T.T./.$XxXGXSX9XY.j.>.+.+.V .V V V c 4 G [ A D Y t $ D L $ D L > Y j v S l R t.P.h.R LX", +"t.6.M.MXNXNXMXMXMXCXNXlXSXDXVXcXgXgXgXgX;XM.&XfXfX!.!.!.!.#X;X;X:X).^.'.'.'.'./.T.'.cXZX*XY.y.+.+.V >.V B V B d 5 J 6 7 Y < $ g l g t , l i n $.t.I.P.b.:.T LX", +"5.0.7XVXNXNXSXNXMXkXwXxXMXDXCXzXfXfX-X&XW.7X=X@X:X9XfX-X].].).).).[.%X;X'.^.I.Y.[.'.T.vXgXT.u.>.&.V ) b B B f ; w x 7 t I - X e q X q i a R 6.a.I.P.I.t.} | Y LX", +"x.0.wXSXGXSXlXxXxXlXlXzXzXCXlX@X@XrX7X&X9X-X&X@X@X&XgXfX#X%X:X:X>X[.^.U.Y.T.T.'.[.%XI.].].T.>.] &.V N f f w 0 0 9 0 i i i q y i P #.3.u.P.I./._./.h.6._ } { R LX", +"d.z.A.BXxXlXMXMXNXVXCXxXwX6XeXxXNXCXwX9X9X9X9X-XfX=XfX:X=XfX:X).z.U./././.'.[.%X[.Y.L.I.Y.v.' 2.t.y.+.=.*.( &.&.( -.~.U.'.'.'.T.'./._.*XqXaXyXw.-.D } _ ..[ R LX", +"t.l.+X{.xXNXSXCXCXlXwXwXhXkXhXkXwXwXwXwXeXrXrX&X}.A.@X@X:X}.x.^.'.].].%X%X%X%X^.I.I.I.I.P.2.*.w.M.<.<.&.=.&.) &.) 4././.].].$X+X0X6X.X3X,.,.U G ] F 1._ [ | R LX", +"t.u.S.OX1X0XlXlXeXkXlXxXxXkXkXhXhXhXrX&X&X#X#XXXoX-X=XgXM.^.[.[.%X-X:X%X[.`.P.I.T.I.R.j.2.` *.9.m.;.<.K.&.&.&.N #.,.A.m.V.8.$.*.%.r z L > / D J ] H 1.` .._ *.LX", +"i.c.v.F.{.0XuX3XsXkXkXxXxXkXhXkXhXhXhX9X9X9X9X-X-X-X$X).!.[.#X#X).^.E.P.Y.T./.`./.h.g.! | { *.[ >.V &.V f N d ; w d s x E r # l L % j I < _ D J ` F } _ _ E =.LX", +"t.c.~.~.V.XX2XpXFXbXbXsX7X*XeXiXhXeXeX9X9X9X-X-X*X-X$X$X$X).^.^.^././.].8XqXBXA.0.J -.W } { ;.6.>.V .V B b B 4 f s : s Y < $ j L $ j G 3 _ D H ] F ` z R 5.' LX", +"g.c.F.`.Z.oX{.{.2X3XAXJXnXFXnXdXuXqXOX8X8XoXoX$XoX_.].$X/.].$X[.8XsX3X2XB.8.U v ` F :.! ..{ ;.E.b.>.| .c V B 4 4 x : : S < $ j m $ z I > ( j D I k M >.c.5.T LX", +"t.h.c.`.Z.oX(.OX0X|.sXtX3XuX3X2XbXtXqXbXdX4XuXqX{.*XOXS.S.G.8.7.8.t P E > H G G ] F 1.W } ` 8.xX_.b.>. .c b c 4 s d : = e 1 $ z l % l l 1 S e n M -.j.c.t.] R LX", +"LXg.i.~.z.$X}.oXrX}.kX0X{.7XD.F.Z.4.k.y.| 4.0.) ( *.5 < U , g m # g I 4 J J G ] H 1.W [ I E.cXfXY.t.V B b B 1 f f * * e & # e , X q q q M $.6.U.v.c.} { | R LX", +"LXLXg.l.f._.J.oX8X XkXwXXX-XS.~.`.u.b.s.| r.t.! _ [ 8 5 Y t $ t L $ 6 I 3 J I D ] F ' D E x.L.%X%XT.4.x f i 0 0 0 - . . 9 9 u u p a $.w.Z.oX8X(.Z.] :.) } } Y LX", +"LXLXLXx.f.Z.A.{.7X|.xX6X}.-XW.`.T.p.I.s.o.r.r.~ { } D 6 Y 7 $ 7 I $ h I 3 J G v Y j l $.W.T.L.R.T.c.*.T N M B n n f N N N &.f.l.XX-X7XlXpX3Xn.*._ H :.` } } R LX", +"LXLXLXLXd.g.z.Z.G.G.2X2X{.7XS.~.`.u.I.s.o.p.r.~ ` ..A 7 Y g % 7 L % 7 S > z t t a $.C.[.P.L.I.I.Y.4.( K.,.&.&.<.&.&.<.&.<.m..d.( LX", +"LXLXLXLXh.j.c._.Z.oX{. XbXtXFXJXbXAXpX7X&XXX_.^.^.'././.'./.T.T.T.'.'./.*X9X4XaXtXN.,.Y _ D [ _ 1.[ ' zX_.j.) 4 s d : : w & , t $ z L > Y j j l a *.u.P.:.( LX", +"LXLXLXLXd.h.z._.Z.oXXX&X6X.XyXtX2XmXyXpXnXbXaXBXuX7XaXiX}.-XOX{.}..XM.m.B.%.E I 2 Y G H _ F } ! 1.` *.ZXcXT.t.x f s : * w & w , # t e = p p M -.h.Y.u.:.{ T LX", +"LXLXLXLXLXg.h.(.F.oXoXXXeX}.aX2X X4XM.S.J.f.v.f.4.f.q.-.;.w.G m Y t 2 t S D S > / J G ` F [ _ } Y 2.:XvX/.c.x w w & & - . . . O 9 q i Z <.x.F.`.g.t._ [ { ( LX", +"LXLXLXLXLXg.g.z.l.+X{.+XrX}.kXwXOX9XC.~.~.t.b.p.| y.1.W ) -.6 5 Y < $ g S D S > J J H _ F [ H E e.P.R.'.Y.h.i 9 9 9 9 9 0 y a E >.x.*X8XsXpXq.3.J [ _ } | ( LX", +"LXLXLXLXLXLXLXg.g.S.A.+X6X{.kXwX&X9XZ.`./.i.b.L.o.r.y.~ ` 3.8 8 Y < $ g S $ S D > Y H D Y 8 I E 0.P.L.I.Y.b.:.T N ) &.) =.<.<.f.J.|..X3Xn.7.E U ] H :._ } | #.LX", +"LXLXLXLXLXLXLXLXg.x.z.A.|..XuX3X X8XC.~./.i.b.O.o.p.o.^ { 1.8 j Y 3 $ 7 S % < S > I z k l a 7.g.L.L.I.I.P.2.=.&.#.&.&.#.&.#.N N @.t I L 1 / v K ] K } _ .._ E LX", +"LXLXLXLXLXLXR 6.d.t.v.z.Z.Z.{.H.{.|.D.S.Z.t.h.p.X.y.2.~ _ :.6 h I , % t < S , q r p P 3.j.L.I.R.I.Y.h.2./ [ w w w & & w . ; e # j I < _ D J ] F } _ _ N LXLX", +"LXLXLXLXLX3.3.e.d.t.v.z.C.{.OXQ.S.S.V.q.q.4.d.2.| >.] W / R e e l q X q + + + L @.:.5.P.Y.T.'._.^.z.5._ | { :.s f s * * w & w k $ v I 3 _ D H ' F _ G N LXLXLX", +"LXLXLX-.6.6.).fXz.i.v.v.(.(.4X.t.Q J -.v 5 z l # 1 I % 1 Y < D _ D { F ] ! ' m t - 9 - . . O . @ @ O + @ LXLXLXLXLXLXLXLXLXLXLXLXLX", +"LXLXd.Z.M.SXlXfX:X-XC.k.g.Z.OXS.rX|.rXkX}.8X).W._.a.i.s.o.X.t. .W 1.F 5 G S % < I $ < J 3 D _ j ] A _ G M p i u u + + O LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLX", +"LXLXt.d.!.W.=XjXxXcXCX&Xz.x.V.A.1X.X0XsX}.8X).E._.a.b.L.O.r.i.^ ! 2.K 6 H G $ < Y $ > I 3 j Y 6 J k t n LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLX", +"LXLXt.t.~.{.XXwXNXzXrX;X-X).~.z.S.A.|.tX.X4X{.x.(.u.c.s.o.X.i.^ ! 1.H 7 G S % , I % , L 2 t l i a i LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLX", +"LXLXu.i.v.Z.5X4X0X}.=X>X>X>X%X;X).~.Z.Q.S.A.M.V.Z.d.i.i.X.y.y.` J -.D 5 D S o - t X X q q y a L ( LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLX", +"LXLXi.h.U.F.W.{.pXuXnXyX7X7X&X;X-X-X%X].).W.Z.V.q.9.7.;.=.$.#.N L P i i i + + u C @.a t Y K ..[ -.LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLX", +"LXLXd.i.`.v.(.(.{.{.bXyXAXJXnXBXsXOX#X#X].[.].^.).(.W.x.k.g.9.7.7.8.m.B.B.H.,Xn.,.P $.D ] Y 1.[ -.LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLX", +"LXLXd.h.U.F._._.XX{.eXH.0XyX3XbXnXtXAXnXqXnXaX4XqXeXOX*X*XoXoXOX1Xm.N.n.$.$.> D H v [ 8 { _ ..{ ( LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLX", +"LXLXLXd.h.z.(.(.$X).eX}.eXuX.d.A.>.=.w.#.x T M $ t L $ < Y > G J v -.F { / } / p LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLX", +"LXLXLX3.g.z.z.(.oX{.eX}.eXwX}.8X(.Z.oXd.h.h.X.>.t.! ) :.v 5 G x $ 3 S $ < Y 3 G J D [ F { W U M LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLX", +"LXLXLXLXLXk.g.Z.S.Q.0X.XeXwX}.9X).U._.s.b.s.O.o.i.~ / 1.H 6 H z $ < S 2 < Y > G H v _ 6 G n i LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLX", +"LXLXLXLXLXLX7.l.V.Z.|.N.0XyX.X8X{.z.].a.a.s.O.y.i.^ ! 2.K 6 H z $ < I $ < E 2 j S 5 n p y LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLX", +"LXLXLXLXLXLXLXLXLXz.Z.Z.{..XG. XG.V.(.t.h.a.o.y.u.~ / 3.D 7 G l < S # , t = y i y i LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLX", +"LXLXLXLXLXLXLXLXLXLXLXLX#.Z.Z.C.k.q.w.0.0.5.} :.:.W R *.g e l r X 9 q O + + O @ LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLX", +"LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXq.w.<.,.&.$.#.#.@.Z a p u u u + + LXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLX" +}; diff --git a/share/pixmaps/nsis-header.bmp b/share/pixmaps/nsis-header.bmp new file mode 100644 index 0000000000000000000000000000000000000000..5eb10ae77393363c2b33b44967f21b78ed5f170f GIT binary patch literal 34254 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 literal 0 HcmV?d00001 diff --git a/share/pixmaps/nsis-wizard.bmp b/share/pixmaps/nsis-wizard.bmp new file mode 100644 index 0000000000000000000000000000000000000000..330de6bc91099c0c2c112a9b8636df1d1a34cf44 GIT binary patch literal 206038 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 literal 0 HcmV?d00001 diff --git a/share/pixmaps/send16.bmp b/share/pixmaps/send16.bmp new file mode 100644 index 0000000000000000000000000000000000000000..676b5c4b490e0859578ae599af54aa3de26d5d3b GIT binary patch literal 1334 zcmYk5Jx^OP6oy|?if#e5^t<#cRBdTfU1zbxz?iX8m;M2UN?sTsJ~LYQ7gH)G3saMH zyB~}P>RC^tqazJ22kKf^qtTH%)*+8HB(`iz!{JD6Ym}Tk7?DT0LLYf-R`9Lfu|hGN{Hh5!TD1R4whhJ=^V zS4W7?sJSvv_HRM}BgTY`h7z_r3!Cba8Q^tE($rUtjC@AJfm>U2Se|a=Q6{ASdcovs^b%O|_~O^A9J3 zo8Gy{cW=fyyV5$bWwCUxW^1*ko3u|Q!khV8bNYU6kCq$$b?M$c zXwEFX|G1o6qw%(MADT1u`ciJFjYp+xPCtD)5ti!rOE=q`nY3s1`n}Q>|Lf1qO_lCf Z2B?~wyHmPbfR5?STF&s_p4QyW^e;amw9xQ85j>RFfcv<;txRlgMp#`4+F#he?a^n U34{0`aiBa{9Y{S$UjqXJ0D?~%)Bpeg literal 0 HcmV?d00001 diff --git a/share/pixmaps/send16masknoshadow.bmp b/share/pixmaps/send16masknoshadow.bmp new file mode 100644 index 0000000000000000000000000000000000000000..faf24e0d8aa81faf80072c7807e52d749cad63c0 GIT binary patch literal 126 zcmZ?rtz&=yJ0PV2!~#&v$iN7eZ~&8-#Q*>Q!Geqp3=E71fcOCre_&wv{{x8s0P#N{ U1}Xv5AU;qWBo9^xQV-Mz01_z}*8l(j literal 0 HcmV?d00001 diff --git a/share/pixmaps/send20.bmp b/share/pixmaps/send20.bmp new file mode 100644 index 0000000000000000000000000000000000000000..2b90422b38467ae30a2b6a866adbf0ad3c3f97f3 GIT binary patch literal 1478 zcmZXSKWrOi7{;GaLQNamP(c#|QaKSso$~KuQiKB~Sfms~DLR@G4onc1ZpoM-LzWCy zrW+Vekxuf^0gqWCdGvy0mVEN)Oz9FSLza-qkr-Zn=R}0i&wlRv?)Sa-KF@b{`Tn27 z8jRb|N;g@(bgCic2K=|7!F2A@wXWLMR;S-l%UasBO*O5lhBegf^^{nmZnvk{Vs*M* z)vd1NFwv^5s?+Ie*_N3*YNI7vQmfNe&1zceFR9&btA#=fRqNMS(;o#EsMUh^LuMEX z`hm(;R&W?-bCV3Ha#)dXz6w@Qv)Pnqp2A+J@_t##W}?cIic0$>h22o2HmXLWA%EXj z&T>i`iR#U|WKyYDQqT#cz7!|1>IwW%T5c>Wx1Up`g-^$q_F>7g^h631&JZt2J*nI* zs}|R!uH?1I5QkDn3gSSeMoE>pBDEzy@#Qr<`LVB3y(BO3REP`m>+sl9exSU(I()UD ze4X{Gr_uVT@~e48a>x`Fg^DObDS{W_5uDA4MHIm!cnV8lDJ+Gh8Kc(09o)ej%)xjU zk--dR4macD#A8bFIr(tUKP1JUD(39oM0*4yQ;KPdK}x0+@087yCMn)2-b^U-hU1^& zpW@FJqCVqfNXd{|mH@8e;Pen?uDkB#>=(#Rj30j=G8h?*3`Pbc1CJnjlEKJeWN>7l z4Wgb5Mg}8;k%86-P6i``k--RH_&b^z4;n=gJtFQSa@e4ERujonQ;Um>T3K1q`ue&y zHa4`qy{)aSE$!^=Xm@v4dwY92I5^PJ(UFdikM-Bzrr#ew*4o+{Zvdf^`qjovy#Co%$I+@J$@dB6B8f(^6~4>pU}%==y%-wtc z`osH^S7B4r)6+9EckZw;_raBil`&XhDx=xP?CkZot6ui($jfisni}w_EB+5zMp!mp zc<$!Ztz7BRSQc>pbPVUO41f0E-0{K-mo9vH_i9%4?^yU_=-W@Ty#E;s&wTgc|Io$i T{JW>;KP#Mn?J0D&nXUc-U3=Ry literal 0 HcmV?d00001 diff --git a/share/pixmaps/send20mask.bmp b/share/pixmaps/send20mask.bmp new file mode 100644 index 0000000000000000000000000000000000000000..f124d0da084e68cac62312e9fa8a0fbf9c7c5075 GIT binary patch literal 142 zcmZ?r?PGudJ0PV2#3E44$iN7e2mq2o+z`wJ7J(4||Nm!TJix%f_yCAM0PznX{=>jv m|BrzIC +// Automatically generated by extract_strings.py +#ifdef __GNUC__ +#define UNUSED __attribute__((unused)) +#else +#define UNUSED +#endif +""") +f.write('static const char UNUSED *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/share/qt/img/reload.xcf b/share/qt/img/reload.xcf new file mode 100644 index 0000000000000000000000000000000000000000..dc8be62831673c2e99f05f5e5b42581e6e4e1db1 GIT binary patch literal 25292 zcmb_^2Y8gl_WtZ{I!Q?Hy(}b<0!T>!i3mz>b^$>V5F6Z!w4YZ&(W@v5C?M7#R#ZTZ zV!0wJ5F1J8AtaO(0x6KXNl140`@ipe--Zo_h|lwfXS3hVoO7m~Ip@4*W_RwqM;1ga zoi!(F&b)_b3;bK5z4+{nmrjR2zIeF|U+eG|UwHPz%K@(lyaKdm()ojaINrVHLTu8! zNA8_FJ8AB}=iQGYfqI?jx$v=N56_NTIRD{!_b0gZoHKXU{D8=`MUo2pO%(3DD}GQ(gqA1 z(4TeMKixJVM&Dukg!dKNi?i)B{N7m)&HeZMMUUPeHEaHYWwCuwD4g)(+9-IrXwRf} z)t>Pt-0Ysa**&}4J$u+acgHhj+5S1>fo<@gy)LLFLO)e|Sb=xGK&xMfk&MMN|E*A8 z`ZYoXe<6f%oe=#nQX!Tb28yPHiE}or-JbQ?2cnOt55NEQ{fGDO`*G)vkKcVmB#WBJ zr8#F$<>&qO^Y@^5=e4z>x41Cj+nV#mCrI|q=UcbDx&Ed9ibPR0<3vL>NDut}%m2RG z_F?9xbuT>qxacV=GEO$vR+JPT$;saR%@?1%_ZG;PFa4J=igQsr+8QsEpFV!@k3BY) zmH%1v=v)ym$`@Q{sjF1Ez9rX&m!Dg)?B5UFKV8I%GW})7AHLrH(dO4*dGVPi7SEqM z^R6i(MwF(W0&i*2(cE8u`tHk5-+z0fjraD6qeQeQnNe-JckT+`n&XvIrNarrJ;B8=92+rrtXKreTBn^+`$U zEyBdfA?mc+R$$fBk3aUvoEdjc9&_Um%1P>#AVNh^_=(~|`z65~KR9dJZS9g0d-fpN zcO6y;(`^$qKo!-~7$-tR;gfQ`S89x*KK>QrIkRN&Q# z+|?E%glhIfL)k9QxYN~NXO@gS3lPV+2ecCw^MYSh2DLe#%E*JL}y6D;~ z3zI~Rmn`$UOqe3-LcwZRk}B#wf&=Ud`U;(2(`Al+!sLAUQ-9IyVH?;0(PGdh&nTAb zeiRmii|8k;29YML14X*9`ikY^SJCv;o1f+8oUN`rPcO9e!#^%G*4NftsH!+$4vTIb z1Akhx?2B_P&8CLB+KV7Ncea$SXhVM6rIx04+OpDOx}wY~bL%A=X~ns-WhG}$!6ZYB zv#3~IWl?$RAiwf_Ie1Q=ETTVJmSd{1%#&@!?fc|K!$abmI6i z`Xf&;wxL1vqXT39u_N?HE2}ML8!x(EZsW~AdiWsyQKqS#8zVkfj_#gDv&WAeIdmY8 z{wP!OW9T&;I{DAAZSo(;%@KhdnWH$9iGek9GA?RjFfdij!MvQDTsoypbc7Ql1z`@Q zpaKXw1VOoZ2kDgF(WI!;UsggX80eA1Qp$lthv}4FROQ�ux+RXp@tZcks}WV=yh& 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" no_smgroup +no_smgroup: + Pop $R0 +SectionEnd + +# Installer functions +Function .onInit + InitPluginsDir +FunctionEnd + +# Uninstaller functions +Function un.onInit + ReadRegStr $INSTDIR HKCU "${REGKEY}" Path + !insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuGroup + !insertmacro SELECT_UNSECTION Main ${UNSEC0000} +FunctionEnd diff --git a/share/ui.rc b/share/ui.rc new file mode 100644 index 00000000..0a7ab98f --- /dev/null +++ b/share/ui.rc @@ -0,0 +1,15 @@ +XP ICON "pixmaps/XP.ico" + +#include "wx/msw/wx.rc" + +check ICON "pixmaps/check.ico" +send16 BITMAP "pixmaps/send16.bmp" +send16mask BITMAP "pixmaps/send16mask.bmp" +send16masknoshadow BITMAP "pixmaps/send16masknoshadow.bmp" +send20 BITMAP "pixmaps/send20.bmp" +send20mask BITMAP "pixmaps/send20mask.bmp" +addressbook16 BITMAP "pixmaps/addressbook16.bmp" +addressbook16mask BITMAP "pixmaps/addressbook16mask.bmp" +addressbook20 BITMAP "pixmaps/addressbook20.bmp" +addressbook20mask BITMAP "pixmaps/addressbook20mask.bmp" +favicon ICON "pixmaps/favicon.ico" diff --git a/src/addrman.cpp b/src/addrman.cpp new file mode 100644 index 00000000..6676f62a --- /dev/null +++ b/src/addrman.cpp @@ -0,0 +1,539 @@ +// Copyright (c) 2012 Pieter Wuille +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "addrman.h" +#include "hash.h" + +using namespace std; + +int CAddrInfo::GetTriedBucket(const std::vector &nKey) const +{ + CDataStream ss1(SER_GETHASH, 0); + std::vector vchKey = GetKey(); + ss1 << nKey << vchKey; + uint64_t hash1 = Hash(ss1.begin(), ss1.end()).Get64(); + + CDataStream ss2(SER_GETHASH, 0); + std::vector vchGroupKey = GetGroup(); + ss2 << nKey << vchGroupKey << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP); + uint64_t hash2 = Hash(ss2.begin(), ss2.end()).Get64(); + return hash2 % ADDRMAN_TRIED_BUCKET_COUNT; +} + +int CAddrInfo::GetNewBucket(const std::vector &nKey, const CNetAddr& src) const +{ + CDataStream ss1(SER_GETHASH, 0); + std::vector vchGroupKey = GetGroup(); + std::vector vchSourceGroupKey = src.GetGroup(); + ss1 << nKey << vchGroupKey << vchSourceGroupKey; + uint64_t hash1 = Hash(ss1.begin(), ss1.end()).Get64(); + + CDataStream ss2(SER_GETHASH, 0); + ss2 << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP); + uint64_t hash2 = Hash(ss2.begin(), ss2.end()).Get64(); + return hash2 % ADDRMAN_NEW_BUCKET_COUNT; +} + +bool CAddrInfo::IsTerrible(int64_t nNow) const +{ + if (nLastTry && nLastTry >= nNow-60) // never remove things tried the last minute + return false; + + if (nTime > nNow + 10*60) // came in a flying DeLorean + return true; + + if (nTime==0 || nNow-nTime > ADDRMAN_HORIZON_DAYS*86400) // not seen in over a month + return true; + + if (nLastSuccess==0 && nAttempts>=ADDRMAN_RETRIES) // tried three times and never a success + return true; + + if (nNow-nLastSuccess > ADDRMAN_MIN_FAIL_DAYS*86400 && nAttempts>=ADDRMAN_MAX_FAILURES) // 10 successive failures in the last week + return true; + + return false; +} + +double CAddrInfo::GetChance(int64_t nNow) const +{ + double fChance = 1.0; + + int64_t nSinceLastSeen = nNow - nTime; + int64_t nSinceLastTry = nNow - nLastTry; + + if (nSinceLastSeen < 0) nSinceLastSeen = 0; + if (nSinceLastTry < 0) nSinceLastTry = 0; + + fChance *= 600.0 / (600.0 + nSinceLastSeen); + + // deprioritize very recent attempts away + if (nSinceLastTry < 60*10) + fChance *= 0.01; + + // deprioritize 50% after each failed attempt + for (int n=0; n::iterator it = mapAddr.find(addr); + if (it == mapAddr.end()) + return NULL; + if (pnId) + *pnId = (*it).second; + std::map::iterator it2 = mapInfo.find((*it).second); + if (it2 != mapInfo.end()) + return &(*it2).second; + return NULL; +} + +CAddrInfo* CAddrMan::Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId) +{ + int nId = nIdCount++; + mapInfo[nId] = CAddrInfo(addr, addrSource); + mapAddr[addr] = nId; + mapInfo[nId].nRandomPos = vRandom.size(); + vRandom.push_back(nId); + if (pnId) + *pnId = nId; + return &mapInfo[nId]; +} + +void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2) +{ + if (nRndPos1 == nRndPos2) + return; + + assert(nRndPos1 < vRandom.size() && nRndPos2 < vRandom.size()); + + int nId1 = vRandom[nRndPos1]; + int nId2 = vRandom[nRndPos2]; + + assert(mapInfo.count(nId1) == 1); + assert(mapInfo.count(nId2) == 1); + + mapInfo[nId1].nRandomPos = nRndPos2; + mapInfo[nId2].nRandomPos = nRndPos1; + + vRandom[nRndPos1] = nId2; + vRandom[nRndPos2] = nId1; +} + +int CAddrMan::SelectTried(int nKBucket) +{ + std::vector &vTried = vvTried[nKBucket]; + + // random shuffle the first few elements (using the entire list) + // find the least recently tried among them + int64_t nOldest = -1; + int nOldestPos = -1; + for (unsigned int i = 0; i < ADDRMAN_TRIED_ENTRIES_INSPECT_ON_EVICT && i < vTried.size(); i++) + { + int nPos = GetRandInt(vTried.size() - i) + i; + int nTemp = vTried[nPos]; + vTried[nPos] = vTried[i]; + vTried[i] = nTemp; + assert(nOldest == -1 || mapInfo.count(nTemp) == 1); + if (nOldest == -1 || mapInfo[nTemp].nLastSuccess < mapInfo[nOldest].nLastSuccess) { + nOldest = nTemp; + nOldestPos = nPos; + } + } + + return nOldestPos; +} + +int CAddrMan::ShrinkNew(int nUBucket) +{ + assert(nUBucket >= 0 && (unsigned int)nUBucket < vvNew.size()); + std::set &vNew = vvNew[nUBucket]; + + // first look for deletable items + for (std::set::iterator it = vNew.begin(); it != vNew.end(); it++) + { + assert(mapInfo.count(*it)); + CAddrInfo &info = mapInfo[*it]; + if (info.IsTerrible()) + { + if (--info.nRefCount == 0) + { + SwapRandom(info.nRandomPos, vRandom.size()-1); + vRandom.pop_back(); + mapAddr.erase(info); + mapInfo.erase(*it); + nNew--; + } + vNew.erase(it); + return 0; + } + } + + // otherwise, select four randomly, and pick the oldest of those to replace + int n[4] = {GetRandInt(vNew.size()), GetRandInt(vNew.size()), GetRandInt(vNew.size()), GetRandInt(vNew.size())}; + int nI = 0; + int nOldest = -1; + for (std::set::iterator it = vNew.begin(); it != vNew.end(); it++) + { + if (nI == n[0] || nI == n[1] || nI == n[2] || nI == n[3]) + { + assert(nOldest == -1 || mapInfo.count(*it) == 1); + if (nOldest == -1 || mapInfo[*it].nTime < mapInfo[nOldest].nTime) + nOldest = *it; + } + nI++; + } + assert(mapInfo.count(nOldest) == 1); + CAddrInfo &info = mapInfo[nOldest]; + if (--info.nRefCount == 0) + { + SwapRandom(info.nRandomPos, vRandom.size()-1); + vRandom.pop_back(); + mapAddr.erase(info); + mapInfo.erase(nOldest); + nNew--; + } + vNew.erase(nOldest); + + return 1; +} + +void CAddrMan::MakeTried(CAddrInfo& info, int nId, int nOrigin) +{ + assert(vvNew[nOrigin].count(nId) == 1); + + // remove the entry from all new buckets + for (std::vector >::iterator it = vvNew.begin(); it != vvNew.end(); it++) + { + if ((*it).erase(nId)) + info.nRefCount--; + } + nNew--; + + assert(info.nRefCount == 0); + + // what tried bucket to move the entry to + int nKBucket = info.GetTriedBucket(nKey); + std::vector &vTried = vvTried[nKBucket]; + + // first check whether there is place to just add it + if (vTried.size() < ADDRMAN_TRIED_BUCKET_SIZE) + { + vTried.push_back(nId); + nTried++; + info.fInTried = true; + return; + } + + // otherwise, find an item to evict + int nPos = SelectTried(nKBucket); + + // find which new bucket it belongs to + assert(mapInfo.count(vTried[nPos]) == 1); + int nUBucket = mapInfo[vTried[nPos]].GetNewBucket(nKey); + std::set &vNew = vvNew[nUBucket]; + + // remove the to-be-replaced tried entry from the tried set + CAddrInfo& infoOld = mapInfo[vTried[nPos]]; + infoOld.fInTried = false; + infoOld.nRefCount = 1; + // do not update nTried, as we are going to move something else there immediately + + // check whether there is place in that one, + if (vNew.size() < ADDRMAN_NEW_BUCKET_SIZE) + { + // if so, move it back there + vNew.insert(vTried[nPos]); + } else { + // otherwise, move it to the new bucket nId came from (there is certainly place there) + vvNew[nOrigin].insert(vTried[nPos]); + } + nNew++; + + vTried[nPos] = nId; + // we just overwrote an entry in vTried; no need to update nTried + info.fInTried = true; + return; +} + +void CAddrMan::Good_(const CService &addr, int64_t nTime) +{ +// printf("Good: addr=%s\n", addr.ToString().c_str()); + + int nId; + CAddrInfo *pinfo = Find(addr, &nId); + + // if not found, bail out + if (!pinfo) + return; + + CAddrInfo &info = *pinfo; + + // check whether we are talking about the exact same CService (including same port) + if (info != addr) + return; + + // update info + info.nLastSuccess = nTime; + info.nLastTry = nTime; + info.nTime = nTime; + info.nAttempts = 0; + + // if it is already in the tried set, don't do anything else + if (info.fInTried) + return; + + // find a bucket it is in now + int nRnd = GetRandInt(vvNew.size()); + int nUBucket = -1; + for (unsigned int n = 0; n < vvNew.size(); n++) + { + int nB = (n+nRnd) % vvNew.size(); + std::set &vNew = vvNew[nB]; + if (vNew.count(nId)) + { + nUBucket = nB; + break; + } + } + + // if no bucket is found, something bad happened; + // TODO: maybe re-add the node, but for now, just bail out + if (nUBucket == -1) return; + + printf("Moving %s to tried\n", addr.ToString().c_str()); + + // move nId to the tried tables + MakeTried(info, nId, nUBucket); +} + +bool CAddrMan::Add_(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty) +{ + if (!addr.IsRoutable()) + return false; + + bool fNew = false; + int nId; + CAddrInfo *pinfo = Find(addr, &nId); + + if (pinfo) + { + // periodically update nTime + bool fCurrentlyOnline = (GetAdjustedTime() - addr.nTime < nOneDay); + int64_t nUpdateInterval = (fCurrentlyOnline ? nOneHour : nOneDay); + if (addr.nTime && (!pinfo->nTime || pinfo->nTime < addr.nTime - nUpdateInterval - nTimePenalty)) + pinfo->nTime = max((int64_t)0, addr.nTime - nTimePenalty); + + // add services + pinfo->nServices |= addr.nServices; + + // do not update if no new information is present + if (!addr.nTime || (pinfo->nTime && addr.nTime <= pinfo->nTime)) + return false; + + // do not update if the entry was already in the "tried" table + if (pinfo->fInTried) + return false; + + // do not update if the max reference count is reached + if (pinfo->nRefCount == ADDRMAN_NEW_BUCKETS_PER_ADDRESS) + return false; + + // stochastic test: previous nRefCount == N: 2^N times harder to increase it + int nFactor = 1; + for (int n=0; nnRefCount; n++) + nFactor *= 2; + if (nFactor > 1 && (GetRandInt(nFactor) != 0)) + return false; + } else { + pinfo = Create(addr, source, &nId); + pinfo->nTime = max((int64_t)0, (int64_t)pinfo->nTime - nTimePenalty); +// printf("Added %s [nTime=%fhr]\n", pinfo->ToString().c_str(), (GetAdjustedTime() - pinfo->nTime) / 3600.0); + nNew++; + fNew = true; + } + + int nUBucket = pinfo->GetNewBucket(nKey, source); + std::set &vNew = vvNew[nUBucket]; + if (!vNew.count(nId)) + { + pinfo->nRefCount++; + if (vNew.size() == ADDRMAN_NEW_BUCKET_SIZE) + ShrinkNew(nUBucket); + vvNew[nUBucket].insert(nId); + } + return fNew; +} + +void CAddrMan::Attempt_(const CService &addr, int64_t nTime) +{ + CAddrInfo *pinfo = Find(addr); + + // if not found, bail out + if (!pinfo) + return; + + CAddrInfo &info = *pinfo; + + // check whether we are talking about the exact same CService (including same port) + if (info != addr) + return; + + // update info + info.nLastTry = nTime; + info.nAttempts++; +} + +CAddress CAddrMan::Select_(int nUnkBias) +{ + if (size() == 0) + return CAddress(); + + double nCorTried = sqrt(nTried) * (100.0 - nUnkBias); + double nCorNew = sqrt(nNew) * nUnkBias; + if ((nCorTried + nCorNew)*GetRandInt(1<<30)/(1<<30) < nCorTried) + { + // use a tried node + double fChanceFactor = 1.0; + while(1) + { + int nKBucket = GetRandInt(vvTried.size()); + std::vector &vTried = vvTried[nKBucket]; + if (vTried.size() == 0) continue; + int nPos = GetRandInt(vTried.size()); + assert(mapInfo.count(vTried[nPos]) == 1); + CAddrInfo &info = mapInfo[vTried[nPos]]; + if (GetRandInt(1<<30) < fChanceFactor*info.GetChance()*(1<<30)) + return info; + fChanceFactor *= 1.2; + } + } else { + // use a new node + double fChanceFactor = 1.0; + while(1) + { + int nUBucket = GetRandInt(vvNew.size()); + std::set &vNew = vvNew[nUBucket]; + if (vNew.size() == 0) continue; + int nPos = GetRandInt(vNew.size()); + std::set::iterator it = vNew.begin(); + while (nPos--) + it++; + assert(mapInfo.count(*it) == 1); + CAddrInfo &info = mapInfo[*it]; + if (GetRandInt(1<<30) < fChanceFactor*info.GetChance()*(1<<30)) + return info; + fChanceFactor *= 1.2; + } + } +} + +#ifdef DEBUG_ADDRMAN +int CAddrMan::Check_() +{ + std::set setTried; + std::map mapNew; + + if (vRandom.size() != nTried + nNew) return -7; + + for (std::map::iterator it = mapInfo.begin(); it != mapInfo.end(); it++) + { + int n = (*it).first; + CAddrInfo &info = (*it).second; + if (info.fInTried) + { + + if (!info.nLastSuccess) return -1; + if (info.nRefCount) return -2; + setTried.insert(n); + } else { + if (info.nRefCount < 0 || info.nRefCount > ADDRMAN_NEW_BUCKETS_PER_ADDRESS) return -3; + if (!info.nRefCount) return -4; + mapNew[n] = info.nRefCount; + } + if (mapAddr[info] != n) return -5; + if (info.nRandomPos<0 || info.nRandomPos>=vRandom.size() || vRandom[info.nRandomPos] != n) return -14; + if (info.nLastTry < 0) return -6; + if (info.nLastSuccess < 0) return -8; + } + + if (setTried.size() != nTried) return -9; + if (mapNew.size() != nNew) return -10; + + for (int n=0; n &vTried = vvTried[n]; + for (std::vector::iterator it = vTried.begin(); it != vTried.end(); it++) + { + if (!setTried.count(*it)) return -11; + setTried.erase(*it); + } + } + + for (int n=0; n &vNew = vvNew[n]; + for (std::set::iterator it = vNew.begin(); it != vNew.end(); it++) + { + if (!mapNew.count(*it)) return -12; + if (--mapNew[*it] == 0) + mapNew.erase(*it); + } + } + + if (setTried.size()) return -13; + if (mapNew.size()) return -15; + + return 0; +} +#endif + +void CAddrMan::GetAddr_(std::vector &vAddr) +{ + size_t nNodes = ADDRMAN_GETADDR_MAX_PCT*vRandom.size()/100; + if (nNodes > ADDRMAN_GETADDR_MAX) + nNodes = ADDRMAN_GETADDR_MAX; + + // perform a random shuffle over the first nNodes elements of vRandom (selecting from all) + for (unsigned int n = 0; n &vAddr) +{ + for (std::map::const_iterator it = mapInfo.begin(); it != mapInfo.end(); it++) + { + CAddrInfo addr = it->second; + bool fCurrentlyOnline = (GetAdjustedTime() - addr.nTime < nOneDay); + if (fCurrentlyOnline) + vAddr.push_back(addr); + } +} + +void CAddrMan::Connected_(const CService &addr, int64_t nTime) +{ + CAddrInfo *pinfo = Find(addr); + + // if not found, bail out + if (!pinfo) + return; + + CAddrInfo &info = *pinfo; + + // check whether we are talking about the exact same CService (including same port) + if (info != addr) + return; + + // update info + int64_t nUpdateInterval = 20 * 60; + if (nTime - info.nTime > nUpdateInterval) + info.nTime = nTime; +} diff --git a/src/addrman.h b/src/addrman.h new file mode 100644 index 00000000..9aa167b3 --- /dev/null +++ b/src/addrman.h @@ -0,0 +1,693 @@ +// Copyright (c) 2012 Pieter Wuille +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef _BITCOIN_ADDRMAN +#define _BITCOIN_ADDRMAN 1 + +#include "netbase.h" +#include "protocol.h" +#include "util.h" +#include "sync.h" + + +#include +#include + +#include + + +/** Extended statistics about a CAddress */ +class CAddrInfo : public CAddress +{ +private: + // where knowledge about this address first came from + CNetAddr source; + + // last successful connection by us + int64_t nLastSuccess; + + // last try whatsoever by us: + // int64_t CAddress::nLastTry + + // connection attempts since last successful attempt + int nAttempts; + + // reference count in new sets (memory only) + int nRefCount; + + // in tried set? (memory only) + bool fInTried; + + // position in vRandom + int nRandomPos; + + friend class CAddrMan; + +public: + + IMPLEMENT_SERIALIZE( + CAddress* pthis = (CAddress*)(this); + READWRITE(*pthis); + READWRITE(source); + READWRITE(nLastSuccess); + READWRITE(nAttempts); + ) + + void Init() + { + nLastSuccess = 0; + nLastTry = 0; + nAttempts = 0; + nRefCount = 0; + fInTried = false; + nRandomPos = -1; + } + + CAddrInfo(const CAddress &addrIn, const CNetAddr &addrSource) : CAddress(addrIn), source(addrSource) + { + Init(); + } + + CAddrInfo() : CAddress(), source() + { + Init(); + } + + // Calculate in which "tried" bucket this entry belongs + int GetTriedBucket(const std::vector &nKey) const; + + // Calculate in which "new" bucket this entry belongs, given a certain source + int GetNewBucket(const std::vector &nKey, const CNetAddr& src) const; + + // Calculate in which "new" bucket this entry belongs, using its default source + int GetNewBucket(const std::vector &nKey) const + { + return GetNewBucket(nKey, source); + } + + // Determine whether the statistics about this entry are bad enough so that it can just be deleted + bool IsTerrible(int64_t nNow = GetAdjustedTime()) const; + + // Calculate the relative chance this entry should be given when selecting nodes to connect to + double GetChance(int64_t nNow = GetAdjustedTime()) const; + +}; + +// Stochastic address manager +// +// Design goals: +// * Only keep a limited number of addresses around, so that addr.dat and memory requirements do not grow without bound. +// * Keep the address tables in-memory, and asynchronously dump the entire to able in addr.dat. +// * Make sure no (localized) attacker can fill the entire table with his nodes/addresses. +// +// To that end: +// * Addresses are organized into buckets. +// * Address that have not yet been tried go into 256 "new" buckets. +// * Based on the address range (/16 for IPv4) of source of the information, 32 buckets are selected at random +// * The actual bucket is chosen from one of these, based on the range the address itself is located. +// * One single address can occur in up to 4 different buckets, to increase selection chances for addresses that +// are seen frequently. The chance for increasing this multiplicity decreases exponentially. +// * When adding a new address to a full bucket, a randomly chosen entry (with a bias favoring less recently seen +// ones) is removed from it first. +// * Addresses of nodes that are known to be accessible go into 64 "tried" buckets. +// * Each address range selects at random 4 of these buckets. +// * The actual bucket is chosen from one of these, based on the full address. +// * When adding a new good address to a full bucket, a randomly chosen entry (with a bias favoring less recently +// tried ones) is evicted from it, back to the "new" buckets. +// * Bucket selection is based on cryptographic hashing, using a randomly-generated 256-bit key, which should not +// be observable by adversaries. +// * Several indexes are kept for high performance. Defining DEBUG_ADDRMAN will introduce frequent (and expensive) +// consistency checks for the entire data structure. + +// total number of buckets for tried addresses +#define ADDRMAN_TRIED_BUCKET_COUNT 64 + +// maximum allowed number of entries in buckets for tried addresses +#define ADDRMAN_TRIED_BUCKET_SIZE 64 + +// total number of buckets for new addresses +#define ADDRMAN_NEW_BUCKET_COUNT 256 + +// maximum allowed number of entries in buckets for new addresses +#define ADDRMAN_NEW_BUCKET_SIZE 64 + +// over how many buckets entries with tried addresses from a single group (/16 for IPv4) are spread +#define ADDRMAN_TRIED_BUCKETS_PER_GROUP 4 + +// over how many buckets entries with new addresses originating from a single group are spread +#define ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP 32 + +// in how many buckets for entries with new addresses a single address may occur +#define ADDRMAN_NEW_BUCKETS_PER_ADDRESS 4 + +// how many entries in a bucket with tried addresses are inspected, when selecting one to replace +#define ADDRMAN_TRIED_ENTRIES_INSPECT_ON_EVICT 4 + +// how old addresses can maximally be +#define ADDRMAN_HORIZON_DAYS 30 + +// after how many failed attempts we give up on a new node +#define ADDRMAN_RETRIES 3 + +// how many successive failures are allowed ... +#define ADDRMAN_MAX_FAILURES 10 + +// ... in at least this many days +#define ADDRMAN_MIN_FAIL_DAYS 7 + +// the maximum percentage of nodes to return in a getaddr call +#define ADDRMAN_GETADDR_MAX_PCT 23 + +// the maximum number of nodes to return in a getaddr call +#define ADDRMAN_GETADDR_MAX 2500 + +/** Stochastical (IP) address manager */ +class CAddrMan +{ +private: + // critical section to protect the inner data structures + mutable CCriticalSection cs; + + // secret key to randomize bucket select with + std::vector nKey; + + // last used nId + int nIdCount; + + // table with information about all nIds + std::map mapInfo; + + // find an nId based on its network address + std::map mapAddr; + + // randomly-ordered vector of all nIds + std::vector vRandom; + + // number of "tried" entries + int nTried; + + // list of "tried" buckets + std::vector > vvTried; + + // number of (unique) "new" entries + int nNew; + + // list of "new" buckets + std::vector > vvNew; + +protected: + + // Find an entry. + CAddrInfo* Find(const CNetAddr& addr, int *pnId = NULL); + + // find an entry, creating it if necessary. + // nTime and nServices of found node is updated, if necessary. + CAddrInfo* Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId = NULL); + + // Swap two elements in vRandom. + void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2); + + // Return position in given bucket to replace. + int SelectTried(int nKBucket); + + // Remove an element from a "new" bucket. + // This is the only place where actual deletes occur. + // They are never deleted while in the "tried" table, only possibly evicted back to the "new" table. + int ShrinkNew(int nUBucket); + + // Move an entry from the "new" table(s) to the "tried" table + // @pre vvUnkown[nOrigin].count(nId) != 0 + void MakeTried(CAddrInfo& info, int nId, int nOrigin); + + // Mark an entry "good", possibly moving it from "new" to "tried". + void Good_(const CService &addr, int64_t nTime); + + // Add an entry to the "new" table. + bool Add_(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty); + + // Mark an entry as attempted to connect. + void Attempt_(const CService &addr, int64_t nTime); + + // Select an address to connect to. + // nUnkBias determines how much to favor new addresses over tried ones (min=0, max=100) + CAddress Select_(int nUnkBias); + +#ifdef DEBUG_ADDRMAN + // Perform consistency check. Returns an error code or zero. + int Check_(); +#endif + + // Select several addresses at once. + void GetAddr_(std::vector &vAddr); + void GetOnlineAddr_(std::vector &vAddr); + + // Mark an entry as currently-connected-to. + void Connected_(const CService &addr, int64_t nTime); + +public: + +#ifndef _MSC_VER + IMPLEMENT_SERIALIZE + (({ + // serialized format: + // * version byte (currently 0) + // * nKey + // * nNew + // * nTried + // * number of "new" buckets + // * all nNew addrinfos in vvNew + // * all nTried addrinfos in vvTried + // * for each bucket: + // * number of elements + // * for each element: index + // + // Notice that vvTried, mapAddr and vVector are never encoded explicitly; + // they are instead reconstructed from the other information. + // + // vvNew is serialized, but only used if ADDRMAN_UNKOWN_BUCKET_COUNT didn't change, + // otherwise it is reconstructed as well. + // + // This format is more complex, but significantly smaller (at most 1.5 MiB), and supports + // changes to the ADDRMAN_ parameters without breaking the on-disk structure. + { + LOCK(cs); + unsigned char nVersion = 0; + READWRITE(nVersion); + READWRITE(nKey); + READWRITE(nNew); + READWRITE(nTried); + + CAddrMan *am = const_cast(this); + if (fWrite) + { + int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT; + READWRITE(nUBuckets); + std::map mapUnkIds; + int nIds = 0; + for (std::map::iterator it = am->mapInfo.begin(); it != am->mapInfo.end(); it++) + { + if (nIds == nNew) break; // this means nNew was wrong, oh ow + mapUnkIds[(*it).first] = nIds; + CAddrInfo &info = (*it).second; + if (info.nRefCount) + { + READWRITE(info); + nIds++; + } + } + nIds = 0; + for (std::map::iterator it = am->mapInfo.begin(); it != am->mapInfo.end(); it++) + { + if (nIds == nTried) break; // this means nTried was wrong, oh ow + CAddrInfo &info = (*it).second; + if (info.fInTried) + { + READWRITE(info); + nIds++; + } + } + for (std::vector >::iterator it = am->vvNew.begin(); it != am->vvNew.end(); it++) + { + const std::set &vNew = (*it); + int nSize = vNew.size(); + READWRITE(nSize); + for (std::set::iterator it2 = vNew.begin(); it2 != vNew.end(); it2++) + { + int nIndex = mapUnkIds[*it2]; + READWRITE(nIndex); + } + } + } else { + int nUBuckets = 0; + READWRITE(nUBuckets); + am->nIdCount = 0; + am->mapInfo.clear(); + am->mapAddr.clear(); + am->vRandom.clear(); + am->vvTried = std::vector >(ADDRMAN_TRIED_BUCKET_COUNT, std::vector(0)); + am->vvNew = std::vector >(ADDRMAN_NEW_BUCKET_COUNT, std::set()); + for (int n = 0; n < am->nNew; n++) + { + CAddrInfo &info = am->mapInfo[n]; + READWRITE(info); + am->mapAddr[info] = n; + info.nRandomPos = vRandom.size(); + am->vRandom.push_back(n); + if (nUBuckets != ADDRMAN_NEW_BUCKET_COUNT) + { + am->vvNew[info.GetNewBucket(am->nKey)].insert(n); + info.nRefCount++; + } + } + am->nIdCount = am->nNew; + int nLost = 0; + for (int n = 0; n < am->nTried; n++) + { + CAddrInfo info; + READWRITE(info); + std::vector &vTried = am->vvTried[info.GetTriedBucket(am->nKey)]; + if (vTried.size() < ADDRMAN_TRIED_BUCKET_SIZE) + { + info.nRandomPos = vRandom.size(); + info.fInTried = true; + am->vRandom.push_back(am->nIdCount); + am->mapInfo[am->nIdCount] = info; + am->mapAddr[info] = am->nIdCount; + vTried.push_back(am->nIdCount); + am->nIdCount++; + } else { + nLost++; + } + } + am->nTried -= nLost; + for (int b = 0; b < nUBuckets; b++) + { + std::set &vNew = am->vvNew[b]; + int nSize = 0; + READWRITE(nSize); + for (int n = 0; n < nSize; n++) + { + int nIndex = 0; + READWRITE(nIndex); + CAddrInfo &info = am->mapInfo[nIndex]; + if (nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS) + { + info.nRefCount++; + vNew.insert(nIndex); + } + } + } + } + } + });) +#else + IMPLEMENT_SERIALIZE + ( + LOCK(cs); + unsigned char + nVersion = 0; + + READWRITE(nVersion); + READWRITE(nKey); + READWRITE(nNew); + READWRITE(nTried); + + CAddrMan + *am = const_cast(this); + + if (fWrite) + { + int + nUBuckets = ADDRMAN_NEW_BUCKET_COUNT; + + READWRITE(nUBuckets); +/************ + std::map + mapUnkIds; +************/ + int + nIds = 0; + for (std::map::iterator it = am->mapInfo.begin(); it != am->mapInfo.end(); it++) + { + if (nIds == nNew) + break; // this means nNew was wrong, oh ow + mapUnkIds[(*it).first] = nIds; + + CAddrInfo + &info = (*it).second; + + if (info.nRefCount) + { + READWRITE(info); + nIds++; + } + } + nIds = 0; + for (std::map::iterator it = am->mapInfo.begin(); it != am->mapInfo.end(); it++) + { + if (nIds == nTried) + break; /* this means nTried was wrong, oh ow */ + + CAddrInfo + &info = (*it).second; + + if (info.fInTried) + { + READWRITE(info); + nIds++; + } + } + for ( + std::vector >::iterator it = am->vvNew.begin(); + it != am->vvNew.end(); + it++ + ) + { + std::set + &vNew = (*it); + + int + nSize = int( vNew.size() ); + + READWRITE(nSize); + for (std::set::iterator it2 = vNew.begin(); it2 != vNew.end(); it2++) + { + int + nIndex = mapUnkIds[*it2]; + READWRITE(nIndex); + } + } + } + else + { + int + nUBuckets = 0; + + READWRITE(nUBuckets); + am->nIdCount = 0; + am->mapInfo.clear(); + am->mapAddr.clear(); + am->vRandom.clear(); + am->vvTried = + std::vector >( + ADDRMAN_TRIED_BUCKET_COUNT, + std::vector(0) + ); + am->vvNew = + std::vector >( + ADDRMAN_NEW_BUCKET_COUNT, + std::set() + ); + for (int n = 0; n < am->nNew; n++) + { + CAddrInfo + &info = am->mapInfo[n]; + + READWRITE(info); + am->mapAddr[info] = n; + info.nRandomPos = int( vRandom.size() ); + am->vRandom.push_back(n); + if (nUBuckets != ADDRMAN_NEW_BUCKET_COUNT) + { + am->vvNew[info.GetNewBucket(am->nKey)].insert(n); + info.nRefCount++; + } + } + am->nIdCount = am->nNew; + + int + nLost = 0; + + for (int n = 0; n < am->nTried; n++) + { + CAddrInfo + info; + + READWRITE(info); + + std::vector + &vTried = am->vvTried[info.GetTriedBucket(am->nKey)]; + + if (vTried.size() < ADDRMAN_TRIED_BUCKET_SIZE) + { + info.nRandomPos = int( vRandom.size() ); + info.fInTried = true; + am->vRandom.push_back(am->nIdCount); + am->mapInfo[am->nIdCount] = info; + am->mapAddr[info] = am->nIdCount; + vTried.push_back(am->nIdCount); + am->nIdCount++; + } + else + { + nLost++; + } + } + am->nTried -= nLost; + for (int b = 0; b < nUBuckets; b++) + { + std::set + &vNew = am->vvNew[b]; + + int + nSize = 0; + + READWRITE(nSize); + for (int n = 0; n < nSize; n++) + { + int + nIndex = 0; + + READWRITE(nIndex); + + CAddrInfo + &info = am->mapInfo[nIndex]; + + if ( + (nUBuckets == ADDRMAN_NEW_BUCKET_COUNT) && + (info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS) + ) + { + info.nRefCount++; + vNew.insert(nIndex); + } + } + } + } + ) + +#endif //_MSC_VER + CAddrMan() : vRandom(0), vvTried(ADDRMAN_TRIED_BUCKET_COUNT, std::vector(0)), vvNew(ADDRMAN_NEW_BUCKET_COUNT, std::set()) + { + nKey.resize(32); + RAND_bytes(&nKey[0], 32); + + nIdCount = 0; + nTried = 0; + nNew = 0; + } + + // Return the number of (unique) addresses in all tables. + int size() + { + return (int) vRandom.size(); + } + + // Consistency check + void Check() + { +#ifdef DEBUG_ADDRMAN + { + LOCK(cs); + int err; + if ((err=Check_())) + printf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err); + } +#endif + } + + // Add a single address. + bool Add(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty = 0) + { + bool fRet = false; + { + LOCK(cs); + Check(); + fRet |= Add_(addr, source, nTimePenalty); + Check(); + } + if (fRet) + printf("Added %s from %s: %i tried, %i new\n", addr.ToStringIPPort().c_str(), source.ToString().c_str(), nTried, nNew); + return fRet; + } + + // Add multiple addresses. + bool Add(const std::vector &vAddr, const CNetAddr& source, int64_t nTimePenalty = 0) + { + int nAdd = 0; + { + LOCK(cs); + Check(); + for (std::vector::const_iterator it = vAddr.begin(); it != vAddr.end(); it++) + nAdd += Add_(*it, source, nTimePenalty) ? 1 : 0; + Check(); + } + if (nAdd) + printf("Added %i addresses from %s: %i tried, %i new\n", nAdd, source.ToString().c_str(), nTried, nNew); + return nAdd > 0; + } + + // Mark an entry as accessible. + void Good(const CService &addr, int64_t nTime = GetAdjustedTime()) + { + { + LOCK(cs); + Check(); + Good_(addr, nTime); + Check(); + } + } + + // Mark an entry as connection attempted to. + void Attempt(const CService &addr, int64_t nTime = GetAdjustedTime()) + { + { + LOCK(cs); + Check(); + Attempt_(addr, nTime); + Check(); + } + } + + // Choose an address to connect to. + // nUnkBias determines how much "new" entries are favored over "tried" ones (0-100). + CAddress Select(int nUnkBias = 50) + { + CAddress addrRet; + { + LOCK(cs); + Check(); + addrRet = Select_(nUnkBias); + Check(); + } + return addrRet; + } + + // Return a bunch of addresses, selected at random. + std::vector GetAddr() + { + Check(); + std::vector vAddr; + { + LOCK(cs); + GetAddr_(vAddr); + } + Check(); + return vAddr; + } + + std::vector GetOnlineAddr() + { + Check(); + std::vector vAddr; + { + LOCK(cs); + GetOnlineAddr_(vAddr); + } + Check(); + return vAddr; + } + + // Mark an entry as currently-connected-to. + void Connected(const CService &addr, int64_t nTime = GetAdjustedTime()) + { + { + LOCK(cs); + Check(); + Connected_(addr, nTime); + Check(); + } + } +}; + +#endif diff --git a/src/alert.cpp b/src/alert.cpp new file mode 100644 index 00000000..e8a51280 --- /dev/null +++ b/src/alert.cpp @@ -0,0 +1,247 @@ +// +// Alert system +// + +#include +#include + +#include "alert.h" +#include "key.h" +#include "net.h" +#include "sync.h" +#include "ui_interface.h" + +using namespace std; + +map mapAlerts; +CCriticalSection cs_mapAlerts; + +static const char* pszMainKey = "043fa442fd4203d03f5df2b75ea14e36f20d30f43e7a61aa7552ab9bcd7ecb0e77a3be4585b13fcdaa22ef6e51f1ff6f2929bec2494385b086fb86610e33193195"; + +// TestNet alerts pubKey +static const char* pszTestKey = "0471dc165db480094d35cde15b1f5d755fa6ad6f2b5ed0e340e3f17f57389c3c2af113a8cbcc885bde73305a553b5640c83021128008ddf882e856336269080496"; + +// TestNet alerts private key +// "308201130201010420b665cff1884e53da26376fd1b433812c9a5a8a4d5221533b15b9629789bb7e42a081a53081a2020101302c06072a8648ce3d0101022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f300604010004010704410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141020101a1440342000471dc165db490094d35cde15b1f5d755fa6ad6f2b5ed0f340e3f17f57389c3c2af113a8cbcc885bde73305a553b5640c83021128008ddf882e856336269080496" + +void CUnsignedAlert::SetNull() +{ + nVersion = 1; + nRelayUntil = 0; + nExpiration = 0; + nID = 0; + nCancel = 0; + setCancel.clear(); + nMinVer = 0; + nMaxVer = 0; + setSubVer.clear(); + nPriority = 0; + + strComment.clear(); + strStatusBar.clear(); + strReserved.clear(); +} + +std::string CUnsignedAlert::ToString() const +{ + std::string strSetCancel; + BOOST_FOREACH(int n, setCancel) + strSetCancel += strprintf("%d ", n); + std::string strSetSubVer; + BOOST_FOREACH(std::string str, setSubVer) + strSetSubVer += "\"" + str + "\" "; + return strprintf( + "CAlert(\n" + " nVersion = %d\n" + " nRelayUntil = %" PRId64 "\n" + " nExpiration = %" PRId64 "\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 CUnsignedAlert::print() const +{ + printf("%s", ToString().c_str()); +} + +void CAlert::SetNull() +{ + CUnsignedAlert::SetNull(); + vchMsg.clear(); + vchSig.clear(); +} + +bool CAlert::IsNull() const +{ + return (nExpiration == 0); +} + +uint256 CAlert::GetHash() const +{ + return Hash(this->vchMsg.begin(), this->vchMsg.end()); +} + +bool CAlert::IsInEffect() const +{ + return (GetAdjustedTime() < nExpiration); +} + +bool CAlert::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 CAlert::AppliesTo(int nVersion, std::string strSubVerIn) const +{ + // TODO: rework for client-version-embedded-in-strSubVer ? + return (IsInEffect() && + nMinVer <= nVersion && nVersion <= nMaxVer && + (setSubVer.empty() || setSubVer.count(strSubVerIn))); +} + +bool CAlert::AppliesToMe() const +{ + return AppliesTo(PROTOCOL_VERSION, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector())); +} + +bool CAlert::RelayTo(CNode* pnode) const +{ + if (!IsInEffect()) + return false; + // don't relay to nodes which haven't sent their version message + if (pnode->nVersion == 0) + 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 CAlert::CheckSignature() const +{ + CKey key; + if (!key.SetPubKey(ParseHex(fTestNet ? pszTestKey : pszMainKey))) + 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, SER_NETWORK, PROTOCOL_VERSION); + sMsg >> *(CUnsignedAlert*)this; + return true; +} + +CAlert CAlert::getAlertByHash(const uint256 &hash) +{ + CAlert retval; + { + LOCK(cs_mapAlerts); + map::iterator mi = mapAlerts.find(hash); + if(mi != mapAlerts.end()) + retval = mi->second; + } + return retval; +} + +bool CAlert::ProcessAlert() +{ + if (!CheckSignature()) + return false; + if (!IsInEffect()) + return false; + + // alert.nID=max is reserved for if the alert key is + // compromised. It must have a pre-defined message, + // must never expire, must apply to all versions, + // and must cancel all previous + // alerts or it will be ignored (so an attacker can't + // send an "everything is OK, don't panic" version that + // cannot be overridden): + int maxInt = std::numeric_limits::max(); + if (nID == maxInt) + { + if (!( + nExpiration == maxInt && + nCancel == (maxInt-1) && + nMinVer == 0 && + nMaxVer == maxInt && + setSubVer.empty() && + nPriority == maxInt && + strStatusBar == "URGENT: Alert key compromised, upgrade required" + )) + return false; + } + + { + LOCK(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); + uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED); + mapAlerts.erase(mi++); + } + else if (!alert.IsInEffect()) + { + printf("expiring alert %d\n", alert.nID); + uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED); + 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)); + // Notify UI if it applies to me + if(AppliesToMe()) + uiInterface.NotifyAlertChanged(GetHash(), CT_NEW); + } + + printf("accepted alert %d, AppliesToMe()=%d\n", nID, AppliesToMe()); + return true; +} diff --git a/src/alert.h b/src/alert.h new file mode 100644 index 00000000..3e4dd532 --- /dev/null +++ b/src/alert.h @@ -0,0 +1,102 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef _BITCOINALERT_H_ +#define _BITCOINALERT_H_ 1 + +#include +#include + +#include "uint256.h" +#include "util.h" + +class CNode; + +/** 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_t nRelayUntil; // when newer nodes stop relaying to newer nodes + int64_t nExpiration; + int nID; + int nCancel; + std::set setCancel; + int nMinVer; // lowest version inclusive + int nMaxVer; // highest version inclusive + std::set setSubVer; // empty matches all + int nPriority; + + // Actions + std::string strComment; + std::string strStatusBar; + std::string strReserved; + + IMPLEMENT_SERIALIZE + ( + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(nRelayUntil); + READWRITE(nExpiration); + READWRITE(nID); + READWRITE(nCancel); + READWRITE(setCancel); + READWRITE(nMinVer); + READWRITE(nMaxVer); + READWRITE(setSubVer); + READWRITE(nPriority); + + READWRITE(strComment); + READWRITE(strStatusBar); + READWRITE(strReserved); + ) + + void SetNull(); + + std::string ToString() const; + void print() const; +}; + +/** An alert is a combination of a serialized CUnsignedAlert and a signature. */ +class CAlert : public CUnsignedAlert +{ +public: + std::vector vchMsg; + std::vector vchSig; + + CAlert() + { + SetNull(); + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(vchMsg); + READWRITE(vchSig); + ) + + void SetNull(); + bool IsNull() const; + uint256 GetHash() const; + bool IsInEffect() const; + bool Cancels(const CAlert& alert) const; + bool AppliesTo(int nVersion, std::string strSubVerIn) const; + bool AppliesToMe() const; + bool RelayTo(CNode* pnode) const; + bool CheckSignature() const; + bool ProcessAlert(); + + /* + * Get copy of (active) alert object by hash. Returns a null alert if it is not found. + */ + static CAlert getAlertByHash(const uint256 &hash); +}; + +#endif diff --git a/src/allocators.h b/src/allocators.h new file mode 100644 index 00000000..2d4ef97e --- /dev/null +++ b/src/allocators.h @@ -0,0 +1,258 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_ALLOCATORS_H +#define BITCOIN_ALLOCATORS_H + +#include +#include +#include +#include +#include // for OPENSSL_cleanse() + +#ifdef WIN32 +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif +#define _WIN32_WINNT 0x0501 +#define WIN32_LEAN_AND_MEAN 1 +#ifndef NOMINMAX +#define NOMINMAX +#endif +#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 +// the pagefile except in rare circumstances where memory is extremely low. +#else +#include +#include // for PAGESIZE +#include // for sysconf +#endif + +/** + * Thread-safe class to keep track of locked (ie, non-swappable) memory pages. + * + * Memory locks do not stack, that is, pages which have been locked several times by calls to mlock() + * will be unlocked by a single call to munlock(). This can result in keying material ending up in swap when + * those functions are used naively. This class simulates stacking memory locks by keeping a counter per page. + * + * @note By using a map from each page base address to lock count, this class is optimized for + * small objects that span up to a few pages, mostly smaller than a page. To support large allocations, + * something like an interval tree would be the preferred data structure. + */ +template class LockedPageManagerBase +{ +public: + LockedPageManagerBase(size_t page_size): + page_size(page_size) + { + // Determine bitmask for extracting page from address + assert(!(page_size & (page_size-1))); // size must be power of two + page_mask = ~(page_size - 1); + } + + // For all pages in affected range, increase lock count + void LockRange(void *p, size_t size) + { + boost::mutex::scoped_lock lock(mutex); + if(!size) return; + const size_t base_addr = reinterpret_cast(p); + const size_t start_page = base_addr & page_mask; + const size_t end_page = (base_addr + size - 1) & page_mask; + for(size_t page = start_page; page <= end_page; page += page_size) + { + Histogram::iterator it = histogram.find(page); + if(it == histogram.end()) // Newly locked page + { + locker.Lock(reinterpret_cast(page), page_size); + histogram.insert(std::make_pair(page, 1)); + } + else // Page was already locked; increase counter + { + it->second += 1; + } + } + } + + // For all pages in affected range, decrease lock count + void UnlockRange(void *p, size_t size) + { + boost::mutex::scoped_lock lock(mutex); + if(!size) return; + const size_t base_addr = reinterpret_cast(p); + const size_t start_page = base_addr & page_mask; + const size_t end_page = (base_addr + size - 1) & page_mask; + for(size_t page = start_page; page <= end_page; page += page_size) + { + Histogram::iterator it = histogram.find(page); + assert(it != histogram.end()); // Cannot unlock an area that was not locked + // Decrease counter for page, when it is zero, the page will be unlocked + it->second -= 1; + if(it->second == 0) // Nothing on the page anymore that keeps it locked + { + // Unlock page and remove the count from histogram + locker.Unlock(reinterpret_cast(page), page_size); + histogram.erase(it); + } + } + } + + // Get number of locked pages for diagnostics + int GetLockedPageCount() + { + boost::mutex::scoped_lock lock(mutex); + return histogram.size(); + } + +private: + Locker locker; + boost::mutex mutex; + size_t page_size, page_mask; + // map of page base address to lock count + typedef std::map Histogram; + Histogram histogram; +}; + +/** Determine system page size in bytes */ +static inline size_t GetSystemPageSize() +{ + size_t page_size; +#if defined(WIN32) + SYSTEM_INFO sSysInfo; + GetSystemInfo(&sSysInfo); + page_size = sSysInfo.dwPageSize; +#elif defined(PAGESIZE) // defined in limits.h + page_size = PAGESIZE; +#else // assume some POSIX OS + page_size = sysconf(_SC_PAGESIZE); +#endif + return page_size; +} + +/** + * OS-dependent memory page locking/unlocking. + * Defined as policy class to make stubbing for test possible. + */ +class MemoryPageLocker +{ +public: + /** Lock memory pages. + * addr and len must be a multiple of the system page size + */ + bool Lock(const void *addr, size_t len) + { +#ifdef WIN32 + return VirtualLock(const_cast(addr), len) != 0; +#else + return mlock(addr, len) == 0; +#endif + } + /** Unlock memory pages. + * addr and len must be a multiple of the system page size + */ + bool Unlock(const void *addr, size_t len) + { +#ifdef WIN32 + return VirtualUnlock(const_cast(addr), len) != 0; +#else + return munlock(addr, len) == 0; +#endif + } +}; + +/** + * Singleton class to keep track of locked (ie, non-swappable) memory pages, for use in + * std::allocator templates. + */ +class LockedPageManager: public LockedPageManagerBase +{ +public: + static LockedPageManager instance; // instantiated in util.cpp +private: + LockedPageManager(): + LockedPageManagerBase(GetSystemPageSize()) + {} +}; + +// +// Allocator that locks its contents from being paged +// out of memory and 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; }; + + T* allocate(std::size_t n, const void *hint = 0) + { + T *p; + p = std::allocator::allocate(n, hint); + if (p != NULL) + LockedPageManager::instance.LockRange(p, sizeof(T) * n); + return p; + } + + void deallocate(T* p, std::size_t n) + { + if (p != NULL) + { + OPENSSL_cleanse(p, sizeof(T) * n); + LockedPageManager::instance.UnlockRange(p, sizeof(T) * n); + } + std::allocator::deallocate(p, n); + } +}; + + +// +// Allocator that clears its contents before deletion. +// +template +struct zero_after_free_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; + zero_after_free_allocator() throw() {} + zero_after_free_allocator(const zero_after_free_allocator& a) throw() : base(a) {} + template + zero_after_free_allocator(const zero_after_free_allocator& a) throw() : base(a) {} + ~zero_after_free_allocator() throw() {} + template struct rebind + { typedef zero_after_free_allocator<_Other> other; }; + + void deallocate(T* p, std::size_t n) + { + if (p != NULL) + OPENSSL_cleanse(p, sizeof(T) * n); + std::allocator::deallocate(p, n); + } +}; + +// This is exactly like std::string, but with a custom allocator. +typedef std::basic_string, secure_allocator > SecureString; + +#endif diff --git a/src/base58.cpp b/src/base58.cpp new file mode 100644 index 00000000..18428532 --- /dev/null +++ b/src/base58.cpp @@ -0,0 +1,373 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin Developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +// +// Why base-58 instead of standard base-64 encoding? +// - Don't want 0OIl characters that look the same in some fonts and +// could be used to create visually identical looking account numbers. +// - A string with non-alphanumeric characters is not as easily accepted as an account number. +// - E-mail usually won't line-break if there's no punctuation to break at. +// - Double-clicking selects the whole number as one word if it's all alphanumeric. +// + +#include +#include +#include // for OPENSSL_cleanse() +#include "bignum.h" +#include "key.h" +#include "script.h" +#include "base58.h" + +static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + +// Encode a byte sequence as a base58-encoded string +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; + // 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; + while (bn > bn0) + { + if (!BN_div(&dv, &rem, &bn, &bn58, pctx)) + throw bignum_error("EncodeBase58 : BN_div failed"); + bn = dv; + unsigned int c = rem.getuint32(); + 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; +} + +// Encode a byte vector as a base58-encoded string +std::string EncodeBase58(const std::vector& vch) +{ + return EncodeBase58(&vch[0], &vch[0] + vch.size()); +} + +// Decode a base58-encoded string psz into byte vector vchRet +// returns true if decoding is successful +bool DecodeBase58(const char* psz, std::vector& vchRet) +{ + CAutoBN_CTX pctx; + vchRet.clear(); + CBigNum bn58 = 58; + CBigNum bn = 0; + CBigNum bnChar; + while (isspace(*psz)) + psz++; + + // Convert big endian string to bignum + for (const char* p = psz; *p; p++) + { + const char* p1 = strchr(pszBase58, *p); + if (p1 == NULL) + { + while (isspace(*p)) + p++; + if (*p != '\0') + return false; + break; + } + bnChar.setuint32((uint32_t)(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; +} + +// Decode a base58-encoded string str into byte vector vchRet +// returns true if decoding is successful +bool DecodeBase58(const std::string& str, std::vector& vchRet) +{ + return DecodeBase58(str.c_str(), vchRet); +} + +// Encode a byte vector to a base58-encoded string, including checksum +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); +} + +// Decode a base58-encoded string psz that includes a checksum, into byte vector vchRet +// returns true if decoding is successful +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; +} + +// Decode a base58-encoded string str that includes a checksum, into byte vector vchRet +// returns true if decoding is successful +bool DecodeBase58Check(const std::string& str, std::vector& vchRet) +{ + return DecodeBase58Check(str.c_str(), vchRet); +} + + CBase58Data::CBase58Data() + { + nVersion = 0; + vchData.clear(); + } + + CBase58Data::~CBase58Data() + { + // zero the memory, as it may contain sensitive data + if (!vchData.empty()) + OPENSSL_cleanse(&vchData[0], vchData.size()); + } + + void CBase58Data::SetData(int nVersionIn, const void* pdata, size_t nSize) + { + nVersion = nVersionIn; + vchData.resize(nSize); + if (!vchData.empty()) + memcpy(&vchData[0], pdata, nSize); + } + + void CBase58Data::SetData(int nVersionIn, const unsigned char *pbegin, const unsigned char *pend) + { + SetData(nVersionIn, (void*)pbegin, pend - pbegin); + } + + bool CBase58Data::SetString(const char* psz) + { + std::vector vchTemp; + DecodeBase58Check(psz, vchTemp); + if (vchTemp.empty()) + { + vchData.clear(); + nVersion = 0; + return false; + } + nVersion = vchTemp[0]; + vchData.resize(vchTemp.size() - 1); + if (!vchData.empty()) + memcpy(&vchData[0], &vchTemp[1], vchData.size()); + OPENSSL_cleanse(&vchTemp[0], vchData.size()); + return true; + } + + bool CBase58Data::SetString(const std::string& str) + { + return SetString(str.c_str()); + } + + std::string CBase58Data::ToString() const + { + std::vector vch(1, nVersion); + vch.insert(vch.end(), vchData.begin(), vchData.end()); + return EncodeBase58Check(vch); + } + + int CBase58Data::CompareTo(const CBase58Data& b58) const + { + if (nVersion < b58.nVersion) return -1; + if (nVersion > b58.nVersion) return 1; + if (vchData < b58.vchData) return -1; + if (vchData > b58.vchData) return 1; + return 0; + } + + bool CBitcoinAddress::Set(const CKeyID &id) { + SetData(fTestNet ? PUBKEY_ADDRESS_TEST : PUBKEY_ADDRESS, &id, 20); + return true; + } + + bool CBitcoinAddress::Set(const CScriptID &id) { + SetData(fTestNet ? SCRIPT_ADDRESS_TEST : SCRIPT_ADDRESS, &id, 20); + return true; + } + + bool CBitcoinAddress::Set(const CTxDestination &dest) + { + return boost::apply_visitor(CBitcoinAddressVisitor(this), dest); + } + + bool CBitcoinAddress::IsValid() const + { + unsigned int nExpectedSize = 20; + bool fExpectTestNet = false; + switch(nVersion) + { + case PUBKEY_ADDRESS: + nExpectedSize = 20; // Hash of public key + fExpectTestNet = false; + break; + case SCRIPT_ADDRESS: + nExpectedSize = 20; // Hash of CScript + fExpectTestNet = false; + break; + + case PUBKEY_ADDRESS_TEST: + nExpectedSize = 20; + fExpectTestNet = true; + break; + case SCRIPT_ADDRESS_TEST: + nExpectedSize = 20; + fExpectTestNet = true; + break; + + default: + return false; + } + return fExpectTestNet == fTestNet && vchData.size() == nExpectedSize; + } + + CTxDestination CBitcoinAddress::Get() const { + if (!IsValid()) + return CNoDestination(); + switch (nVersion) { + case PUBKEY_ADDRESS: + case PUBKEY_ADDRESS_TEST: { + uint160 id; + memcpy(&id, &vchData[0], 20); + return CKeyID(id); + } + case SCRIPT_ADDRESS: + case SCRIPT_ADDRESS_TEST: { + uint160 id; + memcpy(&id, &vchData[0], 20); + return CScriptID(id); + } + } + return CNoDestination(); + } + + bool CBitcoinAddress::GetKeyID(CKeyID &keyID) const { + if (!IsValid()) + return false; + switch (nVersion) { + case PUBKEY_ADDRESS: + case PUBKEY_ADDRESS_TEST: { + uint160 id; + memcpy(&id, &vchData[0], 20); + keyID = CKeyID(id); + return true; + } + default: return false; + } + } + + bool CBitcoinAddress::IsScript() const { + if (!IsValid()) + return false; + switch (nVersion) { + case SCRIPT_ADDRESS: + case SCRIPT_ADDRESS_TEST: { + return true; + } + default: return false; + } + } + + void CBitcoinSecret::SetSecret(const CSecret& vchSecret, bool fCompressed) + { + assert(vchSecret.size() == 32); + SetData(128 + (fTestNet ? CBitcoinAddress::PUBKEY_ADDRESS_TEST : CBitcoinAddress::PUBKEY_ADDRESS), &vchSecret[0], vchSecret.size()); + if (fCompressed) + vchData.push_back(1); + } + + CSecret CBitcoinSecret::GetSecret(bool &fCompressedOut) + { + CSecret vchSecret; + vchSecret.resize(32); + memcpy(&vchSecret[0], &vchData[0], 32); + fCompressedOut = vchData.size() == 33; + return vchSecret; + } + + bool CBitcoinSecret::IsValid() const + { + bool fExpectTestNet = false; + switch(nVersion) + { + case (128 + CBitcoinAddress::PUBKEY_ADDRESS): + break; + + case (128 + CBitcoinAddress::PUBKEY_ADDRESS_TEST): + fExpectTestNet = true; + break; + + default: + return false; + } + return fExpectTestNet == fTestNet && (vchData.size() == 32 || (vchData.size() == 33 && vchData[32] == 1)); + } + + bool CBitcoinSecret::SetString(const char* pszSecret) + { + return CBase58Data::SetString(pszSecret) && IsValid(); + } + + bool CBitcoinSecret::SetString(const std::string& strSecret) + { + return SetString(strSecret.c_str()); + } + + CBitcoinSecret::CBitcoinSecret(const CSecret& vchSecret, bool fCompressed) + { + SetSecret(vchSecret, fCompressed); + } + + diff --git a/src/base58.h b/src/base58.h new file mode 100644 index 00000000..cb9484b9 --- /dev/null +++ b/src/base58.h @@ -0,0 +1,159 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin Developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +// +// Why base-58 instead of standard base-64 encoding? +// - Don't want 0OIl characters that look the same in some fonts and +// could be used to create visually identical looking account numbers. +// - A string with non-alphanumeric characters is not as easily accepted as an account number. +// - E-mail usually won't line-break if there's no punctuation to break at. +// - Double-clicking selects the whole number as one word if it's all alphanumeric. +// +#ifndef BITCOIN_BASE58_H +#define BITCOIN_BASE58_H + +#include +#include +#include // for OPENSSL_cleanse() +#include "bignum.h" +#include "key.h" +#include "script.h" + +// Encode a byte sequence as a base58-encoded string +std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend); + +// Encode a byte vector as a base58-encoded string +std::string EncodeBase58(const std::vector& vch); + +// Decode a base58-encoded string psz into byte vector vchRet +// returns true if decoding is successful +bool DecodeBase58(const char* psz, std::vector& vchRet); + +// Decode a base58-encoded string str into byte vector vchRet +// returns true if decoding is successful +bool DecodeBase58(const std::string& str, std::vector& vchRet); + +// Encode a byte vector to a base58-encoded string, including checksum +std::string EncodeBase58Check(const std::vector& vchIn); + +// Decode a base58-encoded string psz that includes a checksum, into byte vector vchRet +// returns true if decoding is successful +bool DecodeBase58Check(const char* psz, std::vector& vchRet); + +// Decode a base58-encoded string str that includes a checksum, into byte vector vchRet +// returns true if decoding is successful +bool DecodeBase58Check(const std::string& str, std::vector& vchRet); + +/** Base class for all base58-encoded data */ +class CBase58Data +{ +protected: + // the version byte + unsigned char nVersion; + + // the actually encoded data + std::vector vchData; + + CBase58Data(); + ~CBase58Data(); + + void SetData(int nVersionIn, const void* pdata, size_t nSize); + void SetData(int nVersionIn, const unsigned char *pbegin, const unsigned char *pend); + +public: + bool SetString(const char* psz); + bool SetString(const std::string& str); + std::string ToString() const; + + int CompareTo(const CBase58Data& b58) const; + bool operator==(const CBase58Data& b58) const { return CompareTo(b58) == 0; } + bool operator<=(const CBase58Data& b58) const { return CompareTo(b58) <= 0; } + bool operator>=(const CBase58Data& b58) const { return CompareTo(b58) >= 0; } + bool operator< (const CBase58Data& b58) const { return CompareTo(b58) < 0; } + bool operator> (const CBase58Data& b58) const { return CompareTo(b58) > 0; } +}; + +/** base58-encoded Bitcoin addresses. + * Public-key-hash-addresses have version 0 (or 111 testnet). + * The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key. + * Script-hash-addresses have version 5 (or 196 testnet). + * The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script. + */ +class CBitcoinAddress; +class CBitcoinAddressVisitor : public boost::static_visitor +{ +private: + CBitcoinAddress *addr; +public: + CBitcoinAddressVisitor(CBitcoinAddress *addrIn) : addr(addrIn) { } + bool operator()(const CKeyID &id) const; + bool operator()(const CScriptID &id) const; + bool operator()(const CNoDestination &no) const; +}; + +class CBitcoinAddress : public CBase58Data +{ +public: + enum + { + PUBKEY_ADDRESS = 75, + SCRIPT_ADDRESS = 20, + PUBKEY_ADDRESS_TEST = 111, + SCRIPT_ADDRESS_TEST = 196, + }; + + bool Set(const CKeyID &id); + bool Set(const CScriptID &id); + bool Set(const CTxDestination &dest); + bool IsValid() const; + + CBitcoinAddress() + { + } + + CBitcoinAddress(const CTxDestination &dest) + { + Set(dest); + } + + CBitcoinAddress(const std::string& strAddress) + { + SetString(strAddress); + } + + CBitcoinAddress(const char* pszAddress) + { + SetString(pszAddress); + } + + CTxDestination Get() const; + bool GetKeyID(CKeyID &keyID) const; + bool IsScript() const; +}; + +bool inline CBitcoinAddressVisitor::operator()(const CKeyID &id) const { return addr->Set(id); } +bool inline CBitcoinAddressVisitor::operator()(const CScriptID &id) const { return addr->Set(id); } +bool inline CBitcoinAddressVisitor::operator()(const CNoDestination &id) const { return false; } + +/** A base58-encoded secret key */ +class CBitcoinSecret : public CBase58Data +{ +public: + void SetSecret(const CSecret& vchSecret, bool fCompressed); + CSecret GetSecret(bool &fCompressedOut); + + bool IsValid() const; + + bool SetString(const char* pszSecret); + bool SetString(const std::string& strSecret); + + CBitcoinSecret(const CSecret& vchSecret, bool fCompressed); + CBitcoinSecret() + { + } +}; + +#endif diff --git a/src/bignum.h b/src/bignum.h new file mode 100644 index 00000000..7f02fd59 --- /dev/null +++ b/src/bignum.h @@ -0,0 +1,781 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_BIGNUM_H +#define BITCOIN_BIGNUM_H + +#include +#include +#include +#include "util.h" + +/** Errors thrown by the bignum class */ +class bignum_error : public std::runtime_error +{ +public: + explicit bignum_error(const std::string& str) : std::runtime_error(str) {} +}; + + +/** RAII encapsulated BN_CTX (OpenSSL bignum context) */ +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); } +}; + + +/** C++ wrapper for BIGNUM (OpenSSL bignum) */ +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(int8_t n) { BN_init(this); if (n >= 0) setuint32(n); else setint64(n); } + CBigNum(int16_t n) { BN_init(this); if (n >= 0) setuint32(n); else setint64(n); } + CBigNum(int32_t n) { BN_init(this); if (n >= 0) setuint32(n); else setint64(n); } + CBigNum(int64_t n) { BN_init(this); if (n >= 0) setuint64(n); else setint64(n); } + + CBigNum(uint8_t n) { BN_init(this); setuint32(n); } + CBigNum(uint16_t n) { BN_init(this); setuint32(n); } + CBigNum(uint32_t n) { BN_init(this); setuint32(n); } + CBigNum(uint64_t 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); + } + + /** Generates a cryptographically secure random number between zero and range exclusive + * i.e. 0 < returned number < range + * @param range The upper bound on the number. + * @return + */ + static CBigNum randBignum(const CBigNum& range) { + CBigNum ret; + if(!BN_rand_range(&ret, &range)){ + throw bignum_error("CBigNum:rand element : BN_rand_range failed"); + } + return ret; + } + + /** Generates a cryptographically secure random k-bit number + * @param k The bit length of the number. + * @return + */ + static CBigNum RandKBitBigum(const uint32_t k){ + CBigNum ret; + if(!BN_rand(&ret, k, -1, 0)){ + throw bignum_error("CBigNum:rand element : BN_rand failed"); + } + return ret; + } + + /**Returns the size in bits of the underlying bignum. + * + * @return the size + */ + int bitSize() const{ + return BN_num_bits(this); + } + + + void setuint32(uint32_t n) + { + if (!BN_set_word(this, n)) + throw bignum_error("CBigNum conversion from uint32_t : BN_set_word failed"); + } + + uint32_t getuint32() const + { + return BN_get_word(this); + } + + int32_t getint32() const + { + uint64_t n = BN_get_word(this); + if (!BN_is_negative(this)) + return (n > (uint64_t)std::numeric_limits::max() ? std::numeric_limits::max() : (int32_t)n); + else + return (n > (uint64_t)std::numeric_limits::max() ? std::numeric_limits::min() : -(int32_t)n); + } + + void setint64(int64_t sn) + { + uint8_t pch[sizeof(sn) + 6]; + uint8_t* p = pch + 4; + bool fNegative; + uint64_t n; + + if (sn < (int64_t)0) + { + // Since the minimum signed integer cannot be represented as positive so long as its type is signed, and it's not well-defined what happens if you make it unsigned before negating it, we instead increment the negative integer by 1, convert it, then increment the (now positive) unsigned integer by 1 to compensate + n = -(sn + 1); + ++n; + fNegative = true; + } else { + n = sn; + fNegative = false; + } + + bool fLeadingZeroes = true; + for (int i = 0; i < 8; i++) + { + uint8_t 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; + } + uint32_t nSize = (uint32_t) (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, (int)(p - pch), this); + } + + uint64_t getuint64() + { + size_t 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; + uint64_t n = 0; + for (size_t i = 0, j = vch.size()-1; i < sizeof(n) && j >= 4; i++, j--) + ((uint8_t*)&n)[i] = vch[j]; + return n; + } + + void setuint64(uint64_t n) + { + // Use BN_set_word if word size is sufficient for uint64_t + if (sizeof(n) <= sizeof(BN_ULONG)) + { + if (!BN_set_word(this, (BN_ULONG)n)) + throw bignum_error("CBigNum conversion from uint64_t : BN_set_word failed"); + return; + } + + uint8_t pch[sizeof(n) + 6]; + uint8_t* p = pch + 4; + bool fLeadingZeroes = true; + for (int i = 0; i < 8; i++) + { + uint8_t c = (n >> 56) & 0xff; + n <<= 8; + if (fLeadingZeroes) + { + if (c == 0) + continue; + if (c & 0x80) + *p++ = 0; + fLeadingZeroes = false; + } + *p++ = c; + } + uint32_t nSize = (uint32_t) (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, (int)(p - pch), this); + } + + void setuint160(uint160 n) + { + uint8_t pch[sizeof(n) + 6]; + uint8_t* p = pch + 4; + bool fLeadingZeroes = true; + uint8_t* pbegin = (uint8_t*)&n; + uint8_t* psrc = pbegin + sizeof(n); + while (psrc != pbegin) + { + uint8_t c = *(--psrc); + if (fLeadingZeroes) + { + if (c == 0) + continue; + if (c & 0x80) + *p++ = 0; + fLeadingZeroes = false; + } + *p++ = c; + } + uint32_t nSize = (uint32_t) (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, (int) (p - pch), this); + } + + uint160 getuint160() const + { + 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; + uint160 n = 0; + for (size_t i = 0, j = vch.size()-1; i < sizeof(n) && j >= 4; i++, j--) + ((uint8_t*)&n)[i] = vch[j]; + return n; + } + + void setuint256(uint256 n) + { + uint8_t pch[sizeof(n) + 6]; + uint8_t* p = pch + 4; + bool fLeadingZeroes = true; + uint8_t* pbegin = (uint8_t*)&n; + uint8_t* psrc = pbegin + sizeof(n); + while (psrc != pbegin) + { + uint8_t c = *(--psrc); + if (fLeadingZeroes) + { + if (c == 0) + continue; + if (c & 0x80) + *p++ = 0; + fLeadingZeroes = false; + } + *p++ = c; + } + uint32_t nSize = (uint32_t) (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, (int) (p - pch), this); + } + + uint256 getuint256() const + { + 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 (size_t i = 0, j = vch.size()-1; i < sizeof(n) && j >= 4; i++, j--) + ((uint8_t*)&n)[i] = vch[j]; + return n; + } + + void setBytes(const std::vector& vchBytes) + { + BN_bin2bn(&vchBytes[0], (int) vchBytes.size(), this); + } + + std::vector getBytes() const + { + int nBytes = BN_num_bytes(this); + + std::vector vchBytes(nBytes); + + int n = BN_bn2bin(this, &vchBytes[0]); + if (n != nBytes) { + throw bignum_error("CBigNum::getBytes : BN_bn2bin failed"); + } + + return vchBytes; + } + + void setvch(const std::vector& vch) + { + std::vector vch2(vch.size() + 4); + uint32_t nSize = (uint32_t) 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], (int) 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(uint32_t nCompact) + { + uint32_t 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], (int) vch.size(), this); + return *this; + } + + uint32_t GetCompact() const + { + uint32_t nSize = BN_bn2mpi(this, NULL); + std::vector vch(nSize); + nSize -= 4; + BN_bn2mpi(this, &vch[0]); + uint32_t 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 const signed 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[(uint8_t)*psz++]; + *this += n; + } + if (fNegative) + *this = 0 - *this; + } + + std::string ToString(int nBase=10) const + { + CAutoBN_CTX pctx; + CBigNum bnBase = nBase; + CBigNum bn0 = 0; + std::string str; + CBigNum bn = *this; + BN_set_negative(&bn, false); + CBigNum dv; + CBigNum rem; + if (BN_cmp(&bn, &bn0) == 0) + return "0"; + while (BN_cmp(&bn, &bn0) > 0) + { + if (!BN_div(&dv, &rem, &bn, &bnBase, pctx)) + throw bignum_error("CBigNum::ToString() : BN_div failed"); + bn = dv; + uint32_t c = rem.getuint32(); + 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=PROTOCOL_VERSION) const + { + return ::GetSerializeSize(getvch(), nType, nVersion); + } + + template + void Serialize(Stream& s, int nType=0, int nVersion=PROTOCOL_VERSION) const + { + ::Serialize(s, getvch(), nType, nVersion); + } + + template + void Unserialize(Stream& s, int nType=0, int nVersion=PROTOCOL_VERSION) + { + std::vector vch; + ::Unserialize(s, vch, nType, nVersion); + setvch(vch); + } + + /** + * exponentiation with an int. this^e + * @param e the exponent as an int + * @return + */ + CBigNum pow(const int e) const { + return this->pow(CBigNum(e)); + } + + /** + * exponentiation this^e + * @param e the exponent + * @return + */ + CBigNum pow(const CBigNum& e) const { + CAutoBN_CTX pctx; + CBigNum ret; + if (!BN_exp(&ret, this, &e, pctx)) + throw bignum_error("CBigNum::pow : BN_exp failed"); + return ret; + } + + /** + * modular multiplication: (this * b) mod m + * @param b operand + * @param m modulus + */ + CBigNum mul_mod(const CBigNum& b, const CBigNum& m) const { + CAutoBN_CTX pctx; + CBigNum ret; + if (!BN_mod_mul(&ret, this, &b, &m, pctx)) + throw bignum_error("CBigNum::mul_mod : BN_mod_mul failed"); + + return ret; + } + + /** + * modular exponentiation: this^e mod n + * @param e exponent + * @param m modulus + */ + CBigNum pow_mod(const CBigNum& e, const CBigNum& m) const { + CAutoBN_CTX pctx; + CBigNum ret; + if( e < 0){ + // g^-x = (g^-1)^x + CBigNum inv = this->inverse(m); + CBigNum posE = e * -1; + if (!BN_mod_exp(&ret, &inv, &posE, &m, pctx)) + throw bignum_error("CBigNum::pow_mod: BN_mod_exp failed on negative exponent"); + }else + if (!BN_mod_exp(&ret, this, &e, &m, pctx)) + throw bignum_error("CBigNum::pow_mod : BN_mod_exp failed"); + + return ret; + } + + /** + * Calculates the inverse of this element mod m. + * i.e. i such this*i = 1 mod m + * @param m the modu + * @return the inverse + */ + CBigNum inverse(const CBigNum& m) const { + CAutoBN_CTX pctx; + CBigNum ret; + if (!BN_mod_inverse(&ret, this, &m, pctx)) + throw bignum_error("CBigNum::inverse*= :BN_mod_inverse"); + return ret; + } + + /** + * Generates a random (safe) prime of numBits bits + * @param numBits the number of bits + * @param safe true for a safe prime + * @return the prime + */ + static CBigNum generatePrime(const unsigned int numBits, bool safe = false) { + CBigNum ret; + if(!BN_generate_prime_ex(&ret, numBits, (safe == true), NULL, NULL, NULL)) + throw bignum_error("CBigNum::generatePrime*= :BN_generate_prime_ex"); + return ret; + } + + /** + * Calculates the greatest common divisor (GCD) of two numbers. + * @param m the second element + * @return the GCD + */ + CBigNum gcd( const CBigNum& b) const{ + CAutoBN_CTX pctx; + CBigNum ret; + if (!BN_gcd(&ret, this, &b, pctx)) + throw bignum_error("CBigNum::gcd*= :BN_gcd"); + return ret; + } + + /** + * Miller-Rabin primality test on this element + * @param checks: optional, the number of Miller-Rabin tests to run + * default causes error rate of 2^-80. + * @return true if prime + */ + bool isPrime(const int checks=BN_prime_checks) const { + CAutoBN_CTX pctx; + int ret = BN_is_prime(this, checks, NULL, pctx, NULL); + if(ret < 0){ + throw bignum_error("CBigNum::isPrime :BN_is_prime"); + } + return ret != 0; + } + + bool isOne() const { + return BN_is_one(this); + } + + + 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); + friend inline const CBigNum operator*(const CBigNum& a, const CBigNum& b); + friend inline bool 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_nnmod(&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); } + +inline std::ostream& operator<<(std::ostream &strm, const CBigNum &b) { return strm << b.ToString(10); } + +typedef CBigNum Bignum; + +#endif diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp new file mode 100644 index 00000000..524c019c --- /dev/null +++ b/src/bitcoinrpc.cpp @@ -0,0 +1,1351 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "init.h" +#include "util.h" +#include "sync.h" +#include "ui_interface.h" +#include "base58.h" +#include "bitcoinrpc.h" +#include "db.h" + +#undef printf +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define printf OutputDebugStringF + +using namespace std; +using namespace boost; +using namespace boost::asio; +using namespace json_spirit; + +void ThreadRPCServer2(void* parg); + +static std::string strRPCUserColonPass; + +const Object emptyobj; + +void ThreadRPCServer3(void* parg); + +static inline unsigned short GetDefaultRPCPort() +{ + return GetBoolArg("-testnet", false) ? 18345 : 28191; +} + +Object JSONRPCError(int code, const string& message) +{ + Object error; + error.push_back(Pair("code", code)); + error.push_back(Pair("message", message)); + return error; +} + +void RPCTypeCheck(const Array& params, + const list& typesExpected, + bool fAllowNull) +{ + unsigned int i = 0; + BOOST_FOREACH(Value_type t, typesExpected) + { + if (params.size() <= i) + break; + + const Value& v = params[i]; + if (!((v.type() == t) || (fAllowNull && (v.type() == null_type)))) + { + string err = strprintf("Expected type %s, got %s", + Value_type_name[t], Value_type_name[v.type()]); + throw JSONRPCError(RPC_TYPE_ERROR, err); + } + i++; + } +} + +void RPCTypeCheck(const Object& o, + const map& typesExpected, + bool fAllowNull) +{ + BOOST_FOREACH(const PAIRTYPE(string, Value_type)& t, typesExpected) + { + const Value& v = find_value(o, t.first); + if (!fAllowNull && v.type() == null_type) + throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first.c_str())); + + if (!((v.type() == t.second) || (fAllowNull && (v.type() == null_type)))) + { + string err = strprintf("Expected type %s for %s, got %s", + Value_type_name[t.second], t.first.c_str(), Value_type_name[v.type()]); + throw JSONRPCError(RPC_TYPE_ERROR, err); + } + } +} + +int64_t AmountFromValue(const Value& value) +{ + double dAmount = value.get_real(); + if (dAmount <= 0.0 || dAmount > MAX_MONEY) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); + int64_t nAmount = roundint64(dAmount * COIN); + if (!MoneyRange(nAmount)) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); + return nAmount; +} + +Value ValueFromAmount(int64_t amount) +{ + return (double)amount / (double)COIN; +} + +std::string HexBits(unsigned int nBits) +{ + union { + int32_t nBits; + char cBits[4]; + } uBits; + uBits.nBits = htonl((int32_t)nBits); + return HexStr(BEGIN(uBits.cBits), END(uBits.cBits)); +} + + +// +// Utilities: convert hex-encoded Values +// (throws error if not hex). +// +uint256 ParseHashV(const Value& v, string strName) +{ + string strHex; + if (v.type() == str_type) + strHex = v.get_str(); + if (!IsHex(strHex)) // Note: IsHex("") is false + throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); + uint256 result; + result.SetHex(strHex); + return result; +} + +uint256 ParseHashO(const Object& o, string strKey) +{ + return ParseHashV(find_value(o, strKey), strKey); +} + +vector ParseHexV(const Value& v, string strName) +{ + string strHex; + if (v.type() == str_type) + strHex = v.get_str(); + if (!IsHex(strHex)) + throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); + return ParseHex(strHex); +} + +vector ParseHexO(const Object& o, string strKey) +{ + return ParseHexV(find_value(o, strKey), strKey); +} + + +/// +/// Note: This interface may still be subject to change. +/// + +string CRPCTable::help(string strCommand) const +{ + string strRet; + set setDone; + for (map::const_iterator mi = mapCommands.begin(); mi != mapCommands.end(); ++mi) + { + const CRPCCommand *pcmd = mi->second; + string strMethod = mi->first; + // We already filter duplicates, but these deprecated screw up the sort order + if (strMethod.find("label") != string::npos) + continue; + if (strCommand != "" && strMethod != strCommand) + continue; + try + { + Array params; + rpcfn_type pfn = pcmd->actor; + 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') != string::npos) + 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 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(); + + return tableRPC.help(strCommand); +} + + +Value stop(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "stop \n" + " is true or false to detach the database or not for this stop only\n" + "Stop XP server (and possibly override the detachdb config value)."); + // Shutdown will take long enough that the response should get back + if (params.size() > 0) + bitdb.SetDetach(params[0].get_bool()); + StartShutdown(); + return "XP server stopping"; +} + + + +// +// Call Table +// + + +static const CRPCCommand vRPCCommands[] = +{ // name function safemd unlocked + // ------------------------ ----------------------- ------ -------- + { "help", &help, true, true }, + { "stop", &stop, true, true }, + { "getbestblockhash", &getbestblockhash, true, false }, + { "getblockcount", &getblockcount, true, false }, + { "getconnectioncount", &getconnectioncount, true, false }, + { "getaddrmaninfo", &getaddrmaninfo, true, false }, + { "getpeerinfo", &getpeerinfo, true, false }, + { "addnode", &addnode, true, true }, + { "getaddednodeinfo", &getaddednodeinfo, true, true }, + { "getdifficulty", &getdifficulty, true, false }, + { "getinfo", &getinfo, true, false }, + { "getsubsidy", &getsubsidy, true, false }, + { "getmininginfo", &getmininginfo, true, false }, + { "scaninput", &scaninput, true, true }, + { "getnewaddress", &getnewaddress, true, false }, + { "getnettotals", &getnettotals, true, true }, + { "ntptime", &ntptime, true, true }, + { "getaccountaddress", &getaccountaddress, true, false }, + { "setaccount", &setaccount, true, false }, + { "getaccount", &getaccount, false, false }, + { "getaddressesbyaccount", &getaddressesbyaccount, true, false }, + { "sendtoaddress", &sendtoaddress, false, false }, + { "mergecoins", &mergecoins, false, false }, + { "getreceivedbyaddress", &getreceivedbyaddress, false, false }, + { "getreceivedbyaccount", &getreceivedbyaccount, false, false }, + { "listreceivedbyaddress", &listreceivedbyaddress, false, false }, + { "listreceivedbyaccount", &listreceivedbyaccount, false, false }, + { "backupwallet", &backupwallet, true, false }, + { "keypoolrefill", &keypoolrefill, true, false }, + { "keypoolreset", &keypoolreset, true, false }, + { "walletpassphrase", &walletpassphrase, true, false }, + { "walletpassphrasechange", &walletpassphrasechange, false, false }, + { "walletlock", &walletlock, true, false }, + { "encryptwallet", &encryptwallet, false, false }, + { "validateaddress", &validateaddress, true, false }, + { "getbalance", &getbalance, false, false }, + { "move", &movecmd, false, false }, + { "sendfrom", &sendfrom, false, false }, + { "sendmany", &sendmany, false, false }, + { "addmultisigaddress", &addmultisigaddress, false, false }, + { "addredeemscript", &addredeemscript, false, false }, + { "getrawmempool", &getrawmempool, true, false }, + { "getblock", &getblock, false, false }, + { "getblockbynumber", &getblockbynumber, false, false }, + { "dumpblock", &dumpblock, false, false }, + { "dumpblockbynumber", &dumpblockbynumber, false, false }, + { "getblockhash", &getblockhash, false, false }, + { "gettransaction", &gettransaction, false, false }, + { "listtransactions", &listtransactions, false, false }, + { "listaddressgroupings", &listaddressgroupings, false, false }, + { "signmessage", &signmessage, false, false }, + { "verifymessage", &verifymessage, false, false }, + { "getwork", &getwork, true, false }, + { "getworkex", &getworkex, true, false }, + { "listaccounts", &listaccounts, false, false }, + { "settxfee", &settxfee, false, false }, + { "getblocktemplate", &getblocktemplate, true, false }, + { "submitblock", &submitblock, false, false }, + { "listsinceblock", &listsinceblock, false, false }, + { "dumpprivkey", &dumpprivkey, false, false }, + { "dumpwallet", &dumpwallet, true, false }, + { "importwallet", &importwallet, false, false }, + { "importprivkey", &importprivkey, false, false }, + { "importaddress", &importaddress, false, true }, + { "removeaddress", &removeaddress, false, true }, + { "listunspent", &listunspent, false, false }, + { "getrawtransaction", &getrawtransaction, false, false }, + { "createrawtransaction", &createrawtransaction, false, false }, + { "decoderawtransaction", &decoderawtransaction, false, false }, + { "createmultisig", &createmultisig, false, false }, + { "decodescript", &decodescript, false, false }, + { "signrawtransaction", &signrawtransaction, false, false }, + { "sendrawtransaction", &sendrawtransaction, false, false }, + { "getcheckpoint", &getcheckpoint, true, false }, + { "reservebalance", &reservebalance, false, true}, + { "checkwallet", &checkwallet, false, true}, + { "repairwallet", &repairwallet, false, true}, + { "resendtx", &resendtx, false, true}, + { "makekeypair", &makekeypair, false, true}, + { "sendalert", &sendalert, false, false}, +}; + +CRPCTable::CRPCTable() +{ + unsigned int vcidx; + for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++) + { + const CRPCCommand *pcmd; + + pcmd = &vRPCCommands[vcidx]; + mapCommands[pcmd->name] = pcmd; + } +} + +const CRPCCommand *CRPCTable::operator[](string name) const +{ + map::const_iterator it = mapCommands.find(name); + if (it == mapCommands.end()) + return NULL; + return (*it).second; +} + +// +// 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: XP-json-rpc/" << FormatFullVersion() << "\r\n" + << "Host: 127.0.0.1\r\n" + << "Content-Type: application/json\r\n" + << "Content-Length: " << strMsg.size() << "\r\n" + << "Connection: close\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() +{ + return DateTimeStrFormat("%a, %d %b %Y %H:%M:%S +0000", GetTime()); +} + +static string HTTPReply(int nStatus, const string& strMsg, bool keepalive) +{ + if (nStatus == HTTP_UNAUTHORIZED) + return strprintf("HTTP/1.0 401 Authorization Required\r\n" + "Date: %s\r\n" + "Server: XP-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()); + const char *cStatus; + if (nStatus == HTTP_OK) cStatus = "OK"; + else if (nStatus == HTTP_BAD_REQUEST) cStatus = "Bad Request"; + else if (nStatus == HTTP_FORBIDDEN) cStatus = "Forbidden"; + else if (nStatus == HTTP_NOT_FOUND) cStatus = "Not Found"; + else if (nStatus == HTTP_INTERNAL_SERVER_ERROR) cStatus = "Internal Server Error"; + else cStatus = ""; + return strprintf( + "HTTP/1.1 %d %s\r\n" + "Date: %s\r\n" + "Connection: %s\r\n" + "Content-Length: %" PRIszu "\r\n" + "Content-Type: application/json\r\n" + "Server: XP-json-rpc/%s\r\n" + "\r\n" + "%s", + nStatus, + cStatus, + rfc1123Time().c_str(), + keepalive ? "keep-alive" : "close", + strMsg.size(), + FormatFullVersion().c_str(), + strMsg.c_str()); +} + +int ReadHTTPStatus(std::basic_istream& stream, int &proto) +{ + string str; + getline(stream, str); + vector vWords; + boost::split(vWords, str, boost::is_any_of(" ")); + if (vWords.size() < 2) + return HTTP_INTERNAL_SERVER_ERROR; + proto = 0; + const char *ver = strstr(str.c_str(), "HTTP/1."); + if (ver != NULL) + proto = atoi(ver+7); + return atoi(vWords[1].c_str()); +} + +int ReadHTTPHeader(std::basic_istream& stream, map& mapHeadersRet) +{ + int nLen = 0; + while (true) + { + 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 nProto = 0; + int nStatus = ReadHTTPStatus(stream, nProto); + + // Read header + int nLen = ReadHTTPHeader(stream, mapHeadersRet); + if (nLen < 0 || nLen > (int)MAX_SIZE) + return HTTP_INTERNAL_SERVER_ERROR; + + // Read message + if (nLen > 0) + { + vector vch(nLen); + stream.read(&vch[0], nLen); + strMessageRet = string(vch.begin(), vch.end()); + } + + string sConHdr = mapHeadersRet["connection"]; + + if ((sConHdr != "close") && (sConHdr != "keep-alive")) + { + if (nProto >= 1) + mapHeadersRet["connection"] = "keep-alive"; + else + mapHeadersRet["connection"] = "close"; + } + + return nStatus; +} + +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); + return TimingResistantEqual(strUserPass, strRPCUserColonPass); +} + +// +// 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"; +} + +Object JSONRPCReplyObj(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 reply; +} + +string JSONRPCReply(const Value& result, const Value& error, const Value& id) +{ + Object reply = JSONRPCReplyObj(result, error, 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 = HTTP_INTERNAL_SERVER_ERROR; + int code = find_value(objError, "code").get_int(); + if (code == RPC_INVALID_REQUEST) nStatus = HTTP_BAD_REQUEST; + else if (code == RPC_METHOD_NOT_FOUND) nStatus = HTTP_NOT_FOUND; + string strReply = JSONRPCReply(Value::null, objError, id); + stream << HTTPReply(nStatus, strReply, false) << std::flush; +} + +bool ClientAllowed(const boost::asio::ip::address& address) +{ + // Make sure that IPv4-compatible and IPv4-mapped IPv6 addresses are treated as IPv4 addresses + if (address.is_v6() + && (address.to_v6().is_v4_compatible() + || address.to_v6().is_v4_mapped())) + return ClientAllowed(address.to_v6().to_v4()); + + if (address == asio::ip::address_v4::loopback() + || address == asio::ip::address_v6::loopback() + || (address.is_v4() + // Check whether IPv4 addresses match 127.0.0.0/8 (loopback subnet) + && (address.to_v4().to_ulong() & 0xff000000) == 0x7f000000)) + return true; + + const string strAddress = address.to_string(); + const vector& vAllow = mapMultiArgs["-rpcallowip"]; + BOOST_FOREACH(string strAllow, vAllow) + if (WildcardMatch(strAddress, strAllow)) + return true; + return false; +} + +// +// IOStream device that speaks SSL but can also speak non-SSL +// +template +class SSLIOStreamDevice : public iostreams::device { +public: + SSLIOStreamDevice(asio::ssl::stream &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; + SSLIOStreamDevice& operator=(SSLIOStreamDevice const&); + asio::ssl::stream& stream; +}; + +class AcceptedConnection +{ +public: + virtual ~AcceptedConnection() {} + + virtual std::iostream& stream() = 0; + virtual std::string peer_address_to_string() const = 0; + virtual void close() = 0; +}; + +template +class AcceptedConnectionImpl : public AcceptedConnection +{ +public: + AcceptedConnectionImpl( + asio::io_service& io_service, + ssl::context &context, + bool fUseSSL) : + sslStream(io_service, context), + _d(sslStream, fUseSSL), + _stream(_d) + { + } + + virtual std::iostream& stream() + { + return _stream; + } + + virtual std::string peer_address_to_string() const + { + return peer.address().to_string(); + } + + virtual void close() + { + _stream.close(); + } + + typename Protocol::endpoint peer; + asio::ssl::stream sslStream; + +private: + SSLIOStreamDevice _d; + iostreams::stream< SSLIOStreamDevice > _stream; +}; + +void ThreadRPCServer(void* parg) +{ + // Make this thread recognisable as the RPC listener + RenameThread("XP-rpclist"); + + try + { + vnThreadsRunning[THREAD_RPCLISTENER]++; + ThreadRPCServer2(parg); + vnThreadsRunning[THREAD_RPCLISTENER]--; + } + catch (std::exception& e) { + vnThreadsRunning[THREAD_RPCLISTENER]--; + PrintException(&e, "ThreadRPCServer()"); + } catch (...) { + vnThreadsRunning[THREAD_RPCLISTENER]--; + PrintException(NULL, "ThreadRPCServer()"); + } + printf("ThreadRPCServer exited\n"); +} + +// Forward declaration required for RPCListen +template +static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor > acceptor, + ssl::context& context, + bool fUseSSL, + AcceptedConnection* conn, + const boost::system::error_code& error); + +/** + * Sets up I/O resources to accept and handle a new connection. + */ +template +static void RPCListen(boost::shared_ptr< basic_socket_acceptor > acceptor, + ssl::context& context, + const bool fUseSSL) +{ + // Accept connection + AcceptedConnectionImpl* conn = new AcceptedConnectionImpl(acceptor->get_io_service(), context, fUseSSL); + + acceptor->async_accept( + conn->sslStream.lowest_layer(), + conn->peer, + boost::bind(&RPCAcceptHandler, + acceptor, + boost::ref(context), + fUseSSL, + conn, + boost::asio::placeholders::error)); +} + +/** + * Accept and handle incoming connection. + */ +template +static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor > acceptor, + ssl::context& context, + const bool fUseSSL, + AcceptedConnection* conn, + const boost::system::error_code& error) +{ + vnThreadsRunning[THREAD_RPCLISTENER]++; + + // Immediately start accepting new connections, except when we're cancelled or our socket is closed. + if (error != asio::error::operation_aborted + && acceptor->is_open()) + RPCListen(acceptor, context, fUseSSL); + + AcceptedConnectionImpl* tcp_conn = dynamic_cast< AcceptedConnectionImpl* >(conn); + + // TODO: Actually handle errors + if (error) + { + delete conn; + } + + // Restrict callers by IP. It is important to + // do this before starting client thread, to filter out + // certain DoS and misbehaving clients. + else if (tcp_conn + && !ClientAllowed(tcp_conn->peer.address())) + { + // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake. + if (!fUseSSL) + conn->stream() << HTTPReply(HTTP_FORBIDDEN, "", false) << std::flush; + delete conn; + } + + // start HTTP client thread + else if (!NewThread(ThreadRPCServer3, conn)) { + printf("Failed to create RPC server client thread\n"); + delete conn; + } + + vnThreadsRunning[THREAD_RPCLISTENER]--; +} + +void ThreadRPCServer2(void* parg) +{ + printf("ThreadRPCServer started\n"); + + strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]; + if (mapArgs["-rpcpassword"] == "") + { + unsigned char rand_pwd[32]; + RAND_bytes(rand_pwd, 32); + string strWhatAmI = "To use XPd"; + 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\""); + uiInterface.ThreadSafeMessageBox(strprintf( + _("%s, you must set a rpcpassword in the configuration file:\n %s\n" + "It is recommended you use the following random password:\n" + "rpcuser=XPrpc\n" + "rpcpassword=%s\n" + "(you do not need to remember this password)\n" + "If the file does not exist, create it with owner-readable-only file permissions.\n"), + strWhatAmI.c_str(), + GetConfigFile().string().c_str(), + EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str()), + _("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL); + StartShutdown(); + return; + } + + const bool fUseSSL = GetBoolArg("-rpcssl"); + + asio::io_service io_service; + + ssl::context context(io_service, ssl::context::sslv23); + if (fUseSSL) + { + context.set_options(ssl::context::no_sslv2); + + filesystem::path pathCertFile(GetArg("-rpcsslcertificatechainfile", "server.cert")); + if (!pathCertFile.is_complete()) pathCertFile = filesystem::path(GetDataDir()) / pathCertFile; + if (filesystem::exists(pathCertFile)) context.use_certificate_chain_file(pathCertFile.string()); + else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile.string().c_str()); + + filesystem::path pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem")); + if (!pathPKFile.is_complete()) pathPKFile = filesystem::path(GetDataDir()) / pathPKFile; + if (filesystem::exists(pathPKFile)) context.use_private_key_file(pathPKFile.string(), ssl::context::pem); + else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile.string().c_str()); + + string strCiphers = GetArg("-rpcsslciphers", "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH"); + SSL_CTX_set_cipher_list(context.impl(), strCiphers.c_str()); + } + + // Try a dual IPv6/IPv4 socket, falling back to separate IPv4 and IPv6 sockets + const bool loopback = !mapArgs.count("-rpcallowip"); + asio::ip::address bindAddress = loopback ? asio::ip::address_v6::loopback() : asio::ip::address_v6::any(); + ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", GetDefaultRPCPort())); + boost::system::error_code v6_only_error; + boost::shared_ptr acceptor(new ip::tcp::acceptor(io_service)); + + boost::signals2::signal StopRequests; + + bool fListening = false; + std::string strerr; + try + { + acceptor->open(endpoint.protocol()); + acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); + + // Try making the socket dual IPv6/IPv4 (if listening on the "any" address) + acceptor->set_option(boost::asio::ip::v6_only(loopback), v6_only_error); + + acceptor->bind(endpoint); + acceptor->listen(socket_base::max_connections); + + RPCListen(acceptor, context, fUseSSL); + // Cancel outstanding listen-requests for this acceptor when shutting down + StopRequests.connect(signals2::slot( + static_cast(&ip::tcp::acceptor::close), acceptor.get()) + .track(acceptor)); + + fListening = true; + } + catch(boost::system::system_error &e) + { + strerr = strprintf(_("An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s"), endpoint.port(), e.what()); + } + + try { + // If dual IPv6/IPv4 failed (or we're opening loopback interfaces only), open IPv4 separately + if (!fListening || loopback || v6_only_error) + { + bindAddress = loopback ? asio::ip::address_v4::loopback() : asio::ip::address_v4::any(); + endpoint.address(bindAddress); + + acceptor.reset(new ip::tcp::acceptor(io_service)); + acceptor->open(endpoint.protocol()); + acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); + acceptor->bind(endpoint); + acceptor->listen(socket_base::max_connections); + + RPCListen(acceptor, context, fUseSSL); + // Cancel outstanding listen-requests for this acceptor when shutting down + StopRequests.connect(signals2::slot( + static_cast(&ip::tcp::acceptor::close), acceptor.get()) + .track(acceptor)); + + fListening = true; + } + } + catch(boost::system::system_error &e) + { + strerr = strprintf(_("An error occurred while setting up the RPC port %u for listening on IPv4: %s"), endpoint.port(), e.what()); + } + + if (!fListening) { + uiInterface.ThreadSafeMessageBox(strerr, _("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL); + StartShutdown(); + return; + } + + vnThreadsRunning[THREAD_RPCLISTENER]--; + while (!fShutdown) + io_service.run_one(); + vnThreadsRunning[THREAD_RPCLISTENER]++; + StopRequests(); +} + +class JSONRequest +{ +public: + Value id; + string strMethod; + Array params; + + JSONRequest() { id = Value::null; } + void parse(const Value& valRequest); +}; + +void JSONRequest::parse(const Value& valRequest) +{ + // Parse request + if (valRequest.type() != obj_type) + throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object"); + 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(RPC_INVALID_REQUEST, "Missing method"); + if (valMethod.type() != str_type) + throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string"); + strMethod = valMethod.get_str(); + if (strMethod != "getwork" && strMethod != "getblocktemplate") + printf("ThreadRPCServer method=%s\n", strMethod.c_str()); + + // Parse params + Value valParams = find_value(request, "params"); + if (valParams.type() == array_type) + params = valParams.get_array(); + else if (valParams.type() == null_type) + params = Array(); + else + throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array"); +} + +static Object JSONRPCExecOne(const Value& req) +{ + Object rpc_result; + + JSONRequest jreq; + try { + jreq.parse(req); + + Value result = tableRPC.execute(jreq.strMethod, jreq.params); + rpc_result = JSONRPCReplyObj(result, Value::null, jreq.id); + } + catch (Object& objError) + { + rpc_result = JSONRPCReplyObj(Value::null, objError, jreq.id); + } + catch (std::exception& e) + { + rpc_result = JSONRPCReplyObj(Value::null, + JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); + } + + return rpc_result; +} + +static string JSONRPCExecBatch(const Array& vReq) +{ + Array ret; + for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++) + ret.push_back(JSONRPCExecOne(vReq[reqIdx])); + + return write_string(Value(ret), false) + "\n"; +} + +static CCriticalSection cs_THREAD_RPCHANDLER; + +void ThreadRPCServer3(void* parg) +{ + // Make this thread recognisable as the RPC handler + RenameThread("XP-rpchand"); + + { + LOCK(cs_THREAD_RPCHANDLER); + vnThreadsRunning[THREAD_RPCHANDLER]++; + } + AcceptedConnection *conn = (AcceptedConnection *) parg; + + bool fRun = true; + while (true) + { + if (fShutdown || !fRun) + { + conn->close(); + delete conn; + { + LOCK(cs_THREAD_RPCHANDLER); + --vnThreadsRunning[THREAD_RPCHANDLER]; + } + return; + } + map mapHeaders; + string strRequest; + + ReadHTTP(conn->stream(), mapHeaders, strRequest); + + // Check authorization + if (mapHeaders.count("authorization") == 0) + { + conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush; + break; + } + if (!HTTPAuthorized(mapHeaders)) + { + printf("ThreadRPCServer incorrect password attempt from %s\n", conn->peer_address_to_string().c_str()); + /* Deter brute-forcing short passwords. + If this results in a DOS the user really + shouldn't have their RPC port exposed.*/ + if (mapArgs["-rpcpassword"].size() < 20) + Sleep(250); + + conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush; + break; + } + if (mapHeaders["connection"] == "close") + fRun = false; + + JSONRequest jreq; + try + { + // Parse request + Value valRequest; + if (!read_string(strRequest, valRequest)) + throw JSONRPCError(RPC_PARSE_ERROR, "Parse error"); + + string strReply; + + // singleton request + if (valRequest.type() == obj_type) { + jreq.parse(valRequest); + + Value result = tableRPC.execute(jreq.strMethod, jreq.params); + + // Send reply + strReply = JSONRPCReply(result, Value::null, jreq.id); + + // array of requests + } else if (valRequest.type() == array_type) + strReply = JSONRPCExecBatch(valRequest.get_array()); + else + throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error"); + + conn->stream() << HTTPReply(HTTP_OK, strReply, fRun) << std::flush; + } + catch (Object& objError) + { + ErrorReply(conn->stream(), objError, jreq.id); + break; + } + catch (std::exception& e) + { + ErrorReply(conn->stream(), JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); + break; + } + } + + delete conn; + { + LOCK(cs_THREAD_RPCHANDLER); + vnThreadsRunning[THREAD_RPCHANDLER]--; + } +} + +json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_spirit::Array ¶ms) const +{ + // Find method + const CRPCCommand *pcmd = tableRPC[strMethod]; + if (!pcmd) + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found"); + + // Observe safe mode + string strWarning = GetWarnings("rpc"); + if (strWarning != "" && !GetBoolArg("-disablesafemode") && + !pcmd->okSafeMode) + throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, string("Safe mode: ") + strWarning); + + try + { + // Execute + Value result; + { + if (pcmd->unlocked) + result = pcmd->actor(params, false); + else { + LOCK2(cs_main, pwalletMain->cs_wallet); + result = pcmd->actor(params, false); + } + } + return result; + } + catch (std::exception& e) + { + throw JSONRPCError(RPC_MISC_ERROR, e.what()); + } +} + + +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().string().c_str())); + + // Connect to localhost + bool fUseSSL = GetBoolArg("-rpcssl"); + asio::io_service io_service; + ssl::context context(io_service, ssl::context::sslv23); + context.set_options(ssl::context::no_sslv2); + asio::ssl::stream sslStream(io_service, context); + SSLIOStreamDevice d(sslStream, fUseSSL); + iostreams::stream< SSLIOStreamDevice > stream(d); + if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(GetDefaultRPCPort())))) + throw runtime_error("couldn't connect to server"); + + // 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 == HTTP_UNAUTHORIZED) + throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); + else if (nStatus >= 400 && nStatus != HTTP_BAD_REQUEST && nStatus != HTTP_NOT_FOUND && nStatus != HTTP_INTERNAL_SERVER_ERROR) + 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, bool fAllowNull=false) +{ + if (fAllowNull && value.type() == null_type) + return; + if (value.type() == str_type) + { + // reinterpret string as unquoted json value + Value value2; + string strJSON = value.get_str(); + if (!read_string(strJSON, value2)) + throw runtime_error(string("Error parsing JSON:")+strJSON); + ConvertTo(value2, fAllowNull); + value = value2; + } + else + { + value = value.get_value(); + } +} + +// Convert strings to command-specific RPC representation +Array RPCConvertValues(const std::string &strMethod, const std::vector &strParams) +{ + Array params; + BOOST_FOREACH(const std::string ¶m, strParams) + params.push_back(param); + + size_t n = params.size(); + + // + // Special case non-string parameter types + // + if (strMethod == "stop" && n > 0) ConvertTo(params[0]); + if (strMethod == "getaddednodeinfo" && n > 0) ConvertTo(params[0]); + if (strMethod == "sendtoaddress" && n > 1) ConvertTo(params[1]); + if (strMethod == "mergecoins" && n > 0) ConvertTo(params[0]); + if (strMethod == "mergecoins" && n > 1) ConvertTo(params[1]); + if (strMethod == "mergecoins" && n > 2) ConvertTo(params[2]); + if (strMethod == "settxfee" && n > 0) ConvertTo(params[0]); + if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo(params[1]); + if (strMethod == "getreceivedbyaccount" && 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 == "getbalance" && n > 1) ConvertTo(params[1]); + if (strMethod == "getblock" && n > 1) ConvertTo(params[1]); + if (strMethod == "getblockbynumber" && n > 0) ConvertTo(params[0]); + if (strMethod == "dumpblockbynumber" && n > 0) ConvertTo(params[0]); + if (strMethod == "getblockbynumber" && n > 1) ConvertTo(params[1]); + if (strMethod == "getblockhash" && n > 0) ConvertTo(params[0]); + 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 == "walletpassphrase" && n > 1) ConvertTo(params[1]); + if (strMethod == "walletpassphrase" && n > 2) ConvertTo(params[2]); + if (strMethod == "getblocktemplate" && n > 0) ConvertTo(params[0]); + if (strMethod == "listsinceblock" && n > 1) ConvertTo(params[1]); + + if (strMethod == "scaninput" && n > 0) ConvertTo(params[0]); + + if (strMethod == "sendalert" && n > 2) ConvertTo(params[2]); + if (strMethod == "sendalert" && n > 3) ConvertTo(params[3]); + if (strMethod == "sendalert" && n > 4) ConvertTo(params[4]); + if (strMethod == "sendalert" && n > 5) ConvertTo(params[5]); + if (strMethod == "sendalert" && n > 6) ConvertTo(params[6]); + + if (strMethod == "sendmany" && n > 1) ConvertTo(params[1]); + if (strMethod == "sendmany" && n > 2) ConvertTo(params[2]); + if (strMethod == "reservebalance" && n > 0) ConvertTo(params[0]); + if (strMethod == "reservebalance" && n > 1) ConvertTo(params[1]); + if (strMethod == "addmultisigaddress" && n > 0) ConvertTo(params[0]); + if (strMethod == "addmultisigaddress" && n > 1) ConvertTo(params[1]); + if (strMethod == "listunspent" && n > 0) ConvertTo(params[0]); + if (strMethod == "listunspent" && n > 1) ConvertTo(params[1]); + if (strMethod == "listunspent" && n > 2) ConvertTo(params[2]); + if (strMethod == "getrawtransaction" && n > 1) ConvertTo(params[1]); + if (strMethod == "createrawtransaction" && n > 0) ConvertTo(params[0]); + if (strMethod == "createrawtransaction" && n > 1) ConvertTo(params[1]); + if (strMethod == "createmultisig" && n > 0) ConvertTo(params[0]); + if (strMethod == "createmultisig" && n > 1) ConvertTo(params[1]); + if (strMethod == "signrawtransaction" && n > 1) ConvertTo(params[1], true); + if (strMethod == "signrawtransaction" && n > 2) ConvertTo(params[2], true); + if (strMethod == "keypoolrefill" && n > 0) ConvertTo(params[0]); + if (strMethod == "keypoolreset" && n > 0) ConvertTo(params[0]); + if (strMethod == "importaddress" && n > 2) ConvertTo(params[2]); + + return params; +} + +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 + std::vector strParams(&argv[2], &argv[argc]); + Array params = RPCConvertValues(strMethod, strParams); + + // Execute + Object reply = CallRPC(strMethod, params); + + // Parse reply + const Value& result = find_value(reply, "result"); + const Value& error = find_value(reply, "error"); + + 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 != "") + { + fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str()); + } + 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 + +const CRPCTable tableRPC; diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h new file mode 100644 index 00000000..ad64a845 --- /dev/null +++ b/src/bitcoinrpc.h @@ -0,0 +1,231 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef _BITCOINRPC_H_ +#define _BITCOINRPC_H_ 1 + +#include +#include +#include + +class CBlockIndex; + +#include "json/json_spirit_reader_template.h" +#include "json/json_spirit_writer_template.h" +#include "json/json_spirit_utils.h" + +#include "util.h" +#include "checkpoints.h" + +// HTTP status codes +enum HTTPStatusCode +{ + HTTP_OK = 200, + HTTP_BAD_REQUEST = 400, + HTTP_UNAUTHORIZED = 401, + HTTP_FORBIDDEN = 403, + HTTP_NOT_FOUND = 404, + HTTP_INTERNAL_SERVER_ERROR = 500, +}; + +// Bitcoin RPC error codes +enum RPCErrorCode +{ + // Standard JSON-RPC 2.0 errors + RPC_INVALID_REQUEST = -32600, + RPC_METHOD_NOT_FOUND = -32601, + RPC_INVALID_PARAMS = -32602, + RPC_INTERNAL_ERROR = -32603, + RPC_PARSE_ERROR = -32700, + + // General application defined errors + RPC_MISC_ERROR = -1, // std::exception thrown in command handling + RPC_FORBIDDEN_BY_SAFE_MODE = -2, // Server is in safe mode, and command is not allowed in safe mode + RPC_TYPE_ERROR = -3, // Unexpected type was passed as parameter + RPC_INVALID_ADDRESS_OR_KEY = -5, // Invalid address or key + RPC_OUT_OF_MEMORY = -7, // Ran out of memory during operation + RPC_INVALID_PARAMETER = -8, // Invalid, missing or duplicate parameter + RPC_DATABASE_ERROR = -20, // Database error + RPC_DESERIALIZATION_ERROR = -22, // Error parsing or validating structure in raw format + + // P2P client errors + RPC_CLIENT_NOT_CONNECTED = -9, // Bitcoin is not connected + RPC_CLIENT_IN_INITIAL_DOWNLOAD = -10, // Still downloading initial blocks + + // Wallet errors + RPC_WALLET_ERROR = -4, // Unspecified problem with wallet (key not found etc.) + RPC_WALLET_INSUFFICIENT_FUNDS = -6, // Not enough funds in wallet or account + RPC_WALLET_INVALID_ACCOUNT_NAME = -11, // Invalid account name + RPC_WALLET_KEYPOOL_RAN_OUT = -12, // Keypool ran out, call keypoolrefill first + RPC_WALLET_UNLOCK_NEEDED = -13, // Enter the wallet passphrase with walletpassphrase first + RPC_WALLET_PASSPHRASE_INCORRECT = -14, // The wallet passphrase entered was incorrect + RPC_WALLET_WRONG_ENC_STATE = -15, // Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.) + RPC_WALLET_ENCRYPTION_FAILED = -16, // Failed to encrypt the wallet + RPC_WALLET_ALREADY_UNLOCKED = -17, // Wallet is already unlocked +}; + +json_spirit::Object JSONRPCError(int code, const std::string& message); + +void ThreadRPCServer(void* parg); +int CommandLineRPC(int argc, char *argv[]); + +/** Convert parameter values for RPC call from strings to command-specific JSON objects. */ +json_spirit::Array RPCConvertValues(const std::string &strMethod, const std::vector &strParams); + +/* + Type-check arguments; throws JSONRPCError if wrong type given. Does not check that + the right number of arguments are passed, just that any passed are the correct type. + Use like: RPCTypeCheck(params, boost::assign::list_of(str_type)(int_type)(obj_type)); +*/ +void RPCTypeCheck(const json_spirit::Array& params, + const std::list& typesExpected, bool fAllowNull=false); +/* + Check for expected keys/value types in an Object. + Use like: RPCTypeCheck(object, boost::assign::map_list_of("name", str_type)("value", int_type)); +*/ +void RPCTypeCheck(const json_spirit::Object& o, + const std::map& typesExpected, bool fAllowNull=false); + +typedef json_spirit::Value(*rpcfn_type)(const json_spirit::Array& params, bool fHelp); + +class CRPCCommand +{ +public: + std::string name; + rpcfn_type actor; + bool okSafeMode; + bool unlocked; +}; + +/** + * Bitcoin RPC command dispatcher. + */ +class CRPCTable +{ +private: + std::map mapCommands; +public: + CRPCTable(); + const CRPCCommand* operator[](std::string name) const; + std::string help(std::string name) const; + + /** + * Execute a method. + * @param method Method to execute + * @param params Array of arguments (JSON objects) + * @returns Result of the call. + * @throws an exception (json_spirit::Value) when an error happens. + */ + json_spirit::Value execute(const std::string &method, const json_spirit::Array ¶ms) const; +}; + +extern const CRPCTable tableRPC; + +extern int64_t nWalletUnlockTime; +extern int64_t AmountFromValue(const json_spirit::Value& value); +extern json_spirit::Value ValueFromAmount(int64_t amount); +extern double GetDifficulty(const CBlockIndex* blockindex = NULL); + +extern double GetPoWMHashPS(); +extern double GetPoSKernelPS(); + +extern std::string HexBits(unsigned int nBits); +extern std::string HelpRequiringPassphrase(); +extern void EnsureWalletIsUnlocked(); + +// +// Utilities: convert hex-encoded Values +// (throws error if not hex). +// +extern uint256 ParseHashV(const json_spirit::Value& v, std::string strName); +extern uint256 ParseHashO(const json_spirit::Object& o, std::string strKey); +extern std::vector ParseHexV(const json_spirit::Value& v, std::string strName); +extern std::vector ParseHexO(const json_spirit::Object& o, std::string strKey); + +extern json_spirit::Value getconnectioncount(const json_spirit::Array& params, bool fHelp); // in rpcnet.cpp +extern json_spirit::Value getpeerinfo(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getaddrmaninfo(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value addnode(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getaddednodeinfo(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value dumpwallet(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value importwallet(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value dumpprivkey(const json_spirit::Array& params, bool fHelp); // in rpcdump.cpp +extern json_spirit::Value importprivkey(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value importaddress(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value removeaddress(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getnettotals(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value ntptime(const json_spirit::Array& params, bool fHelp); + +extern json_spirit::Value sendalert(const json_spirit::Array& params, bool fHelp); + +extern json_spirit::Value getsubsidy(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getmininginfo(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value scaninput(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getwork(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getworkex(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getblocktemplate(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value submitblock(const json_spirit::Array& params, bool fHelp); + +extern json_spirit::Value getnewaddress(const json_spirit::Array& params, bool fHelp); // in rpcwallet.cpp +extern json_spirit::Value getaccountaddress(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value setaccount(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getaccount(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getaddressesbyaccount(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value sendtoaddress(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value signmessage(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value verifymessage(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getreceivedbyaddress(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getreceivedbyaccount(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getbalance(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value movecmd(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value sendfrom(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value sendmany(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value addmultisigaddress(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value addredeemscript(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value listreceivedbyaddress(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value listreceivedbyaccount(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value listtransactions(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value listaddressgroupings(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value listaccounts(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value listsinceblock(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value gettransaction(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value backupwallet(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value keypoolrefill(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value keypoolreset(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value walletpassphrase(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value walletpassphrasechange(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value walletlock(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value encryptwallet(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value validateaddress(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getinfo(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value reservebalance(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value checkwallet(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value repairwallet(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value resendtx(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value makekeypair(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value mergecoins(const json_spirit::Array& params, bool fHelp); + +extern json_spirit::Value getrawtransaction(const json_spirit::Array& params, bool fHelp); // in rcprawtransaction.cpp +extern json_spirit::Value listunspent(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value createrawtransaction(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value decoderawtransaction(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value createmultisig(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value decodescript(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value signrawtransaction(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value sendrawtransaction(const json_spirit::Array& params, bool fHelp); + +extern json_spirit::Value getbestblockhash(const json_spirit::Array& params, bool fHelp); // in rpcblockchain.cpp +extern json_spirit::Value getblockcount(const json_spirit::Array& params, bool fHelp); // in rpcblockchain.cpp +extern json_spirit::Value getdifficulty(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value settxfee(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getrawmempool(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getblockhash(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getblock(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getblockbynumber(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value dumpblock(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value dumpblockbynumber(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getcheckpoint(const json_spirit::Array& params, bool fHelp); + +#endif diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp new file mode 100644 index 00000000..618f3afe --- /dev/null +++ b/src/checkpoints.cpp @@ -0,0 +1,442 @@ +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include // for 'map_list_of()' +#include + +#include "checkpoints.h" + +#include "txdb.h" +#include "main.h" +#include "uint256.h" + +namespace Checkpoints +{ + typedef std::map > MapCheckpoints; + + // + // What makes a good checkpoint block? + // + Is surrounded by blocks with reasonable timestamps + // (no blocks before with a timestamp after, none after with + // timestamp before) + // + Contains no strange transactions + // + static MapCheckpoints mapCheckpoints = + boost::assign::map_list_of + ( 0, std::make_pair(hashGenesisBlock, 1360105017) ) + ( 1, std::make_pair(uint256("0x00000551af8b05828ce8bfd2c7fc753935ac90408bbc41589e5ffa38bd49490e"), 1464674052) ) + ; + + // TestNet has no checkpoints + static MapCheckpoints mapCheckpointsTestnet = + boost::assign::map_list_of + ( 0, std::make_pair(hashGenesisBlockTestNet, 1360105017) ) + ; + + bool CheckHardened(int nHeight, const uint256& hash) + { + MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints); + + MapCheckpoints::const_iterator i = checkpoints.find(nHeight); + if (i == checkpoints.end()) return true; + return hash == i->second.first; + } + + int GetTotalBlocksEstimate() + { + MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints); + + return checkpoints.rbegin()->first; + } + + unsigned int GetLastCheckpointTime() + { + MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints); + + return checkpoints.rbegin()->second.second; + } + + CBlockIndex* GetLastCheckpoint(const std::map& mapBlockIndex) + { + MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints); + + BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, checkpoints) + { + const uint256& hash = i.second.first; + std::map::const_iterator t = mapBlockIndex.find(hash); + if (t != mapBlockIndex.end()) + return t->second; + } + return NULL; + } + + // ppcoin: synchronized checkpoint (centrally broadcasted) + uint256 hashSyncCheckpoint = 0; + uint256 hashPendingCheckpoint = 0; + CSyncCheckpoint checkpointMessage; + CSyncCheckpoint checkpointMessagePending; + uint256 hashInvalidCheckpoint = 0; + CCriticalSection cs_hashSyncCheckpoint; + + // ppcoin: get last synchronized checkpoint + CBlockIndex* GetLastSyncCheckpoint() + { + LOCK(cs_hashSyncCheckpoint); + if (!mapBlockIndex.count(hashSyncCheckpoint)) + error("GetSyncCheckpoint: block index missing for current sync-checkpoint %s", hashSyncCheckpoint.ToString().c_str()); + else + return mapBlockIndex[hashSyncCheckpoint]; + return NULL; + } + + // ppcoin: only descendant of current sync-checkpoint is allowed + bool ValidateSyncCheckpoint(uint256 hashCheckpoint) + { + if (!mapBlockIndex.count(hashSyncCheckpoint)) + return error("ValidateSyncCheckpoint: block index missing for current sync-checkpoint %s", hashSyncCheckpoint.ToString().c_str()); + if (!mapBlockIndex.count(hashCheckpoint)) + return error("ValidateSyncCheckpoint: block index missing for received sync-checkpoint %s", hashCheckpoint.ToString().c_str()); + + CBlockIndex* pindexSyncCheckpoint = mapBlockIndex[hashSyncCheckpoint]; + CBlockIndex* pindexCheckpointRecv = mapBlockIndex[hashCheckpoint]; + + if (pindexCheckpointRecv->nHeight <= pindexSyncCheckpoint->nHeight) + { + // Received an older checkpoint, trace back from current checkpoint + // to the same height of the received checkpoint to verify + // that current checkpoint should be a descendant block + CBlockIndex* pindex = pindexSyncCheckpoint; + while (pindex->nHeight > pindexCheckpointRecv->nHeight) + if (!(pindex = pindex->pprev)) + return error("ValidateSyncCheckpoint: pprev null - block index structure failure"); + if (pindex->GetBlockHash() != hashCheckpoint) + { + hashInvalidCheckpoint = hashCheckpoint; + return error("ValidateSyncCheckpoint: new sync-checkpoint %s is conflicting with current sync-checkpoint %s", hashCheckpoint.ToString().c_str(), hashSyncCheckpoint.ToString().c_str()); + } + return false; // ignore older checkpoint + } + + // Received checkpoint should be a descendant block of the current + // checkpoint. Trace back to the same height of current checkpoint + // to verify. + CBlockIndex* pindex = pindexCheckpointRecv; + while (pindex->nHeight > pindexSyncCheckpoint->nHeight) + if (!(pindex = pindex->pprev)) + return error("ValidateSyncCheckpoint: pprev2 null - block index structure failure"); + if (pindex->GetBlockHash() != hashSyncCheckpoint) + { + hashInvalidCheckpoint = hashCheckpoint; + return error("ValidateSyncCheckpoint: new sync-checkpoint %s is not a descendant of current sync-checkpoint %s", hashCheckpoint.ToString().c_str(), hashSyncCheckpoint.ToString().c_str()); + } + return true; + } + + bool WriteSyncCheckpoint(const uint256& hashCheckpoint) + { + CTxDB txdb; + txdb.TxnBegin(); + if (!txdb.WriteSyncCheckpoint(hashCheckpoint)) + { + txdb.TxnAbort(); + return error("WriteSyncCheckpoint(): failed to write to db sync checkpoint %s", hashCheckpoint.ToString().c_str()); + } + if (!txdb.TxnCommit()) + return error("WriteSyncCheckpoint(): failed to commit to db sync checkpoint %s", hashCheckpoint.ToString().c_str()); + +#ifndef USE_LEVELDB + txdb.Close(); +#endif + + Checkpoints::hashSyncCheckpoint = hashCheckpoint; + return true; + } + + bool AcceptPendingSyncCheckpoint() + { + LOCK(cs_hashSyncCheckpoint); + if (hashPendingCheckpoint != 0 && mapBlockIndex.count(hashPendingCheckpoint)) + { + if (!ValidateSyncCheckpoint(hashPendingCheckpoint)) + { + hashPendingCheckpoint = 0; + checkpointMessagePending.SetNull(); + return false; + } + + CTxDB txdb; + CBlockIndex* pindexCheckpoint = mapBlockIndex[hashPendingCheckpoint]; + if (!pindexCheckpoint->IsInMainChain()) + { + CBlock block; + if (!block.ReadFromDisk(pindexCheckpoint)) + return error("AcceptPendingSyncCheckpoint: ReadFromDisk failed for sync checkpoint %s", hashPendingCheckpoint.ToString().c_str()); + if (!block.SetBestChain(txdb, pindexCheckpoint)) + { + hashInvalidCheckpoint = hashPendingCheckpoint; + return error("AcceptPendingSyncCheckpoint: SetBestChain failed for sync checkpoint %s", hashPendingCheckpoint.ToString().c_str()); + } + } + +#ifndef USE_LEVELDB + txdb.Close(); +#endif + if (!WriteSyncCheckpoint(hashPendingCheckpoint)) + return error("AcceptPendingSyncCheckpoint(): failed to write sync checkpoint %s", hashPendingCheckpoint.ToString().c_str()); + hashPendingCheckpoint = 0; + checkpointMessage = checkpointMessagePending; + checkpointMessagePending.SetNull(); + printf("AcceptPendingSyncCheckpoint : sync-checkpoint at %s\n", hashSyncCheckpoint.ToString().c_str()); + // relay the checkpoint + if (!checkpointMessage.IsNull()) + { + BOOST_FOREACH(CNode* pnode, vNodes) + checkpointMessage.RelayTo(pnode); + } + return true; + } + return false; + } + + // Automatically select a suitable sync-checkpoint + uint256 AutoSelectSyncCheckpoint() + { + const CBlockIndex *pindex = pindexBest; + // Search backward for a block within max span and maturity window + while (pindex->pprev && (pindex->GetBlockTime() + CHECKPOINT_MAX_SPAN > pindexBest->GetBlockTime() || pindex->nHeight + 8 > pindexBest->nHeight)) + pindex = pindex->pprev; + return pindex->GetBlockHash(); + } + + // Check against synchronized checkpoint + bool CheckSync(const uint256& hashBlock, const CBlockIndex* pindexPrev) + { + if (fTestNet) return true; // Testnet has no checkpoints + int nHeight = pindexPrev->nHeight + 1; + + LOCK(cs_hashSyncCheckpoint); + // sync-checkpoint should always be accepted block + assert(mapBlockIndex.count(hashSyncCheckpoint)); + const CBlockIndex* pindexSync = mapBlockIndex[hashSyncCheckpoint]; + + if (nHeight > pindexSync->nHeight) + { + // trace back to same height as sync-checkpoint + const CBlockIndex* pindex = pindexPrev; + while (pindex->nHeight > pindexSync->nHeight) + if (!(pindex = pindex->pprev)) + return error("CheckSync: pprev null - block index structure failure"); + if (pindex->nHeight < pindexSync->nHeight || pindex->GetBlockHash() != hashSyncCheckpoint) + return false; // only descendant of sync-checkpoint can pass check + } + if (nHeight == pindexSync->nHeight && hashBlock != hashSyncCheckpoint) + return false; // same height with sync-checkpoint + if (nHeight < pindexSync->nHeight && !mapBlockIndex.count(hashBlock)) + return false; // lower height than sync-checkpoint + return true; + } + + bool WantedByPendingSyncCheckpoint(uint256 hashBlock) + { + LOCK(cs_hashSyncCheckpoint); + if (hashPendingCheckpoint == 0) + return false; + if (hashBlock == hashPendingCheckpoint) + return true; + if (mapOrphanBlocks.count(hashPendingCheckpoint) + && hashBlock == WantedByOrphan(mapOrphanBlocks[hashPendingCheckpoint])) + return true; + return false; + } + + // ppcoin: reset synchronized checkpoint to last hardened checkpoint + bool ResetSyncCheckpoint() + { + LOCK(cs_hashSyncCheckpoint); + const uint256& hash = mapCheckpoints.rbegin()->second.first; + if (mapBlockIndex.count(hash) && !mapBlockIndex[hash]->IsInMainChain()) + { + // checkpoint block accepted but not yet in main chain + printf("ResetSyncCheckpoint: SetBestChain to hardened checkpoint %s\n", hash.ToString().c_str()); + CTxDB txdb; + CBlock block; + if (!block.ReadFromDisk(mapBlockIndex[hash])) + return error("ResetSyncCheckpoint: ReadFromDisk failed for hardened checkpoint %s", hash.ToString().c_str()); + if (!block.SetBestChain(txdb, mapBlockIndex[hash])) + { + return error("ResetSyncCheckpoint: SetBestChain failed for hardened checkpoint %s", hash.ToString().c_str()); + } + +#ifndef USE_LEVELDB + txdb.Close(); +#endif + + } + else if(!mapBlockIndex.count(hash)) + { + // checkpoint block not yet accepted + hashPendingCheckpoint = hash; + checkpointMessagePending.SetNull(); + printf("ResetSyncCheckpoint: pending for sync-checkpoint %s\n", hashPendingCheckpoint.ToString().c_str()); + } + + BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, mapCheckpoints) + { + const uint256& hash = i.second.first; + if (mapBlockIndex.count(hash) && mapBlockIndex[hash]->IsInMainChain()) + { + if (!WriteSyncCheckpoint(hash)) + return error("ResetSyncCheckpoint: failed to write sync checkpoint %s", hash.ToString().c_str()); + printf("ResetSyncCheckpoint: sync-checkpoint reset to %s\n", hashSyncCheckpoint.ToString().c_str()); + return true; + } + } + + return false; + } + + void AskForPendingSyncCheckpoint(CNode* pfrom) + { + LOCK(cs_hashSyncCheckpoint); + if (pfrom && hashPendingCheckpoint != 0 && (!mapBlockIndex.count(hashPendingCheckpoint)) && (!mapOrphanBlocks.count(hashPendingCheckpoint))) + pfrom->AskFor(CInv(MSG_BLOCK, hashPendingCheckpoint)); + } + + bool SetCheckpointPrivKey(std::string strPrivKey) + { + // Test signing a sync-checkpoint with genesis block + CSyncCheckpoint checkpoint; + checkpoint.hashCheckpoint = !fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet; + CDataStream sMsg(SER_NETWORK, PROTOCOL_VERSION); + sMsg << (CUnsignedSyncCheckpoint)checkpoint; + checkpoint.vchMsg = std::vector(sMsg.begin(), sMsg.end()); + + std::vector vchPrivKey = ParseHex(strPrivKey); + CKey key; + key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash + if (!key.Sign(Hash(checkpoint.vchMsg.begin(), checkpoint.vchMsg.end()), checkpoint.vchSig)) + return false; + + // Test signing successful, proceed + CSyncCheckpoint::strMasterPrivKey = strPrivKey; + return true; + } + + bool SendSyncCheckpoint(uint256 hashCheckpoint) + { + CSyncCheckpoint checkpoint; + checkpoint.hashCheckpoint = hashCheckpoint; + CDataStream sMsg(SER_NETWORK, PROTOCOL_VERSION); + sMsg << (CUnsignedSyncCheckpoint)checkpoint; + checkpoint.vchMsg = std::vector(sMsg.begin(), sMsg.end()); + + if (CSyncCheckpoint::strMasterPrivKey.empty()) + return error("SendSyncCheckpoint: Checkpoint master key unavailable."); + std::vector vchPrivKey = ParseHex(CSyncCheckpoint::strMasterPrivKey); + CKey key; + key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash + if (!key.Sign(Hash(checkpoint.vchMsg.begin(), checkpoint.vchMsg.end()), checkpoint.vchSig)) + return error("SendSyncCheckpoint: Unable to sign checkpoint, check private key?"); + + if(!checkpoint.ProcessSyncCheckpoint(NULL)) + { + printf("WARNING: SendSyncCheckpoint: Failed to process checkpoint.\n"); + return false; + } + + // Relay checkpoint + { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + checkpoint.RelayTo(pnode); + } + return true; + } + + // Is the sync-checkpoint outside maturity window? + bool IsMatureSyncCheckpoint() + { + LOCK(cs_hashSyncCheckpoint); + // sync-checkpoint should always be accepted block + assert(mapBlockIndex.count(hashSyncCheckpoint)); + const CBlockIndex* pindexSync = mapBlockIndex[hashSyncCheckpoint]; + return (nBestHeight >= pindexSync->nHeight + nCoinbaseMaturity || + pindexSync->GetBlockTime() + nStakeMinAge < GetAdjustedTime()); + } +} + +// ppcoin: sync-checkpoint master key +const std::string CSyncCheckpoint::strMasterPubKey = "04b51b735a816de4dc3f891d5b37bbc91e1f7245c7c08d17990760b86b4d8fc3910a850ffecf73bfa0886f01739a054c4322201282d07b6e48ce931cc92af94850"; + +std::string CSyncCheckpoint::strMasterPrivKey = ""; + +// ppcoin: verify signature of sync-checkpoint message +bool CSyncCheckpoint::CheckSignature() +{ + CKey key; + if (!key.SetPubKey(ParseHex(CSyncCheckpoint::strMasterPubKey))) + return error("CSyncCheckpoint::CheckSignature() : SetPubKey failed"); + if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig)) + return error("CSyncCheckpoint::CheckSignature() : verify signature failed"); + + // Now unserialize the data + CDataStream sMsg(vchMsg, SER_NETWORK, PROTOCOL_VERSION); + sMsg >> *(CUnsignedSyncCheckpoint*)this; + return true; +} + +// process synchronized checkpoint +bool CSyncCheckpoint::ProcessSyncCheckpoint(CNode* pfrom) +{ + if (!CheckSignature()) + return false; + + LOCK(Checkpoints::cs_hashSyncCheckpoint); + if (!mapBlockIndex.count(hashCheckpoint)) + { + // We haven't received the checkpoint chain, keep the checkpoint as pending + Checkpoints::hashPendingCheckpoint = hashCheckpoint; + Checkpoints::checkpointMessagePending = *this; + printf("ProcessSyncCheckpoint: pending for sync-checkpoint %s\n", hashCheckpoint.ToString().c_str()); + // Ask this guy to fill in what we're missing + if (pfrom) + { + pfrom->PushGetBlocks(pindexBest, hashCheckpoint); + // ask directly as well in case rejected earlier by duplicate + // proof-of-stake because getblocks may not get it this time + pfrom->AskFor(CInv(MSG_BLOCK, mapOrphanBlocks.count(hashCheckpoint)? WantedByOrphan(mapOrphanBlocks[hashCheckpoint]) : hashCheckpoint)); + } + return false; + } + + if (!Checkpoints::ValidateSyncCheckpoint(hashCheckpoint)) + return false; + + CTxDB txdb; + CBlockIndex* pindexCheckpoint = mapBlockIndex[hashCheckpoint]; + if (!pindexCheckpoint->IsInMainChain()) + { + // checkpoint chain received but not yet main chain + CBlock block; + if (!block.ReadFromDisk(pindexCheckpoint)) + return error("ProcessSyncCheckpoint: ReadFromDisk failed for sync checkpoint %s", hashCheckpoint.ToString().c_str()); + if (!block.SetBestChain(txdb, pindexCheckpoint)) + { + Checkpoints::hashInvalidCheckpoint = hashCheckpoint; + return error("ProcessSyncCheckpoint: SetBestChain failed for sync checkpoint %s", hashCheckpoint.ToString().c_str()); + } + } + +#ifndef USE_LEVELDB + txdb.Close(); +#endif + + if (!Checkpoints::WriteSyncCheckpoint(hashCheckpoint)) + return error("ProcessSyncCheckpoint(): failed to write sync checkpoint %s", hashCheckpoint.ToString().c_str()); + Checkpoints::checkpointMessage = *this; + Checkpoints::hashPendingCheckpoint = 0; + Checkpoints::checkpointMessagePending.SetNull(); + printf("ProcessSyncCheckpoint: sync-checkpoint at %s\n", hashCheckpoint.ToString().c_str()); + return true; +} diff --git a/src/checkpoints.h b/src/checkpoints.h new file mode 100644 index 00000000..135838d0 --- /dev/null +++ b/src/checkpoints.h @@ -0,0 +1,159 @@ +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_CHECKPOINT_H +#define BITCOIN_CHECKPOINT_H + +#include +#include "util.h" +#include "net.h" + +#define CHECKPOINT_MAX_SPAN (60 * 60) // max 1 hour before latest block + +#ifdef WIN32 +#undef STRICT +#undef PERMISSIVE +#undef ADVISORY +#endif + +class uint256; +class CBlockIndex; +class CSyncCheckpoint; + +/** Block-chain checkpoints are compiled-in sanity checks. + * They are updated every release or three. + */ +namespace Checkpoints +{ + /** Checkpointing mode */ + enum CPMode + { + // Scrict checkpoints policy, perform conflicts verification and resolve conflicts + STRICT = 0, + // Advisory checkpoints policy, perform conflicts verification but don't try to resolve them + ADVISORY = 1, + // Permissive checkpoints policy, don't perform any checking + PERMISSIVE = 2 + }; + + // Returns true if block passes checkpoint checks + bool CheckHardened(int nHeight, const uint256& hash); + + // Return conservative estimate of total number of blocks, 0 if unknown + int GetTotalBlocksEstimate(); + + // Returns last CBlockIndex* in mapBlockIndex that is a checkpoint + CBlockIndex* GetLastCheckpoint(const std::map& mapBlockIndex); + + // Returns last checkpoint timestamp + unsigned int GetLastCheckpointTime(); + + extern uint256 hashSyncCheckpoint; + extern CSyncCheckpoint checkpointMessage; + extern uint256 hashInvalidCheckpoint; + extern CCriticalSection cs_hashSyncCheckpoint; + + CBlockIndex* GetLastSyncCheckpoint(); + bool WriteSyncCheckpoint(const uint256& hashCheckpoint); + bool AcceptPendingSyncCheckpoint(); + uint256 AutoSelectSyncCheckpoint(); + bool CheckSync(const uint256& hashBlock, const CBlockIndex* pindexPrev); + bool WantedByPendingSyncCheckpoint(uint256 hashBlock); + bool ResetSyncCheckpoint(); + void AskForPendingSyncCheckpoint(CNode* pfrom); + bool SetCheckpointPrivKey(std::string strPrivKey); + bool SendSyncCheckpoint(uint256 hashCheckpoint); + bool IsMatureSyncCheckpoint(); +} + +// ppcoin: synchronized checkpoint +class CUnsignedSyncCheckpoint +{ +public: + int nVersion; + uint256 hashCheckpoint; // checkpoint block + + IMPLEMENT_SERIALIZE + ( + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(hashCheckpoint); + ) + + void SetNull() + { + nVersion = 1; + hashCheckpoint = 0; + } + + std::string ToString() const + { + return strprintf( + "CSyncCheckpoint(\n" + " nVersion = %d\n" + " hashCheckpoint = %s\n" + ")\n", + nVersion, + hashCheckpoint.ToString().c_str()); + } + + void print() const + { + printf("%s", ToString().c_str()); + } +}; + +class CSyncCheckpoint : public CUnsignedSyncCheckpoint +{ +public: + static const std::string strMasterPubKey; + static std::string strMasterPrivKey; + + std::vector vchMsg; + std::vector vchSig; + + CSyncCheckpoint() + { + SetNull(); + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(vchMsg); + READWRITE(vchSig); + ) + + void SetNull() + { + CUnsignedSyncCheckpoint::SetNull(); + vchMsg.clear(); + vchSig.clear(); + } + + bool IsNull() const + { + return (hashCheckpoint == 0); + } + + uint256 GetHash() const + { + return SerializeHash(*this); + } + + bool RelayTo(CNode* pnode) const + { + // returns true if wasn't already sent + if (pnode->hashCheckpointKnown != hashCheckpoint) + { + pnode->hashCheckpointKnown = hashCheckpoint; + pnode->PushMessage("checkpoint", *this); + return true; + } + return false; + } + + bool CheckSignature(); + bool ProcessSyncCheckpoint(CNode* pfrom); +}; + +#endif diff --git a/src/checkqueue.h b/src/checkqueue.h new file mode 100644 index 00000000..c4c35e5f --- /dev/null +++ b/src/checkqueue.h @@ -0,0 +1,218 @@ +// Copyright (c) 2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef CHECKQUEUE_H +#define CHECKQUEUE_H + +#include +#include + +#include +#include +#include +#include + +extern bool fShutdown; + +template class CCheckQueueControl; + +/** Queue for verifications that have to be performed. + * The verifications are represented by a type T, which must provide an + * operator(), returning a bool. + * + * One thread (the master) is assumed to push batches of verifications + * onto the queue, where they are processed by N-1 worker threads. When + * the master is done adding work, it temporarily joins the worker pool + * as an N'th worker, until all jobs are done. + */ +template class CCheckQueue { +private: + // Mutex to protect the inner state + boost::mutex mutex; + + // Worker threads block on this when out of work + boost::condition_variable condWorker; + + // Master thread blocks on this when out of work + boost::condition_variable condMaster; + + // Quit method blocks on this until all workers are gone + boost::condition_variable condQuit; + + // The queue of elements to be processed. + // As the order of booleans doesn't matter, it is used as a LIFO (stack) + std::vector queue; + + // The number of workers (including the master) that are idle. + int nIdle; + + // The total number of workers (including the master). + int nTotal; + + // The temporary evaluation result. + bool fAllOk; + + // Number of verifications that haven't completed yet. + // This includes elements that are not anymore in queue, but still in + // worker's own batches. + unsigned int nTodo; + + // Whether we're shutting down. + bool fQuit; + + // The maximum number of elements to be processed in one batch + unsigned int nBatchSize; + + // Internal function that does bulk of the verification work. + bool Loop(bool fMaster = false) { + boost::condition_variable &cond = fMaster ? condMaster : condWorker; + std::vector vChecks; + vChecks.reserve(nBatchSize); + unsigned int nNow = 0; + bool fOk = true; + do { + { + boost::unique_lock lock(mutex); + // first do the clean-up of the previous loop run (allowing us to do it in the same critsect) + if (nNow) { + fAllOk &= fOk; + nTodo -= nNow; + if (nTodo == 0 && !fMaster) + // We processed the last element; inform the master he can exit and return the result + condMaster.notify_one(); + } else { + // first iteration + nTotal++; + } + // logically, the do loop starts here + while (queue.empty()) { + if ((fMaster || fQuit) && nTodo == 0) { + nTotal--; + if (nTotal==0) + condQuit.notify_one(); + bool fRet = fAllOk; + // reset the status for new work later + if (fMaster) + fAllOk = true; + // return the current status + return fRet; + } + nIdle++; + cond.wait(lock); // wait + nIdle--; + } + // Decide how many work units to process now. + // * Do not try to do everything at once, but aim for increasingly smaller batches so + // all workers finish approximately simultaneously. + // * Try to account for idle jobs which will instantly start helping. + // * Don't do batches smaller than 1 (duh), or larger than nBatchSize. + nNow = std::max(1U, std::min(nBatchSize, (unsigned int)queue.size() / (nTotal + nIdle + 1))); + vChecks.resize(nNow); + for (unsigned int i = 0; i < nNow; i++) { + // We want the lock on the mutex to be as short as possible, so swap jobs from the global + // queue to the local batch vector instead of copying. + vChecks[i].swap(queue.back()); + queue.pop_back(); + } + // Check whether we need to do work at all + fOk = fAllOk; + } + // execute work + BOOST_FOREACH(T &check, vChecks) + if (fOk) + fOk = check(); + vChecks.clear(); + } while(true && !fShutdown); // HACK: force queue to shut down + return false; + } + +public: + // Create a new check queue + CCheckQueue(unsigned int nBatchSizeIn) : + nIdle(0), nTotal(0), fAllOk(true), nTodo(0), fQuit(false), nBatchSize(nBatchSizeIn) {} + + // Worker thread + void Thread() { + Loop(); + } + + // Wait until execution finishes, and return whether all evaluations where succesful. + bool Wait() { + return Loop(true); + } + + // Add a batch of checks to the queue + void Add(std::vector &vChecks) { + boost::unique_lock lock(mutex); + BOOST_FOREACH(T &check, vChecks) { + queue.push_back(T()); + check.swap(queue.back()); + } + nTodo += vChecks.size(); + if (vChecks.size() == 1) + condWorker.notify_one(); + else if (vChecks.size() > 1) + condWorker.notify_all(); + } + + // Shut the queue down + void Quit() { + boost::unique_lock lock(mutex); + fQuit = true; + // No need to wake the master, as he will quit automatically when all jobs are + // done. + condWorker.notify_all(); + + while (nTotal > 0) + condQuit.wait(lock); + } + + ~CCheckQueue() { + Quit(); + } + + bool IsIdle() + { + boost::unique_lock lock(mutex); + return (nTotal == nIdle && nTodo == 0 && fAllOk == true); + } +}; + +/** RAII-style controller object for a CCheckQueue that guarantees the passed + * queue is finished before continuing. + */ +template class CCheckQueueControl { +private: + CCheckQueue *pqueue; + bool fDone; + +public: + CCheckQueueControl(CCheckQueue *pqueueIn) : pqueue(pqueueIn), fDone(false) { + // passed queue is supposed to be unused, or NULL + if (pqueue != NULL) { + bool isIdle = pqueue->IsIdle(); + assert(isIdle); + } + } + + bool Wait() { + if (pqueue == NULL) + return true; + bool fRet = pqueue->Wait(); + fDone = true; + return fRet; + } + + void Add(std::vector &vChecks) { + if (pqueue != NULL) + pqueue->Add(vChecks); + } + + ~CCheckQueueControl() { + if (!fDone) + Wait(); + } +}; + +#endif diff --git a/src/clientversion.h b/src/clientversion.h new file mode 100644 index 00000000..4a206e88 --- /dev/null +++ b/src/clientversion.h @@ -0,0 +1,19 @@ +#ifndef CLIENTVERSION_H +#define CLIENTVERSION_H + +// +// client versioning +// + +// These need to be macros, as version.cpp's and bitcoin-qt.rc's voodoo requires it +#define CLIENT_VERSION_MAJOR 1 +#define CLIENT_VERSION_MINOR 0 +#define CLIENT_VERSION_REVISION 0 +#define CLIENT_VERSION_BUILD 1 + +// Converts the parameter X to a string after macro replacement on X has been performed. +// Don't merge these into one macro! +#define STRINGIZE(X) DO_STRINGIZE(X) +#define DO_STRINGIZE(X) #X + +#endif // CLIENTVERSION_H diff --git a/src/coincontrol.h b/src/coincontrol.h new file mode 100644 index 00000000..236b5865 --- /dev/null +++ b/src/coincontrol.h @@ -0,0 +1,57 @@ +#ifndef COINCONTROL_H +#define COINCONTROL_H + +/** Coin Control Features. */ +class CCoinControl +{ +public: + CTxDestination destChange; + + CCoinControl() + { + SetNull(); + } + + void SetNull() + { + destChange = CNoDestination(); + setSelected.clear(); + } + + bool HasSelected() const + { + return (setSelected.size() > 0); + } + + bool IsSelected(const uint256& hash, unsigned int n) const + { + COutPoint outpt(hash, n); + return (setSelected.count(outpt) > 0); + } + + void Select(COutPoint& output) + { + setSelected.insert(output); + } + + void UnSelect(COutPoint& output) + { + setSelected.erase(output); + } + + void UnSelectAll() + { + setSelected.clear(); + } + + void ListSelected(std::vector& vOutpoints) + { + vOutpoints.assign(setSelected.begin(), setSelected.end()); + } + +private: + std::set setSelected; + +}; + +#endif // COINCONTROL_H diff --git a/src/compat.h b/src/compat.h new file mode 100644 index 00000000..c38e2fa7 --- /dev/null +++ b/src/compat.h @@ -0,0 +1,73 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef _BITCOIN_COMPAT_H +#define _BITCOIN_COMPAT_H 1 + +#ifdef WIN32 +#define _WIN32_WINNT 0x0501 +#define WIN32_LEAN_AND_MEAN 1 +#ifndef NOMINMAX +#define NOMINMAX +#endif +#ifndef _MSC_VER +#ifdef FD_SETSIZE +#undef FD_SETSIZE +#endif +#define FD_SETSIZE 1024 // max number of fds in fd_set +#endif +#include +#include +#include +#else +#include +#include +#ifdef ANDROID +#include +#else +#include +#endif +#include +#include +#include +#include +#include + +typedef u_int SOCKET; +#endif + +#ifdef WIN32 +#define MSG_NOSIGNAL 0 +#define MSG_DONTWAIT 0 +typedef int socklen_t; +#else +#include "errno.h" +#define WSAGetLastError() errno +#define WSAEINVAL EINVAL +#define WSAEALREADY EALREADY +#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 +#endif + +inline int myclosesocket(SOCKET& hSocket) +{ + if (hSocket == INVALID_SOCKET) + return WSAENOTSOCK; +#ifdef WIN32 + int ret = closesocket(hSocket); +#else + int ret = close(hSocket); +#endif + hSocket = INVALID_SOCKET; + return ret; +} +#define closesocket(s) myclosesocket(s) + +#endif diff --git a/src/crypter.cpp b/src/crypter.cpp new file mode 100644 index 00000000..2a6f36d1 --- /dev/null +++ b/src/crypter.cpp @@ -0,0 +1,124 @@ +// Copyright (c) 2009-2012 The Bitcoin Developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include + +#include "crypter.h" + +#ifdef WIN32 +#include +#endif + +bool CCrypter::SetKeyFromPassphrase(const SecureString& strKeyData, const std::vector& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod) +{ + if (nRounds < 1 || chSalt.size() != WALLET_CRYPTO_SALT_SIZE) + return false; + + int i = 0; + if (nDerivationMethod == 0) + { + i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha512(), &chSalt[0], + (unsigned char *)&strKeyData[0], strKeyData.size(), nRounds, chKey, chIV); + } + + if (i != (int)WALLET_CRYPTO_KEY_SIZE) + { + OPENSSL_cleanse(&chKey, sizeof chKey); + OPENSSL_cleanse(&chIV, sizeof chIV); + return false; + } + + fKeySet = true; + return true; +} + +bool CCrypter::SetKey(const CKeyingMaterial& chNewKey, const std::vector& chNewIV) +{ + if (chNewKey.size() != WALLET_CRYPTO_KEY_SIZE || chNewIV.size() != WALLET_CRYPTO_KEY_SIZE) + return false; + + memcpy(&chKey[0], &chNewKey[0], sizeof chKey); + memcpy(&chIV[0], &chNewIV[0], sizeof chIV); + + fKeySet = true; + return true; +} + +bool CCrypter::Encrypt(const CKeyingMaterial& vchPlaintext, std::vector &vchCiphertext) +{ + if (!fKeySet) + return false; + + // max ciphertext len for a n bytes of plaintext is + // n + AES_BLOCK_SIZE - 1 bytes + int nLen = vchPlaintext.size(); + int nCLen = nLen + AES_BLOCK_SIZE, nFLen = 0; + vchCiphertext = std::vector (nCLen); + + EVP_CIPHER_CTX ctx; + + bool fOk = true; + + EVP_CIPHER_CTX_init(&ctx); + if (fOk) fOk = EVP_EncryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV) != 0; + if (fOk) fOk = EVP_EncryptUpdate(&ctx, &vchCiphertext[0], &nCLen, &vchPlaintext[0], nLen) != 0; + if (fOk) fOk = EVP_EncryptFinal_ex(&ctx, (&vchCiphertext[0]) + nCLen, &nFLen) != 0; + EVP_CIPHER_CTX_cleanup(&ctx); + + if (!fOk) return false; + + vchCiphertext.resize(nCLen + nFLen); + return true; +} + +bool CCrypter::Decrypt(const std::vector& vchCiphertext, CKeyingMaterial& vchPlaintext) +{ + if (!fKeySet) + return false; + + // plaintext will always be equal to or lesser than length of ciphertext + int nLen = vchCiphertext.size(); + int nPLen = nLen, nFLen = 0; + + vchPlaintext = CKeyingMaterial(nPLen); + + EVP_CIPHER_CTX ctx; + + bool fOk = true; + + EVP_CIPHER_CTX_init(&ctx); + if (fOk) fOk = EVP_DecryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV) != 0; + if (fOk) fOk = EVP_DecryptUpdate(&ctx, &vchPlaintext[0], &nPLen, &vchCiphertext[0], nLen) != 0; + if (fOk) fOk = EVP_DecryptFinal_ex(&ctx, (&vchPlaintext[0]) + nPLen, &nFLen) != 0; + EVP_CIPHER_CTX_cleanup(&ctx); + + if (!fOk) return false; + + vchPlaintext.resize(nPLen + nFLen); + return true; +} + + +bool EncryptSecret(CKeyingMaterial& vMasterKey, const CSecret &vchPlaintext, const uint256& nIV, std::vector &vchCiphertext) +{ + CCrypter cKeyCrypter; + std::vector chIV(WALLET_CRYPTO_KEY_SIZE); + memcpy(&chIV[0], &nIV, WALLET_CRYPTO_KEY_SIZE); + if(!cKeyCrypter.SetKey(vMasterKey, chIV)) + return false; + return cKeyCrypter.Encrypt((CKeyingMaterial)vchPlaintext, vchCiphertext); +} + +bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector& vchCiphertext, const uint256& nIV, CSecret& vchPlaintext) +{ + CCrypter cKeyCrypter; + std::vector chIV(WALLET_CRYPTO_KEY_SIZE); + memcpy(&chIV[0], &nIV, WALLET_CRYPTO_KEY_SIZE); + if(!cKeyCrypter.SetKey(vMasterKey, chIV)) + return false; + return cKeyCrypter.Decrypt(vchCiphertext, *((CKeyingMaterial*)&vchPlaintext)); +} diff --git a/src/crypter.h b/src/crypter.h new file mode 100644 index 00000000..733d9a31 --- /dev/null +++ b/src/crypter.h @@ -0,0 +1,107 @@ +// Copyright (c) 2009-2012 The Bitcoin Developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef __CRYPTER_H__ +#define __CRYPTER_H__ + +#include "allocators.h" /* for SecureString */ +#include "key.h" +#include "serialize.h" + +const unsigned int WALLET_CRYPTO_KEY_SIZE = 32; +const unsigned int WALLET_CRYPTO_SALT_SIZE = 8; + +/* +Private key encryption is done based on a CMasterKey, +which holds a salt and random encryption key. + +CMasterKeys are encrypted using AES-256-CBC using a key +derived using derivation method nDerivationMethod +(0 == EVP_sha512()) and derivation iterations nDeriveIterations. +vchOtherDerivationParameters is provided for alternative algorithms +which may require more parameters (such as scrypt). + +Wallet Private Keys are then encrypted using AES-256-CBC +with the double-sha256 of the public key as the IV, and the +master key's key as the encryption key (see keystore.[ch]). +*/ + +/** Master key for wallet encryption */ +class CMasterKey +{ +public: + std::vector vchCryptedKey; + std::vector vchSalt; + // 0 = EVP_sha512() + // 1 = scrypt() + unsigned int nDerivationMethod; + unsigned int nDeriveIterations; + // Use this for more parameters to key derivation, + // such as the various parameters to scrypt + std::vector vchOtherDerivationParameters; + + IMPLEMENT_SERIALIZE + ( + READWRITE(vchCryptedKey); + READWRITE(vchSalt); + READWRITE(nDerivationMethod); + READWRITE(nDeriveIterations); + READWRITE(vchOtherDerivationParameters); + ) + CMasterKey() + { + // 25000 rounds is just under 0.1 seconds on a 1.86 GHz Pentium M + // ie slightly lower than the lowest hardware we need bother supporting + nDeriveIterations = 25000; + nDerivationMethod = 0; + vchOtherDerivationParameters = std::vector(0); + } +}; + +typedef std::vector > CKeyingMaterial; + +/** Encryption/decryption context with key information */ +class CCrypter +{ +private: + unsigned char chKey[WALLET_CRYPTO_KEY_SIZE]; + unsigned char chIV[WALLET_CRYPTO_KEY_SIZE]; + bool fKeySet; + +public: + bool SetKeyFromPassphrase(const SecureString &strKeyData, const std::vector& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod); + bool Encrypt(const CKeyingMaterial& vchPlaintext, std::vector &vchCiphertext); + bool Decrypt(const std::vector& vchCiphertext, CKeyingMaterial& vchPlaintext); + bool SetKey(const CKeyingMaterial& chNewKey, const std::vector& chNewIV); + + void CleanKey() + { + OPENSSL_cleanse(&chKey, sizeof chKey); + OPENSSL_cleanse(&chIV, sizeof chIV); + fKeySet = false; + } + + CCrypter() + { + fKeySet = false; + + // Try to keep the key data out of swap (and be a bit over-careful to keep the IV that we don't even use out of swap) + // Note that this does nothing about suspend-to-disk (which will put all our key data on disk) + // Note as well that at no point in this program is any attempt made to prevent stealing of keys by reading the memory of the running process. + LockedPageManager::instance.LockRange(&chKey[0], sizeof chKey); + LockedPageManager::instance.LockRange(&chIV[0], sizeof chIV); + } + + ~CCrypter() + { + CleanKey(); + + LockedPageManager::instance.UnlockRange(&chKey[0], sizeof chKey); + LockedPageManager::instance.UnlockRange(&chIV[0], sizeof chIV); + } +}; + +bool EncryptSecret(CKeyingMaterial& vMasterKey, const CSecret &vchPlaintext, const uint256& nIV, std::vector &vchCiphertext); +bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector &vchCiphertext, const uint256& nIV, CSecret &vchPlaintext); + +#endif diff --git a/src/crypto/scrypt/asm/asm-wrapper.cpp b/src/crypto/scrypt/asm/asm-wrapper.cpp new file mode 100644 index 00000000..05914ed8 --- /dev/null +++ b/src/crypto/scrypt/asm/asm-wrapper.cpp @@ -0,0 +1,22 @@ +#include "scrypt.h" + +extern "C" void scrypt_core(uint32_t *X, uint32_t *V); + +/* cpu and memory intensive function to transform a 80 byte buffer into a 32 byte output + scratchpad size needs to be at least 63 + (128 * r * p) + (256 * r + 64) + (128 * r * N) bytes + r = 1, p = 1, N = 1024 + */ +uint256 scrypt_blockhash(const uint8_t* input) +{ + uint8_t scratchpad[SCRYPT_BUFFER_SIZE]; + uint32_t X[32]; + uint256 result = 0; + + uint32_t *V = (uint32_t *)(((uintptr_t)(scratchpad) + 63) & ~ (uintptr_t)(63)); + + PKCS5_PBKDF2_HMAC((const char*)input, 80, input, 80, 1, EVP_sha256(), 128, (unsigned char *)X); + scrypt_core(X, V); + PKCS5_PBKDF2_HMAC((const char*)input, 80, (const unsigned char*)X, 128, 1, EVP_sha256(), 32, (unsigned char*)&result); + + return result; +} diff --git a/src/crypto/scrypt/asm/obj/.gitignore b/src/crypto/scrypt/asm/obj/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/src/crypto/scrypt/asm/obj/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/src/crypto/scrypt/asm/scrypt-arm.S b/src/crypto/scrypt/asm/scrypt-arm.S new file mode 100644 index 00000000..65b9c7ff --- /dev/null +++ b/src/crypto/scrypt/asm/scrypt-arm.S @@ -0,0 +1,393 @@ +/* + * Copyright 2012 pooler@litecoinpool.org + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. See COPYING for more details. + */ + +#if defined(__arm__) && defined(__APCS_32__) + +.macro salsa8_core_doubleround_body + ldr r8, [sp, #8*4] + add r11, r11, r10 + ldr lr, [sp, #13*4] + add r12, r12, r3 + eor r2, r2, r11, ror #23 + add r11, r4, r0 + eor r7, r7, r12, ror #23 + add r12, r9, r5 + str r9, [sp, #9*4] + eor r8, r8, r11, ror #23 + str r10, [sp, #14*4] + eor lr, lr, r12, ror #23 + + ldr r11, [sp, #11*4] + add r9, lr, r9 + ldr r12, [sp, #12*4] + add r10, r2, r10 + eor r1, r1, r9, ror #19 + add r9, r7, r3 + eor r6, r6, r10, ror #19 + add r10, r8, r4 + str r8, [sp, #8*4] + eor r11, r11, r9, ror #19 + str lr, [sp, #13*4] + eor r12, r12, r10, ror #19 + + ldr r9, [sp, #10*4] + add r8, r12, r8 + ldr r10, [sp, #15*4] + add lr, r1, lr + eor r0, r0, r8, ror #14 + add r8, r6, r2 + eor r5, r5, lr, ror #14 + add lr, r11, r7 + eor r9, r9, r8, ror #14 + ldr r8, [sp, #9*4] + eor r10, r10, lr, ror #14 + ldr lr, [sp, #14*4] + + + add r8, r9, r8 + str r9, [sp, #10*4] + add lr, r10, lr + str r10, [sp, #15*4] + eor r11, r11, r8, ror #25 + add r8, r0, r3 + eor r12, r12, lr, ror #25 + add lr, r5, r4 + eor r1, r1, r8, ror #25 + ldr r8, [sp, #8*4] + eor r6, r6, lr, ror #25 + + add r9, r11, r9 + ldr lr, [sp, #13*4] + add r10, r12, r10 + eor r8, r8, r9, ror #23 + add r9, r1, r0 + eor lr, lr, r10, ror #23 + add r10, r6, r5 + str r11, [sp, #11*4] + eor r2, r2, r9, ror #23 + str r12, [sp, #12*4] + eor r7, r7, r10, ror #23 + + ldr r9, [sp, #9*4] + add r11, r8, r11 + ldr r10, [sp, #14*4] + add r12, lr, r12 + eor r9, r9, r11, ror #19 + add r11, r2, r1 + eor r10, r10, r12, ror #19 + add r12, r7, r6 + str r8, [sp, #8*4] + eor r3, r3, r11, ror #19 + str lr, [sp, #13*4] + eor r4, r4, r12, ror #19 +.endm + +.macro salsa8_core + ldmia sp, {r0-r7} + + ldr r12, [sp, #15*4] + ldr r8, [sp, #11*4] + ldr lr, [sp, #12*4] + + ldr r9, [sp, #9*4] + add r8, r8, r12 + ldr r11, [sp, #10*4] + add lr, lr, r0 + eor r3, r3, r8, ror #25 + add r8, r5, r1 + ldr r10, [sp, #14*4] + eor r4, r4, lr, ror #25 + add lr, r11, r6 + eor r9, r9, r8, ror #25 + eor r10, r10, lr, ror #25 + + salsa8_core_doubleround_body + + ldr r11, [sp, #10*4] + add r8, r9, r8 + ldr r12, [sp, #15*4] + add lr, r10, lr + eor r11, r11, r8, ror #14 + str r9, [sp, #9*4] + eor r12, r12, lr, ror #14 + add r8, r3, r2 + add lr, r4, r7 + str r10, [sp, #14*4] + eor r0, r0, r8, ror #14 + ldr r8, [sp, #11*4] + eor r5, r5, lr, ror #14 + ldr lr, [sp, #12*4] + + add r8, r8, r12 + str r11, [sp, #10*4] + add lr, lr, r0 + str r12, [sp, #15*4] + eor r3, r3, r8, ror #25 + add r8, r5, r1 + eor r4, r4, lr, ror #25 + add lr, r11, r6 + eor r9, r9, r8, ror #25 + eor r10, r10, lr, ror #25 + + salsa8_core_doubleround_body + + ldr r11, [sp, #10*4] + add r8, r9, r8 + ldr r12, [sp, #15*4] + add lr, r10, lr + eor r11, r11, r8, ror #14 + str r9, [sp, #9*4] + eor r12, r12, lr, ror #14 + add r8, r3, r2 + add lr, r4, r7 + str r10, [sp, #14*4] + eor r0, r0, r8, ror #14 + ldr r8, [sp, #11*4] + eor r5, r5, lr, ror #14 + ldr lr, [sp, #12*4] + + add r8, r8, r12 + str r11, [sp, #10*4] + add lr, lr, r0 + str r12, [sp, #15*4] + eor r3, r3, r8, ror #25 + add r8, r5, r1 + eor r4, r4, lr, ror #25 + add lr, r11, r6 + eor r9, r9, r8, ror #25 + eor r10, r10, lr, ror #25 + + salsa8_core_doubleround_body + + ldr r11, [sp, #10*4] + add r8, r9, r8 + ldr r12, [sp, #15*4] + add lr, r10, lr + eor r11, r11, r8, ror #14 + str r9, [sp, #9*4] + eor r12, r12, lr, ror #14 + add r8, r3, r2 + add lr, r4, r7 + str r10, [sp, #14*4] + eor r0, r0, r8, ror #14 + ldr r8, [sp, #11*4] + eor r5, r5, lr, ror #14 + ldr lr, [sp, #12*4] + + add r8, r8, r12 + str r11, [sp, #10*4] + add lr, lr, r0 + str r12, [sp, #15*4] + eor r3, r3, r8, ror #25 + add r8, r5, r1 + eor r4, r4, lr, ror #25 + add lr, r11, r6 + eor r9, r9, r8, ror #25 + eor r10, r10, lr, ror #25 + + salsa8_core_doubleround_body + + ldr r11, [sp, #10*4] + add r8, r9, r8 + ldr r12, [sp, #15*4] + add lr, r10, lr + str r9, [sp, #9*4] + eor r11, r11, r8, ror #14 + eor r12, r12, lr, ror #14 + add r8, r3, r2 + str r10, [sp, #14*4] + add lr, r4, r7 + str r11, [sp, #10*4] + eor r0, r0, r8, ror #14 + str r12, [sp, #15*4] + eor r5, r5, lr, ror #14 + + stmia sp, {r0-r7} +.endm + + +.macro scrypt_core_macro1a_x4 + ldmia r0, {r4-r7} + ldmia lr!, {r8-r11} + stmia r1!, {r4-r7} + stmia r3!, {r8-r11} + eor r4, r4, r8 + eor r5, r5, r9 + eor r6, r6, r10 + eor r7, r7, r11 + stmia r0!, {r4-r7} + stmia r12!, {r4-r7} +.endm + +.macro scrypt_core_macro1b_x4 + ldmia r3!, {r8-r11} + ldmia r2, {r4-r7} + eor r8, r8, r4 + eor r9, r9, r5 + eor r10, r10, r6 + eor r11, r11, r7 + ldmia r0, {r4-r7} + stmia r2!, {r8-r11} + eor r4, r4, r8 + eor r5, r5, r9 + eor r6, r6, r10 + eor r7, r7, r11 + ldmia r1!, {r8-r11} + eor r4, r4, r8 + eor r5, r5, r9 + eor r6, r6, r10 + eor r7, r7, r11 + stmia r0!, {r4-r7} + stmia r12!, {r4-r7} +.endm + +.macro scrypt_core_macro2_x4 + ldmia r12, {r4-r7} + ldmia r0, {r8-r11} + add r4, r4, r8 + add r5, r5, r9 + add r6, r6, r10 + add r7, r7, r11 + stmia r0!, {r4-r7} + ldmia r2, {r8-r11} + eor r4, r4, r8 + eor r5, r5, r9 + eor r6, r6, r10 + eor r7, r7, r11 + stmia r2!, {r4-r7} + stmia r12!, {r4-r7} +.endm + +.macro scrypt_core_macro3_x4 + ldmia r1!, {r4-r7} + ldmia r0, {r8-r11} + add r4, r4, r8 + add r5, r5, r9 + add r6, r6, r10 + add r7, r7, r11 + stmia r0!, {r4-r7} +.endm + +.macro scrypt_core_macro3_x6 + ldmia r1!, {r2-r7} + ldmia r0, {r8-r12, lr} + add r2, r2, r8 + add r3, r3, r9 + add r4, r4, r10 + add r5, r5, r11 + add r6, r6, r12 + add r7, r7, lr + stmia r0!, {r2-r7} +.endm + + + .text + .code 32 + .align 2 + .globl scrypt_core + .globl _scrypt_core +#ifdef __ELF__ + .type scrypt_core, %function +#endif +scrypt_core: +_scrypt_core: + stmfd sp!, {r4-r11, lr} + sub sp, sp, #20*4 + + str r0, [sp, #16*4] + add r12, r1, #1024*32*4 + str r12, [sp, #18*4] +scrypt_core_loop1: + add lr, r0, #16*4 + add r3, r1, #16*4 + mov r12, sp + scrypt_core_macro1a_x4 + scrypt_core_macro1a_x4 + scrypt_core_macro1a_x4 + scrypt_core_macro1a_x4 + str r1, [sp, #17*4] + + salsa8_core + + ldr r0, [sp, #16*4] + mov r12, sp + add r2, r0, #16*4 + scrypt_core_macro2_x4 + scrypt_core_macro2_x4 + scrypt_core_macro2_x4 + scrypt_core_macro2_x4 + + salsa8_core + + ldr r0, [sp, #16*4] + mov r1, sp + add r0, r0, #16*4 + scrypt_core_macro3_x6 + scrypt_core_macro3_x6 + ldr r3, [sp, #17*4] + ldr r12, [sp, #18*4] + scrypt_core_macro3_x4 + + add r1, r3, #16*4 + sub r0, r0, #32*4 + cmp r1, r12 + bne scrypt_core_loop1 + + sub r1, r1, #1024*32*4 + str r1, [sp, #17*4] + mov r12, #1024 +scrypt_core_loop2: + str r12, [sp, #18*4] + + ldr r4, [r0, #16*4] + mov r4, r4, lsl #32-10 + add r1, r1, r4, lsr #32-10-7 + + add r2, r0, #16*4 + add r3, r1, #16*4 + mov r12, sp + scrypt_core_macro1b_x4 + scrypt_core_macro1b_x4 + scrypt_core_macro1b_x4 + scrypt_core_macro1b_x4 + + salsa8_core + + ldr r0, [sp, #16*4] + mov r12, sp + add r2, r0, #16*4 + scrypt_core_macro2_x4 + scrypt_core_macro2_x4 + scrypt_core_macro2_x4 + scrypt_core_macro2_x4 + + salsa8_core + + ldr r0, [sp, #16*4] + mov r1, sp + add r0, r0, #16*4 + scrypt_core_macro3_x6 + scrypt_core_macro3_x6 + scrypt_core_macro3_x4 + + ldr r12, [sp, #18*4] + sub r0, r0, #32*4 + ldr r1, [sp, #17*4] + subs r12, r12, #1 + bne scrypt_core_loop2 + + add sp, sp, #20*4 +#ifdef __thumb__ + ldmfd sp!, {r4-r11, lr} + bx lr +#else + ldmfd sp!, {r4-r11, pc} +#endif + +#endif diff --git a/src/crypto/scrypt/asm/scrypt-x86.S b/src/crypto/scrypt/asm/scrypt-x86.S new file mode 100644 index 00000000..bfca2ed5 --- /dev/null +++ b/src/crypto/scrypt/asm/scrypt-x86.S @@ -0,0 +1,819 @@ +/* + * Copyright 2011-2012 pooler@litecoinpool.org + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(__linux__) && defined(__ELF__) + .section .note.GNU-stack,"",%progbits +#endif + +#if defined(__i386__) + +.macro scrypt_shuffle src, so, dest, do + movl \so+60(\src), %eax + movl \so+44(\src), %ebx + movl \so+28(\src), %ecx + movl \so+12(\src), %edx + movl %eax, \do+12(\dest) + movl %ebx, \do+28(\dest) + movl %ecx, \do+44(\dest) + movl %edx, \do+60(\dest) + movl \so+40(\src), %eax + movl \so+8(\src), %ebx + movl \so+48(\src), %ecx + movl \so+16(\src), %edx + movl %eax, \do+8(\dest) + movl %ebx, \do+40(\dest) + movl %ecx, \do+16(\dest) + movl %edx, \do+48(\dest) + movl \so+20(\src), %eax + movl \so+4(\src), %ebx + movl \so+52(\src), %ecx + movl \so+36(\src), %edx + movl %eax, \do+4(\dest) + movl %ebx, \do+20(\dest) + movl %ecx, \do+36(\dest) + movl %edx, \do+52(\dest) + movl \so+0(\src), %eax + movl \so+24(\src), %ebx + movl \so+32(\src), %ecx + movl \so+56(\src), %edx + movl %eax, \do+0(\dest) + movl %ebx, \do+24(\dest) + movl %ecx, \do+32(\dest) + movl %edx, \do+56(\dest) +.endm + +.macro salsa8_core_gen_quadround + movl 52(%esp), %ecx + movl 4(%esp), %edx + movl 20(%esp), %ebx + movl 8(%esp), %esi + leal (%ecx, %edx), %edi + roll $7, %edi + xorl %edi, %ebx + movl %ebx, 4(%esp) + movl 36(%esp), %edi + leal (%edx, %ebx), %ebp + roll $9, %ebp + xorl %ebp, %edi + movl 24(%esp), %ebp + movl %edi, 8(%esp) + addl %edi, %ebx + roll $13, %ebx + xorl %ebx, %ecx + movl 40(%esp), %ebx + movl %ecx, 20(%esp) + addl %edi, %ecx + roll $18, %ecx + leal (%esi, %ebp), %edi + roll $7, %edi + xorl %edi, %ebx + movl %ebx, 24(%esp) + movl 56(%esp), %edi + xorl %ecx, %edx + leal (%ebp, %ebx), %ecx + roll $9, %ecx + xorl %ecx, %edi + movl %edi, 36(%esp) + movl 28(%esp), %ecx + movl %edx, 28(%esp) + movl 44(%esp), %edx + addl %edi, %ebx + roll $13, %ebx + xorl %ebx, %esi + movl 60(%esp), %ebx + movl %esi, 40(%esp) + addl %edi, %esi + roll $18, %esi + leal (%ecx, %edx), %edi + roll $7, %edi + xorl %edi, %ebx + movl %ebx, 44(%esp) + movl 12(%esp), %edi + xorl %esi, %ebp + leal (%edx, %ebx), %esi + roll $9, %esi + xorl %esi, %edi + movl %edi, 12(%esp) + movl 48(%esp), %esi + movl %ebp, 48(%esp) + movl 64(%esp), %ebp + addl %edi, %ebx + roll $13, %ebx + xorl %ebx, %ecx + movl 16(%esp), %ebx + movl %ecx, 16(%esp) + addl %edi, %ecx + roll $18, %ecx + leal (%esi, %ebp), %edi + roll $7, %edi + xorl %edi, %ebx + movl 32(%esp), %edi + xorl %ecx, %edx + leal (%ebp, %ebx), %ecx + roll $9, %ecx + xorl %ecx, %edi + movl %edi, 32(%esp) + movl %ebx, %ecx + movl %edx, 52(%esp) + movl 28(%esp), %edx + addl %edi, %ebx + roll $13, %ebx + xorl %ebx, %esi + movl 40(%esp), %ebx + movl %esi, 28(%esp) + addl %edi, %esi + roll $18, %esi + leal (%ecx, %edx), %edi + roll $7, %edi + xorl %edi, %ebx + movl %ebx, 40(%esp) + movl 12(%esp), %edi + xorl %esi, %ebp + leal (%edx, %ebx), %esi + roll $9, %esi + xorl %esi, %edi + movl %edi, 12(%esp) + movl 4(%esp), %esi + movl %ebp, 4(%esp) + movl 48(%esp), %ebp + addl %edi, %ebx + roll $13, %ebx + xorl %ebx, %ecx + movl 16(%esp), %ebx + movl %ecx, 16(%esp) + addl %edi, %ecx + roll $18, %ecx + leal (%esi, %ebp), %edi + roll $7, %edi + xorl %edi, %ebx + movl %ebx, 48(%esp) + movl 32(%esp), %edi + xorl %ecx, %edx + leal (%ebp, %ebx), %ecx + roll $9, %ecx + xorl %ecx, %edi + movl %edi, 32(%esp) + movl 24(%esp), %ecx + movl %edx, 24(%esp) + movl 52(%esp), %edx + addl %edi, %ebx + roll $13, %ebx + xorl %ebx, %esi + movl 28(%esp), %ebx + movl %esi, 28(%esp) + addl %edi, %esi + roll $18, %esi + leal (%ecx, %edx), %edi + roll $7, %edi + xorl %edi, %ebx + movl %ebx, 52(%esp) + movl 8(%esp), %edi + xorl %esi, %ebp + leal (%edx, %ebx), %esi + roll $9, %esi + xorl %esi, %edi + movl %edi, 8(%esp) + movl 44(%esp), %esi + movl %ebp, 44(%esp) + movl 4(%esp), %ebp + addl %edi, %ebx + roll $13, %ebx + xorl %ebx, %ecx + movl 20(%esp), %ebx + movl %ecx, 4(%esp) + addl %edi, %ecx + roll $18, %ecx + leal (%esi, %ebp), %edi + roll $7, %edi + xorl %edi, %ebx + movl 36(%esp), %edi + xorl %ecx, %edx + leal (%ebp, %ebx), %ecx + roll $9, %ecx + xorl %ecx, %edi + movl %edi, 20(%esp) + movl %ebx, %ecx + movl %edx, 36(%esp) + movl 24(%esp), %edx + addl %edi, %ebx + roll $13, %ebx + xorl %ebx, %esi + movl 28(%esp), %ebx + movl %esi, 24(%esp) + addl %edi, %esi + roll $18, %esi + leal (%ecx, %edx), %edi + roll $7, %edi + xorl %edi, %ebx + movl %ebx, 28(%esp) + xorl %esi, %ebp + movl 8(%esp), %esi + leal (%edx, %ebx), %edi + roll $9, %edi + xorl %edi, %esi + movl 40(%esp), %edi + movl %ebp, 8(%esp) + movl 44(%esp), %ebp + movl %esi, 40(%esp) + addl %esi, %ebx + roll $13, %ebx + xorl %ebx, %ecx + movl 4(%esp), %ebx + movl %ecx, 44(%esp) + addl %esi, %ecx + roll $18, %ecx + leal (%edi, %ebp), %esi + roll $7, %esi + xorl %esi, %ebx + movl %ebx, 4(%esp) + movl 20(%esp), %esi + xorl %ecx, %edx + leal (%ebp, %ebx), %ecx + roll $9, %ecx + xorl %ecx, %esi + movl %esi, 56(%esp) + movl 48(%esp), %ecx + movl %edx, 20(%esp) + movl 36(%esp), %edx + addl %esi, %ebx + roll $13, %ebx + xorl %ebx, %edi + movl 24(%esp), %ebx + movl %edi, 24(%esp) + addl %esi, %edi + roll $18, %edi + leal (%ecx, %edx), %esi + roll $7, %esi + xorl %esi, %ebx + movl %ebx, 60(%esp) + movl 12(%esp), %esi + xorl %edi, %ebp + leal (%edx, %ebx), %edi + roll $9, %edi + xorl %edi, %esi + movl %esi, 12(%esp) + movl 52(%esp), %edi + movl %ebp, 36(%esp) + movl 8(%esp), %ebp + addl %esi, %ebx + roll $13, %ebx + xorl %ebx, %ecx + movl 16(%esp), %ebx + movl %ecx, 16(%esp) + addl %esi, %ecx + roll $18, %ecx + leal (%edi, %ebp), %esi + roll $7, %esi + xorl %esi, %ebx + movl 32(%esp), %esi + xorl %ecx, %edx + leal (%ebp, %ebx), %ecx + roll $9, %ecx + xorl %ecx, %esi + movl %esi, 32(%esp) + movl %ebx, %ecx + movl %edx, 48(%esp) + movl 20(%esp), %edx + addl %esi, %ebx + roll $13, %ebx + xorl %ebx, %edi + movl 24(%esp), %ebx + movl %edi, 20(%esp) + addl %esi, %edi + roll $18, %edi + leal (%ecx, %edx), %esi + roll $7, %esi + xorl %esi, %ebx + movl %ebx, 8(%esp) + movl 12(%esp), %esi + xorl %edi, %ebp + leal (%edx, %ebx), %edi + roll $9, %edi + xorl %edi, %esi + movl %esi, 12(%esp) + movl 28(%esp), %edi + movl %ebp, 52(%esp) + movl 36(%esp), %ebp + addl %esi, %ebx + roll $13, %ebx + xorl %ebx, %ecx + movl 16(%esp), %ebx + movl %ecx, 16(%esp) + addl %esi, %ecx + roll $18, %ecx + leal (%edi, %ebp), %esi + roll $7, %esi + xorl %esi, %ebx + movl %ebx, 28(%esp) + movl 32(%esp), %esi + xorl %ecx, %edx + leal (%ebp, %ebx), %ecx + roll $9, %ecx + xorl %ecx, %esi + movl %esi, 32(%esp) + movl 4(%esp), %ecx + movl %edx, 4(%esp) + movl 48(%esp), %edx + addl %esi, %ebx + roll $13, %ebx + xorl %ebx, %edi + movl 20(%esp), %ebx + movl %edi, 20(%esp) + addl %esi, %edi + roll $18, %edi + leal (%ecx, %edx), %esi + roll $7, %esi + xorl %esi, %ebx + movl %ebx, 48(%esp) + movl 40(%esp), %esi + xorl %edi, %ebp + leal (%edx, %ebx), %edi + roll $9, %edi + xorl %edi, %esi + movl %esi, 36(%esp) + movl 60(%esp), %edi + movl %ebp, 24(%esp) + movl 52(%esp), %ebp + addl %esi, %ebx + roll $13, %ebx + xorl %ebx, %ecx + movl 44(%esp), %ebx + movl %ecx, 40(%esp) + addl %esi, %ecx + roll $18, %ecx + leal (%edi, %ebp), %esi + roll $7, %esi + xorl %esi, %ebx + movl %ebx, 52(%esp) + movl 56(%esp), %esi + xorl %ecx, %edx + leal (%ebp, %ebx), %ecx + roll $9, %ecx + xorl %ecx, %esi + movl %esi, 56(%esp) + addl %esi, %ebx + movl %edx, 44(%esp) + roll $13, %ebx + xorl %ebx, %edi + movl %edi, 60(%esp) + addl %esi, %edi + roll $18, %edi + xorl %edi, %ebp + movl %ebp, 64(%esp) +.endm + + .text + .p2align 5 +salsa8_core_gen: + salsa8_core_gen_quadround + salsa8_core_gen_quadround + ret + + + .text + .p2align 5 + .globl scrypt_core + .globl _scrypt_core +scrypt_core: +_scrypt_core: + pushl %ebx + pushl %ebp + pushl %edi + pushl %esi + + /* Check for SSE2 availability */ + movl $1, %eax + cpuid + andl $0x04000000, %edx + jnz scrypt_core_sse2 + +scrypt_core_gen: + movl 20(%esp), %edi + movl 24(%esp), %esi + subl $72, %esp + +.macro scrypt_core_macro1a p, q + movl \p(%edi), %eax + movl \q(%edi), %edx + movl %eax, \p(%esi) + movl %edx, \q(%esi) + xorl %edx, %eax + movl %eax, \p(%edi) + movl %eax, \p(%esp) +.endm + +.macro scrypt_core_macro1b p, q + movl \p(%edi), %eax + xorl \p(%esi, %edx), %eax + movl \q(%edi), %ebx + xorl \q(%esi, %edx), %ebx + movl %ebx, \q(%edi) + xorl %ebx, %eax + movl %eax, \p(%edi) + movl %eax, \p(%esp) +.endm + +.macro scrypt_core_macro2 p, q + movl \p(%esp), %eax + addl \p(%edi), %eax + movl %eax, \p(%edi) + xorl \q(%edi), %eax + movl %eax, \q(%edi) + movl %eax, \p(%esp) +.endm + +.macro scrypt_core_macro3 p, q + movl \p(%esp), %eax + addl \q(%edi), %eax + movl %eax, \q(%edi) +.endm + + leal 131072(%esi), %ecx +scrypt_core_gen_loop1: + movl %esi, 64(%esp) + movl %ecx, 68(%esp) + + scrypt_core_macro1a 0, 64 + scrypt_core_macro1a 4, 68 + scrypt_core_macro1a 8, 72 + scrypt_core_macro1a 12, 76 + scrypt_core_macro1a 16, 80 + scrypt_core_macro1a 20, 84 + scrypt_core_macro1a 24, 88 + scrypt_core_macro1a 28, 92 + scrypt_core_macro1a 32, 96 + scrypt_core_macro1a 36, 100 + scrypt_core_macro1a 40, 104 + scrypt_core_macro1a 44, 108 + scrypt_core_macro1a 48, 112 + scrypt_core_macro1a 52, 116 + scrypt_core_macro1a 56, 120 + scrypt_core_macro1a 60, 124 + + call salsa8_core_gen + + movl 92(%esp), %edi + scrypt_core_macro2 0, 64 + scrypt_core_macro2 4, 68 + scrypt_core_macro2 8, 72 + scrypt_core_macro2 12, 76 + scrypt_core_macro2 16, 80 + scrypt_core_macro2 20, 84 + scrypt_core_macro2 24, 88 + scrypt_core_macro2 28, 92 + scrypt_core_macro2 32, 96 + scrypt_core_macro2 36, 100 + scrypt_core_macro2 40, 104 + scrypt_core_macro2 44, 108 + scrypt_core_macro2 48, 112 + scrypt_core_macro2 52, 116 + scrypt_core_macro2 56, 120 + scrypt_core_macro2 60, 124 + + call salsa8_core_gen + + movl 92(%esp), %edi + scrypt_core_macro3 0, 64 + scrypt_core_macro3 4, 68 + scrypt_core_macro3 8, 72 + scrypt_core_macro3 12, 76 + scrypt_core_macro3 16, 80 + scrypt_core_macro3 20, 84 + scrypt_core_macro3 24, 88 + scrypt_core_macro3 28, 92 + scrypt_core_macro3 32, 96 + scrypt_core_macro3 36, 100 + scrypt_core_macro3 40, 104 + scrypt_core_macro3 44, 108 + scrypt_core_macro3 48, 112 + scrypt_core_macro3 52, 116 + scrypt_core_macro3 56, 120 + scrypt_core_macro3 60, 124 + + movl 64(%esp), %esi + movl 68(%esp), %ecx + addl $128, %esi + cmpl %ecx, %esi + jne scrypt_core_gen_loop1 + + movl 96(%esp), %esi + movl $1024, %ecx +scrypt_core_gen_loop2: + movl %ecx, 68(%esp) + + movl 64(%edi), %edx + andl $1023, %edx + shll $7, %edx + + scrypt_core_macro1b 0, 64 + scrypt_core_macro1b 4, 68 + scrypt_core_macro1b 8, 72 + scrypt_core_macro1b 12, 76 + scrypt_core_macro1b 16, 80 + scrypt_core_macro1b 20, 84 + scrypt_core_macro1b 24, 88 + scrypt_core_macro1b 28, 92 + scrypt_core_macro1b 32, 96 + scrypt_core_macro1b 36, 100 + scrypt_core_macro1b 40, 104 + scrypt_core_macro1b 44, 108 + scrypt_core_macro1b 48, 112 + scrypt_core_macro1b 52, 116 + scrypt_core_macro1b 56, 120 + scrypt_core_macro1b 60, 124 + + call salsa8_core_gen + + movl 92(%esp), %edi + scrypt_core_macro2 0, 64 + scrypt_core_macro2 4, 68 + scrypt_core_macro2 8, 72 + scrypt_core_macro2 12, 76 + scrypt_core_macro2 16, 80 + scrypt_core_macro2 20, 84 + scrypt_core_macro2 24, 88 + scrypt_core_macro2 28, 92 + scrypt_core_macro2 32, 96 + scrypt_core_macro2 36, 100 + scrypt_core_macro2 40, 104 + scrypt_core_macro2 44, 108 + scrypt_core_macro2 48, 112 + scrypt_core_macro2 52, 116 + scrypt_core_macro2 56, 120 + scrypt_core_macro2 60, 124 + + call salsa8_core_gen + + movl 92(%esp), %edi + movl 96(%esp), %esi + scrypt_core_macro3 0, 64 + scrypt_core_macro3 4, 68 + scrypt_core_macro3 8, 72 + scrypt_core_macro3 12, 76 + scrypt_core_macro3 16, 80 + scrypt_core_macro3 20, 84 + scrypt_core_macro3 24, 88 + scrypt_core_macro3 28, 92 + scrypt_core_macro3 32, 96 + scrypt_core_macro3 36, 100 + scrypt_core_macro3 40, 104 + scrypt_core_macro3 44, 108 + scrypt_core_macro3 48, 112 + scrypt_core_macro3 52, 116 + scrypt_core_macro3 56, 120 + scrypt_core_macro3 60, 124 + + movl 68(%esp), %ecx + subl $1, %ecx + ja scrypt_core_gen_loop2 + + addl $72, %esp + popl %esi + popl %edi + popl %ebp + popl %ebx + ret + + +.macro salsa8_core_sse2_doubleround + movdqa %xmm1, %xmm4 + paddd %xmm0, %xmm4 + movdqa %xmm4, %xmm5 + pslld $7, %xmm4 + psrld $25, %xmm5 + pxor %xmm4, %xmm3 + movdqa %xmm0, %xmm4 + pxor %xmm5, %xmm3 + + paddd %xmm3, %xmm4 + movdqa %xmm4, %xmm5 + pslld $9, %xmm4 + psrld $23, %xmm5 + pxor %xmm4, %xmm2 + movdqa %xmm3, %xmm4 + pxor %xmm5, %xmm2 + pshufd $0x93, %xmm3, %xmm3 + + paddd %xmm2, %xmm4 + movdqa %xmm4, %xmm5 + pslld $13, %xmm4 + psrld $19, %xmm5 + pxor %xmm4, %xmm1 + movdqa %xmm2, %xmm4 + pxor %xmm5, %xmm1 + pshufd $0x4e, %xmm2, %xmm2 + + paddd %xmm1, %xmm4 + movdqa %xmm4, %xmm5 + pslld $18, %xmm4 + psrld $14, %xmm5 + pxor %xmm4, %xmm0 + movdqa %xmm3, %xmm4 + pxor %xmm5, %xmm0 + pshufd $0x39, %xmm1, %xmm1 + + paddd %xmm0, %xmm4 + movdqa %xmm4, %xmm5 + pslld $7, %xmm4 + psrld $25, %xmm5 + pxor %xmm4, %xmm1 + movdqa %xmm0, %xmm4 + pxor %xmm5, %xmm1 + + paddd %xmm1, %xmm4 + movdqa %xmm4, %xmm5 + pslld $9, %xmm4 + psrld $23, %xmm5 + pxor %xmm4, %xmm2 + movdqa %xmm1, %xmm4 + pxor %xmm5, %xmm2 + pshufd $0x93, %xmm1, %xmm1 + + paddd %xmm2, %xmm4 + movdqa %xmm4, %xmm5 + pslld $13, %xmm4 + psrld $19, %xmm5 + pxor %xmm4, %xmm3 + movdqa %xmm2, %xmm4 + pxor %xmm5, %xmm3 + pshufd $0x4e, %xmm2, %xmm2 + + paddd %xmm3, %xmm4 + movdqa %xmm4, %xmm5 + pslld $18, %xmm4 + psrld $14, %xmm5 + pxor %xmm4, %xmm0 + pshufd $0x39, %xmm3, %xmm3 + pxor %xmm5, %xmm0 +.endm + +.macro salsa8_core_sse2 + salsa8_core_sse2_doubleround + salsa8_core_sse2_doubleround + salsa8_core_sse2_doubleround + salsa8_core_sse2_doubleround +.endm + + .p2align 5 +scrypt_core_sse2: + movl 20(%esp), %edi + movl 24(%esp), %esi + movl %esp, %ebp + subl $128, %esp + andl $-16, %esp + + scrypt_shuffle %edi, 0, %esp, 0 + scrypt_shuffle %edi, 64, %esp, 64 + + movdqa 96(%esp), %xmm6 + movdqa 112(%esp), %xmm7 + + movl %esi, %edx + leal 131072(%esi), %ecx +scrypt_core_sse2_loop1: + movdqa 0(%esp), %xmm0 + movdqa 16(%esp), %xmm1 + movdqa 32(%esp), %xmm2 + movdqa 48(%esp), %xmm3 + movdqa 64(%esp), %xmm4 + movdqa 80(%esp), %xmm5 + pxor %xmm4, %xmm0 + pxor %xmm5, %xmm1 + movdqa %xmm0, 0(%edx) + movdqa %xmm1, 16(%edx) + pxor %xmm6, %xmm2 + pxor %xmm7, %xmm3 + movdqa %xmm2, 32(%edx) + movdqa %xmm3, 48(%edx) + movdqa %xmm4, 64(%edx) + movdqa %xmm5, 80(%edx) + movdqa %xmm6, 96(%edx) + movdqa %xmm7, 112(%edx) + + salsa8_core_sse2 + paddd 0(%edx), %xmm0 + paddd 16(%edx), %xmm1 + paddd 32(%edx), %xmm2 + paddd 48(%edx), %xmm3 + movdqa %xmm0, 0(%esp) + movdqa %xmm1, 16(%esp) + movdqa %xmm2, 32(%esp) + movdqa %xmm3, 48(%esp) + + pxor 64(%esp), %xmm0 + pxor 80(%esp), %xmm1 + pxor %xmm6, %xmm2 + pxor %xmm7, %xmm3 + movdqa %xmm0, 64(%esp) + movdqa %xmm1, 80(%esp) + movdqa %xmm2, %xmm6 + movdqa %xmm3, %xmm7 + salsa8_core_sse2 + paddd 64(%esp), %xmm0 + paddd 80(%esp), %xmm1 + paddd %xmm2, %xmm6 + paddd %xmm3, %xmm7 + movdqa %xmm0, 64(%esp) + movdqa %xmm1, 80(%esp) + + addl $128, %edx + cmpl %ecx, %edx + jne scrypt_core_sse2_loop1 + + movdqa 64(%esp), %xmm4 + movdqa 80(%esp), %xmm5 + + movl $1024, %ecx +scrypt_core_sse2_loop2: + movd %xmm4, %edx + movdqa 0(%esp), %xmm0 + movdqa 16(%esp), %xmm1 + movdqa 32(%esp), %xmm2 + movdqa 48(%esp), %xmm3 + andl $1023, %edx + shll $7, %edx + pxor 0(%esi, %edx), %xmm0 + pxor 16(%esi, %edx), %xmm1 + pxor 32(%esi, %edx), %xmm2 + pxor 48(%esi, %edx), %xmm3 + + pxor %xmm4, %xmm0 + pxor %xmm5, %xmm1 + movdqa %xmm0, 0(%esp) + movdqa %xmm1, 16(%esp) + pxor %xmm6, %xmm2 + pxor %xmm7, %xmm3 + movdqa %xmm2, 32(%esp) + movdqa %xmm3, 48(%esp) + salsa8_core_sse2 + paddd 0(%esp), %xmm0 + paddd 16(%esp), %xmm1 + paddd 32(%esp), %xmm2 + paddd 48(%esp), %xmm3 + movdqa %xmm0, 0(%esp) + movdqa %xmm1, 16(%esp) + movdqa %xmm2, 32(%esp) + movdqa %xmm3, 48(%esp) + + pxor 64(%esi, %edx), %xmm0 + pxor 80(%esi, %edx), %xmm1 + pxor 96(%esi, %edx), %xmm2 + pxor 112(%esi, %edx), %xmm3 + pxor 64(%esp), %xmm0 + pxor 80(%esp), %xmm1 + pxor %xmm6, %xmm2 + pxor %xmm7, %xmm3 + movdqa %xmm0, 64(%esp) + movdqa %xmm1, 80(%esp) + movdqa %xmm2, %xmm6 + movdqa %xmm3, %xmm7 + salsa8_core_sse2 + paddd 64(%esp), %xmm0 + paddd 80(%esp), %xmm1 + paddd %xmm2, %xmm6 + paddd %xmm3, %xmm7 + movdqa %xmm0, %xmm4 + movdqa %xmm1, %xmm5 + movdqa %xmm0, 64(%esp) + movdqa %xmm1, 80(%esp) + + subl $1, %ecx + ja scrypt_core_sse2_loop2 + + movdqa %xmm6, 96(%esp) + movdqa %xmm7, 112(%esp) + + scrypt_shuffle %esp, 0, %edi, 0 + scrypt_shuffle %esp, 64, %edi, 64 + + movl %ebp, %esp + popl %esi + popl %edi + popl %ebp + popl %ebx + ret + +#endif diff --git a/src/crypto/scrypt/asm/scrypt-x86_64.S b/src/crypto/scrypt/asm/scrypt-x86_64.S new file mode 100644 index 00000000..36054f15 --- /dev/null +++ b/src/crypto/scrypt/asm/scrypt-x86_64.S @@ -0,0 +1,758 @@ +/* + * Copyright 2011-2012 pooler@litecoinpool.org + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(__linux__) && defined(__ELF__) + .section .note.GNU-stack,"",%progbits +#endif + +#if defined(__x86_64__) + +.macro scrypt_shuffle src, so, dest, do + movl \so+60(\src), %r8d + movl \so+44(\src), %r9d + movl \so+28(\src), %r10d + movl \so+12(\src), %r11d + movl %r8d, \do+12(\dest) + movl %r9d, \do+28(\dest) + movl %r10d, \do+44(\dest) + movl %r11d, \do+60(\dest) + movl \so+40(\src), %r8d + movl \so+8(\src), %r9d + movl \so+48(\src), %r10d + movl \so+16(\src), %r11d + movl %r8d, \do+8(\dest) + movl %r9d, \do+40(\dest) + movl %r10d, \do+16(\dest) + movl %r11d, \do+48(\dest) + movl \so+20(\src), %r8d + movl \so+4(\src), %r9d + movl \so+52(\src), %r10d + movl \so+36(\src), %r11d + movl %r8d, \do+4(\dest) + movl %r9d, \do+20(\dest) + movl %r10d, \do+36(\dest) + movl %r11d, \do+52(\dest) + movl \so+0(\src), %r8d + movl \so+24(\src), %r9d + movl \so+32(\src), %r10d + movl \so+56(\src), %r11d + movl %r8d, \do+0(\dest) + movl %r9d, \do+24(\dest) + movl %r10d, \do+32(\dest) + movl %r11d, \do+56(\dest) +.endm + + +.macro salsa8_core_gen_doubleround + movq 72(%rsp), %r15 + + leaq (%r14, %rdx), %rbp + roll $7, %ebp + xorl %ebp, %r9d + leaq (%rdi, %r15), %rbp + roll $7, %ebp + xorl %ebp, %r10d + leaq (%rdx, %r9), %rbp + roll $9, %ebp + xorl %ebp, %r11d + leaq (%r15, %r10), %rbp + roll $9, %ebp + xorl %ebp, %r13d + + leaq (%r9, %r11), %rbp + roll $13, %ebp + xorl %ebp, %r14d + leaq (%r10, %r13), %rbp + roll $13, %ebp + xorl %ebp, %edi + leaq (%r11, %r14), %rbp + roll $18, %ebp + xorl %ebp, %edx + leaq (%r13, %rdi), %rbp + roll $18, %ebp + xorl %ebp, %r15d + + movq 48(%rsp), %rbp + movq %r15, 72(%rsp) + + leaq (%rax, %rbp), %r15 + roll $7, %r15d + xorl %r15d, %ebx + leaq (%rbp, %rbx), %r15 + roll $9, %r15d + xorl %r15d, %ecx + leaq (%rbx, %rcx), %r15 + roll $13, %r15d + xorl %r15d, %eax + leaq (%rcx, %rax), %r15 + roll $18, %r15d + xorl %r15d, %ebp + + movq 88(%rsp), %r15 + movq %rbp, 48(%rsp) + + leaq (%r12, %r15), %rbp + roll $7, %ebp + xorl %ebp, %esi + leaq (%r15, %rsi), %rbp + roll $9, %ebp + xorl %ebp, %r8d + leaq (%rsi, %r8), %rbp + roll $13, %ebp + xorl %ebp, %r12d + leaq (%r8, %r12), %rbp + roll $18, %ebp + xorl %ebp, %r15d + + movq %r15, 88(%rsp) + movq 72(%rsp), %r15 + + leaq (%rsi, %rdx), %rbp + roll $7, %ebp + xorl %ebp, %edi + leaq (%r9, %r15), %rbp + roll $7, %ebp + xorl %ebp, %eax + leaq (%rdx, %rdi), %rbp + roll $9, %ebp + xorl %ebp, %ecx + leaq (%r15, %rax), %rbp + roll $9, %ebp + xorl %ebp, %r8d + + leaq (%rdi, %rcx), %rbp + roll $13, %ebp + xorl %ebp, %esi + leaq (%rax, %r8), %rbp + roll $13, %ebp + xorl %ebp, %r9d + leaq (%rcx, %rsi), %rbp + roll $18, %ebp + xorl %ebp, %edx + leaq (%r8, %r9), %rbp + roll $18, %ebp + xorl %ebp, %r15d + + movq 48(%rsp), %rbp + movq %r15, 72(%rsp) + + leaq (%r10, %rbp), %r15 + roll $7, %r15d + xorl %r15d, %r12d + leaq (%rbp, %r12), %r15 + roll $9, %r15d + xorl %r15d, %r11d + leaq (%r12, %r11), %r15 + roll $13, %r15d + xorl %r15d, %r10d + leaq (%r11, %r10), %r15 + roll $18, %r15d + xorl %r15d, %ebp + + movq 88(%rsp), %r15 + movq %rbp, 48(%rsp) + + leaq (%rbx, %r15), %rbp + roll $7, %ebp + xorl %ebp, %r14d + leaq (%r15, %r14), %rbp + roll $9, %ebp + xorl %ebp, %r13d + leaq (%r14, %r13), %rbp + roll $13, %ebp + xorl %ebp, %ebx + leaq (%r13, %rbx), %rbp + roll $18, %ebp + xorl %ebp, %r15d + + movq %r15, 88(%rsp) +.endm + + .text + .p2align 6 +salsa8_core_gen: + /* 0: %rdx, %rdi, %rcx, %rsi */ + movq 8(%rsp), %rdi + movq %rdi, %rdx + shrq $32, %rdi + movq 16(%rsp), %rsi + movq %rsi, %rcx + shrq $32, %rsi + /* 1: %r9, 72(%rsp), %rax, %r8 */ + movq 24(%rsp), %r8 + movq %r8, %r9 + shrq $32, %r8 + movq %r8, 72(%rsp) + movq 32(%rsp), %r8 + movq %r8, %rax + shrq $32, %r8 + /* 2: %r11, %r10, 48(%rsp), %r12 */ + movq 40(%rsp), %r10 + movq %r10, %r11 + shrq $32, %r10 + movq 48(%rsp), %r12 + /* movq %r12, %r13 */ + /* movq %r13, 48(%rsp) */ + shrq $32, %r12 + /* 3: %r14, %r13, %rbx, 88(%rsp) */ + movq 56(%rsp), %r13 + movq %r13, %r14 + shrq $32, %r13 + movq 64(%rsp), %r15 + movq %r15, %rbx + shrq $32, %r15 + movq %r15, 88(%rsp) + + salsa8_core_gen_doubleround + salsa8_core_gen_doubleround + salsa8_core_gen_doubleround + salsa8_core_gen_doubleround + + shlq $32, %rdi + xorq %rdi, %rdx + movq %rdx, 24(%rsp) + + shlq $32, %rsi + xorq %rsi, %rcx + movq %rcx, 32(%rsp) + + movl 72(%rsp), %edi + shlq $32, %rdi + xorq %rdi, %r9 + movq %r9, 40(%rsp) + + movl 48(%rsp), %ebp + shlq $32, %r8 + xorq %r8, %rax + movq %rax, 48(%rsp) + + shlq $32, %r10 + xorq %r10, %r11 + movq %r11, 56(%rsp) + + shlq $32, %r12 + xorq %r12, %rbp + movq %rbp, 64(%rsp) + + shlq $32, %r13 + xorq %r13, %r14 + movq %r14, 72(%rsp) + + movdqa 24(%rsp), %xmm0 + + shlq $32, %r15 + xorq %r15, %rbx + movq %rbx, 80(%rsp) + + movdqa 40(%rsp), %xmm1 + movdqa 56(%rsp), %xmm2 + movdqa 72(%rsp), %xmm3 + + ret + + + .text + .p2align 6 + .globl scrypt_core + .globl _scrypt_core +scrypt_core: +_scrypt_core: + pushq %rbx + pushq %rbp + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 +#if defined(WIN64) + subq $176, %rsp + movdqa %xmm6, 8(%rsp) + movdqa %xmm7, 24(%rsp) + movdqa %xmm8, 40(%rsp) + movdqa %xmm9, 56(%rsp) + movdqa %xmm10, 72(%rsp) + movdqa %xmm11, 88(%rsp) + movdqa %xmm12, 104(%rsp) + movdqa %xmm13, 120(%rsp) + movdqa %xmm14, 136(%rsp) + movdqa %xmm15, 152(%rsp) + pushq %rdi + pushq %rsi + movq %rcx, %rdi + movq %rdx, %rsi +#endif + +.macro scrypt_core_cleanup +#if defined(WIN64) + popq %rsi + popq %rdi + movdqa 8(%rsp), %xmm6 + movdqa 24(%rsp), %xmm7 + movdqa 40(%rsp), %xmm8 + movdqa 56(%rsp), %xmm9 + movdqa 72(%rsp), %xmm10 + movdqa 88(%rsp), %xmm11 + movdqa 104(%rsp), %xmm12 + movdqa 120(%rsp), %xmm13 + movdqa 136(%rsp), %xmm14 + movdqa 152(%rsp), %xmm15 + addq $176, %rsp +#endif + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %rbp + popq %rbx +.endm + + /* GenuineIntel processors have fast SIMD */ + xorl %eax, %eax + cpuid + cmpl $0x6c65746e, %ecx + jne scrypt_core_gen + cmpl $0x49656e69, %edx + jne scrypt_core_gen + cmpl $0x756e6547, %ebx + je scrypt_core_xmm + + .p2align 6 +scrypt_core_gen: + subq $136, %rsp + movdqa 0(%rdi), %xmm8 + movdqa 16(%rdi), %xmm9 + movdqa 32(%rdi), %xmm10 + movdqa 48(%rdi), %xmm11 + movdqa 64(%rdi), %xmm12 + movdqa 80(%rdi), %xmm13 + movdqa 96(%rdi), %xmm14 + movdqa 112(%rdi), %xmm15 + + leaq 131072(%rsi), %rcx + movq %rdi, 104(%rsp) + movq %rsi, 112(%rsp) + movq %rcx, 120(%rsp) +scrypt_core_gen_loop1: + movdqa %xmm8, 0(%rsi) + movdqa %xmm9, 16(%rsi) + movdqa %xmm10, 32(%rsi) + movdqa %xmm11, 48(%rsi) + movdqa %xmm12, 64(%rsi) + movdqa %xmm13, 80(%rsi) + movdqa %xmm14, 96(%rsi) + movdqa %xmm15, 112(%rsi) + + pxor %xmm12, %xmm8 + pxor %xmm13, %xmm9 + pxor %xmm14, %xmm10 + pxor %xmm15, %xmm11 + movdqa %xmm8, 0(%rsp) + movdqa %xmm9, 16(%rsp) + movdqa %xmm10, 32(%rsp) + movdqa %xmm11, 48(%rsp) + movq %rsi, 128(%rsp) + call salsa8_core_gen + paddd %xmm0, %xmm8 + paddd %xmm1, %xmm9 + paddd %xmm2, %xmm10 + paddd %xmm3, %xmm11 + + pxor %xmm8, %xmm12 + pxor %xmm9, %xmm13 + pxor %xmm10, %xmm14 + pxor %xmm11, %xmm15 + movdqa %xmm12, 0(%rsp) + movdqa %xmm13, 16(%rsp) + movdqa %xmm14, 32(%rsp) + movdqa %xmm15, 48(%rsp) + call salsa8_core_gen + movq 128(%rsp), %rsi + paddd %xmm0, %xmm12 + paddd %xmm1, %xmm13 + paddd %xmm2, %xmm14 + paddd %xmm3, %xmm15 + + addq $128, %rsi + movq 120(%rsp), %rcx + cmpq %rcx, %rsi + jne scrypt_core_gen_loop1 + + movq $1024, %rcx + movd %xmm12, %edx +scrypt_core_gen_loop2: + movq 112(%rsp), %rsi + andl $1023, %edx + shll $7, %edx + addq %rsi, %rdx + movdqa 0(%rdx), %xmm0 + movdqa 16(%rdx), %xmm1 + movdqa 32(%rdx), %xmm2 + movdqa 48(%rdx), %xmm3 + movdqa 64(%rdx), %xmm4 + movdqa 80(%rdx), %xmm5 + movdqa 96(%rdx), %xmm6 + movdqa 112(%rdx), %xmm7 + pxor %xmm0, %xmm8 + pxor %xmm1, %xmm9 + pxor %xmm2, %xmm10 + pxor %xmm3, %xmm11 + pxor %xmm4, %xmm12 + pxor %xmm5, %xmm13 + pxor %xmm6, %xmm14 + pxor %xmm7, %xmm15 + + pxor %xmm12, %xmm8 + pxor %xmm13, %xmm9 + pxor %xmm14, %xmm10 + pxor %xmm15, %xmm11 + movdqa %xmm8, 0(%rsp) + movdqa %xmm9, 16(%rsp) + movdqa %xmm10, 32(%rsp) + movdqa %xmm11, 48(%rsp) + movq %rcx, 128(%rsp) + call salsa8_core_gen + paddd %xmm0, %xmm8 + paddd %xmm1, %xmm9 + paddd %xmm2, %xmm10 + paddd %xmm3, %xmm11 + + pxor %xmm8, %xmm12 + pxor %xmm9, %xmm13 + pxor %xmm10, %xmm14 + pxor %xmm11, %xmm15 + movdqa %xmm12, 0(%rsp) + movdqa %xmm13, 16(%rsp) + movdqa %xmm14, 32(%rsp) + movdqa %xmm15, 48(%rsp) + call salsa8_core_gen + movq 128(%rsp), %rcx + addl 0(%rsp), %edx + paddd %xmm0, %xmm12 + paddd %xmm1, %xmm13 + paddd %xmm2, %xmm14 + paddd %xmm3, %xmm15 + + subq $1, %rcx + ja scrypt_core_gen_loop2 + + movq 104(%rsp), %rdi + movdqa %xmm8, 0(%rdi) + movdqa %xmm9, 16(%rdi) + movdqa %xmm10, 32(%rdi) + movdqa %xmm11, 48(%rdi) + movdqa %xmm12, 64(%rdi) + movdqa %xmm13, 80(%rdi) + movdqa %xmm14, 96(%rdi) + movdqa %xmm15, 112(%rdi) + + addq $136, %rsp + scrypt_core_cleanup + ret + + +.macro salsa8_core_xmm_doubleround + movdqa %xmm1, %xmm4 + paddd %xmm0, %xmm4 + movdqa %xmm4, %xmm5 + pslld $7, %xmm4 + psrld $25, %xmm5 + pxor %xmm4, %xmm3 + movdqa %xmm0, %xmm4 + pxor %xmm5, %xmm3 + + paddd %xmm3, %xmm4 + movdqa %xmm4, %xmm5 + pslld $9, %xmm4 + psrld $23, %xmm5 + pxor %xmm4, %xmm2 + movdqa %xmm3, %xmm4 + pxor %xmm5, %xmm2 + pshufd $0x93, %xmm3, %xmm3 + + paddd %xmm2, %xmm4 + movdqa %xmm4, %xmm5 + pslld $13, %xmm4 + psrld $19, %xmm5 + pxor %xmm4, %xmm1 + movdqa %xmm2, %xmm4 + pxor %xmm5, %xmm1 + pshufd $0x4e, %xmm2, %xmm2 + + paddd %xmm1, %xmm4 + movdqa %xmm4, %xmm5 + pslld $18, %xmm4 + psrld $14, %xmm5 + pxor %xmm4, %xmm0 + movdqa %xmm3, %xmm4 + pxor %xmm5, %xmm0 + pshufd $0x39, %xmm1, %xmm1 + + paddd %xmm0, %xmm4 + movdqa %xmm4, %xmm5 + pslld $7, %xmm4 + psrld $25, %xmm5 + pxor %xmm4, %xmm1 + movdqa %xmm0, %xmm4 + pxor %xmm5, %xmm1 + + paddd %xmm1, %xmm4 + movdqa %xmm4, %xmm5 + pslld $9, %xmm4 + psrld $23, %xmm5 + pxor %xmm4, %xmm2 + movdqa %xmm1, %xmm4 + pxor %xmm5, %xmm2 + pshufd $0x93, %xmm1, %xmm1 + + paddd %xmm2, %xmm4 + movdqa %xmm4, %xmm5 + pslld $13, %xmm4 + psrld $19, %xmm5 + pxor %xmm4, %xmm3 + movdqa %xmm2, %xmm4 + pxor %xmm5, %xmm3 + pshufd $0x4e, %xmm2, %xmm2 + + paddd %xmm3, %xmm4 + movdqa %xmm4, %xmm5 + pslld $18, %xmm4 + psrld $14, %xmm5 + pxor %xmm4, %xmm0 + pshufd $0x39, %xmm3, %xmm3 + pxor %xmm5, %xmm0 +.endm + +.macro salsa8_core_xmm + salsa8_core_xmm_doubleround + salsa8_core_xmm_doubleround + salsa8_core_xmm_doubleround + salsa8_core_xmm_doubleround +.endm + + .p2align 6 +scrypt_core_xmm: + pcmpeqw %xmm1, %xmm1 + psrlq $32, %xmm1 + + movdqa 0(%rdi), %xmm8 + movdqa 16(%rdi), %xmm11 + movdqa 32(%rdi), %xmm10 + movdqa 48(%rdi), %xmm9 + movdqa %xmm8, %xmm0 + pxor %xmm11, %xmm8 + pand %xmm1, %xmm8 + pxor %xmm11, %xmm8 + pxor %xmm10, %xmm11 + pand %xmm1, %xmm11 + pxor %xmm10, %xmm11 + pxor %xmm9, %xmm10 + pand %xmm1, %xmm10 + pxor %xmm9, %xmm10 + pxor %xmm0, %xmm9 + pand %xmm1, %xmm9 + pxor %xmm0, %xmm9 + movdqa %xmm8, %xmm0 + pshufd $0x4e, %xmm10, %xmm10 + punpcklqdq %xmm10, %xmm8 + punpckhqdq %xmm0, %xmm10 + movdqa %xmm11, %xmm0 + pshufd $0x4e, %xmm9, %xmm9 + punpcklqdq %xmm9, %xmm11 + punpckhqdq %xmm0, %xmm9 + + movdqa 64(%rdi), %xmm12 + movdqa 80(%rdi), %xmm15 + movdqa 96(%rdi), %xmm14 + movdqa 112(%rdi), %xmm13 + movdqa %xmm12, %xmm0 + pxor %xmm15, %xmm12 + pand %xmm1, %xmm12 + pxor %xmm15, %xmm12 + pxor %xmm14, %xmm15 + pand %xmm1, %xmm15 + pxor %xmm14, %xmm15 + pxor %xmm13, %xmm14 + pand %xmm1, %xmm14 + pxor %xmm13, %xmm14 + pxor %xmm0, %xmm13 + pand %xmm1, %xmm13 + pxor %xmm0, %xmm13 + movdqa %xmm12, %xmm0 + pshufd $0x4e, %xmm14, %xmm14 + punpcklqdq %xmm14, %xmm12 + punpckhqdq %xmm0, %xmm14 + movdqa %xmm15, %xmm0 + pshufd $0x4e, %xmm13, %xmm13 + punpcklqdq %xmm13, %xmm15 + punpckhqdq %xmm0, %xmm13 + + movq %rsi, %rdx + leaq 131072(%rsi), %rcx +scrypt_core_xmm_loop1: + pxor %xmm12, %xmm8 + pxor %xmm13, %xmm9 + pxor %xmm14, %xmm10 + pxor %xmm15, %xmm11 + movdqa %xmm8, 0(%rdx) + movdqa %xmm9, 16(%rdx) + movdqa %xmm10, 32(%rdx) + movdqa %xmm11, 48(%rdx) + movdqa %xmm12, 64(%rdx) + movdqa %xmm13, 80(%rdx) + movdqa %xmm14, 96(%rdx) + movdqa %xmm15, 112(%rdx) + + movdqa %xmm8, %xmm0 + movdqa %xmm9, %xmm1 + movdqa %xmm10, %xmm2 + movdqa %xmm11, %xmm3 + salsa8_core_xmm + paddd %xmm0, %xmm8 + paddd %xmm1, %xmm9 + paddd %xmm2, %xmm10 + paddd %xmm3, %xmm11 + + pxor %xmm8, %xmm12 + pxor %xmm9, %xmm13 + pxor %xmm10, %xmm14 + pxor %xmm11, %xmm15 + movdqa %xmm12, %xmm0 + movdqa %xmm13, %xmm1 + movdqa %xmm14, %xmm2 + movdqa %xmm15, %xmm3 + salsa8_core_xmm + paddd %xmm0, %xmm12 + paddd %xmm1, %xmm13 + paddd %xmm2, %xmm14 + paddd %xmm3, %xmm15 + + addq $128, %rdx + cmpq %rcx, %rdx + jne scrypt_core_xmm_loop1 + + movq $1024, %rcx +scrypt_core_xmm_loop2: + movd %xmm12, %edx + andl $1023, %edx + shll $7, %edx + pxor 0(%rsi, %rdx), %xmm8 + pxor 16(%rsi, %rdx), %xmm9 + pxor 32(%rsi, %rdx), %xmm10 + pxor 48(%rsi, %rdx), %xmm11 + + pxor %xmm12, %xmm8 + pxor %xmm13, %xmm9 + pxor %xmm14, %xmm10 + pxor %xmm15, %xmm11 + movdqa %xmm8, %xmm0 + movdqa %xmm9, %xmm1 + movdqa %xmm10, %xmm2 + movdqa %xmm11, %xmm3 + salsa8_core_xmm + paddd %xmm0, %xmm8 + paddd %xmm1, %xmm9 + paddd %xmm2, %xmm10 + paddd %xmm3, %xmm11 + + pxor 64(%rsi, %rdx), %xmm12 + pxor 80(%rsi, %rdx), %xmm13 + pxor 96(%rsi, %rdx), %xmm14 + pxor 112(%rsi, %rdx), %xmm15 + pxor %xmm8, %xmm12 + pxor %xmm9, %xmm13 + pxor %xmm10, %xmm14 + pxor %xmm11, %xmm15 + movdqa %xmm12, %xmm0 + movdqa %xmm13, %xmm1 + movdqa %xmm14, %xmm2 + movdqa %xmm15, %xmm3 + salsa8_core_xmm + paddd %xmm0, %xmm12 + paddd %xmm1, %xmm13 + paddd %xmm2, %xmm14 + paddd %xmm3, %xmm15 + + subq $1, %rcx + ja scrypt_core_xmm_loop2 + + pcmpeqw %xmm1, %xmm1 + psrlq $32, %xmm1 + + movdqa %xmm8, %xmm0 + pxor %xmm9, %xmm8 + pand %xmm1, %xmm8 + pxor %xmm9, %xmm8 + pxor %xmm10, %xmm9 + pand %xmm1, %xmm9 + pxor %xmm10, %xmm9 + pxor %xmm11, %xmm10 + pand %xmm1, %xmm10 + pxor %xmm11, %xmm10 + pxor %xmm0, %xmm11 + pand %xmm1, %xmm11 + pxor %xmm0, %xmm11 + movdqa %xmm8, %xmm0 + pshufd $0x4e, %xmm10, %xmm10 + punpcklqdq %xmm10, %xmm8 + punpckhqdq %xmm0, %xmm10 + movdqa %xmm9, %xmm0 + pshufd $0x4e, %xmm11, %xmm11 + punpcklqdq %xmm11, %xmm9 + punpckhqdq %xmm0, %xmm11 + movdqa %xmm8, 0(%rdi) + movdqa %xmm11, 16(%rdi) + movdqa %xmm10, 32(%rdi) + movdqa %xmm9, 48(%rdi) + + movdqa %xmm12, %xmm0 + pxor %xmm13, %xmm12 + pand %xmm1, %xmm12 + pxor %xmm13, %xmm12 + pxor %xmm14, %xmm13 + pand %xmm1, %xmm13 + pxor %xmm14, %xmm13 + pxor %xmm15, %xmm14 + pand %xmm1, %xmm14 + pxor %xmm15, %xmm14 + pxor %xmm0, %xmm15 + pand %xmm1, %xmm15 + pxor %xmm0, %xmm15 + movdqa %xmm12, %xmm0 + pshufd $0x4e, %xmm14, %xmm14 + punpcklqdq %xmm14, %xmm12 + punpckhqdq %xmm0, %xmm14 + movdqa %xmm13, %xmm0 + pshufd $0x4e, %xmm15, %xmm15 + punpcklqdq %xmm15, %xmm13 + punpckhqdq %xmm0, %xmm15 + movdqa %xmm12, 64(%rdi) + movdqa %xmm15, 80(%rdi) + movdqa %xmm14, 96(%rdi) + movdqa %xmm13, 112(%rdi) + + scrypt_core_cleanup + ret + +#endif diff --git a/src/crypto/scrypt/generic/obj/.gitignore b/src/crypto/scrypt/generic/obj/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/src/crypto/scrypt/generic/obj/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/src/crypto/scrypt/generic/scrypt-generic.cpp b/src/crypto/scrypt/generic/scrypt-generic.cpp new file mode 100644 index 00000000..c4dc5b5c --- /dev/null +++ b/src/crypto/scrypt/generic/scrypt-generic.cpp @@ -0,0 +1,138 @@ +/* + * Copyright 2009 Colin Percival, 2011 ArtForz, 2011 pooler, 2013 Balthazar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file was originally written by Colin Percival as part of the Tarsnap + * online backup system. + */ + +#include "scrypt.h" + +#ifdef _MSC_VER +#define INLINE __inline +#else +#define INLINE inline +#endif + +// Generic scrypt_core implementation + +static INLINE void xor_salsa8(uint32_t B[16], const uint32_t Bx[16]) +{ + uint32_t x00,x01,x02,x03,x04,x05,x06,x07,x08,x09,x10,x11,x12,x13,x14,x15; + int8_t i; + + x00 = (B[0] ^= Bx[0]); + x01 = (B[1] ^= Bx[1]); + x02 = (B[2] ^= Bx[2]); + x03 = (B[3] ^= Bx[3]); + x04 = (B[4] ^= Bx[4]); + x05 = (B[5] ^= Bx[5]); + x06 = (B[6] ^= Bx[6]); + x07 = (B[7] ^= Bx[7]); + x08 = (B[8] ^= Bx[8]); + x09 = (B[9] ^= Bx[9]); + x10 = (B[10] ^= Bx[10]); + x11 = (B[11] ^= Bx[11]); + x12 = (B[12] ^= Bx[12]); + x13 = (B[13] ^= Bx[13]); + x14 = (B[14] ^= Bx[14]); + x15 = (B[15] ^= Bx[15]); + for (i = 0; i < 8; i += 2) { +#define R(a, b) (((a) << (b)) | ((a) >> (32 - (b)))) + /* Operate on columns. */ + x04 ^= R(x00+x12, 7); x09 ^= R(x05+x01, 7); + x14 ^= R(x10+x06, 7); x03 ^= R(x15+x11, 7); + + x08 ^= R(x04+x00, 9); x13 ^= R(x09+x05, 9); + x02 ^= R(x14+x10, 9); x07 ^= R(x03+x15, 9); + + x12 ^= R(x08+x04,13); x01 ^= R(x13+x09,13); + x06 ^= R(x02+x14,13); x11 ^= R(x07+x03,13); + + x00 ^= R(x12+x08,18); x05 ^= R(x01+x13,18); + x10 ^= R(x06+x02,18); x15 ^= R(x11+x07,18); + + /* Operate on rows. */ + x01 ^= R(x00+x03, 7); x06 ^= R(x05+x04, 7); + x11 ^= R(x10+x09, 7); x12 ^= R(x15+x14, 7); + + x02 ^= R(x01+x00, 9); x07 ^= R(x06+x05, 9); + x08 ^= R(x11+x10, 9); x13 ^= R(x12+x15, 9); + + x03 ^= R(x02+x01,13); x04 ^= R(x07+x06,13); + x09 ^= R(x08+x11,13); x14 ^= R(x13+x12,13); + + x00 ^= R(x03+x02,18); x05 ^= R(x04+x07,18); + x10 ^= R(x09+x08,18); x15 ^= R(x14+x13,18); +#undef R + } + B[0] += x00; + B[1] += x01; + B[2] += x02; + B[3] += x03; + B[4] += x04; + B[5] += x05; + B[6] += x06; + B[7] += x07; + B[8] += x08; + B[9] += x09; + B[10] += x10; + B[11] += x11; + B[12] += x12; + B[13] += x13; + B[14] += x14; + B[15] += x15; +} + +/* cpu and memory intensive function to transform a 80 byte buffer into a 32 byte output + scratchpad size needs to be at least 63 + (128 * r * p) + (256 * r + 64) + (128 * r * N) bytes + r = 1, p = 1, N = 1024 + */ +uint256 scrypt_blockhash(const uint8_t* input) +{ + uint8_t scratchpad[SCRYPT_BUFFER_SIZE]; + uint32_t X[32]; + uint256 result = 0; + + uint32_t *V = (uint32_t *)(((uintptr_t)(scratchpad) + 63) & ~ (uintptr_t)(63)); + + PKCS5_PBKDF2_HMAC((const char*)input, 80, input, 80, 1, EVP_sha256(), 128, (unsigned char *)X); + + for (uint16_t i = 0; i < 1024; i++) { + memcpy(&V[i * 32], X, 128); + xor_salsa8(&X[0], &X[16]); + xor_salsa8(&X[16], &X[0]); + } + for (uint16_t i = 0; i < 1024; i++) { + uint16_t j = 32 * (X[16] & 1023); + for (uint16_t k = 0; k < 32; k++) + X[k] ^= V[j + k]; + xor_salsa8(&X[0], &X[16]); + xor_salsa8(&X[16], &X[0]); + } + + PKCS5_PBKDF2_HMAC((const char*)input, 80, (const unsigned char*)X, 128, 1, EVP_sha256(), 32, (unsigned char*)&result); + + return result; +} diff --git a/src/crypto/scrypt/intrin/obj/.gitignore b/src/crypto/scrypt/intrin/obj/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/src/crypto/scrypt/intrin/obj/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/src/crypto/scrypt/intrin/scrypt-sse2.cpp b/src/crypto/scrypt/intrin/scrypt-sse2.cpp new file mode 100644 index 00000000..63c0a7ac --- /dev/null +++ b/src/crypto/scrypt/intrin/scrypt-sse2.cpp @@ -0,0 +1,152 @@ +/* + * Copyright 2009 Colin Percival, 2011 ArtForz, 2012-2013 pooler + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file was originally written by Colin Percival as part of the Tarsnap + * online backup system. + */ + +#include +#include "scrypt.h" + +static inline uint32_t le32dec(const void *pp) +{ + const uint8_t *p = (uint8_t const *)pp; + return ((uint32_t)(p[0]) + ((uint32_t)(p[1]) << 8) + + ((uint32_t)(p[2]) << 16) + ((uint32_t)(p[3]) << 24)); +} + +static inline void le32enc(void *pp, uint32_t x) +{ + uint8_t *p = (uint8_t *)pp; + p[0] = x & 0xff; + p[1] = (x >> 8) & 0xff; + p[2] = (x >> 16) & 0xff; + p[3] = (x >> 24) & 0xff; +} + +static inline void xor_salsa8_sse2(__m128i B[4], const __m128i Bx[4]) +{ + __m128i X0, X1, X2, X3; + __m128i T; + int i; + + X0 = B[0] = _mm_xor_si128(B[0], Bx[0]); + X1 = B[1] = _mm_xor_si128(B[1], Bx[1]); + X2 = B[2] = _mm_xor_si128(B[2], Bx[2]); + X3 = B[3] = _mm_xor_si128(B[3], Bx[3]); + + for (i = 0; i < 8; i += 2) { + /* Operate on "columns". */ + T = _mm_add_epi32(X0, X3); + X1 = _mm_xor_si128(X1, _mm_slli_epi32(T, 7)); + X1 = _mm_xor_si128(X1, _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X1, X0); + X2 = _mm_xor_si128(X2, _mm_slli_epi32(T, 9)); + X2 = _mm_xor_si128(X2, _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X1); + X3 = _mm_xor_si128(X3, _mm_slli_epi32(T, 13)); + X3 = _mm_xor_si128(X3, _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X3, X2); + X0 = _mm_xor_si128(X0, _mm_slli_epi32(T, 18)); + X0 = _mm_xor_si128(X0, _mm_srli_epi32(T, 14)); + + /* Rearrange data. */ + X1 = _mm_shuffle_epi32(X1, 0x93); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x39); + + /* Operate on "rows". */ + T = _mm_add_epi32(X0, X1); + X3 = _mm_xor_si128(X3, _mm_slli_epi32(T, 7)); + X3 = _mm_xor_si128(X3, _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X3, X0); + X2 = _mm_xor_si128(X2, _mm_slli_epi32(T, 9)); + X2 = _mm_xor_si128(X2, _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X3); + X1 = _mm_xor_si128(X1, _mm_slli_epi32(T, 13)); + X1 = _mm_xor_si128(X1, _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X1, X2); + X0 = _mm_xor_si128(X0, _mm_slli_epi32(T, 18)); + X0 = _mm_xor_si128(X0, _mm_srli_epi32(T, 14)); + + /* Rearrange data. */ + X1 = _mm_shuffle_epi32(X1, 0x39); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x93); + } + + B[0] = _mm_add_epi32(B[0], X0); + B[1] = _mm_add_epi32(B[1], X1); + B[2] = _mm_add_epi32(B[2], X2); + B[3] = _mm_add_epi32(B[3], X3); +} + +uint256 scrypt_blockhash(const uint8_t* input) +{ + uint256 result = 0; + uint8_t scratchpad[SCRYPT_BUFFER_SIZE]; + uint8_t B[128]; + union { + __m128i i128[8]; + uint32_t u32[32]; + } X; + __m128i *V; + uint32_t i, j, k; + + V = (__m128i *)(((uintptr_t)(scratchpad) + 63) & ~ (uintptr_t)(63)); + + void *const tmp = const_cast(input); + PKCS5_PBKDF2_HMAC(static_cast(tmp), 80, input, 80, 1, EVP_sha256(), 128, B); + + for (k = 0; k < 2; k++) { + for (i = 0; i < 16; i++) { + X.u32[k * 16 + i] = le32dec(&B[(k * 16 + (i * 5 % 16)) * 4]); + } + } + + for (i = 0; i < 1024; i++) { + for (k = 0; k < 8; k++) + V[i * 8 + k] = X.i128[k]; + xor_salsa8_sse2(&X.i128[0], &X.i128[4]); + xor_salsa8_sse2(&X.i128[4], &X.i128[0]); + } + for (i = 0; i < 1024; i++) { + j = 8 * (X.u32[16] & 1023); + for (k = 0; k < 8; k++) + X.i128[k] = _mm_xor_si128(X.i128[k], V[j + k]); + xor_salsa8_sse2(&X.i128[0], &X.i128[4]); + xor_salsa8_sse2(&X.i128[4], &X.i128[0]); + } + + for (k = 0; k < 2; k++) { + for (i = 0; i < 16; i++) { + le32enc(&B[(k * 16 + (i * 5 % 16)) * 4], X.u32[k * 16 + i]); + } + } + + PKCS5_PBKDF2_HMAC(static_cast(tmp), 80, B, 128, 1, EVP_sha256(), 32, (unsigned char*)&result); + + return result; +} diff --git a/src/crypto/sha2/asm/obj/.gitignore b/src/crypto/sha2/asm/obj/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/src/crypto/sha2/asm/obj/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/src/crypto/sha2/asm/sha2-arm.S b/src/crypto/sha2/asm/sha2-arm.S new file mode 100644 index 00000000..eb10dd90 --- /dev/null +++ b/src/crypto/sha2/asm/sha2-arm.S @@ -0,0 +1,613 @@ +/* + * Copyright 2012 pooler@litecoinpool.org + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. See COPYING for more details. + */ + +#if defined(__arm__) && defined(__APCS_32__) + +.macro sha256_k + .align 2 + .long 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5 + .long 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5 + .long 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3 + .long 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174 + .long 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc + .long 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da + .long 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7 + .long 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967 + .long 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13 + .long 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85 + .long 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3 + .long 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070 + .long 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5 + .long 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3 + .long 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208 + .long 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +.endm + +.macro sha256_extend_doubleround_core i, rw, ra, rb, ry, rz + mov r12, \ry, ror #17 + add r11, r11, \ra + eor r12, r12, \ry, ror #19 + mov \ra, lr, ror #7 + eor r12, r12, \ry, lsr #10 + eor \ra, \ra, lr, ror #18 + add r12, r12, r11 + ldr r11, [\rw, #(\i+2)*4] + eor \ra, \ra, lr, lsr #3 + add \ra, \ra, r12 + + mov r12, \rz, ror #17 + str \ra, [\rw, #(\i+16)*4] + add lr, lr, \rb + eor r12, r12, \rz, ror #19 + mov \rb, r11, ror #7 + eor r12, r12, \rz, lsr #10 + eor \rb, \rb, r11, ror #18 + add lr, lr, r12 + eor \rb, \rb, r11, lsr #3 + add \rb, \rb, lr +.endm + +.macro sha256_extend_doubleround_head i, rw, ra, rb, ry, rz + ldr lr, [\rw, #(\i+1)*4] + sha256_extend_doubleround_core \i, \rw, \ra, \rb, \ry, \rz + ldr lr, [\rw, #(\i+3)*4] +.endm + +.macro sha256_extend_doubleround_body i, rw, ra, rb, ry, rz + str \rz, [\rw, #(\i+15)*4] + sha256_extend_doubleround_core \i, \rw, \ra, \rb, \ry, \rz + ldr lr, [\rw, #(\i+3)*4] +.endm + +.macro sha256_extend_doubleround_foot i, rw, ra, rb, ry, rz + str \rz, [\rw, #(\i+15)*4] + sha256_extend_doubleround_core \i, \rw, \ra, \rb, \ry, \rz + str \rb, [\rw, #(\i+17)*4] +.endm + +.macro sha256_main_round i, ka, rw, ra, rb, rc, rd, re, rf, rg, rh + ldr r12, [\rw, #(\i)*4] + and r3, \rf, \re + bic lr, \rg, \re + orr lr, lr, r3 + ldr r3, \ka + (\i)*4 + add \rh, \rh, lr + eor lr, \re, \re, ror #5 + add \rh, \rh, r12 + eor lr, lr, \re, ror #19 + add \rh, \rh, r3 + eor r3, \ra, \rb + add \rh, \rh, lr, ror #6 + + and r3, r3, \rc + eor r12, \ra, \ra, ror #11 + and lr, \ra, \rb + eor r12, r12, \ra, ror #20 + eor lr, lr, r3 + add r3, \rh, lr + add \rh, \rh, \rd + add \rd, r3, r12, ror #2 +.endm + +.macro sha256_main_quadround i, ka, rw + sha256_main_round \i+0, \ka, \rw, r4, r5, r6, r7, r8, r9, r10, r11 + sha256_main_round \i+1, \ka, \rw, r7, r4, r5, r6, r11, r8, r9, r10 + sha256_main_round \i+2, \ka, \rw, r6, r7, r4, r5, r10, r11, r8, r9 + sha256_main_round \i+3, \ka, \rw, r5, r6, r7, r4, r9, r10, r11, r8 +.endm + + + .text + .code 32 + .align 2 + .globl sha256_transform + .globl _sha256_transform +#ifdef __ELF__ + .type sha256_transform, %function +#endif +sha256_transform: +_sha256_transform: + stmfd sp!, {r4-r11, lr} + cmp r2, #0 + sub sp, sp, #64*4 + bne sha256_transform_swap + + ldmia r1!, {r4-r11} + stmia sp, {r4-r11} + add r3, sp, #8*4 + ldmia r1, {r4-r11} + stmia r3, {r4-r11} + b sha256_transform_extend + +.macro bswap rd, rn + eor r12, \rn, \rn, ror #16 + bic r12, r12, #0x00ff0000 + mov \rd, \rn, ror #8 + eor \rd, \rd, r12, lsr #8 +.endm + +sha256_transform_swap: + ldmia r1!, {r4-r11} + bswap r4, r4 + bswap r5, r5 + bswap r6, r6 + bswap r7, r7 + bswap r8, r8 + bswap r9, r9 + bswap r10, r10 + bswap r11, r11 + stmia sp, {r4-r11} + add r3, sp, #8*4 + ldmia r1, {r4-r11} + bswap r4, r4 + bswap r5, r5 + bswap r6, r6 + bswap r7, r7 + bswap r8, r8 + bswap r9, r9 + bswap r10, r10 + bswap r11, r11 + stmia r3, {r4-r11} + +sha256_transform_extend: + add r12, sp, #9*4 + ldr r11, [sp, #0*4] + ldmia r12, {r4-r10} + sha256_extend_doubleround_head 0, sp, r4, r5, r9, r10 + sha256_extend_doubleround_body 2, sp, r6, r7, r4, r5 + sha256_extend_doubleround_body 4, sp, r8, r9, r6, r7 + sha256_extend_doubleround_body 6, sp, r10, r4, r8, r9 + sha256_extend_doubleround_body 8, sp, r5, r6, r10, r4 + sha256_extend_doubleround_body 10, sp, r7, r8, r5, r6 + sha256_extend_doubleround_body 12, sp, r9, r10, r7, r8 + sha256_extend_doubleround_body 14, sp, r4, r5, r9, r10 + sha256_extend_doubleround_body 16, sp, r6, r7, r4, r5 + sha256_extend_doubleround_body 18, sp, r8, r9, r6, r7 + sha256_extend_doubleround_body 20, sp, r10, r4, r8, r9 + sha256_extend_doubleround_body 22, sp, r5, r6, r10, r4 + sha256_extend_doubleround_body 24, sp, r7, r8, r5, r6 + sha256_extend_doubleround_body 26, sp, r9, r10, r7, r8 + sha256_extend_doubleround_body 28, sp, r4, r5, r9, r10 + sha256_extend_doubleround_body 30, sp, r6, r7, r4, r5 + sha256_extend_doubleround_body 32, sp, r8, r9, r6, r7 + sha256_extend_doubleround_body 34, sp, r10, r4, r8, r9 + sha256_extend_doubleround_body 36, sp, r5, r6, r10, r4 + sha256_extend_doubleround_body 38, sp, r7, r8, r5, r6 + sha256_extend_doubleround_body 40, sp, r9, r10, r7, r8 + sha256_extend_doubleround_body 42, sp, r4, r5, r9, r10 + sha256_extend_doubleround_body 44, sp, r6, r7, r4, r5 + sha256_extend_doubleround_foot 46, sp, r8, r9, r6, r7 + + ldmia r0, {r4-r11} + sha256_main_quadround 0, sha256_transform_k, sp + sha256_main_quadround 4, sha256_transform_k, sp + sha256_main_quadround 8, sha256_transform_k, sp + sha256_main_quadround 12, sha256_transform_k, sp + sha256_main_quadround 16, sha256_transform_k, sp + sha256_main_quadround 20, sha256_transform_k, sp + sha256_main_quadround 24, sha256_transform_k, sp + sha256_main_quadround 28, sha256_transform_k, sp + b sha256_transform_k_over +sha256_transform_k: + sha256_k +sha256_transform_k_over: + sha256_main_quadround 32, sha256_transform_k, sp + sha256_main_quadround 36, sha256_transform_k, sp + sha256_main_quadround 40, sha256_transform_k, sp + sha256_main_quadround 44, sha256_transform_k, sp + sha256_main_quadround 48, sha256_transform_k, sp + sha256_main_quadround 52, sha256_transform_k, sp + sha256_main_quadround 56, sha256_transform_k, sp + sha256_main_quadround 60, sha256_transform_k, sp + + ldmia r0, {r1, r2, r3, r12} + add r4, r4, r1 + add r5, r5, r2 + add r6, r6, r3 + add r7, r7, r12 + stmia r0!, {r4-r7} + ldmia r0, {r1, r2, r3, r12} + add r8, r8, r1 + add r9, r9, r2 + add r10, r10, r3 + add r11, r11, r12 + stmia r0, {r8-r11} + + add sp, sp, #64*4 +#ifdef __thumb__ + ldmfd sp!, {r4-r11, lr} + bx lr +#else + ldmfd sp!, {r4-r11, pc} +#endif + +.macro sha256_main_round_red i, ka, rw, rd, re, rf, rg, rh + ldr r12, [\rw, #(\i)*4] + and r3, \rf, \re + bic lr, \rg, \re + add \rh, \rh, \rd + orr lr, lr, r3 + ldr r3, \ka + (\i)*4 + add \rh, \rh, lr + eor lr, \re, \re, ror #5 + add \rh, \rh, r12 + eor lr, lr, \re, ror #19 + add \rh, \rh, r3 + add \rh, \rh, lr, ror #6 +.endm + + .text + .code 32 + .align 2 + .globl sha256_init_4way + .globl _sha256_init_4way +#ifdef __ELF__ + .type sha256_init_4way, %function +#endif +sha256_init_4way: +_sha256_init_4way: + adr r12, sha256_4h + vldmia r12, {q8-q15} + vstmia r0, {q8-q15} + bx lr + .align 4 +sha256_4h: + .long 0x6a09e667, 0x6a09e667, 0x6a09e667, 0x6a09e667 + .long 0xbb67ae85, 0xbb67ae85, 0xbb67ae85, 0xbb67ae85 + .long 0x3c6ef372, 0x3c6ef372, 0x3c6ef372, 0x3c6ef372 + .long 0xa54ff53a, 0xa54ff53a, 0xa54ff53a, 0xa54ff53a + .long 0x510e527f, 0x510e527f, 0x510e527f, 0x510e527f + .long 0x9b05688c, 0x9b05688c, 0x9b05688c, 0x9b05688c + .long 0x1f83d9ab, 0x1f83d9ab, 0x1f83d9ab, 0x1f83d9ab + .long 0x5be0cd19, 0x5be0cd19, 0x5be0cd19, 0x5be0cd19 + +.macro sha256_4k + .long 0x428a2f98, 0x428a2f98, 0x428a2f98, 0x428a2f98 + .long 0x71374491, 0x71374491, 0x71374491, 0x71374491 + .long 0xb5c0fbcf, 0xb5c0fbcf, 0xb5c0fbcf, 0xb5c0fbcf + .long 0xe9b5dba5, 0xe9b5dba5, 0xe9b5dba5, 0xe9b5dba5 + .long 0x3956c25b, 0x3956c25b, 0x3956c25b, 0x3956c25b + .long 0x59f111f1, 0x59f111f1, 0x59f111f1, 0x59f111f1 + .long 0x923f82a4, 0x923f82a4, 0x923f82a4, 0x923f82a4 + .long 0xab1c5ed5, 0xab1c5ed5, 0xab1c5ed5, 0xab1c5ed5 + .long 0xd807aa98, 0xd807aa98, 0xd807aa98, 0xd807aa98 + .long 0x12835b01, 0x12835b01, 0x12835b01, 0x12835b01 + .long 0x243185be, 0x243185be, 0x243185be, 0x243185be + .long 0x550c7dc3, 0x550c7dc3, 0x550c7dc3, 0x550c7dc3 + .long 0x72be5d74, 0x72be5d74, 0x72be5d74, 0x72be5d74 + .long 0x80deb1fe, 0x80deb1fe, 0x80deb1fe, 0x80deb1fe + .long 0x9bdc06a7, 0x9bdc06a7, 0x9bdc06a7, 0x9bdc06a7 + .long 0xc19bf174, 0xc19bf174, 0xc19bf174, 0xc19bf174 + .long 0xe49b69c1, 0xe49b69c1, 0xe49b69c1, 0xe49b69c1 + .long 0xefbe4786, 0xefbe4786, 0xefbe4786, 0xefbe4786 + .long 0x0fc19dc6, 0x0fc19dc6, 0x0fc19dc6, 0x0fc19dc6 + .long 0x240ca1cc, 0x240ca1cc, 0x240ca1cc, 0x240ca1cc + .long 0x2de92c6f, 0x2de92c6f, 0x2de92c6f, 0x2de92c6f + .long 0x4a7484aa, 0x4a7484aa, 0x4a7484aa, 0x4a7484aa + .long 0x5cb0a9dc, 0x5cb0a9dc, 0x5cb0a9dc, 0x5cb0a9dc + .long 0x76f988da, 0x76f988da, 0x76f988da, 0x76f988da + .long 0x983e5152, 0x983e5152, 0x983e5152, 0x983e5152 + .long 0xa831c66d, 0xa831c66d, 0xa831c66d, 0xa831c66d + .long 0xb00327c8, 0xb00327c8, 0xb00327c8, 0xb00327c8 + .long 0xbf597fc7, 0xbf597fc7, 0xbf597fc7, 0xbf597fc7 + .long 0xc6e00bf3, 0xc6e00bf3, 0xc6e00bf3, 0xc6e00bf3 + .long 0xd5a79147, 0xd5a79147, 0xd5a79147, 0xd5a79147 + .long 0x06ca6351, 0x06ca6351, 0x06ca6351, 0x06ca6351 + .long 0x14292967, 0x14292967, 0x14292967, 0x14292967 + .long 0x27b70a85, 0x27b70a85, 0x27b70a85, 0x27b70a85 + .long 0x2e1b2138, 0x2e1b2138, 0x2e1b2138, 0x2e1b2138 + .long 0x4d2c6dfc, 0x4d2c6dfc, 0x4d2c6dfc, 0x4d2c6dfc + .long 0x53380d13, 0x53380d13, 0x53380d13, 0x53380d13 + .long 0x650a7354, 0x650a7354, 0x650a7354, 0x650a7354 + .long 0x766a0abb, 0x766a0abb, 0x766a0abb, 0x766a0abb + .long 0x81c2c92e, 0x81c2c92e, 0x81c2c92e, 0x81c2c92e + .long 0x92722c85, 0x92722c85, 0x92722c85, 0x92722c85 + .long 0xa2bfe8a1, 0xa2bfe8a1, 0xa2bfe8a1, 0xa2bfe8a1 + .long 0xa81a664b, 0xa81a664b, 0xa81a664b, 0xa81a664b + .long 0xc24b8b70, 0xc24b8b70, 0xc24b8b70, 0xc24b8b70 + .long 0xc76c51a3, 0xc76c51a3, 0xc76c51a3, 0xc76c51a3 + .long 0xd192e819, 0xd192e819, 0xd192e819, 0xd192e819 + .long 0xd6990624, 0xd6990624, 0xd6990624, 0xd6990624 + .long 0xf40e3585, 0xf40e3585, 0xf40e3585, 0xf40e3585 + .long 0x106aa070, 0x106aa070, 0x106aa070, 0x106aa070 + .long 0x19a4c116, 0x19a4c116, 0x19a4c116, 0x19a4c116 + .long 0x1e376c08, 0x1e376c08, 0x1e376c08, 0x1e376c08 + .long 0x2748774c, 0x2748774c, 0x2748774c, 0x2748774c + .long 0x34b0bcb5, 0x34b0bcb5, 0x34b0bcb5, 0x34b0bcb5 + .long 0x391c0cb3, 0x391c0cb3, 0x391c0cb3, 0x391c0cb3 + .long 0x4ed8aa4a, 0x4ed8aa4a, 0x4ed8aa4a, 0x4ed8aa4a + .long 0x5b9cca4f, 0x5b9cca4f, 0x5b9cca4f, 0x5b9cca4f + .long 0x682e6ff3, 0x682e6ff3, 0x682e6ff3, 0x682e6ff3 + .long 0x748f82ee, 0x748f82ee, 0x748f82ee, 0x748f82ee + .long 0x78a5636f, 0x78a5636f, 0x78a5636f, 0x78a5636f + .long 0x84c87814, 0x84c87814, 0x84c87814, 0x84c87814 + .long 0x8cc70208, 0x8cc70208, 0x8cc70208, 0x8cc70208 + .long 0x90befffa, 0x90befffa, 0x90befffa, 0x90befffa + .long 0xa4506ceb, 0xa4506ceb, 0xa4506ceb, 0xa4506ceb + .long 0xbef9a3f7, 0xbef9a3f7, 0xbef9a3f7, 0xbef9a3f7 + .long 0xc67178f2, 0xc67178f2, 0xc67178f2, 0xc67178f2 +.endm + +.macro sha256_4way_extend_doubleround_core i, rr, rw, ra, rb, ry, rz + vadd.u32 q5, q5, \ra + veor.u32 q4, q4, q0 + vshr.u32 q0, \ry, #19 + vshl.u32 q1, \ry, #32-19 + veor.u32 q4, q4, q0 + vshr.u32 \ra, q6, #7 + vshl.u32 q0, q6, #32-7 + veor.u32 q4, q4, q1 + veor.u32 \ra, \ra, q0 + vshr.u32 q1, \ry, #10 + vshr.u32 q0, q6, #18 + veor.u32 q4, q4, q1 + veor.u32 \ra, \ra, q0 + vshl.u32 q1, q6, #32-18 + vshr.u32 q0, q6, #3 + veor.u32 \ra, \ra, q1 + vadd.u32 q4, q4, q5 + veor.u32 \ra, \ra, q0 + vld1.u32 {q5}, [\rr]! + vadd.u32 \ra, \ra, q4 + + vshr.u32 q4, \rz, #17 + vshl.u32 q0, \rz, #32-17 + vadd.u32 q6, q6, \rb + vst1.u32 {\ra}, [\rw]! + veor.u32 q4, q4, q0 + vshr.u32 q0, \rz, #19 + vshl.u32 q1, \rz, #32-19 + veor.u32 q4, q4, q0 + vshr.u32 \rb, q5, #7 + veor.u32 q4, q4, q1 + vshl.u32 q0, q5, #32-7 + vshr.u32 q1, \rz, #10 + veor.u32 \rb, \rb, q0 + vshr.u32 q0, q5, #18 + veor.u32 q4, q4, q1 + veor.u32 \rb, \rb, q0 + vshl.u32 q1, q5, #32-18 + vshr.u32 q0, q5, #3 + veor.u32 \rb, \rb, q1 + vadd.u32 q1, q6, q4 + veor.u32 \rb, \rb, q0 +.endm + +.macro sha256_4way_extend_doubleround_head i, rr, rw, ra, rb, ry, rz + vld1.u32 {q6}, [\rr]! + vshr.u32 q4, \ry, #17 + vshl.u32 q0, \ry, #32-17 + sha256_4way_extend_doubleround_core \i, \rr, \rw, \ra, \rb, \ry, \rz + vld1.u32 {q6}, [\rr]! + vadd.u32 \rb, \rb, q1 +.endm + +.macro sha256_4way_extend_doubleround_body i, rr, rw, ra, rb, ry, rz + vshr.u32 q4, \ry, #17 + vshl.u32 q0, \ry, #32-17 + vst1.u32 {\rz}, [\rw]! + sha256_4way_extend_doubleround_core \i, \rr, \rw, \ra, \rb, \ry, \rz + vld1.u32 {q6}, [\rr]! + vadd.u32 \rb, \rb, q1 +.endm + +.macro sha256_4way_extend_doubleround_foot i, rr, rw, ra, rb, ry, rz + vshr.u32 q4, \ry, #17 + vshl.u32 q0, \ry, #32-17 + vst1.u32 {\rz}, [\rw]! + sha256_4way_extend_doubleround_core \i, \rr, \rw, \ra, \rb, \ry, \rz + vadd.u32 \rb, \rb, q1 + vst1.u32 {\rb}, [\rw]! +.endm + +.macro sha256_4way_main_round i, rk, rw, ra, rb, rc, rd, re, rf, rg, rh + vld1.u32 {q8}, [\rw]! + vand.u32 q9, \rf, \re + vbic.u32 q10, \rg, \re + vshr.u32 q11, \re, #5 + vorr.u32 q10, q10, q9 + vld1.u32 {q9}, [\rk]! + vadd.u32 \rh, \rh, q10 + vshl.u32 q12, \re, #32-5 + veor.u32 q10, \re, q11 + vshr.u32 q11, \re, #19 + veor.u32 q10, q10, q12 + vshl.u32 q12, \re, #32-19 + veor.u32 q10, q10, q11 + vadd.u32 \rh, \rh, q8 + veor.u32 q10, q10, q12 + vadd.u32 \rh, \rh, q9 + veor.u32 q9, \ra, \rb + vshr.u32 q11, q10, #6 + vshl.u32 q13, q10, #32-6 + vadd.u32 \rh, \rh, q11 + + vshr.u32 q11, \ra, #11 + vshl.u32 q12, \ra, #32-11 + veor.u32 q8, \ra, q11 + vand.u32 q10, \ra, \rb + veor.u32 q8, q8, q12 + vshr.u32 q11, \ra, #20 + vshl.u32 q12, \ra, #32-20 + veor.u32 q8, q8, q11 + vand.u32 q9, q9, \rc + veor.u32 q8, q8, q12 + vadd.u32 \rh, \rh, q13 + veor.u32 q10, q10, q9 + vshr.u32 q11, q8, #2 + vshl.u32 q12, q8, #32-2 + vadd.u32 q9, \rh, q10 + vadd.u32 q12, q12, q11 + vadd.u32 \rh, \rh, \rd + vadd.u32 \rd, q9, q12 +.endm + +.macro sha256_4way_main_quadround i, rk, rw + sha256_4way_main_round \i+0, \rk, \rw, q0, q1, q2, q3, q4, q5, q6, q7 + sha256_4way_main_round \i+1, \rk, \rw, q3, q0, q1, q2, q7, q4, q5, q6 + sha256_4way_main_round \i+2, \rk, \rw, q2, q3, q0, q1, q6, q7, q4, q5 + sha256_4way_main_round \i+3, \rk, \rw, q1, q2, q3, q0, q5, q6, q7, q4 +.endm + + + .text + .code 32 + .align 2 + .globl sha256_transform_4way + .globl _sha256_transform_4way +#ifdef __ELF__ + .type sha256_transform_4way, %function +#endif +sha256_transform_4way: +_sha256_transform_4way: + stmfd sp!, {r4, lr} + vpush {q4-q7} + mov r12, sp + sub sp, sp, #64*16 + bic sp, sp, #63 + cmp r2, #0 + bne sha256_transform_4way_swap + + vldmia r1!, {q0-q7} + vstmia sp, {q0-q7} + add r3, sp, #8*16 + vldmia r1, {q8-q15} + vstmia r3, {q8-q15} + b sha256_transform_4way_extend + +sha256_transform_4way_swap: + vldmia r1!, {q0-q7} + vrev32.8 q0, q0 + vrev32.8 q1, q1 + vrev32.8 q2, q2 + vrev32.8 q3, q3 + vldmia r1, {q8-q15} + vrev32.8 q4, q4 + vrev32.8 q5, q5 + vrev32.8 q6, q6 + vrev32.8 q7, q7 + vstmia sp, {q0-q7} + vrev32.8 q8, q8 + vrev32.8 q9, q9 + vrev32.8 q10, q10 + vrev32.8 q11, q11 + vrev32.8 q12, q12 + vrev32.8 q13, q13 + vrev32.8 q14, q14 + vrev32.8 q15, q15 + add r3, sp, #8*16 + vstmia r3, {q8-q15} + +sha256_transform_4way_extend: + add r1, sp, #1*16 + add r2, sp, #16*16 + vmov.u32 q5, q0 + sha256_4way_extend_doubleround_head 0, r1, r2, q9, q10, q14, q15 + sha256_4way_extend_doubleround_body 2, r1, r2, q11, q12, q9, q10 + sha256_4way_extend_doubleround_body 4, r1, r2, q13, q14, q11, q12 + sha256_4way_extend_doubleround_body 6, r1, r2, q15, q9, q13, q14 + sha256_4way_extend_doubleround_body 8, r1, r2, q10, q11, q15, q9 + sha256_4way_extend_doubleround_body 10, r1, r2, q12, q13, q10, q11 + sha256_4way_extend_doubleround_body 12, r1, r2, q14, q15, q12, q13 + sha256_4way_extend_doubleround_body 14, r1, r2, q9, q10, q14, q15 + sha256_4way_extend_doubleround_body 16, r1, r2, q11, q12, q9, q10 + sha256_4way_extend_doubleround_body 18, r1, r2, q13, q14, q11, q12 + sha256_4way_extend_doubleround_body 20, r1, r2, q15, q9, q13, q14 + sha256_4way_extend_doubleround_body 22, r1, r2, q10, q11, q15, q9 + sha256_4way_extend_doubleround_body 24, r1, r2, q12, q13, q10, q11 + sha256_4way_extend_doubleround_body 26, r1, r2, q14, q15, q12, q13 + sha256_4way_extend_doubleround_body 28, r1, r2, q9, q10, q14, q15 + sha256_4way_extend_doubleround_body 30, r1, r2, q11, q12, q9, q10 + sha256_4way_extend_doubleround_body 32, r1, r2, q13, q14, q11, q12 + sha256_4way_extend_doubleround_body 34, r1, r2, q15, q9, q13, q14 + sha256_4way_extend_doubleround_body 36, r1, r2, q10, q11, q15, q9 + sha256_4way_extend_doubleround_body 38, r1, r2, q12, q13, q10, q11 + sha256_4way_extend_doubleround_body 40, r1, r2, q14, q15, q12, q13 + sha256_4way_extend_doubleround_body 42, r1, r2, q9, q10, q14, q15 + sha256_4way_extend_doubleround_body 44, r1, r2, q11, q12, q9, q10 + sha256_4way_extend_doubleround_foot 46, r1, r2, q13, q14, q11, q12 + + vldmia r0, {q0-q7} + adr r4, sha256_transform_4way_4k + b sha256_transform_4way_4k_over + .align 4 +sha256_transform_4way_4k: + sha256_4k +sha256_transform_4way_4k_over: + sha256_4way_main_quadround 0, r4, sp + sha256_4way_main_quadround 4, r4, sp + sha256_4way_main_quadround 8, r4, sp + sha256_4way_main_quadround 12, r4, sp + sha256_4way_main_quadround 16, r4, sp + sha256_4way_main_quadround 20, r4, sp + sha256_4way_main_quadround 24, r4, sp + sha256_4way_main_quadround 28, r4, sp + sha256_4way_main_quadround 32, r4, sp + sha256_4way_main_quadround 36, r4, sp + sha256_4way_main_quadround 40, r4, sp + sha256_4way_main_quadround 44, r4, sp + sha256_4way_main_quadround 48, r4, sp + sha256_4way_main_quadround 52, r4, sp + sha256_4way_main_quadround 56, r4, sp + sha256_4way_main_quadround 60, r4, sp + + vldmia r0, {q8-q15} + vadd.u32 q0, q0, q8 + vadd.u32 q1, q1, q9 + vadd.u32 q2, q2, q10 + vadd.u32 q3, q3, q11 + vadd.u32 q4, q4, q12 + vadd.u32 q5, q5, q13 + vadd.u32 q6, q6, q14 + vadd.u32 q7, q7, q15 + vstmia r0, {q0-q7} + + mov sp, r12 + vpop {q4-q7} + ldmfd sp!, {r4, pc} + + +.macro sha256_4way_main_round_red i, rk, rw, rd, re, rf, rg, rh + vld1.u32 {q8}, [\rw]! + vand.u32 q9, \rf, \re + vbic.u32 q10, \rg, \re + vshr.u32 q11, \re, #5 + vorr.u32 q10, q10, q9 + vshl.u32 q12, \re, #32-5 + vadd.u32 \rh, \rh, q10 + veor.u32 q10, \re, q11 + vshr.u32 q11, \re, #19 + veor.u32 q10, q10, q12 + vshl.u32 q12, \re, #32-19 + veor.u32 q10, q10, q11 + vadd.u32 \rh, \rh, q8 + veor.u32 q10, q10, q12 + vld1.u32 {q9}, [\rk]! + vadd.u32 \rh, \rh, \rd + vshr.u32 q11, q10, #6 + vadd.u32 \rh, \rh, q9 + vshl.u32 q13, q10, #32-6 + vadd.u32 \rh, \rh, q11 + vadd.u32 \rh, \rh, q13 +.endm + + .text + .code 32 + .align 2 + .globl sha256_use_4way + .globl _sha256_use_4way +#ifdef __ELF__ + .type sha256_use_4way, %function +#endif +sha256_use_4way: +_sha256_use_4way: + mov r0, #1 + bx lr + +#endif diff --git a/src/crypto/sha2/asm/sha2-x86.S b/src/crypto/sha2/asm/sha2-x86.S new file mode 100644 index 00000000..f0cd5f43 --- /dev/null +++ b/src/crypto/sha2/asm/sha2-x86.S @@ -0,0 +1,626 @@ +/* + * Copyright 2012 pooler@litecoinpool.org + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. See COPYING for more details. + */ + +#if defined(__linux__) && defined(__ELF__) + .section .note.GNU-stack,"",%progbits +#endif + +#if defined(__i386__) + + .data + .p2align 7 +sha256_4h: + .long 0x6a09e667, 0x6a09e667, 0x6a09e667, 0x6a09e667 + .long 0xbb67ae85, 0xbb67ae85, 0xbb67ae85, 0xbb67ae85 + .long 0x3c6ef372, 0x3c6ef372, 0x3c6ef372, 0x3c6ef372 + .long 0xa54ff53a, 0xa54ff53a, 0xa54ff53a, 0xa54ff53a + .long 0x510e527f, 0x510e527f, 0x510e527f, 0x510e527f + .long 0x9b05688c, 0x9b05688c, 0x9b05688c, 0x9b05688c + .long 0x1f83d9ab, 0x1f83d9ab, 0x1f83d9ab, 0x1f83d9ab + .long 0x5be0cd19, 0x5be0cd19, 0x5be0cd19, 0x5be0cd19 + + .data + .p2align 7 +sha256_4k: + .long 0x428a2f98, 0x428a2f98, 0x428a2f98, 0x428a2f98 + .long 0x71374491, 0x71374491, 0x71374491, 0x71374491 + .long 0xb5c0fbcf, 0xb5c0fbcf, 0xb5c0fbcf, 0xb5c0fbcf + .long 0xe9b5dba5, 0xe9b5dba5, 0xe9b5dba5, 0xe9b5dba5 + .long 0x3956c25b, 0x3956c25b, 0x3956c25b, 0x3956c25b + .long 0x59f111f1, 0x59f111f1, 0x59f111f1, 0x59f111f1 + .long 0x923f82a4, 0x923f82a4, 0x923f82a4, 0x923f82a4 + .long 0xab1c5ed5, 0xab1c5ed5, 0xab1c5ed5, 0xab1c5ed5 + .long 0xd807aa98, 0xd807aa98, 0xd807aa98, 0xd807aa98 + .long 0x12835b01, 0x12835b01, 0x12835b01, 0x12835b01 + .long 0x243185be, 0x243185be, 0x243185be, 0x243185be + .long 0x550c7dc3, 0x550c7dc3, 0x550c7dc3, 0x550c7dc3 + .long 0x72be5d74, 0x72be5d74, 0x72be5d74, 0x72be5d74 + .long 0x80deb1fe, 0x80deb1fe, 0x80deb1fe, 0x80deb1fe + .long 0x9bdc06a7, 0x9bdc06a7, 0x9bdc06a7, 0x9bdc06a7 + .long 0xc19bf174, 0xc19bf174, 0xc19bf174, 0xc19bf174 + .long 0xe49b69c1, 0xe49b69c1, 0xe49b69c1, 0xe49b69c1 + .long 0xefbe4786, 0xefbe4786, 0xefbe4786, 0xefbe4786 + .long 0x0fc19dc6, 0x0fc19dc6, 0x0fc19dc6, 0x0fc19dc6 + .long 0x240ca1cc, 0x240ca1cc, 0x240ca1cc, 0x240ca1cc + .long 0x2de92c6f, 0x2de92c6f, 0x2de92c6f, 0x2de92c6f + .long 0x4a7484aa, 0x4a7484aa, 0x4a7484aa, 0x4a7484aa + .long 0x5cb0a9dc, 0x5cb0a9dc, 0x5cb0a9dc, 0x5cb0a9dc + .long 0x76f988da, 0x76f988da, 0x76f988da, 0x76f988da + .long 0x983e5152, 0x983e5152, 0x983e5152, 0x983e5152 + .long 0xa831c66d, 0xa831c66d, 0xa831c66d, 0xa831c66d + .long 0xb00327c8, 0xb00327c8, 0xb00327c8, 0xb00327c8 + .long 0xbf597fc7, 0xbf597fc7, 0xbf597fc7, 0xbf597fc7 + .long 0xc6e00bf3, 0xc6e00bf3, 0xc6e00bf3, 0xc6e00bf3 + .long 0xd5a79147, 0xd5a79147, 0xd5a79147, 0xd5a79147 + .long 0x06ca6351, 0x06ca6351, 0x06ca6351, 0x06ca6351 + .long 0x14292967, 0x14292967, 0x14292967, 0x14292967 + .long 0x27b70a85, 0x27b70a85, 0x27b70a85, 0x27b70a85 + .long 0x2e1b2138, 0x2e1b2138, 0x2e1b2138, 0x2e1b2138 + .long 0x4d2c6dfc, 0x4d2c6dfc, 0x4d2c6dfc, 0x4d2c6dfc + .long 0x53380d13, 0x53380d13, 0x53380d13, 0x53380d13 + .long 0x650a7354, 0x650a7354, 0x650a7354, 0x650a7354 + .long 0x766a0abb, 0x766a0abb, 0x766a0abb, 0x766a0abb + .long 0x81c2c92e, 0x81c2c92e, 0x81c2c92e, 0x81c2c92e + .long 0x92722c85, 0x92722c85, 0x92722c85, 0x92722c85 + .long 0xa2bfe8a1, 0xa2bfe8a1, 0xa2bfe8a1, 0xa2bfe8a1 + .long 0xa81a664b, 0xa81a664b, 0xa81a664b, 0xa81a664b + .long 0xc24b8b70, 0xc24b8b70, 0xc24b8b70, 0xc24b8b70 + .long 0xc76c51a3, 0xc76c51a3, 0xc76c51a3, 0xc76c51a3 + .long 0xd192e819, 0xd192e819, 0xd192e819, 0xd192e819 + .long 0xd6990624, 0xd6990624, 0xd6990624, 0xd6990624 + .long 0xf40e3585, 0xf40e3585, 0xf40e3585, 0xf40e3585 + .long 0x106aa070, 0x106aa070, 0x106aa070, 0x106aa070 + .long 0x19a4c116, 0x19a4c116, 0x19a4c116, 0x19a4c116 + .long 0x1e376c08, 0x1e376c08, 0x1e376c08, 0x1e376c08 + .long 0x2748774c, 0x2748774c, 0x2748774c, 0x2748774c + .long 0x34b0bcb5, 0x34b0bcb5, 0x34b0bcb5, 0x34b0bcb5 + .long 0x391c0cb3, 0x391c0cb3, 0x391c0cb3, 0x391c0cb3 + .long 0x4ed8aa4a, 0x4ed8aa4a, 0x4ed8aa4a, 0x4ed8aa4a + .long 0x5b9cca4f, 0x5b9cca4f, 0x5b9cca4f, 0x5b9cca4f + .long 0x682e6ff3, 0x682e6ff3, 0x682e6ff3, 0x682e6ff3 + .long 0x748f82ee, 0x748f82ee, 0x748f82ee, 0x748f82ee + .long 0x78a5636f, 0x78a5636f, 0x78a5636f, 0x78a5636f + .long 0x84c87814, 0x84c87814, 0x84c87814, 0x84c87814 + .long 0x8cc70208, 0x8cc70208, 0x8cc70208, 0x8cc70208 + .long 0x90befffa, 0x90befffa, 0x90befffa, 0x90befffa + .long 0xa4506ceb, 0xa4506ceb, 0xa4506ceb, 0xa4506ceb + .long 0xbef9a3f7, 0xbef9a3f7, 0xbef9a3f7, 0xbef9a3f7 + .long 0xc67178f2, 0xc67178f2, 0xc67178f2, 0xc67178f2 + + .text + .p2align 5 + .globl sha256_init_4way + .globl _sha256_init_4way +sha256_init_4way: +_sha256_init_4way: + movl 4(%esp), %edx + movdqa sha256_4h+0, %xmm0 + movdqa sha256_4h+16, %xmm1 + movdqa sha256_4h+32, %xmm2 + movdqa sha256_4h+48, %xmm3 + movdqu %xmm0, 0(%edx) + movdqu %xmm1, 16(%edx) + movdqu %xmm2, 32(%edx) + movdqu %xmm3, 48(%edx) + movdqa sha256_4h+64, %xmm0 + movdqa sha256_4h+80, %xmm1 + movdqa sha256_4h+96, %xmm2 + movdqa sha256_4h+112, %xmm3 + movdqu %xmm0, 64(%edx) + movdqu %xmm1, 80(%edx) + movdqu %xmm2, 96(%edx) + movdqu %xmm3, 112(%edx) + ret + + +.macro sha256_sse2_extend_round i + movdqa (\i-15)*16(%eax), %xmm0 + movdqa %xmm0, %xmm2 + psrld $3, %xmm0 + movdqa %xmm0, %xmm1 + pslld $14, %xmm2 + psrld $4, %xmm1 + pxor %xmm1, %xmm0 + pxor %xmm2, %xmm0 + psrld $11, %xmm1 + pslld $11, %xmm2 + pxor %xmm1, %xmm0 + pxor %xmm2, %xmm0 + paddd (\i-16)*16(%eax), %xmm0 + paddd (\i-7)*16(%eax), %xmm0 + + movdqa %xmm3, %xmm2 + psrld $10, %xmm3 + pslld $13, %xmm2 + movdqa %xmm3, %xmm1 + psrld $7, %xmm1 + pxor %xmm1, %xmm3 + pxor %xmm2, %xmm3 + psrld $2, %xmm1 + pslld $2, %xmm2 + pxor %xmm1, %xmm3 + pxor %xmm2, %xmm3 + paddd %xmm0, %xmm3 + movdqa %xmm3, \i*16(%eax) +.endm + +.macro sha256_sse2_extend_doubleround i + movdqa (\i-15)*16(%eax), %xmm0 + movdqa (\i-14)*16(%eax), %xmm4 + movdqa %xmm0, %xmm2 + movdqa %xmm4, %xmm6 + psrld $3, %xmm0 + psrld $3, %xmm4 + movdqa %xmm0, %xmm1 + movdqa %xmm4, %xmm5 + pslld $14, %xmm2 + pslld $14, %xmm6 + psrld $4, %xmm1 + psrld $4, %xmm5 + pxor %xmm1, %xmm0 + pxor %xmm5, %xmm4 + psrld $11, %xmm1 + psrld $11, %xmm5 + pxor %xmm2, %xmm0 + pxor %xmm6, %xmm4 + pslld $11, %xmm2 + pslld $11, %xmm6 + pxor %xmm1, %xmm0 + pxor %xmm5, %xmm4 + pxor %xmm2, %xmm0 + pxor %xmm6, %xmm4 + + paddd (\i-16)*16(%eax), %xmm0 + paddd (\i-15)*16(%eax), %xmm4 + + movdqa %xmm3, %xmm2 + movdqa %xmm7, %xmm6 + psrld $10, %xmm3 + psrld $10, %xmm7 + movdqa %xmm3, %xmm1 + movdqa %xmm7, %xmm5 + pslld $13, %xmm2 + pslld $13, %xmm6 + psrld $7, %xmm1 + psrld $7, %xmm5 + + paddd (\i-7)*16(%eax), %xmm0 + paddd (\i-6)*16(%eax), %xmm4 + + pxor %xmm1, %xmm3 + pxor %xmm5, %xmm7 + psrld $2, %xmm1 + psrld $2, %xmm5 + pxor %xmm2, %xmm3 + pxor %xmm6, %xmm7 + pslld $2, %xmm2 + pslld $2, %xmm6 + pxor %xmm1, %xmm3 + pxor %xmm5, %xmm7 + pxor %xmm2, %xmm3 + pxor %xmm6, %xmm7 + + paddd %xmm0, %xmm3 + paddd %xmm4, %xmm7 + movdqa %xmm3, \i*16(%eax) + movdqa %xmm7, (\i+1)*16(%eax) +.endm + +.macro sha256_sse2_main_round i + movdqa 16*(\i)(%eax), %xmm6 + + movdqa %xmm0, %xmm1 + movdqa 16(%esp), %xmm2 + pandn %xmm2, %xmm1 + paddd 32(%esp), %xmm6 + + movdqa %xmm2, 32(%esp) + movdqa 0(%esp), %xmm2 + movdqa %xmm2, 16(%esp) + + pand %xmm0, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm0, 0(%esp) + + paddd %xmm1, %xmm6 + + movdqa %xmm0, %xmm1 + psrld $6, %xmm0 + paddd 16*(\i)+sha256_4k, %xmm6 + movdqa %xmm0, %xmm2 + pslld $7, %xmm1 + psrld $5, %xmm2 + pxor %xmm1, %xmm0 + pxor %xmm2, %xmm0 + pslld $14, %xmm1 + psrld $14, %xmm2 + pxor %xmm1, %xmm0 + pslld $5, %xmm1 + pxor %xmm2, %xmm0 + pxor %xmm1, %xmm0 + movdqa %xmm5, %xmm1 + paddd %xmm0, %xmm6 + + movdqa %xmm3, %xmm0 + movdqa %xmm4, %xmm3 + movdqa %xmm4, %xmm2 + paddd %xmm6, %xmm0 + pand %xmm5, %xmm2 + pand %xmm7, %xmm1 + pand %xmm7, %xmm4 + pxor %xmm4, %xmm1 + movdqa %xmm5, %xmm4 + movdqa %xmm7, %xmm5 + pxor %xmm2, %xmm1 + paddd %xmm1, %xmm6 + + movdqa %xmm7, %xmm2 + psrld $2, %xmm7 + movdqa %xmm7, %xmm1 + pslld $10, %xmm2 + psrld $11, %xmm1 + pxor %xmm2, %xmm7 + pslld $9, %xmm2 + pxor %xmm1, %xmm7 + psrld $9, %xmm1 + pxor %xmm2, %xmm7 + pslld $11, %xmm2 + pxor %xmm1, %xmm7 + pxor %xmm2, %xmm7 + paddd %xmm6, %xmm7 +.endm + +.macro sha256_sse2_main_quadround i + sha256_sse2_main_round \i+0 + sha256_sse2_main_round \i+1 + sha256_sse2_main_round \i+2 + sha256_sse2_main_round \i+3 +.endm + + +.macro p2bswap_esi_esp i + movdqu \i*16(%esi), %xmm0 + movdqu (\i+1)*16(%esi), %xmm2 + pshuflw $0xb1, %xmm0, %xmm0 + pshuflw $0xb1, %xmm2, %xmm2 + pshufhw $0xb1, %xmm0, %xmm0 + pshufhw $0xb1, %xmm2, %xmm2 + movdqa %xmm0, %xmm1 + movdqa %xmm2, %xmm3 + psrlw $8, %xmm1 + psrlw $8, %xmm3 + psllw $8, %xmm0 + psllw $8, %xmm2 + pxor %xmm1, %xmm0 + pxor %xmm3, %xmm2 + movdqa %xmm0, (\i+3)*16(%esp) + movdqa %xmm2, (\i+4)*16(%esp) +.endm + + .text + .p2align 5 + .globl sha256_transform_4way + .globl _sha256_transform_4way +sha256_transform_4way: +_sha256_transform_4way: + pushl %edi + pushl %esi + movl 12(%esp), %edi + movl 16(%esp), %esi + movl 20(%esp), %ecx + movl %esp, %edx + subl $67*16, %esp + andl $-128, %esp + + testl %ecx, %ecx + jnz sha256_transform_4way_swap + + movdqu 0*16(%esi), %xmm0 + movdqu 1*16(%esi), %xmm1 + movdqu 2*16(%esi), %xmm2 + movdqu 3*16(%esi), %xmm3 + movdqu 4*16(%esi), %xmm4 + movdqu 5*16(%esi), %xmm5 + movdqu 6*16(%esi), %xmm6 + movdqu 7*16(%esi), %xmm7 + movdqa %xmm0, 3*16(%esp) + movdqa %xmm1, 4*16(%esp) + movdqa %xmm2, 5*16(%esp) + movdqa %xmm3, 6*16(%esp) + movdqa %xmm4, 7*16(%esp) + movdqa %xmm5, 8*16(%esp) + movdqa %xmm6, 9*16(%esp) + movdqa %xmm7, 10*16(%esp) + movdqu 8*16(%esi), %xmm0 + movdqu 9*16(%esi), %xmm1 + movdqu 10*16(%esi), %xmm2 + movdqu 11*16(%esi), %xmm3 + movdqu 12*16(%esi), %xmm4 + movdqu 13*16(%esi), %xmm5 + movdqu 14*16(%esi), %xmm6 + movdqu 15*16(%esi), %xmm7 + movdqa %xmm0, 11*16(%esp) + movdqa %xmm1, 12*16(%esp) + movdqa %xmm2, 13*16(%esp) + movdqa %xmm3, 14*16(%esp) + movdqa %xmm4, 15*16(%esp) + movdqa %xmm5, 16*16(%esp) + movdqa %xmm6, 17*16(%esp) + movdqa %xmm7, 18*16(%esp) + jmp sha256_transform_4way_extend + + .p2align 5 +sha256_transform_4way_swap: + p2bswap_esi_esp 0 + p2bswap_esi_esp 2 + p2bswap_esi_esp 4 + p2bswap_esi_esp 6 + p2bswap_esi_esp 8 + p2bswap_esi_esp 10 + p2bswap_esi_esp 12 + p2bswap_esi_esp 14 + +sha256_transform_4way_extend: + leal 19*16(%esp), %ecx + leal 48*16(%ecx), %eax + movdqa -2*16(%ecx), %xmm3 + movdqa -1*16(%ecx), %xmm7 +sha256_transform_4way_extend_loop: + movdqa -15*16(%ecx), %xmm0 + movdqa -14*16(%ecx), %xmm4 + movdqa %xmm0, %xmm2 + movdqa %xmm4, %xmm6 + psrld $3, %xmm0 + psrld $3, %xmm4 + movdqa %xmm0, %xmm1 + movdqa %xmm4, %xmm5 + pslld $14, %xmm2 + pslld $14, %xmm6 + psrld $4, %xmm1 + psrld $4, %xmm5 + pxor %xmm1, %xmm0 + pxor %xmm5, %xmm4 + psrld $11, %xmm1 + psrld $11, %xmm5 + pxor %xmm2, %xmm0 + pxor %xmm6, %xmm4 + pslld $11, %xmm2 + pslld $11, %xmm6 + pxor %xmm1, %xmm0 + pxor %xmm5, %xmm4 + pxor %xmm2, %xmm0 + pxor %xmm6, %xmm4 + + paddd -16*16(%ecx), %xmm0 + paddd -15*16(%ecx), %xmm4 + + movdqa %xmm3, %xmm2 + movdqa %xmm7, %xmm6 + psrld $10, %xmm3 + psrld $10, %xmm7 + movdqa %xmm3, %xmm1 + movdqa %xmm7, %xmm5 + pslld $13, %xmm2 + pslld $13, %xmm6 + psrld $7, %xmm1 + psrld $7, %xmm5 + + paddd -7*16(%ecx), %xmm0 + paddd -6*16(%ecx), %xmm4 + + pxor %xmm1, %xmm3 + pxor %xmm5, %xmm7 + psrld $2, %xmm1 + psrld $2, %xmm5 + pxor %xmm2, %xmm3 + pxor %xmm6, %xmm7 + pslld $2, %xmm2 + pslld $2, %xmm6 + pxor %xmm1, %xmm3 + pxor %xmm5, %xmm7 + pxor %xmm2, %xmm3 + pxor %xmm6, %xmm7 + + paddd %xmm0, %xmm3 + paddd %xmm4, %xmm7 + movdqa %xmm3, (%ecx) + movdqa %xmm7, 16(%ecx) + addl $2*16, %ecx + cmpl %ecx, %eax + jne sha256_transform_4way_extend_loop + + movdqu 0(%edi), %xmm7 + movdqu 16(%edi), %xmm5 + movdqu 32(%edi), %xmm4 + movdqu 48(%edi), %xmm3 + movdqu 64(%edi), %xmm0 + movdqu 80(%edi), %xmm1 + movdqu 96(%edi), %xmm2 + movdqu 112(%edi), %xmm6 + movdqa %xmm1, 0(%esp) + movdqa %xmm2, 16(%esp) + movdqa %xmm6, 32(%esp) + + xorl %eax, %eax +sha256_transform_4way_main_loop: + movdqa 3*16(%esp, %eax), %xmm6 + paddd sha256_4k(%eax), %xmm6 + paddd 32(%esp), %xmm6 + + movdqa %xmm0, %xmm1 + movdqa 16(%esp), %xmm2 + pandn %xmm2, %xmm1 + + movdqa %xmm2, 32(%esp) + movdqa 0(%esp), %xmm2 + movdqa %xmm2, 16(%esp) + + pand %xmm0, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm0, 0(%esp) + + paddd %xmm1, %xmm6 + + movdqa %xmm0, %xmm1 + psrld $6, %xmm0 + movdqa %xmm0, %xmm2 + pslld $7, %xmm1 + psrld $5, %xmm2 + pxor %xmm1, %xmm0 + pxor %xmm2, %xmm0 + pslld $14, %xmm1 + psrld $14, %xmm2 + pxor %xmm1, %xmm0 + pxor %xmm2, %xmm0 + pslld $5, %xmm1 + pxor %xmm1, %xmm0 + paddd %xmm0, %xmm6 + + movdqa %xmm3, %xmm0 + paddd %xmm6, %xmm0 + + movdqa %xmm5, %xmm1 + movdqa %xmm4, %xmm3 + movdqa %xmm4, %xmm2 + pand %xmm5, %xmm2 + pand %xmm7, %xmm4 + pand %xmm7, %xmm1 + pxor %xmm4, %xmm1 + movdqa %xmm5, %xmm4 + movdqa %xmm7, %xmm5 + pxor %xmm2, %xmm1 + paddd %xmm1, %xmm6 + + movdqa %xmm7, %xmm2 + psrld $2, %xmm7 + movdqa %xmm7, %xmm1 + pslld $10, %xmm2 + psrld $11, %xmm1 + pxor %xmm2, %xmm7 + pxor %xmm1, %xmm7 + pslld $9, %xmm2 + psrld $9, %xmm1 + pxor %xmm2, %xmm7 + pxor %xmm1, %xmm7 + pslld $11, %xmm2 + pxor %xmm2, %xmm7 + paddd %xmm6, %xmm7 + + addl $16, %eax + cmpl $16*64, %eax + jne sha256_transform_4way_main_loop + + movdqu 0(%edi), %xmm1 + movdqu 16(%edi), %xmm2 + paddd %xmm1, %xmm7 + paddd %xmm2, %xmm5 + movdqu 32(%edi), %xmm1 + movdqu 48(%edi), %xmm2 + paddd %xmm1, %xmm4 + paddd %xmm2, %xmm3 + + movdqu %xmm7, 0(%edi) + movdqu %xmm5, 16(%edi) + movdqu %xmm4, 32(%edi) + movdqu %xmm3, 48(%edi) + + movdqu 64(%edi), %xmm1 + movdqu 80(%edi), %xmm2 + movdqu 96(%edi), %xmm6 + movdqu 112(%edi), %xmm7 + paddd %xmm1, %xmm0 + paddd 0(%esp), %xmm2 + paddd 16(%esp), %xmm6 + paddd 32(%esp), %xmm7 + + movdqu %xmm0, 64(%edi) + movdqu %xmm2, 80(%edi) + movdqu %xmm6, 96(%edi) + movdqu %xmm7, 112(%edi) + + movl %edx, %esp + popl %esi + popl %edi + ret + +.macro sha256_sse2_main_round_red i, r7 + movdqa 16*(\i)(%eax), %xmm6 + paddd 16*(\i)+sha256_4k, %xmm6 + paddd 32(%esp), %xmm6 + movdqa %xmm0, %xmm1 + movdqa 16(%esp), %xmm2 + paddd \r7, %xmm6 + pandn %xmm2, %xmm1 + movdqa %xmm2, 32(%esp) + movdqa 0(%esp), %xmm2 + movdqa %xmm2, 16(%esp) + pand %xmm0, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm0, 0(%esp) + paddd %xmm1, %xmm6 + movdqa %xmm0, %xmm1 + psrld $6, %xmm0 + movdqa %xmm0, %xmm2 + pslld $7, %xmm1 + psrld $5, %xmm2 + pxor %xmm1, %xmm0 + pxor %xmm2, %xmm0 + pslld $14, %xmm1 + psrld $14, %xmm2 + pxor %xmm1, %xmm0 + pxor %xmm2, %xmm0 + pslld $5, %xmm1 + pxor %xmm1, %xmm0 + paddd %xmm6, %xmm0 +.endm + + .text + .p2align 5 + .globl sha256_use_4way + .globl _sha256_use_4way +sha256_use_4way: +_sha256_use_4way: + pushl %ebx + + /* Check for SSE2 availability */ + movl $1, %eax + cpuid + andl $0x04000000, %edx + jnz sha256_use_4way_sse2 + xorl %eax, %eax + popl %ebx + ret + +sha256_use_4way_sse2: + movl $1, %eax + popl %ebx + ret + + .text + .p2align 5 + .globl sha256_use_ssse3 + .globl _sha256_use_ssse3 +sha256_use_ssse3: +_sha256_use_ssse3: + pushl %ebx + + movl $1, %eax + cpuid + andl $0x00000200, %ecx + jnz sha256_use_ssse3_done + xorl %eax, %eax + popl %ebx + ret + +sha256_use_ssse3_done: + movl $1, %eax + popl %ebx + ret + +#endif diff --git a/src/crypto/sha2/asm/sha2-x86_64.S b/src/crypto/sha2/asm/sha2-x86_64.S new file mode 100644 index 00000000..eabb50f4 --- /dev/null +++ b/src/crypto/sha2/asm/sha2-x86_64.S @@ -0,0 +1,2056 @@ +/* + * Copyright 2012-2015 pooler@litecoinpool.org + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. See COPYING for more details. + */ + +#if defined(__linux__) && defined(__ELF__) + .section .note.GNU-stack,"",%progbits +#endif + +#if defined(__x86_64__) + .data + .p2align 4 +sha256_h: + .long 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a + .long 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 + + .data + .p2align 6 +sha256_k: + .long 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5 + .long 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5 + .long 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3 + .long 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174 + .long 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc + .long 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da + .long 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7 + .long 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967 + .long 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13 + .long 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85 + .long 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3 + .long 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070 + .long 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5 + .long 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3 + .long 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208 + .long 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + +bswap_xmm_mask: + .long 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f + + +.macro sha256_mixed_quadround ra, rb, rc, rd, re, rf, rg, rh, x0, x1, x2, x3 + movdqa \x3, %xmm4 + movl \re, %eax + movdqa \x2, %xmm6 + rorl $(25-11), %eax + movl \ra, %ebx + pslldq $12, %xmm4 + rorl $(22-13), %ebx + psrldq $4, %xmm6 + xorl \re, %eax + movl \rf, %ecx + rorl $(11-6), %eax + pxor %xmm6, %xmm4 + movdqa \x1, %xmm5 + xorl \ra, %ebx + xorl \rg, %ecx + xorl \re, %eax + paddd \x0, %xmm4 + movdqa \x0, %xmm7 + andl \re, %ecx + rorl $(13-2), %ebx + xorl \ra, %ebx + pslldq $12, %xmm5 + psrldq $4, %xmm7 + rorl $6, %eax + xorl \rg, %ecx + pxor %xmm7, %xmm5 + rorl $2, %ebx + addl %eax, %ecx + addl (%rsp) , %ecx + movdqa %xmm5, %xmm6 + movl \ra, %eax + addl %ecx, \rh + movl \ra, %ecx + movdqa %xmm5, %xmm7 + orl \rc, %eax + addl \rh, \rd + andl \rc, %ecx + pslld $(32-7), %xmm5 + psrld $7, %xmm6 + andl \rb, %eax + addl %ebx, \rh + orl %ecx, %eax + por %xmm6, %xmm5 + addl %eax, \rh + + movl \rd, %eax + movdqa %xmm7, %xmm6 + movl \rh, %ebx + rorl $(25-11), %eax + xorl \rd, %eax + movdqa %xmm7, %xmm8 + movl \re, %ecx + rorl $(22-13), %ebx + xorl \rh, %ebx + pslld $(32-18), %xmm7 + rorl $(11-6), %eax + xorl \rf, %ecx + rorl $(13-2), %ebx + psrld $18, %xmm6 + xorl \rd, %eax + andl \rd, %ecx + rorl $6, %eax + pxor %xmm7, %xmm5 + xorl \rh, %ebx + xorl \rf, %ecx + psrld $3, %xmm8 + addl %eax, %ecx + addl 1*4(%rsp), %ecx + rorl $2, %ebx + pxor %xmm6, %xmm5 + movl \rh, %eax + addl %ecx, \rg + movl \rh, %ecx + pxor %xmm8, %xmm5 + orl \rb, %eax + addl \rg, \rc + andl \rb, %ecx + pshufd $0xfa, \x3, %xmm6 + andl \ra, %eax + addl %ebx, \rg + paddd %xmm5, %xmm4 + orl %ecx, %eax + addl %eax, \rg + + movl \rc, %eax + movdqa %xmm6, %xmm7 + movl \rg, %ebx + rorl $(25-11), %eax + xorl \rc, %eax + movdqa %xmm6, %xmm8 + rorl $(22-13), %ebx + movl \rd, %ecx + xorl \rg, %ebx + psrlq $17, %xmm6 + psrlq $19, %xmm7 + rorl $(11-6), %eax + xorl \re, %ecx + xorl \rc, %eax + psrld $10, %xmm8 + pxor %xmm7, %xmm6 + andl \rc, %ecx + rorl $(13-2), %ebx + xorl \rg, %ebx + pxor %xmm6, %xmm8 + xorl \re, %ecx + rorl $6, %eax + addl %eax, %ecx + pshufd $0x8f, %xmm8, %xmm8 + rorl $2, %ebx + addl 2*4(%rsp), %ecx + movl \rg, %eax + psrldq $8, %xmm8 + addl %ecx, \rf + movl \rg, %ecx + orl \ra, %eax + paddd %xmm8, %xmm4 + addl \rf, \rb + andl \ra, %ecx + andl \rh, %eax + pshufd $0x50, %xmm4, %xmm6 + addl %ebx, \rf + orl %ecx, %eax + addl %eax, \rf + + movdqa %xmm6, %xmm7 + movl \rb, %eax + rorl $(25-11), %eax + movl \rf, %ebx + movdqa %xmm6, \x0 + rorl $(22-13), %ebx + xorl \rb, %eax + movl \rc, %ecx + psrlq $17, %xmm6 + rorl $(11-6), %eax + xorl \rf, %ebx + xorl \rd, %ecx + psrlq $19, %xmm7 + xorl \rb, %eax + andl \rb, %ecx + rorl $(13-2), %ebx + psrld $10, \x0 + xorl \rf, %ebx + rorl $6, %eax + pxor %xmm7, %xmm6 + xorl \rd, %ecx + rorl $2, %ebx + addl %eax, %ecx + pxor %xmm6, \x0 + addl 3*4(%rsp), %ecx + movl \rf, %eax + addl %ecx, \re + pshufd $0xf8, \x0, \x0 + movl \rf, %ecx + orl \rh, %eax + addl \re, \ra + pslldq $8, \x0 + andl \rh, %ecx + andl \rg, %eax + paddd %xmm4, \x0 + addl %ebx, \re + orl %ecx, %eax + addl %eax, \re +.endm + +.macro sha256_main_round i, ra, rb, rc, rd, re, rf, rg, rh + movl \re, %eax + rorl $(25-11), %eax + movl \ra, %ebx + xorl \re, %eax + rorl $(22-13), %ebx + movl \rf, %ecx + xorl \ra, %ebx + rorl $(11-6), %eax + xorl \rg, %ecx + xorl \re, %eax + rorl $(13-2), %ebx + andl \re, %ecx + xorl \ra, %ebx + rorl $6, %eax + xorl \rg, %ecx + addl %eax, %ecx + rorl $2, %ebx + addl \i*4(%rsp), %ecx + movl \ra, %eax + addl %ecx, \rh + movl \ra, %ecx + orl \rc, %eax + addl \rh, \rd + andl \rc, %ecx + andl \rb, %eax + addl %ebx, \rh + orl %ecx, %eax + addl %eax, \rh +.endm + + + .text + .p2align 6 +sha256_transform_sse2: + pushq %rbx + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 +#if defined(_WIN64) || defined(__CYGWIN__) + pushq %rdi + pushq %rsi + subq $5*16, %rsp + movdqa %xmm6, 1*16(%rsp) + movdqa %xmm7, 2*16(%rsp) + movdqa %xmm8, 3*16(%rsp) + movdqa %xmm9, 4*16(%rsp) + movq %rcx, %rdi + movq %rdx, %rsi + movq %r8, %rdx +#else + subq $16, %rsp +#endif + + movl 0*4(%rdi), %r8d + movl 1*4(%rdi), %r9d + movl 2*4(%rdi), %r10d + movl 3*4(%rdi), %r11d + movl 4*4(%rdi), %r12d + movl 5*4(%rdi), %r13d + movl 6*4(%rdi), %r14d + movl 7*4(%rdi), %r15d + + testq %rdx, %rdx + jnz sha256_transform_sse2_swap + + movdqu 0*16(%rsi), %xmm0 + movdqu 1*16(%rsi), %xmm1 + movdqu 2*16(%rsi), %xmm2 + movdqu 3*16(%rsi), %xmm3 + jmp sha256_transform_sse2_core + +sha256_transform_sse2_swap: + movdqu 0*16(%rsi), %xmm0 + movdqu 1*16(%rsi), %xmm1 + movdqu 2*16(%rsi), %xmm2 + movdqu 3*16(%rsi), %xmm3 + pshuflw $0xb1, %xmm0, %xmm0 + pshuflw $0xb1, %xmm1, %xmm1 + pshuflw $0xb1, %xmm2, %xmm2 + pshuflw $0xb1, %xmm3, %xmm3 + pshufhw $0xb1, %xmm0, %xmm0 + pshufhw $0xb1, %xmm1, %xmm1 + pshufhw $0xb1, %xmm2, %xmm2 + pshufhw $0xb1, %xmm3, %xmm3 + movdqa %xmm0, %xmm4 + movdqa %xmm1, %xmm5 + movdqa %xmm2, %xmm6 + movdqa %xmm3, %xmm7 + psrlw $8, %xmm4 + psrlw $8, %xmm5 + psrlw $8, %xmm6 + psrlw $8, %xmm7 + psllw $8, %xmm0 + psllw $8, %xmm1 + psllw $8, %xmm2 + psllw $8, %xmm3 + pxor %xmm4, %xmm0 + pxor %xmm5, %xmm1 + pxor %xmm6, %xmm2 + pxor %xmm7, %xmm3 + +sha256_transform_sse2_core: + leaq sha256_k(%rip), %rdx + movq $48, %rsi + .p2align 4 +sha256_transform_sse2_loop: + movdqa 0*16(%rdx), %xmm9 + paddd %xmm0, %xmm9 + movdqa %xmm9, (%rsp) + sha256_mixed_quadround %r8d, %r9d, %r10d, %r11d, %r12d, %r13d, %r14d, %r15d, %xmm0, %xmm1, %xmm2, %xmm3 + movdqa 1*16(%rdx), %xmm9 + paddd %xmm1, %xmm9 + movdqa %xmm9, (%rsp) + sha256_mixed_quadround %r12d, %r13d, %r14d, %r15d, %r8d, %r9d, %r10d, %r11d, %xmm1, %xmm2, %xmm3, %xmm0 + movdqa 2*16(%rdx), %xmm9 + paddd %xmm2, %xmm9 + movdqa %xmm9, (%rsp) + sha256_mixed_quadround %r8d, %r9d, %r10d, %r11d, %r12d, %r13d, %r14d, %r15d, %xmm2, %xmm3, %xmm0, %xmm1 + movdqa 3*16(%rdx), %xmm9 + paddd %xmm3, %xmm9 + movdqa %xmm9, (%rsp) + addq $4*16, %rdx + sha256_mixed_quadround %r12d, %r13d, %r14d, %r15d, %r8d, %r9d, %r10d, %r11d, %xmm3, %xmm0, %xmm1, %xmm2 + + subq $16, %rsi + jne sha256_transform_sse2_loop + + paddd 0*16(%rdx), %xmm0 + movdqa %xmm0, (%rsp) + sha256_main_round 0, %r8d, %r9d, %r10d, %r11d, %r12d, %r13d, %r14d, %r15d + sha256_main_round 1, %r15d, %r8d, %r9d, %r10d, %r11d, %r12d, %r13d, %r14d + sha256_main_round 2, %r14d, %r15d, %r8d, %r9d, %r10d, %r11d, %r12d, %r13d + sha256_main_round 3, %r13d, %r14d, %r15d, %r8d, %r9d, %r10d, %r11d, %r12d + paddd 1*16(%rdx), %xmm1 + movdqa %xmm1, (%rsp) + sha256_main_round 0, %r12d, %r13d, %r14d, %r15d, %r8d, %r9d, %r10d, %r11d + sha256_main_round 1, %r11d, %r12d, %r13d, %r14d, %r15d, %r8d, %r9d, %r10d + sha256_main_round 2, %r10d, %r11d, %r12d, %r13d, %r14d, %r15d, %r8d, %r9d + sha256_main_round 3, %r9d, %r10d, %r11d, %r12d, %r13d, %r14d, %r15d, %r8d + paddd 2*16(%rdx), %xmm2 + movdqa %xmm2, (%rsp) + sha256_main_round 0, %r8d, %r9d, %r10d, %r11d, %r12d, %r13d, %r14d, %r15d + sha256_main_round 1, %r15d, %r8d, %r9d, %r10d, %r11d, %r12d, %r13d, %r14d + sha256_main_round 2, %r14d, %r15d, %r8d, %r9d, %r10d, %r11d, %r12d, %r13d + sha256_main_round 3, %r13d, %r14d, %r15d, %r8d, %r9d, %r10d, %r11d, %r12d + paddd 3*16(%rdx), %xmm3 + movdqa %xmm3, (%rsp) + sha256_main_round 0, %r12d, %r13d, %r14d, %r15d, %r8d, %r9d, %r10d, %r11d + sha256_main_round 1, %r11d, %r12d, %r13d, %r14d, %r15d, %r8d, %r9d, %r10d + sha256_main_round 2, %r10d, %r11d, %r12d, %r13d, %r14d, %r15d, %r8d, %r9d + sha256_main_round 3, %r9d, %r10d, %r11d, %r12d, %r13d, %r14d, %r15d, %r8d + + addl %r8d, 0*4(%rdi) + addl %r9d, 1*4(%rdi) + addl %r10d, 2*4(%rdi) + addl %r11d, 3*4(%rdi) + addl %r12d, 4*4(%rdi) + addl %r13d, 5*4(%rdi) + addl %r14d, 6*4(%rdi) + addl %r15d, 7*4(%rdi) + +#if defined(_WIN64) || defined(__CYGWIN__) + movdqa 1*16(%rsp), %xmm6 + movdqa 2*16(%rsp), %xmm7 + movdqa 3*16(%rsp), %xmm8 + movdqa 4*16(%rsp), %xmm9 + addq $5*16, %rsp + popq %rsi + popq %rdi +#else + addq $16, %rsp +#endif + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %rbx + ret + + + .text + .p2align 6 +sha256_transform_phe: +#if defined(_WIN64) || defined(__CYGWIN__) + pushq %rdi + pushq %rsi + movq %rcx, %rdi + movq %rdx, %rsi + movq %r8, %rdx +#endif + movq %rsp, %r8 + subq $64, %rsp + andq $-64, %rsp + + testq %rdx, %rdx + jnz sha256_transform_phe_noswap + + movl 0*4(%rsi), %eax + movl 1*4(%rsi), %ecx + movl 2*4(%rsi), %edx + movl 3*4(%rsi), %r9d + bswapl %eax + bswapl %ecx + bswapl %edx + bswapl %r9d + movl %eax, 0*4(%rsp) + movl %ecx, 1*4(%rsp) + movl %edx, 2*4(%rsp) + movl %r9d, 3*4(%rsp) + movl 4*4(%rsi), %eax + movl 5*4(%rsi), %ecx + movl 6*4(%rsi), %edx + movl 7*4(%rsi), %r9d + bswapl %eax + bswapl %ecx + bswapl %edx + bswapl %r9d + movl %eax, 4*4(%rsp) + movl %ecx, 5*4(%rsp) + movl %edx, 6*4(%rsp) + movl %r9d, 7*4(%rsp) + + movdqu 2*16(%rsi), %xmm0 + movdqu 3*16(%rsi), %xmm2 + pshuflw $0xb1, %xmm0, %xmm0 + pshuflw $0xb1, %xmm2, %xmm2 + pshufhw $0xb1, %xmm0, %xmm0 + pshufhw $0xb1, %xmm2, %xmm2 + movdqa %xmm0, %xmm1 + movdqa %xmm2, %xmm3 + psrlw $8, %xmm1 + psrlw $8, %xmm3 + psllw $8, %xmm0 + psllw $8, %xmm2 + pxor %xmm1, %xmm0 + pxor %xmm3, %xmm2 + movdqa %xmm0, 2*16(%rsp) + movdqa %xmm2, 3*16(%rsp) + + jmp sha256_transform_phe_core + +sha256_transform_phe_noswap: + movdqu 0*16(%rsi), %xmm0 + movdqu 1*16(%rsi), %xmm1 + movdqu 2*16(%rsi), %xmm2 + movdqu 3*16(%rsi), %xmm3 + movdqa %xmm0, 0*16(%rsp) + movdqa %xmm1, 1*16(%rsp) + movdqa %xmm2, 2*16(%rsp) + movdqa %xmm3, 3*16(%rsp) + +sha256_transform_phe_core: + movq %rsp, %rsi + movq $-1, %rax + movq $1, %rcx + /* rep xsha256 */ + .byte 0xf3, 0x0f, 0xa6, 0xd0 + + movq %r8, %rsp +#if defined(_WIN64) || defined(__CYGWIN__) + popq %rsi + popq %rdi +#endif + ret + + + .data + .p2align 3 +sha256_transform_addr: + .quad sha256_transform_sse2 + + .text + .p2align 3 + .globl sha256_transform + .globl _sha256_transform +sha256_transform: +_sha256_transform: + jmp *sha256_transform_addr(%rip) + + + .data + .p2align 7 +sha256_4h: + .long 0x6a09e667, 0x6a09e667, 0x6a09e667, 0x6a09e667 + .long 0xbb67ae85, 0xbb67ae85, 0xbb67ae85, 0xbb67ae85 + .long 0x3c6ef372, 0x3c6ef372, 0x3c6ef372, 0x3c6ef372 + .long 0xa54ff53a, 0xa54ff53a, 0xa54ff53a, 0xa54ff53a + .long 0x510e527f, 0x510e527f, 0x510e527f, 0x510e527f + .long 0x9b05688c, 0x9b05688c, 0x9b05688c, 0x9b05688c + .long 0x1f83d9ab, 0x1f83d9ab, 0x1f83d9ab, 0x1f83d9ab + .long 0x5be0cd19, 0x5be0cd19, 0x5be0cd19, 0x5be0cd19 + + .data + .p2align 7 +sha256_4k: + .long 0x428a2f98, 0x428a2f98, 0x428a2f98, 0x428a2f98 + .long 0x71374491, 0x71374491, 0x71374491, 0x71374491 + .long 0xb5c0fbcf, 0xb5c0fbcf, 0xb5c0fbcf, 0xb5c0fbcf + .long 0xe9b5dba5, 0xe9b5dba5, 0xe9b5dba5, 0xe9b5dba5 + .long 0x3956c25b, 0x3956c25b, 0x3956c25b, 0x3956c25b + .long 0x59f111f1, 0x59f111f1, 0x59f111f1, 0x59f111f1 + .long 0x923f82a4, 0x923f82a4, 0x923f82a4, 0x923f82a4 + .long 0xab1c5ed5, 0xab1c5ed5, 0xab1c5ed5, 0xab1c5ed5 + .long 0xd807aa98, 0xd807aa98, 0xd807aa98, 0xd807aa98 + .long 0x12835b01, 0x12835b01, 0x12835b01, 0x12835b01 + .long 0x243185be, 0x243185be, 0x243185be, 0x243185be + .long 0x550c7dc3, 0x550c7dc3, 0x550c7dc3, 0x550c7dc3 + .long 0x72be5d74, 0x72be5d74, 0x72be5d74, 0x72be5d74 + .long 0x80deb1fe, 0x80deb1fe, 0x80deb1fe, 0x80deb1fe + .long 0x9bdc06a7, 0x9bdc06a7, 0x9bdc06a7, 0x9bdc06a7 + .long 0xc19bf174, 0xc19bf174, 0xc19bf174, 0xc19bf174 + .long 0xe49b69c1, 0xe49b69c1, 0xe49b69c1, 0xe49b69c1 + .long 0xefbe4786, 0xefbe4786, 0xefbe4786, 0xefbe4786 + .long 0x0fc19dc6, 0x0fc19dc6, 0x0fc19dc6, 0x0fc19dc6 + .long 0x240ca1cc, 0x240ca1cc, 0x240ca1cc, 0x240ca1cc + .long 0x2de92c6f, 0x2de92c6f, 0x2de92c6f, 0x2de92c6f + .long 0x4a7484aa, 0x4a7484aa, 0x4a7484aa, 0x4a7484aa + .long 0x5cb0a9dc, 0x5cb0a9dc, 0x5cb0a9dc, 0x5cb0a9dc + .long 0x76f988da, 0x76f988da, 0x76f988da, 0x76f988da + .long 0x983e5152, 0x983e5152, 0x983e5152, 0x983e5152 + .long 0xa831c66d, 0xa831c66d, 0xa831c66d, 0xa831c66d + .long 0xb00327c8, 0xb00327c8, 0xb00327c8, 0xb00327c8 + .long 0xbf597fc7, 0xbf597fc7, 0xbf597fc7, 0xbf597fc7 + .long 0xc6e00bf3, 0xc6e00bf3, 0xc6e00bf3, 0xc6e00bf3 + .long 0xd5a79147, 0xd5a79147, 0xd5a79147, 0xd5a79147 + .long 0x06ca6351, 0x06ca6351, 0x06ca6351, 0x06ca6351 + .long 0x14292967, 0x14292967, 0x14292967, 0x14292967 + .long 0x27b70a85, 0x27b70a85, 0x27b70a85, 0x27b70a85 + .long 0x2e1b2138, 0x2e1b2138, 0x2e1b2138, 0x2e1b2138 + .long 0x4d2c6dfc, 0x4d2c6dfc, 0x4d2c6dfc, 0x4d2c6dfc + .long 0x53380d13, 0x53380d13, 0x53380d13, 0x53380d13 + .long 0x650a7354, 0x650a7354, 0x650a7354, 0x650a7354 + .long 0x766a0abb, 0x766a0abb, 0x766a0abb, 0x766a0abb + .long 0x81c2c92e, 0x81c2c92e, 0x81c2c92e, 0x81c2c92e + .long 0x92722c85, 0x92722c85, 0x92722c85, 0x92722c85 + .long 0xa2bfe8a1, 0xa2bfe8a1, 0xa2bfe8a1, 0xa2bfe8a1 + .long 0xa81a664b, 0xa81a664b, 0xa81a664b, 0xa81a664b + .long 0xc24b8b70, 0xc24b8b70, 0xc24b8b70, 0xc24b8b70 + .long 0xc76c51a3, 0xc76c51a3, 0xc76c51a3, 0xc76c51a3 + .long 0xd192e819, 0xd192e819, 0xd192e819, 0xd192e819 + .long 0xd6990624, 0xd6990624, 0xd6990624, 0xd6990624 + .long 0xf40e3585, 0xf40e3585, 0xf40e3585, 0xf40e3585 + .long 0x106aa070, 0x106aa070, 0x106aa070, 0x106aa070 + .long 0x19a4c116, 0x19a4c116, 0x19a4c116, 0x19a4c116 + .long 0x1e376c08, 0x1e376c08, 0x1e376c08, 0x1e376c08 + .long 0x2748774c, 0x2748774c, 0x2748774c, 0x2748774c + .long 0x34b0bcb5, 0x34b0bcb5, 0x34b0bcb5, 0x34b0bcb5 + .long 0x391c0cb3, 0x391c0cb3, 0x391c0cb3, 0x391c0cb3 + .long 0x4ed8aa4a, 0x4ed8aa4a, 0x4ed8aa4a, 0x4ed8aa4a + .long 0x5b9cca4f, 0x5b9cca4f, 0x5b9cca4f, 0x5b9cca4f + .long 0x682e6ff3, 0x682e6ff3, 0x682e6ff3, 0x682e6ff3 + .long 0x748f82ee, 0x748f82ee, 0x748f82ee, 0x748f82ee + .long 0x78a5636f, 0x78a5636f, 0x78a5636f, 0x78a5636f + .long 0x84c87814, 0x84c87814, 0x84c87814, 0x84c87814 + .long 0x8cc70208, 0x8cc70208, 0x8cc70208, 0x8cc70208 + .long 0x90befffa, 0x90befffa, 0x90befffa, 0x90befffa + .long 0xa4506ceb, 0xa4506ceb, 0xa4506ceb, 0xa4506ceb + .long 0xbef9a3f7, 0xbef9a3f7, 0xbef9a3f7, 0xbef9a3f7 + .long 0xc67178f2, 0xc67178f2, 0xc67178f2, 0xc67178f2 + + .data + .p2align 7 +sha256_8h: + .long 0x6a09e667, 0x6a09e667, 0x6a09e667, 0x6a09e667, 0x6a09e667, 0x6a09e667, 0x6a09e667, 0x6a09e667 + .long 0xbb67ae85, 0xbb67ae85, 0xbb67ae85, 0xbb67ae85, 0xbb67ae85, 0xbb67ae85, 0xbb67ae85, 0xbb67ae85 + .long 0x3c6ef372, 0x3c6ef372, 0x3c6ef372, 0x3c6ef372, 0x3c6ef372, 0x3c6ef372, 0x3c6ef372, 0x3c6ef372 + .long 0xa54ff53a, 0xa54ff53a, 0xa54ff53a, 0xa54ff53a, 0xa54ff53a, 0xa54ff53a, 0xa54ff53a, 0xa54ff53a + .long 0x510e527f, 0x510e527f, 0x510e527f, 0x510e527f, 0x510e527f, 0x510e527f, 0x510e527f, 0x510e527f + .long 0x9b05688c, 0x9b05688c, 0x9b05688c, 0x9b05688c, 0x9b05688c, 0x9b05688c, 0x9b05688c, 0x9b05688c + .long 0x1f83d9ab, 0x1f83d9ab, 0x1f83d9ab, 0x1f83d9ab, 0x1f83d9ab, 0x1f83d9ab, 0x1f83d9ab, 0x1f83d9ab + .long 0x5be0cd19, 0x5be0cd19, 0x5be0cd19, 0x5be0cd19, 0x5be0cd19, 0x5be0cd19, 0x5be0cd19, 0x5be0cd19 + + .data + .p2align 7 +sha256_8k: + .long 0x428a2f98, 0x428a2f98, 0x428a2f98, 0x428a2f98, 0x428a2f98, 0x428a2f98, 0x428a2f98, 0x428a2f98 + .long 0x71374491, 0x71374491, 0x71374491, 0x71374491, 0x71374491, 0x71374491, 0x71374491, 0x71374491 + .long 0xb5c0fbcf, 0xb5c0fbcf, 0xb5c0fbcf, 0xb5c0fbcf, 0xb5c0fbcf, 0xb5c0fbcf, 0xb5c0fbcf, 0xb5c0fbcf + .long 0xe9b5dba5, 0xe9b5dba5, 0xe9b5dba5, 0xe9b5dba5, 0xe9b5dba5, 0xe9b5dba5, 0xe9b5dba5, 0xe9b5dba5 + .long 0x3956c25b, 0x3956c25b, 0x3956c25b, 0x3956c25b, 0x3956c25b, 0x3956c25b, 0x3956c25b, 0x3956c25b + .long 0x59f111f1, 0x59f111f1, 0x59f111f1, 0x59f111f1, 0x59f111f1, 0x59f111f1, 0x59f111f1, 0x59f111f1 + .long 0x923f82a4, 0x923f82a4, 0x923f82a4, 0x923f82a4, 0x923f82a4, 0x923f82a4, 0x923f82a4, 0x923f82a4 + .long 0xab1c5ed5, 0xab1c5ed5, 0xab1c5ed5, 0xab1c5ed5, 0xab1c5ed5, 0xab1c5ed5, 0xab1c5ed5, 0xab1c5ed5 + .long 0xd807aa98, 0xd807aa98, 0xd807aa98, 0xd807aa98, 0xd807aa98, 0xd807aa98, 0xd807aa98, 0xd807aa98 + .long 0x12835b01, 0x12835b01, 0x12835b01, 0x12835b01, 0x12835b01, 0x12835b01, 0x12835b01, 0x12835b01 + .long 0x243185be, 0x243185be, 0x243185be, 0x243185be, 0x243185be, 0x243185be, 0x243185be, 0x243185be + .long 0x550c7dc3, 0x550c7dc3, 0x550c7dc3, 0x550c7dc3, 0x550c7dc3, 0x550c7dc3, 0x550c7dc3, 0x550c7dc3 + .long 0x72be5d74, 0x72be5d74, 0x72be5d74, 0x72be5d74, 0x72be5d74, 0x72be5d74, 0x72be5d74, 0x72be5d74 + .long 0x80deb1fe, 0x80deb1fe, 0x80deb1fe, 0x80deb1fe, 0x80deb1fe, 0x80deb1fe, 0x80deb1fe, 0x80deb1fe + .long 0x9bdc06a7, 0x9bdc06a7, 0x9bdc06a7, 0x9bdc06a7, 0x9bdc06a7, 0x9bdc06a7, 0x9bdc06a7, 0x9bdc06a7 + .long 0xc19bf174, 0xc19bf174, 0xc19bf174, 0xc19bf174, 0xc19bf174, 0xc19bf174, 0xc19bf174, 0xc19bf174 + .long 0xe49b69c1, 0xe49b69c1, 0xe49b69c1, 0xe49b69c1, 0xe49b69c1, 0xe49b69c1, 0xe49b69c1, 0xe49b69c1 + .long 0xefbe4786, 0xefbe4786, 0xefbe4786, 0xefbe4786, 0xefbe4786, 0xefbe4786, 0xefbe4786, 0xefbe4786 + .long 0x0fc19dc6, 0x0fc19dc6, 0x0fc19dc6, 0x0fc19dc6, 0x0fc19dc6, 0x0fc19dc6, 0x0fc19dc6, 0x0fc19dc6 + .long 0x240ca1cc, 0x240ca1cc, 0x240ca1cc, 0x240ca1cc, 0x240ca1cc, 0x240ca1cc, 0x240ca1cc, 0x240ca1cc + .long 0x2de92c6f, 0x2de92c6f, 0x2de92c6f, 0x2de92c6f, 0x2de92c6f, 0x2de92c6f, 0x2de92c6f, 0x2de92c6f + .long 0x4a7484aa, 0x4a7484aa, 0x4a7484aa, 0x4a7484aa, 0x4a7484aa, 0x4a7484aa, 0x4a7484aa, 0x4a7484aa + .long 0x5cb0a9dc, 0x5cb0a9dc, 0x5cb0a9dc, 0x5cb0a9dc, 0x5cb0a9dc, 0x5cb0a9dc, 0x5cb0a9dc, 0x5cb0a9dc + .long 0x76f988da, 0x76f988da, 0x76f988da, 0x76f988da, 0x76f988da, 0x76f988da, 0x76f988da, 0x76f988da + .long 0x983e5152, 0x983e5152, 0x983e5152, 0x983e5152, 0x983e5152, 0x983e5152, 0x983e5152, 0x983e5152 + .long 0xa831c66d, 0xa831c66d, 0xa831c66d, 0xa831c66d, 0xa831c66d, 0xa831c66d, 0xa831c66d, 0xa831c66d + .long 0xb00327c8, 0xb00327c8, 0xb00327c8, 0xb00327c8, 0xb00327c8, 0xb00327c8, 0xb00327c8, 0xb00327c8 + .long 0xbf597fc7, 0xbf597fc7, 0xbf597fc7, 0xbf597fc7, 0xbf597fc7, 0xbf597fc7, 0xbf597fc7, 0xbf597fc7 + .long 0xc6e00bf3, 0xc6e00bf3, 0xc6e00bf3, 0xc6e00bf3, 0xc6e00bf3, 0xc6e00bf3, 0xc6e00bf3, 0xc6e00bf3 + .long 0xd5a79147, 0xd5a79147, 0xd5a79147, 0xd5a79147, 0xd5a79147, 0xd5a79147, 0xd5a79147, 0xd5a79147 + .long 0x06ca6351, 0x06ca6351, 0x06ca6351, 0x06ca6351, 0x06ca6351, 0x06ca6351, 0x06ca6351, 0x06ca6351 + .long 0x14292967, 0x14292967, 0x14292967, 0x14292967, 0x14292967, 0x14292967, 0x14292967, 0x14292967 + .long 0x27b70a85, 0x27b70a85, 0x27b70a85, 0x27b70a85, 0x27b70a85, 0x27b70a85, 0x27b70a85, 0x27b70a85 + .long 0x2e1b2138, 0x2e1b2138, 0x2e1b2138, 0x2e1b2138, 0x2e1b2138, 0x2e1b2138, 0x2e1b2138, 0x2e1b2138 + .long 0x4d2c6dfc, 0x4d2c6dfc, 0x4d2c6dfc, 0x4d2c6dfc, 0x4d2c6dfc, 0x4d2c6dfc, 0x4d2c6dfc, 0x4d2c6dfc + .long 0x53380d13, 0x53380d13, 0x53380d13, 0x53380d13, 0x53380d13, 0x53380d13, 0x53380d13, 0x53380d13 + .long 0x650a7354, 0x650a7354, 0x650a7354, 0x650a7354, 0x650a7354, 0x650a7354, 0x650a7354, 0x650a7354 + .long 0x766a0abb, 0x766a0abb, 0x766a0abb, 0x766a0abb, 0x766a0abb, 0x766a0abb, 0x766a0abb, 0x766a0abb + .long 0x81c2c92e, 0x81c2c92e, 0x81c2c92e, 0x81c2c92e, 0x81c2c92e, 0x81c2c92e, 0x81c2c92e, 0x81c2c92e + .long 0x92722c85, 0x92722c85, 0x92722c85, 0x92722c85, 0x92722c85, 0x92722c85, 0x92722c85, 0x92722c85 + .long 0xa2bfe8a1, 0xa2bfe8a1, 0xa2bfe8a1, 0xa2bfe8a1, 0xa2bfe8a1, 0xa2bfe8a1, 0xa2bfe8a1, 0xa2bfe8a1 + .long 0xa81a664b, 0xa81a664b, 0xa81a664b, 0xa81a664b, 0xa81a664b, 0xa81a664b, 0xa81a664b, 0xa81a664b + .long 0xc24b8b70, 0xc24b8b70, 0xc24b8b70, 0xc24b8b70, 0xc24b8b70, 0xc24b8b70, 0xc24b8b70, 0xc24b8b70 + .long 0xc76c51a3, 0xc76c51a3, 0xc76c51a3, 0xc76c51a3, 0xc76c51a3, 0xc76c51a3, 0xc76c51a3, 0xc76c51a3 + .long 0xd192e819, 0xd192e819, 0xd192e819, 0xd192e819, 0xd192e819, 0xd192e819, 0xd192e819, 0xd192e819 + .long 0xd6990624, 0xd6990624, 0xd6990624, 0xd6990624, 0xd6990624, 0xd6990624, 0xd6990624, 0xd6990624 + .long 0xf40e3585, 0xf40e3585, 0xf40e3585, 0xf40e3585, 0xf40e3585, 0xf40e3585, 0xf40e3585, 0xf40e3585 + .long 0x106aa070, 0x106aa070, 0x106aa070, 0x106aa070, 0x106aa070, 0x106aa070, 0x106aa070, 0x106aa070 + .long 0x19a4c116, 0x19a4c116, 0x19a4c116, 0x19a4c116, 0x19a4c116, 0x19a4c116, 0x19a4c116, 0x19a4c116 + .long 0x1e376c08, 0x1e376c08, 0x1e376c08, 0x1e376c08, 0x1e376c08, 0x1e376c08, 0x1e376c08, 0x1e376c08 + .long 0x2748774c, 0x2748774c, 0x2748774c, 0x2748774c, 0x2748774c, 0x2748774c, 0x2748774c, 0x2748774c + .long 0x34b0bcb5, 0x34b0bcb5, 0x34b0bcb5, 0x34b0bcb5, 0x34b0bcb5, 0x34b0bcb5, 0x34b0bcb5, 0x34b0bcb5 + .long 0x391c0cb3, 0x391c0cb3, 0x391c0cb3, 0x391c0cb3, 0x391c0cb3, 0x391c0cb3, 0x391c0cb3, 0x391c0cb3 + .long 0x4ed8aa4a, 0x4ed8aa4a, 0x4ed8aa4a, 0x4ed8aa4a, 0x4ed8aa4a, 0x4ed8aa4a, 0x4ed8aa4a, 0x4ed8aa4a + .long 0x5b9cca4f, 0x5b9cca4f, 0x5b9cca4f, 0x5b9cca4f, 0x5b9cca4f, 0x5b9cca4f, 0x5b9cca4f, 0x5b9cca4f + .long 0x682e6ff3, 0x682e6ff3, 0x682e6ff3, 0x682e6ff3, 0x682e6ff3, 0x682e6ff3, 0x682e6ff3, 0x682e6ff3 + .long 0x748f82ee, 0x748f82ee, 0x748f82ee, 0x748f82ee, 0x748f82ee, 0x748f82ee, 0x748f82ee, 0x748f82ee + .long 0x78a5636f, 0x78a5636f, 0x78a5636f, 0x78a5636f, 0x78a5636f, 0x78a5636f, 0x78a5636f, 0x78a5636f + .long 0x84c87814, 0x84c87814, 0x84c87814, 0x84c87814, 0x84c87814, 0x84c87814, 0x84c87814, 0x84c87814 + .long 0x8cc70208, 0x8cc70208, 0x8cc70208, 0x8cc70208, 0x8cc70208, 0x8cc70208, 0x8cc70208, 0x8cc70208 + .long 0x90befffa, 0x90befffa, 0x90befffa, 0x90befffa, 0x90befffa, 0x90befffa, 0x90befffa, 0x90befffa + .long 0xa4506ceb, 0xa4506ceb, 0xa4506ceb, 0xa4506ceb, 0xa4506ceb, 0xa4506ceb, 0xa4506ceb, 0xa4506ceb + .long 0xbef9a3f7, 0xbef9a3f7, 0xbef9a3f7, 0xbef9a3f7, 0xbef9a3f7, 0xbef9a3f7, 0xbef9a3f7, 0xbef9a3f7 + .long 0xc67178f2, 0xc67178f2, 0xc67178f2, 0xc67178f2, 0xc67178f2, 0xc67178f2, 0xc67178f2, 0xc67178f2 + + .text + .p2align 6 + .globl sha256_init_4way + .globl _sha256_init_4way +sha256_init_4way: +_sha256_init_4way: +#if defined(_WIN64) || defined(__CYGWIN__) + pushq %rdi + movq %rcx, %rdi +#endif + movdqa sha256_4h+0(%rip), %xmm0 + movdqa sha256_4h+16(%rip), %xmm1 + movdqa sha256_4h+32(%rip), %xmm2 + movdqa sha256_4h+48(%rip), %xmm3 + movdqu %xmm0, 0(%rdi) + movdqu %xmm1, 16(%rdi) + movdqu %xmm2, 32(%rdi) + movdqu %xmm3, 48(%rdi) + movdqa sha256_4h+64(%rip), %xmm0 + movdqa sha256_4h+80(%rip), %xmm1 + movdqa sha256_4h+96(%rip), %xmm2 + movdqa sha256_4h+112(%rip), %xmm3 + movdqu %xmm0, 64(%rdi) + movdqu %xmm1, 80(%rdi) + movdqu %xmm2, 96(%rdi) + movdqu %xmm3, 112(%rdi) +#if defined(_WIN64) || defined(__CYGWIN__) + popq %rdi +#endif + ret + + .text + .p2align 6 + .globl sha256_init_8way + .globl _sha256_init_8way +sha256_init_8way: +_sha256_init_8way: +#if defined(_WIN64) || defined(__CYGWIN__) + pushq %rdi + movq %rcx, %rdi +#endif + vpbroadcastd sha256_4h+0(%rip), %ymm0 + vpbroadcastd sha256_4h+16(%rip), %ymm1 + vpbroadcastd sha256_4h+32(%rip), %ymm2 + vpbroadcastd sha256_4h+48(%rip), %ymm3 + vmovdqu %ymm0, 0*32(%rdi) + vmovdqu %ymm1, 1*32(%rdi) + vmovdqu %ymm2, 2*32(%rdi) + vmovdqu %ymm3, 3*32(%rdi) + vpbroadcastd sha256_4h+64(%rip), %ymm0 + vpbroadcastd sha256_4h+80(%rip), %ymm1 + vpbroadcastd sha256_4h+96(%rip), %ymm2 + vpbroadcastd sha256_4h+112(%rip), %ymm3 + vmovdqu %ymm0, 4*32(%rdi) + vmovdqu %ymm1, 5*32(%rdi) + vmovdqu %ymm2, 6*32(%rdi) + vmovdqu %ymm3, 7*32(%rdi) +#if defined(_WIN64) || defined(__CYGWIN__) + popq %rdi +#endif + ret + +.macro sha256_sse2_extend_round i + movdqa (\i-15)*16(%rax), %xmm0 + movdqa %xmm0, %xmm2 + psrld $3, %xmm0 + movdqa %xmm0, %xmm1 + pslld $14, %xmm2 + psrld $4, %xmm1 + pxor %xmm1, %xmm0 + pxor %xmm2, %xmm0 + psrld $11, %xmm1 + pslld $11, %xmm2 + pxor %xmm1, %xmm0 + pxor %xmm2, %xmm0 + paddd (\i-16)*16(%rax), %xmm0 + paddd (\i-7)*16(%rax), %xmm0 + + movdqa %xmm3, %xmm2 + psrld $10, %xmm3 + pslld $13, %xmm2 + movdqa %xmm3, %xmm1 + psrld $7, %xmm1 + pxor %xmm1, %xmm3 + pxor %xmm2, %xmm3 + psrld $2, %xmm1 + pslld $2, %xmm2 + pxor %xmm1, %xmm3 + pxor %xmm2, %xmm3 + paddd %xmm0, %xmm3 + movdqa %xmm3, \i*16(%rax) +.endm + +.macro sha256_sse2_extend_doubleround i + movdqa (\i-15)*16(%rax), %xmm0 + movdqa (\i-14)*16(%rax), %xmm4 + movdqa %xmm0, %xmm2 + movdqa %xmm4, %xmm6 + psrld $3, %xmm0 + psrld $3, %xmm4 + movdqa %xmm0, %xmm1 + movdqa %xmm4, %xmm5 + pslld $14, %xmm2 + pslld $14, %xmm6 + psrld $4, %xmm1 + psrld $4, %xmm5 + pxor %xmm1, %xmm0 + pxor %xmm5, %xmm4 + psrld $11, %xmm1 + psrld $11, %xmm5 + pxor %xmm2, %xmm0 + pxor %xmm6, %xmm4 + pslld $11, %xmm2 + pslld $11, %xmm6 + pxor %xmm1, %xmm0 + pxor %xmm5, %xmm4 + pxor %xmm2, %xmm0 + pxor %xmm6, %xmm4 + + paddd (\i-16)*16(%rax), %xmm0 + paddd (\i-15)*16(%rax), %xmm4 + + movdqa %xmm3, %xmm2 + movdqa %xmm7, %xmm6 + psrld $10, %xmm3 + psrld $10, %xmm7 + movdqa %xmm3, %xmm1 + movdqa %xmm7, %xmm5 + pslld $13, %xmm2 + pslld $13, %xmm6 + psrld $7, %xmm1 + psrld $7, %xmm5 + + paddd (\i-7)*16(%rax), %xmm0 + paddd (\i-6)*16(%rax), %xmm4 + + pxor %xmm1, %xmm3 + pxor %xmm5, %xmm7 + psrld $2, %xmm1 + psrld $2, %xmm5 + pxor %xmm2, %xmm3 + pxor %xmm6, %xmm7 + pslld $2, %xmm2 + pslld $2, %xmm6 + pxor %xmm1, %xmm3 + pxor %xmm5, %xmm7 + pxor %xmm2, %xmm3 + pxor %xmm6, %xmm7 + + paddd %xmm0, %xmm3 + paddd %xmm4, %xmm7 + movdqa %xmm3, \i*16(%rax) + movdqa %xmm7, (\i+1)*16(%rax) +.endm + +.macro sha256_sse2_main_round i + movdqa 16*(\i)(%rax), %xmm6 + + movdqa %xmm0, %xmm1 + movdqa 16(%rsp), %xmm2 + pandn %xmm2, %xmm1 + paddd 32(%rsp), %xmm6 + + movdqa %xmm2, 32(%rsp) + movdqa 0(%rsp), %xmm2 + movdqa %xmm2, 16(%rsp) + + pand %xmm0, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm0, 0(%rsp) + + paddd %xmm1, %xmm6 + + movdqa %xmm0, %xmm1 + psrld $6, %xmm0 + paddd 16*(\i)(%rcx), %xmm6 + movdqa %xmm0, %xmm2 + pslld $7, %xmm1 + psrld $5, %xmm2 + pxor %xmm1, %xmm0 + pxor %xmm2, %xmm0 + pslld $14, %xmm1 + psrld $14, %xmm2 + pxor %xmm1, %xmm0 + pslld $5, %xmm1 + pxor %xmm2, %xmm0 + pxor %xmm1, %xmm0 + movdqa %xmm5, %xmm1 + paddd %xmm0, %xmm6 + + movdqa %xmm3, %xmm0 + movdqa %xmm4, %xmm3 + movdqa %xmm4, %xmm2 + paddd %xmm6, %xmm0 + pand %xmm5, %xmm2 + pand %xmm7, %xmm1 + pand %xmm7, %xmm4 + pxor %xmm4, %xmm1 + movdqa %xmm5, %xmm4 + movdqa %xmm7, %xmm5 + pxor %xmm2, %xmm1 + paddd %xmm1, %xmm6 + + movdqa %xmm7, %xmm2 + psrld $2, %xmm7 + movdqa %xmm7, %xmm1 + pslld $10, %xmm2 + psrld $11, %xmm1 + pxor %xmm2, %xmm7 + pslld $9, %xmm2 + pxor %xmm1, %xmm7 + psrld $9, %xmm1 + pxor %xmm2, %xmm7 + pslld $11, %xmm2 + pxor %xmm1, %xmm7 + pxor %xmm2, %xmm7 + paddd %xmm6, %xmm7 +.endm + +.macro sha256_sse2_main_quadround i + sha256_sse2_main_round \i+0 + sha256_sse2_main_round \i+1 + sha256_sse2_main_round \i+2 + sha256_sse2_main_round \i+3 +.endm + + +.macro sha256_avx_extend_round i + vmovdqa (\i-15)*16(%rax), %xmm0 + vpslld $14, %xmm0, %xmm2 + vpsrld $3, %xmm0, %xmm0 + vpsrld $4, %xmm0, %xmm1 + vpxor %xmm1, %xmm0, %xmm0 + vpxor %xmm2, %xmm0, %xmm0 + vpsrld $11, %xmm1, %xmm1 + vpslld $11, %xmm2, %xmm2 + vpxor %xmm1, %xmm0, %xmm0 + vpxor %xmm2, %xmm0, %xmm0 + vpaddd (\i-16)*16(%rax), %xmm0, %xmm0 + vpaddd (\i-7)*16(%rax), %xmm0, %xmm0 + + vpslld $13, %xmm3, %xmm2 + vpsrld $10, %xmm3, %xmm3 + vpsrld $7, %xmm3, %xmm1 + vpxor %xmm1, %xmm3, %xmm3 + vpxor %xmm2, %xmm3, %xmm3 + vpsrld $2, %xmm1, %xmm1 + vpslld $2, %xmm2, %xmm2 + vpxor %xmm1, %xmm3, %xmm3 + vpxor %xmm2, %xmm3, %xmm3 + vpaddd %xmm0, %xmm3, %xmm3 + vmovdqa %xmm3, \i*16(%rax) +.endm + +.macro sha256_avx_extend_doubleround i + vmovdqa (\i-15)*16(%rax), %xmm0 + vmovdqa (\i-14)*16(%rax), %xmm4 + vpslld $14, %xmm0, %xmm2 + vpslld $14, %xmm4, %xmm6 + vpsrld $3, %xmm0, %xmm8 + vpsrld $3, %xmm4, %xmm4 + vpsrld $7, %xmm0, %xmm1 + vpsrld $4, %xmm4, %xmm5 + vpxor %xmm1, %xmm8, %xmm8 + vpxor %xmm5, %xmm4, %xmm4 + vpsrld $11, %xmm1, %xmm1 + vpsrld $11, %xmm5, %xmm5 + vpxor %xmm2, %xmm8, %xmm8 + vpxor %xmm6, %xmm4, %xmm4 + vpslld $11, %xmm2, %xmm2 + vpslld $11, %xmm6, %xmm6 + vpxor %xmm1, %xmm8, %xmm8 + vpxor %xmm5, %xmm4, %xmm4 + vpxor %xmm2, %xmm8, %xmm8 + vpxor %xmm6, %xmm4, %xmm4 + + vpaddd %xmm0, %xmm4, %xmm4 + vpaddd (\i-16)*16(%rax), %xmm8, %xmm0 + + vpslld $13, %xmm3, %xmm2 + vpslld $13, %xmm7, %xmm6 + vpsrld $10, %xmm3, %xmm3 + vpsrld $10, %xmm7, %xmm7 + + vpaddd (\i-7)*16(%rax), %xmm0, %xmm0 + vpaddd (\i-6)*16(%rax), %xmm4, %xmm4 + + vpsrld $7, %xmm3, %xmm1 + vpsrld $7, %xmm7, %xmm5 + vpxor %xmm1, %xmm3, %xmm3 + vpxor %xmm5, %xmm7, %xmm7 + vpsrld $2, %xmm1, %xmm1 + vpsrld $2, %xmm5, %xmm5 + vpxor %xmm2, %xmm3, %xmm3 + vpxor %xmm6, %xmm7, %xmm7 + vpslld $2, %xmm2, %xmm2 + vpslld $2, %xmm6, %xmm6 + vpxor %xmm1, %xmm3, %xmm3 + vpxor %xmm5, %xmm7, %xmm7 + vpxor %xmm2, %xmm3, %xmm3 + vpxor %xmm6, %xmm7, %xmm7 + + vpaddd %xmm0, %xmm3, %xmm3 + vpaddd %xmm4, %xmm7, %xmm7 + vmovdqa %xmm3, \i*16(%rax) + vmovdqa %xmm7, (\i+1)*16(%rax) +.endm + +.macro sha256_avx_main_round i, r0, r1, r2, r3, r4, r5, r6, r7 + vpaddd 16*(\i)(%rax), \r0, %xmm6 + vpaddd 16*(\i)(%rcx), %xmm6, %xmm6 + + vpandn \r1, \r3, %xmm1 + vpand \r3, \r2, %xmm2 + vpxor %xmm2, %xmm1, %xmm1 + vpaddd %xmm1, %xmm6, %xmm6 + + vpslld $7, \r3, %xmm1 + vpsrld $6, \r3, \r0 + vpsrld $5, \r0, %xmm2 + vpxor %xmm1, \r0, \r0 + vpxor %xmm2, \r0, \r0 + vpslld $14, %xmm1, %xmm1 + vpsrld $14, %xmm2, %xmm2 + vpxor %xmm1, \r0, \r0 + vpxor %xmm2, \r0, \r0 + vpslld $5, %xmm1, %xmm1 + vpxor %xmm1, \r0, \r0 + vpaddd \r0, %xmm6, %xmm6 + vpaddd %xmm6, \r4, \r0 + + vpand \r6, \r5, %xmm2 + vpand \r7, \r5, \r4 + vpand \r7, \r6, %xmm1 + vpxor \r4, %xmm1, %xmm1 + vpxor %xmm2, %xmm1, %xmm1 + vpaddd %xmm1, %xmm6, %xmm6 + + vpslld $10, \r7, %xmm2 + vpsrld $2, \r7, \r4 + vpsrld $11, \r4, %xmm1 + vpxor %xmm2, \r4, \r4 + vpxor %xmm1, \r4, \r4 + vpslld $9, %xmm2, %xmm2 + vpsrld $9, %xmm1, %xmm1 + vpxor %xmm2, \r4, \r4 + vpxor %xmm1, \r4, \r4 + vpslld $11, %xmm2, %xmm2 + vpxor %xmm2, \r4, \r4 + vpaddd %xmm6, \r4, \r4 +.endm + +.macro sha256_avx_main_quadround i + sha256_avx_main_round \i+0, %xmm10, %xmm9, %xmm8, %xmm0, %xmm3, %xmm4, %xmm5, %xmm7 + sha256_avx_main_round \i+1, %xmm9, %xmm8, %xmm0, %xmm10, %xmm4, %xmm5, %xmm7, %xmm3 + sha256_avx_main_round \i+2, %xmm8, %xmm0, %xmm10, %xmm9, %xmm5, %xmm7, %xmm3, %xmm4 + sha256_avx_main_round \i+3, %xmm0, %xmm10, %xmm9, %xmm8, %xmm7, %xmm3, %xmm4, %xmm5 +.endm + + +.macro sha256_avx2_extend_round i + vmovdqa (\i-15)*32(%rax), %ymm0 + vpslld $14, %ymm0, %ymm2 + vpsrld $3, %ymm0, %ymm0 + vpsrld $4, %ymm0, %ymm1 + vpxor %ymm1, %ymm0, %ymm0 + vpxor %ymm2, %ymm0, %ymm0 + vpsrld $11, %ymm1, %ymm1 + vpslld $11, %ymm2, %ymm2 + vpxor %ymm1, %ymm0, %ymm0 + vpxor %ymm2, %ymm0, %ymm0 + vpaddd (\i-16)*32(%rax), %ymm0, %ymm0 + vpaddd (\i-7)*32(%rax), %ymm0, %ymm0 + + vpslld $13, %ymm3, %ymm2 + vpsrld $10, %ymm3, %ymm3 + vpsrld $7, %ymm3, %ymm1 + vpxor %ymm1, %ymm3, %ymm3 + vpxor %ymm2, %ymm3, %ymm3 + vpsrld $2, %ymm1, %ymm1 + vpslld $2, %ymm2, %ymm2 + vpxor %ymm1, %ymm3, %ymm3 + vpxor %ymm2, %ymm3, %ymm3 + vpaddd %ymm0, %ymm3, %ymm3 + vmovdqa %ymm3, \i*32(%rax) +.endm + +.macro sha256_avx2_extend_doubleround i + vmovdqa (\i-15)*32(%rax), %ymm0 + vmovdqa (\i-14)*32(%rax), %ymm4 + vpslld $14, %ymm0, %ymm2 + vpslld $14, %ymm4, %ymm6 + vpsrld $3, %ymm0, %ymm8 + vpsrld $3, %ymm4, %ymm4 + vpsrld $7, %ymm0, %ymm1 + vpsrld $4, %ymm4, %ymm5 + vpxor %ymm1, %ymm8, %ymm8 + vpxor %ymm5, %ymm4, %ymm4 + vpsrld $11, %ymm1, %ymm1 + vpsrld $11, %ymm5, %ymm5 + vpxor %ymm2, %ymm8, %ymm8 + vpxor %ymm6, %ymm4, %ymm4 + vpslld $11, %ymm2, %ymm2 + vpslld $11, %ymm6, %ymm6 + vpxor %ymm1, %ymm8, %ymm8 + vpxor %ymm5, %ymm4, %ymm4 + vpxor %ymm2, %ymm8, %ymm8 + vpxor %ymm6, %ymm4, %ymm4 + + vpaddd %ymm0, %ymm4, %ymm4 + vpaddd (\i-16)*32(%rax), %ymm8, %ymm0 + + vpslld $13, %ymm3, %ymm2 + vpslld $13, %ymm7, %ymm6 + vpsrld $10, %ymm3, %ymm3 + vpsrld $10, %ymm7, %ymm7 + + vpaddd (\i-7)*32(%rax), %ymm0, %ymm0 + vpaddd (\i-6)*32(%rax), %ymm4, %ymm4 + + vpsrld $7, %ymm3, %ymm1 + vpsrld $7, %ymm7, %ymm5 + vpxor %ymm1, %ymm3, %ymm3 + vpxor %ymm5, %ymm7, %ymm7 + vpsrld $2, %ymm1, %ymm1 + vpsrld $2, %ymm5, %ymm5 + vpxor %ymm2, %ymm3, %ymm3 + vpxor %ymm6, %ymm7, %ymm7 + vpslld $2, %ymm2, %ymm2 + vpslld $2, %ymm6, %ymm6 + vpxor %ymm1, %ymm3, %ymm3 + vpxor %ymm5, %ymm7, %ymm7 + vpxor %ymm2, %ymm3, %ymm3 + vpxor %ymm6, %ymm7, %ymm7 + + vpaddd %ymm0, %ymm3, %ymm3 + vpaddd %ymm4, %ymm7, %ymm7 + vmovdqa %ymm3, \i*32(%rax) + vmovdqa %ymm7, (\i+1)*32(%rax) +.endm + +.macro sha256_avx2_main_round i, r0, r1, r2, r3, r4, r5, r6, r7 + vpaddd 32*(\i)(%rax), \r0, %ymm6 + vpaddd 32*(\i)(%rcx), %ymm6, %ymm6 + + vpandn \r1, \r3, %ymm1 + vpand \r3, \r2, %ymm2 + vpxor %ymm2, %ymm1, %ymm1 + vpaddd %ymm1, %ymm6, %ymm6 + + vpslld $7, \r3, %ymm1 + vpsrld $6, \r3, \r0 + vpsrld $5, \r0, %ymm2 + vpxor %ymm1, \r0, \r0 + vpxor %ymm2, \r0, \r0 + vpslld $14, %ymm1, %ymm1 + vpsrld $14, %ymm2, %ymm2 + vpxor %ymm1, \r0, \r0 + vpxor %ymm2, \r0, \r0 + vpslld $5, %ymm1, %ymm1 + vpxor %ymm1, \r0, \r0 + vpaddd \r0, %ymm6, %ymm6 + vpaddd %ymm6, \r4, \r0 + + vpand \r6, \r5, %ymm2 + vpand \r7, \r5, \r4 + vpand \r7, \r6, %ymm1 + vpxor \r4, %ymm1, %ymm1 + vpxor %ymm2, %ymm1, %ymm1 + vpaddd %ymm1, %ymm6, %ymm6 + + vpslld $10, \r7, %ymm2 + vpsrld $2, \r7, \r4 + vpsrld $11, \r4, %ymm1 + vpxor %ymm2, \r4, \r4 + vpxor %ymm1, \r4, \r4 + vpslld $9, %ymm2, %ymm2 + vpsrld $9, %ymm1, %ymm1 + vpxor %ymm2, \r4, \r4 + vpxor %ymm1, \r4, \r4 + vpslld $11, %ymm2, %ymm2 + vpxor %ymm2, \r4, \r4 + vpaddd %ymm6, \r4, \r4 +.endm + +.macro sha256_avx2_main_quadround i + sha256_avx2_main_round \i+0, %ymm10, %ymm9, %ymm8, %ymm0, %ymm3, %ymm4, %ymm5, %ymm7 + sha256_avx2_main_round \i+1, %ymm9, %ymm8, %ymm0, %ymm10, %ymm4, %ymm5, %ymm7, %ymm3 + sha256_avx2_main_round \i+2, %ymm8, %ymm0, %ymm10, %ymm9, %ymm5, %ymm7, %ymm3, %ymm4 + sha256_avx2_main_round \i+3, %ymm0, %ymm10, %ymm9, %ymm8, %ymm7, %ymm3, %ymm4, %ymm5 +.endm + +.macro sha256_xop_extend_round i + vmovdqa (\i-15)*16(%rax), %xmm0 + vprotd $25, %xmm0, %xmm1 + vprotd $14, %xmm0, %xmm2 + vpsrld $3, %xmm0, %xmm0 + vpxor %xmm1, %xmm2, %xmm2 + vpxor %xmm2, %xmm0, %xmm0 + + vpaddd (\i-16)*16(%rax), %xmm0, %xmm0 + vpaddd (\i-7)*16(%rax), %xmm0, %xmm0 + + vprotd $15, %xmm3, %xmm1 + vprotd $13, %xmm3, %xmm2 + vpsrld $10, %xmm3, %xmm3 + vpxor %xmm1, %xmm2, %xmm2 + vpxor %xmm2, %xmm3, %xmm3 + vpaddd %xmm0, %xmm3, %xmm3 + vmovdqa %xmm3, \i*16(%rax) +.endm + +.macro sha256_xop_extend_doubleround i + vmovdqa (\i-15)*16(%rax), %xmm0 + vmovdqa (\i-14)*16(%rax), %xmm4 + vprotd $25, %xmm0, %xmm1 + vprotd $25, %xmm4, %xmm5 + vprotd $14, %xmm0, %xmm2 + vprotd $14, %xmm4, %xmm6 + vpxor %xmm1, %xmm2, %xmm2 + vpxor %xmm5, %xmm6, %xmm6 + vpsrld $3, %xmm0, %xmm0 + vpsrld $3, %xmm4, %xmm4 + vpxor %xmm2, %xmm0, %xmm0 + vpxor %xmm6, %xmm4, %xmm4 + + vpaddd (\i-16)*16(%rax), %xmm0, %xmm0 + vpaddd (\i-15)*16(%rax), %xmm4, %xmm4 + + vprotd $15, %xmm3, %xmm1 + vprotd $15, %xmm7, %xmm5 + vprotd $13, %xmm3, %xmm2 + vprotd $13, %xmm7, %xmm6 + vpxor %xmm1, %xmm2, %xmm2 + vpxor %xmm5, %xmm6, %xmm6 + + vpaddd (\i-7)*16(%rax), %xmm0, %xmm0 + vpaddd (\i-6)*16(%rax), %xmm4, %xmm4 + + vpsrld $10, %xmm3, %xmm3 + vpsrld $10, %xmm7, %xmm7 + vpxor %xmm2, %xmm3, %xmm3 + vpxor %xmm6, %xmm7, %xmm7 + + vpaddd %xmm0, %xmm3, %xmm3 + vpaddd %xmm4, %xmm7, %xmm7 + vmovdqa %xmm3, \i*16(%rax) + vmovdqa %xmm7, (\i+1)*16(%rax) +.endm + +.macro sha256_xop_main_round i, r0, r1, r2, r3, r4, r5, r6, r7 + vpaddd 16*(\i)(%rax), \r0, %xmm6 + vpaddd 16*(\i)(%rcx), %xmm6, %xmm6 + + vpandn \r1, \r3, %xmm1 + vpand \r3, \r2, %xmm2 + vpxor %xmm2, %xmm1, %xmm1 + vpaddd %xmm1, %xmm6, %xmm6 + + vprotd $26, \r3, %xmm1 + vprotd $21, \r3, %xmm2 + vpxor %xmm1, %xmm2, %xmm2 + vprotd $7, \r3, \r0 + vpxor %xmm2, \r0, \r0 + vpaddd \r0, %xmm6, %xmm6 + vpaddd %xmm6, \r4, \r0 + + vpand \r6, \r5, %xmm2 + vpand \r7, \r5, \r4 + vpand \r7, \r6, %xmm1 + vpxor \r4, %xmm1, %xmm1 + vpxor %xmm2, %xmm1, %xmm1 + vpaddd %xmm1, %xmm6, %xmm6 + + vprotd $30, \r7, %xmm1 + vprotd $19, \r7, %xmm2 + vpxor %xmm1, %xmm2, %xmm2 + vprotd $10, \r7, \r4 + vpxor %xmm2, \r4, \r4 + vpaddd %xmm6, \r4, \r4 +.endm + +.macro sha256_xop_main_quadround i + sha256_xop_main_round \i+0, %xmm10, %xmm9, %xmm8, %xmm0, %xmm3, %xmm4, %xmm5, %xmm7 + sha256_xop_main_round \i+1, %xmm9, %xmm8, %xmm0, %xmm10, %xmm4, %xmm5, %xmm7, %xmm3 + sha256_xop_main_round \i+2, %xmm8, %xmm0, %xmm10, %xmm9, %xmm5, %xmm7, %xmm3, %xmm4 + sha256_xop_main_round \i+3, %xmm0, %xmm10, %xmm9, %xmm8, %xmm7, %xmm3, %xmm4, %xmm5 +.endm + + .text + .p2align 6 +sha256_transform_4way_core_sse2: + leaq 256(%rsp), %rcx + leaq 48*16(%rcx), %rax + movdqa -2*16(%rcx), %xmm3 + movdqa -1*16(%rcx), %xmm7 +sha256_transform_4way_sse2_extend_loop: + movdqa -15*16(%rcx), %xmm0 + movdqa -14*16(%rcx), %xmm4 + movdqa %xmm0, %xmm2 + movdqa %xmm4, %xmm6 + psrld $3, %xmm0 + psrld $3, %xmm4 + movdqa %xmm0, %xmm1 + movdqa %xmm4, %xmm5 + pslld $14, %xmm2 + pslld $14, %xmm6 + psrld $4, %xmm1 + psrld $4, %xmm5 + pxor %xmm1, %xmm0 + pxor %xmm5, %xmm4 + psrld $11, %xmm1 + psrld $11, %xmm5 + pxor %xmm2, %xmm0 + pxor %xmm6, %xmm4 + pslld $11, %xmm2 + pslld $11, %xmm6 + pxor %xmm1, %xmm0 + pxor %xmm5, %xmm4 + pxor %xmm2, %xmm0 + pxor %xmm6, %xmm4 + + paddd -16*16(%rcx), %xmm0 + paddd -15*16(%rcx), %xmm4 + + movdqa %xmm3, %xmm2 + movdqa %xmm7, %xmm6 + psrld $10, %xmm3 + psrld $10, %xmm7 + movdqa %xmm3, %xmm1 + movdqa %xmm7, %xmm5 + pslld $13, %xmm2 + pslld $13, %xmm6 + psrld $7, %xmm1 + psrld $7, %xmm5 + + paddd -7*16(%rcx), %xmm0 + paddd -6*16(%rcx), %xmm4 + + pxor %xmm1, %xmm3 + pxor %xmm5, %xmm7 + psrld $2, %xmm1 + psrld $2, %xmm5 + pxor %xmm2, %xmm3 + pxor %xmm6, %xmm7 + pslld $2, %xmm2 + pslld $2, %xmm6 + pxor %xmm1, %xmm3 + pxor %xmm5, %xmm7 + pxor %xmm2, %xmm3 + pxor %xmm6, %xmm7 + + paddd %xmm0, %xmm3 + paddd %xmm4, %xmm7 + movdqa %xmm3, (%rcx) + movdqa %xmm7, 16(%rcx) + addq $2*16, %rcx + cmpq %rcx, %rax + jne sha256_transform_4way_sse2_extend_loop + + movdqu 0(%rdi), %xmm7 + movdqu 16(%rdi), %xmm5 + movdqu 32(%rdi), %xmm4 + movdqu 48(%rdi), %xmm3 + movdqu 64(%rdi), %xmm0 + movdqu 80(%rdi), %xmm8 + movdqu 96(%rdi), %xmm9 + movdqu 112(%rdi), %xmm10 + + leaq sha256_4k(%rip), %rcx + xorq %rax, %rax +sha256_transform_4way_sse2_main_loop: + movdqa (%rsp, %rax), %xmm6 + paddd (%rcx, %rax), %xmm6 + paddd %xmm10, %xmm6 + + movdqa %xmm0, %xmm1 + movdqa %xmm9, %xmm2 + pandn %xmm2, %xmm1 + + movdqa %xmm2, %xmm10 + movdqa %xmm8, %xmm2 + movdqa %xmm2, %xmm9 + + pand %xmm0, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm0, %xmm8 + + paddd %xmm1, %xmm6 + + movdqa %xmm0, %xmm1 + psrld $6, %xmm0 + movdqa %xmm0, %xmm2 + pslld $7, %xmm1 + psrld $5, %xmm2 + pxor %xmm1, %xmm0 + pxor %xmm2, %xmm0 + pslld $14, %xmm1 + psrld $14, %xmm2 + pxor %xmm1, %xmm0 + pxor %xmm2, %xmm0 + pslld $5, %xmm1 + pxor %xmm1, %xmm0 + paddd %xmm0, %xmm6 + + movdqa %xmm3, %xmm0 + paddd %xmm6, %xmm0 + + movdqa %xmm5, %xmm1 + movdqa %xmm4, %xmm3 + movdqa %xmm4, %xmm2 + pand %xmm5, %xmm2 + pand %xmm7, %xmm4 + pand %xmm7, %xmm1 + pxor %xmm4, %xmm1 + movdqa %xmm5, %xmm4 + movdqa %xmm7, %xmm5 + pxor %xmm2, %xmm1 + paddd %xmm1, %xmm6 + + movdqa %xmm7, %xmm2 + psrld $2, %xmm7 + movdqa %xmm7, %xmm1 + pslld $10, %xmm2 + psrld $11, %xmm1 + pxor %xmm2, %xmm7 + pxor %xmm1, %xmm7 + pslld $9, %xmm2 + psrld $9, %xmm1 + pxor %xmm2, %xmm7 + pxor %xmm1, %xmm7 + pslld $11, %xmm2 + pxor %xmm2, %xmm7 + paddd %xmm6, %xmm7 + + addq $16, %rax + cmpq $16*64, %rax + jne sha256_transform_4way_sse2_main_loop + jmp sha256_transform_4way_finish + + .text + .p2align 6 +sha256_transform_4way_core_avx: + leaq 256(%rsp), %rax + movdqa -2*16(%rax), %xmm3 + movdqa -1*16(%rax), %xmm7 + sha256_avx_extend_doubleround 0 + sha256_avx_extend_doubleround 2 + sha256_avx_extend_doubleround 4 + sha256_avx_extend_doubleround 6 + sha256_avx_extend_doubleround 8 + sha256_avx_extend_doubleround 10 + sha256_avx_extend_doubleround 12 + sha256_avx_extend_doubleround 14 + sha256_avx_extend_doubleround 16 + sha256_avx_extend_doubleround 18 + sha256_avx_extend_doubleround 20 + sha256_avx_extend_doubleround 22 + sha256_avx_extend_doubleround 24 + sha256_avx_extend_doubleround 26 + sha256_avx_extend_doubleround 28 + sha256_avx_extend_doubleround 30 + sha256_avx_extend_doubleround 32 + sha256_avx_extend_doubleround 34 + sha256_avx_extend_doubleround 36 + sha256_avx_extend_doubleround 38 + sha256_avx_extend_doubleround 40 + sha256_avx_extend_doubleround 42 + sha256_avx_extend_doubleround 44 + sha256_avx_extend_doubleround 46 + movdqu 0(%rdi), %xmm7 + movdqu 16(%rdi), %xmm5 + movdqu 32(%rdi), %xmm4 + movdqu 48(%rdi), %xmm3 + movdqu 64(%rdi), %xmm0 + movdqu 80(%rdi), %xmm8 + movdqu 96(%rdi), %xmm9 + movdqu 112(%rdi), %xmm10 + movq %rsp, %rax + leaq sha256_4k(%rip), %rcx + sha256_avx_main_quadround 0 + sha256_avx_main_quadround 4 + sha256_avx_main_quadround 8 + sha256_avx_main_quadround 12 + sha256_avx_main_quadround 16 + sha256_avx_main_quadround 20 + sha256_avx_main_quadround 24 + sha256_avx_main_quadround 28 + sha256_avx_main_quadround 32 + sha256_avx_main_quadround 36 + sha256_avx_main_quadround 40 + sha256_avx_main_quadround 44 + sha256_avx_main_quadround 48 + sha256_avx_main_quadround 52 + sha256_avx_main_quadround 56 + sha256_avx_main_quadround 60 + jmp sha256_transform_4way_finish + + .text + .p2align 6 +sha256_transform_4way_core_xop: + leaq 256(%rsp), %rax + movdqa -2*16(%rax), %xmm3 + movdqa -1*16(%rax), %xmm7 + sha256_xop_extend_doubleround 0 + sha256_xop_extend_doubleround 2 + sha256_xop_extend_doubleround 4 + sha256_xop_extend_doubleround 6 + sha256_xop_extend_doubleround 8 + sha256_xop_extend_doubleround 10 + sha256_xop_extend_doubleround 12 + sha256_xop_extend_doubleround 14 + sha256_xop_extend_doubleround 16 + sha256_xop_extend_doubleround 18 + sha256_xop_extend_doubleround 20 + sha256_xop_extend_doubleround 22 + sha256_xop_extend_doubleround 24 + sha256_xop_extend_doubleround 26 + sha256_xop_extend_doubleround 28 + sha256_xop_extend_doubleround 30 + sha256_xop_extend_doubleround 32 + sha256_xop_extend_doubleround 34 + sha256_xop_extend_doubleround 36 + sha256_xop_extend_doubleround 38 + sha256_xop_extend_doubleround 40 + sha256_xop_extend_doubleround 42 + sha256_xop_extend_doubleround 44 + sha256_xop_extend_doubleround 46 + movdqu 0(%rdi), %xmm7 + movdqu 16(%rdi), %xmm5 + movdqu 32(%rdi), %xmm4 + movdqu 48(%rdi), %xmm3 + movdqu 64(%rdi), %xmm0 + movdqu 80(%rdi), %xmm8 + movdqu 96(%rdi), %xmm9 + movdqu 112(%rdi), %xmm10 + movq %rsp, %rax + leaq sha256_4k(%rip), %rcx + sha256_xop_main_quadround 0 + sha256_xop_main_quadround 4 + sha256_xop_main_quadround 8 + sha256_xop_main_quadround 12 + sha256_xop_main_quadround 16 + sha256_xop_main_quadround 20 + sha256_xop_main_quadround 24 + sha256_xop_main_quadround 28 + sha256_xop_main_quadround 32 + sha256_xop_main_quadround 36 + sha256_xop_main_quadround 40 + sha256_xop_main_quadround 44 + sha256_xop_main_quadround 48 + sha256_xop_main_quadround 52 + sha256_xop_main_quadround 56 + sha256_xop_main_quadround 60 + jmp sha256_transform_4way_finish + + .data + .p2align 3 +sha256_transform_4way_core_addr: + .quad 0x0 + +.macro p2bswap_rsi_rsp i + movdqu \i*16(%rsi), %xmm0 + movdqu (\i+1)*16(%rsi), %xmm2 + pshuflw $0xb1, %xmm0, %xmm0 + pshuflw $0xb1, %xmm2, %xmm2 + pshufhw $0xb1, %xmm0, %xmm0 + pshufhw $0xb1, %xmm2, %xmm2 + movdqa %xmm0, %xmm1 + movdqa %xmm2, %xmm3 + psrlw $8, %xmm1 + psrlw $8, %xmm3 + psllw $8, %xmm0 + psllw $8, %xmm2 + pxor %xmm1, %xmm0 + pxor %xmm3, %xmm2 + movdqa %xmm0, \i*16(%rsp) + movdqa %xmm2, (\i+1)*16(%rsp) +.endm + + .text + .p2align 6 + .globl sha256_transform_4way + .globl _sha256_transform_4way +sha256_transform_4way: +_sha256_transform_4way: +#if defined(_WIN64) || defined(__CYGWIN__) + pushq %rdi + subq $96, %rsp + movdqa %xmm6, 0(%rsp) + movdqa %xmm7, 16(%rsp) + movdqa %xmm8, 32(%rsp) + movdqa %xmm9, 48(%rsp) + movdqa %xmm10, 64(%rsp) + movdqa %xmm11, 80(%rsp) + pushq %rsi + movq %rcx, %rdi + movq %rdx, %rsi + movq %r8, %rdx +#endif + movq %rsp, %r8 + subq $1032, %rsp + andq $-128, %rsp + + testq %rdx, %rdx + jnz sha256_transform_4way_swap + + movdqu 0*16(%rsi), %xmm0 + movdqu 1*16(%rsi), %xmm1 + movdqu 2*16(%rsi), %xmm2 + movdqu 3*16(%rsi), %xmm3 + movdqu 4*16(%rsi), %xmm4 + movdqu 5*16(%rsi), %xmm5 + movdqu 6*16(%rsi), %xmm6 + movdqu 7*16(%rsi), %xmm7 + movdqa %xmm0, 0*16(%rsp) + movdqa %xmm1, 1*16(%rsp) + movdqa %xmm2, 2*16(%rsp) + movdqa %xmm3, 3*16(%rsp) + movdqa %xmm4, 4*16(%rsp) + movdqa %xmm5, 5*16(%rsp) + movdqa %xmm6, 6*16(%rsp) + movdqa %xmm7, 7*16(%rsp) + movdqu 8*16(%rsi), %xmm0 + movdqu 9*16(%rsi), %xmm1 + movdqu 10*16(%rsi), %xmm2 + movdqu 11*16(%rsi), %xmm3 + movdqu 12*16(%rsi), %xmm4 + movdqu 13*16(%rsi), %xmm5 + movdqu 14*16(%rsi), %xmm6 + movdqu 15*16(%rsi), %xmm7 + movdqa %xmm0, 8*16(%rsp) + movdqa %xmm1, 9*16(%rsp) + movdqa %xmm2, 10*16(%rsp) + movdqa %xmm3, 11*16(%rsp) + movdqa %xmm4, 12*16(%rsp) + movdqa %xmm5, 13*16(%rsp) + movdqa %xmm6, 14*16(%rsp) + movdqa %xmm7, 15*16(%rsp) + jmp *sha256_transform_4way_core_addr(%rip) + + .p2align 6 +sha256_transform_4way_swap: + p2bswap_rsi_rsp 0 + p2bswap_rsi_rsp 2 + p2bswap_rsi_rsp 4 + p2bswap_rsi_rsp 6 + p2bswap_rsi_rsp 8 + p2bswap_rsi_rsp 10 + p2bswap_rsi_rsp 12 + p2bswap_rsi_rsp 14 + jmp *sha256_transform_4way_core_addr(%rip) + + .p2align 6 +sha256_transform_4way_finish: + movdqu 0(%rdi), %xmm2 + movdqu 16(%rdi), %xmm6 + movdqu 32(%rdi), %xmm11 + movdqu 48(%rdi), %xmm1 + paddd %xmm2, %xmm7 + paddd %xmm6, %xmm5 + paddd %xmm11, %xmm4 + paddd %xmm1, %xmm3 + movdqu 64(%rdi), %xmm2 + movdqu 80(%rdi), %xmm6 + movdqu 96(%rdi), %xmm11 + movdqu 112(%rdi), %xmm1 + paddd %xmm2, %xmm0 + paddd %xmm6, %xmm8 + paddd %xmm11, %xmm9 + paddd %xmm1, %xmm10 + + movdqu %xmm7, 0(%rdi) + movdqu %xmm5, 16(%rdi) + movdqu %xmm4, 32(%rdi) + movdqu %xmm3, 48(%rdi) + movdqu %xmm0, 64(%rdi) + movdqu %xmm8, 80(%rdi) + movdqu %xmm9, 96(%rdi) + movdqu %xmm10, 112(%rdi) + + movq %r8, %rsp +#if defined(_WIN64) || defined(__CYGWIN__) + popq %rsi + movdqa 0(%rsp), %xmm6 + movdqa 16(%rsp), %xmm7 + movdqa 32(%rsp), %xmm8 + movdqa 48(%rsp), %xmm9 + movdqa 64(%rsp), %xmm10 + movdqa 80(%rsp), %xmm11 + addq $96, %rsp + popq %rdi +#endif + ret + + .text + .p2align 6 +sha256_transform_8way_core_avx2: + leaq 8*64(%rsp), %rax + vmovdqa -2*32(%rax), %ymm3 + vmovdqa -1*32(%rax), %ymm7 + sha256_avx2_extend_doubleround 0 + sha256_avx2_extend_doubleround 2 + sha256_avx2_extend_doubleround 4 + sha256_avx2_extend_doubleround 6 + sha256_avx2_extend_doubleround 8 + sha256_avx2_extend_doubleround 10 + sha256_avx2_extend_doubleround 12 + sha256_avx2_extend_doubleround 14 + sha256_avx2_extend_doubleround 16 + sha256_avx2_extend_doubleround 18 + sha256_avx2_extend_doubleround 20 + sha256_avx2_extend_doubleround 22 + sha256_avx2_extend_doubleround 24 + sha256_avx2_extend_doubleround 26 + sha256_avx2_extend_doubleround 28 + sha256_avx2_extend_doubleround 30 + sha256_avx2_extend_doubleround 32 + sha256_avx2_extend_doubleround 34 + sha256_avx2_extend_doubleround 36 + sha256_avx2_extend_doubleround 38 + sha256_avx2_extend_doubleround 40 + sha256_avx2_extend_doubleround 42 + sha256_avx2_extend_doubleround 44 + sha256_avx2_extend_doubleround 46 + vmovdqu 0*32(%rdi), %ymm7 + vmovdqu 1*32(%rdi), %ymm5 + vmovdqu 2*32(%rdi), %ymm4 + vmovdqu 3*32(%rdi), %ymm3 + vmovdqu 4*32(%rdi), %ymm0 + vmovdqu 5*32(%rdi), %ymm8 + vmovdqu 6*32(%rdi), %ymm9 + vmovdqu 7*32(%rdi), %ymm10 + movq %rsp, %rax + leaq sha256_8k(%rip), %rcx + sha256_avx2_main_quadround 0 + sha256_avx2_main_quadround 4 + sha256_avx2_main_quadround 8 + sha256_avx2_main_quadround 12 + sha256_avx2_main_quadround 16 + sha256_avx2_main_quadround 20 + sha256_avx2_main_quadround 24 + sha256_avx2_main_quadround 28 + sha256_avx2_main_quadround 32 + sha256_avx2_main_quadround 36 + sha256_avx2_main_quadround 40 + sha256_avx2_main_quadround 44 + sha256_avx2_main_quadround 48 + sha256_avx2_main_quadround 52 + sha256_avx2_main_quadround 56 + sha256_avx2_main_quadround 60 + jmp sha256_transform_8way_finish + +.macro p2bswap_avx2_rsi_rsp i + vmovdqu \i*32(%rsi), %ymm0 + vmovdqu (\i+1)*32(%rsi), %ymm2 + vpshuflw $0xb1, %ymm0, %ymm0 + vpshuflw $0xb1, %ymm2, %ymm2 + vpshufhw $0xb1, %ymm0, %ymm0 + vpshufhw $0xb1, %ymm2, %ymm2 + vpsrlw $8, %ymm0, %ymm1 + vpsrlw $8, %ymm2, %ymm3 + vpsllw $8, %ymm0, %ymm0 + vpsllw $8, %ymm2, %ymm2 + vpxor %ymm1, %ymm0, %ymm0 + vpxor %ymm3, %ymm2, %ymm2 + vmovdqa %ymm0, \i*32(%rsp) + vmovdqa %ymm2, (\i+1)*32(%rsp) +.endm + + .text + .p2align 6 + .globl sha256_transform_8way + .globl _sha256_transform_8way +sha256_transform_8way: +_sha256_transform_8way: +#if defined(_WIN64) || defined(__CYGWIN__) + pushq %rdi + subq $96, %rsp + vmovdqa %xmm6, 0(%rsp) + vmovdqa %xmm7, 16(%rsp) + vmovdqa %xmm8, 32(%rsp) + vmovdqa %xmm9, 48(%rsp) + vmovdqa %xmm10, 64(%rsp) + vmovdqa %xmm11, 80(%rsp) + pushq %rsi + movq %rcx, %rdi + movq %rdx, %rsi + movq %r8, %rdx +#endif + movq %rsp, %r8 + subq $64*32, %rsp + andq $-128, %rsp + + testq %rdx, %rdx + jnz sha256_transform_8way_swap + + vmovdqu 0*32(%rsi), %ymm0 + vmovdqu 1*32(%rsi), %ymm1 + vmovdqu 2*32(%rsi), %ymm2 + vmovdqu 3*32(%rsi), %ymm3 + vmovdqu 4*32(%rsi), %ymm4 + vmovdqu 5*32(%rsi), %ymm5 + vmovdqu 6*32(%rsi), %ymm6 + vmovdqu 7*32(%rsi), %ymm7 + vmovdqa %ymm0, 0*32(%rsp) + vmovdqa %ymm1, 1*32(%rsp) + vmovdqa %ymm2, 2*32(%rsp) + vmovdqa %ymm3, 3*32(%rsp) + vmovdqa %ymm4, 4*32(%rsp) + vmovdqa %ymm5, 5*32(%rsp) + vmovdqa %ymm6, 6*32(%rsp) + vmovdqa %ymm7, 7*32(%rsp) + vmovdqu 8*32(%rsi), %ymm0 + vmovdqu 9*32(%rsi), %ymm1 + vmovdqu 10*32(%rsi), %ymm2 + vmovdqu 11*32(%rsi), %ymm3 + vmovdqu 12*32(%rsi), %ymm4 + vmovdqu 13*32(%rsi), %ymm5 + vmovdqu 14*32(%rsi), %ymm6 + vmovdqu 15*32(%rsi), %ymm7 + vmovdqa %ymm0, 8*32(%rsp) + vmovdqa %ymm1, 9*32(%rsp) + vmovdqa %ymm2, 10*32(%rsp) + vmovdqa %ymm3, 11*32(%rsp) + vmovdqa %ymm4, 12*32(%rsp) + vmovdqa %ymm5, 13*32(%rsp) + vmovdqa %ymm6, 14*32(%rsp) + vmovdqa %ymm7, 15*32(%rsp) + jmp sha256_transform_8way_core_avx2 + + .p2align 6 +sha256_transform_8way_swap: + p2bswap_avx2_rsi_rsp 0 + p2bswap_avx2_rsi_rsp 2 + p2bswap_avx2_rsi_rsp 4 + p2bswap_avx2_rsi_rsp 6 + p2bswap_avx2_rsi_rsp 8 + p2bswap_avx2_rsi_rsp 10 + p2bswap_avx2_rsi_rsp 12 + p2bswap_avx2_rsi_rsp 14 + jmp sha256_transform_8way_core_avx2 + + .p2align 6 +sha256_transform_8way_finish: + vmovdqu 0*32(%rdi), %ymm2 + vmovdqu 1*32(%rdi), %ymm6 + vmovdqu 2*32(%rdi), %ymm11 + vmovdqu 3*32(%rdi), %ymm1 + vpaddd %ymm2, %ymm7, %ymm7 + vpaddd %ymm6, %ymm5, %ymm5 + vpaddd %ymm11, %ymm4, %ymm4 + vpaddd %ymm1, %ymm3, %ymm3 + vmovdqu 4*32(%rdi), %ymm2 + vmovdqu 5*32(%rdi), %ymm6 + vmovdqu 6*32(%rdi), %ymm11 + vmovdqu 7*32(%rdi), %ymm1 + vpaddd %ymm2, %ymm0, %ymm0 + vpaddd %ymm6, %ymm8, %ymm8 + vpaddd %ymm11, %ymm9, %ymm9 + vpaddd %ymm1, %ymm10, %ymm10 + + vmovdqu %ymm7, 0*32(%rdi) + vmovdqu %ymm5, 1*32(%rdi) + vmovdqu %ymm4, 2*32(%rdi) + vmovdqu %ymm3, 3*32(%rdi) + vmovdqu %ymm0, 4*32(%rdi) + vmovdqu %ymm8, 5*32(%rdi) + vmovdqu %ymm9, 6*32(%rdi) + vmovdqu %ymm10, 7*32(%rdi) + + movq %r8, %rsp +#if defined(_WIN64) || defined(__CYGWIN__) + popq %rsi + vmovdqa 0(%rsp), %xmm6 + vmovdqa 16(%rsp), %xmm7 + vmovdqa 32(%rsp), %xmm8 + vmovdqa 48(%rsp), %xmm9 + vmovdqa 64(%rsp), %xmm10 + vmovdqa 80(%rsp), %xmm11 + addq $96, %rsp + popq %rdi +#endif + ret + + +.macro sha256_sse2_main_round_red i, r7 + movdqa 16*\i(%rax), %xmm6 + paddd 16*\i(%rcx), %xmm6 + paddd 32(%rsp), %xmm6 + movdqa %xmm0, %xmm1 + movdqa 16(%rsp), %xmm2 + paddd \r7, %xmm6 + pandn %xmm2, %xmm1 + movdqa %xmm2, 32(%rsp) + movdqa 0(%rsp), %xmm2 + movdqa %xmm2, 16(%rsp) + pand %xmm0, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm0, 0(%rsp) + paddd %xmm1, %xmm6 + movdqa %xmm0, %xmm1 + psrld $6, %xmm0 + movdqa %xmm0, %xmm2 + pslld $7, %xmm1 + psrld $5, %xmm2 + pxor %xmm1, %xmm0 + pxor %xmm2, %xmm0 + pslld $14, %xmm1 + psrld $14, %xmm2 + pxor %xmm1, %xmm0 + pxor %xmm2, %xmm0 + pslld $5, %xmm1 + pxor %xmm1, %xmm0 + paddd %xmm6, %xmm0 +.endm + +.macro sha256_avx_main_round_red i, r0, r1, r2, r3, r4 + vpaddd 16*\i(%rax), \r0, %xmm6 + vpaddd 16*\i(%rcx), %xmm6, %xmm6 + vpandn \r1, \r3, %xmm1 + vpand \r3, \r2, %xmm2 + vpxor %xmm2, %xmm1, %xmm1 + vpaddd %xmm1, %xmm6, %xmm6 + vpslld $7, \r3, %xmm1 + vpsrld $6, \r3, \r0 + vpsrld $5, \r0, %xmm2 + vpxor %xmm1, \r0, \r0 + vpxor %xmm2, \r0, \r0 + vpslld $14, %xmm1, %xmm1 + vpsrld $14, %xmm2, %xmm2 + vpxor %xmm1, \r0, \r0 + vpxor %xmm2, \r0, \r0 + vpslld $5, %xmm1, %xmm1 + vpxor %xmm1, \r0, \r0 + vpaddd \r0, %xmm6, %xmm6 + vpaddd %xmm6, \r4, \r0 +.endm + +.macro sha256_xop_main_round_red i, r0, r1, r2, r3, r4 + vpaddd 16*\i(%rax), \r0, %xmm6 + vpaddd 16*\i(%rcx), %xmm6, %xmm6 + vpandn \r1, \r3, %xmm1 + vpand \r3, \r2, %xmm2 + vpxor %xmm2, %xmm1, %xmm1 + vpaddd %xmm1, %xmm6, %xmm6 + vprotd $26, \r3, %xmm1 + vprotd $21, \r3, %xmm2 + vpxor %xmm1, %xmm2, %xmm2 + vprotd $7, \r3, \r0 + vpxor %xmm2, \r0, \r0 + vpaddd \r0, %xmm6, %xmm6 + vpaddd %xmm6, \r4, \r0 +.endm + + .text + .p2align 6 + .globl sha256_use_4way + .globl _sha256_use_4way +sha256_use_4way: +_sha256_use_4way: + pushq %rbx + pushq %rcx + pushq %rdx + + /* Check for VIA PadLock Hash Engine */ + movl $0xc0000000, %eax + cpuid + cmpl $0xc0000001, %eax + jb sha256_use_4way_no_phe + movl $0xc0000001, %eax + cpuid + andl $0x00000c00, %edx + cmpl $0x00000c00, %edx + jne sha256_use_4way_no_phe + leaq sha256_transform_phe(%rip), %rdx + movq %rdx, sha256_transform_addr(%rip) + xorl %eax, %eax + jmp sha256_use_4way_exit +sha256_use_4way_no_phe: + /* Check for AVX and OSXSAVE support */ + movl $1, %eax + cpuid + andl $0x18000000, %ecx + cmpl $0x18000000, %ecx + jne sha256_use_4way_base + /* Check for XMM and YMM state support */ + xorl %ecx, %ecx + xgetbv + andl $0x00000006, %eax + cmpl $0x00000006, %eax + jne sha256_use_4way_base + /* Check for XOP support */ + movl $0x80000001, %eax + cpuid + andl $0x00000800, %ecx + jz sha256_use_4way_avx + +sha256_use_4way_xop: + leaq sha256_transform_4way_core_xop(%rip), %rdx + jmp sha256_use_4way_done + +sha256_use_4way_avx: + leaq sha256_transform_4way_core_avx(%rip), %rdx + jmp sha256_use_4way_done + +sha256_use_4way_base: + leaq sha256_transform_4way_core_sse2(%rip), %rdx + +sha256_use_4way_done: + movq %rdx, sha256_transform_4way_core_addr(%rip) + movl $1, %eax +sha256_use_4way_exit: + popq %rdx + popq %rcx + popq %rbx + ret + + .text + .p2align 6 + .globl sha256_use_ssse3 + .globl _sha256_use_ssse3 +sha256_use_ssse3: +_sha256_use_ssse3: + pushq %rbx + pushq %rcx + pushq %rdx + cpuid + andl $0x00000200, %ecx + jz sha256_use_ssse3_done + xorl %eax, %eax + popq %rdx + popq %rcx + popq %rbx + ret + +sha256_use_ssse3_done: + movl $1, %eax + popq %rdx + popq %rcx + popq %rbx + ret + +.macro sha256_avx2_main_round_red i, r0, r1, r2, r3, r4 + vpaddd 32*\i(%rax), \r0, %ymm6 + vpaddd 32*\i(%rcx), %ymm6, %ymm6 + vpandn \r1, \r3, %ymm1 + vpand \r3, \r2, %ymm2 + vpxor %ymm2, %ymm1, %ymm1 + vpaddd %ymm1, %ymm6, %ymm6 + vpslld $7, \r3, %ymm1 + vpsrld $6, \r3, \r0 + vpsrld $5, \r0, %ymm2 + vpxor %ymm1, \r0, \r0 + vpxor %ymm2, \r0, \r0 + vpslld $14, %ymm1, %ymm1 + vpsrld $14, %ymm2, %ymm2 + vpxor %ymm1, \r0, \r0 + vpxor %ymm2, \r0, \r0 + vpslld $5, %ymm1, %ymm1 + vpxor %ymm1, \r0, \r0 + vpaddd \r0, %ymm6, %ymm6 + vpaddd %ymm6, \r4, \r0 +.endm + + .text + .p2align 6 + .globl sha256_use_8way + .globl _sha256_use_8way +sha256_use_8way: +_sha256_use_8way: + + pushq %rbx + /* Check for AVX and OSXSAVE support */ + movl $1, %eax + cpuid + andl $0x18000000, %ecx + cmpl $0x18000000, %ecx + jne sha256_use_8way_no + /* Check for AVX2 support */ + movl $7, %eax + xorl %ecx, %ecx + cpuid + andl $0x00000020, %ebx + cmpl $0x00000020, %ebx + jne sha256_use_8way_no + /* Check for XMM and YMM state support */ + xorl %ecx, %ecx + xgetbv + andl $0x00000006, %eax + cmpl $0x00000006, %eax + jne sha256_use_8way_no + +sha256_use_8way_yes: + movl $1, %eax + jmp sha256_use_8way_done + +sha256_use_8way_no: + xorl %eax, %eax + +sha256_use_8way_done: + popq %rbx + ret + +#endif diff --git a/src/db.cpp b/src/db.cpp new file mode 100644 index 00000000..65d11a33 --- /dev/null +++ b/src/db.cpp @@ -0,0 +1,611 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "db.h" +#include "net.h" +#include "util.h" +#include "main.h" +#include "ui_interface.h" +#include +#include + +#ifndef WIN32 +#include "sys/stat.h" +#endif + +using namespace std; +using namespace boost; + + +unsigned int nWalletDBUpdated; +extern bool fUseMemoryLog; + + +// +// CDB +// + +CDBEnv bitdb; + +void CDBEnv::EnvShutdown() +{ + if (!fDbEnvInit) + return; + + fDbEnvInit = false; + int ret = dbenv.close(0); + if (ret != 0) + printf("EnvShutdown exception: %s (%d)\n", DbEnv::strerror(ret), ret); + if (!fMockDb) + DbEnv(0).remove(strPath.c_str(), 0); +} + +CDBEnv::CDBEnv() : dbenv(DB_CXX_NO_EXCEPTIONS) +{ + fDbEnvInit = false; + fMockDb = false; +} + +CDBEnv::~CDBEnv() +{ + EnvShutdown(); +} + +void CDBEnv::Close() +{ + EnvShutdown(); +} + +bool CDBEnv::Open(boost::filesystem::path pathEnv_) +{ + if (fDbEnvInit) + return true; + + if (fShutdown) + return false; + + pathEnv = pathEnv_; + filesystem::path pathDataDir = pathEnv; + strPath = pathDataDir.string(); + filesystem::path pathLogDir = pathDataDir / "database"; + filesystem::create_directory(pathLogDir); + filesystem::path pathErrorFile = pathDataDir / "db.log"; + printf("dbenv.open LogDir=%s ErrorFile=%s\n", pathLogDir.string().c_str(), pathErrorFile.string().c_str()); + + unsigned int nEnvFlags = 0; + if (GetBoolArg("-privdb", true)) + nEnvFlags |= DB_PRIVATE; + + int nDbCache = GetArgInt("-dbcache", 25); + dbenv.set_lg_dir(pathLogDir.string().c_str()); + dbenv.set_cachesize(nDbCache / 1024, (nDbCache % 1024)*1048576, 1); + dbenv.set_lg_bsize(1048576); + dbenv.set_lg_max(10485760); + + // Bugfix: Bump lk_max_locks default to 537000, to safely handle reorgs with up to 5 blocks reversed + // dbenv.set_lk_max_locks(10000); + dbenv.set_lk_max_locks(537000); + + dbenv.set_lk_max_objects(10000); + dbenv.set_errfile(fopen(pathErrorFile.string().c_str(), "a")); /// debug + dbenv.set_flags(DB_AUTO_COMMIT, 1); + dbenv.set_flags(DB_TXN_WRITE_NOSYNC, 1); +#ifdef DB_LOG_AUTO_REMOVE + dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1); +#endif + int ret = dbenv.open(strPath.c_str(), + DB_CREATE | + DB_INIT_LOCK | + DB_INIT_LOG | + DB_INIT_MPOOL | + DB_INIT_TXN | + DB_THREAD | + DB_RECOVER | + nEnvFlags, + S_IRUSR | S_IWUSR); + if (ret != 0) + return error("CDB() : error %s (%d) opening database environment", DbEnv::strerror(ret), ret); + + fDbEnvInit = true; + fMockDb = false; + +#ifndef USE_LEVELDB + // Check that the number of locks is sufficient (to prevent chain fork possibility, read http://bitcoin.org/may15 for more info) + u_int32_t nMaxLocks; + if (!dbenv.get_lk_max_locks(&nMaxLocks)) + { + int nBlocks, nDeepReorg; + std::string strMessage; + + nBlocks = nMaxLocks / 48768; + nDeepReorg = (nBlocks - 1) / 2; + + printf("Final lk_max_locks is %lu, sufficient for (worst case) %d block%s in a single transaction (up to a %d-deep reorganization)\n", (unsigned long)nMaxLocks, nBlocks, (nBlocks == 1) ? "" : "s", nDeepReorg); + if (nDeepReorg < 3) + { + if (nBlocks < 1) + strMessage = strprintf(_("Warning: DB_CONFIG has set_lk_max_locks %lu, which may be too low for a single block. If this limit is reached, XP may stop working."), (unsigned long)nMaxLocks); + else + strMessage = strprintf(_("Warning: DB_CONFIG has set_lk_max_locks %lu, which may be too low for a common blockchain reorganization. If this limit is reached, XP may stop working."), (unsigned long)nMaxLocks); + + strMiscWarning = strMessage; + printf("*** %s\n", strMessage.c_str()); + } + } +#endif + + return true; +} + +void CDBEnv::MakeMock() +{ + if (fDbEnvInit) + throw runtime_error("CDBEnv::MakeMock(): already initialized"); + + if (fShutdown) + throw runtime_error("CDBEnv::MakeMock(): during shutdown"); + + printf("CDBEnv::MakeMock()\n"); + + dbenv.set_cachesize(1, 0, 1); + dbenv.set_lg_bsize(10485760*4); + dbenv.set_lg_max(10485760); + dbenv.set_lk_max_locks(10000); + dbenv.set_lk_max_objects(10000); + dbenv.set_flags(DB_AUTO_COMMIT, 1); +#ifdef DB_LOG_IN_MEMORY + dbenv.log_set_config(DB_LOG_IN_MEMORY, fUseMemoryLog ? 1 : 0); +#endif + int ret = dbenv.open(NULL, + DB_CREATE | + DB_INIT_LOCK | + DB_INIT_LOG | + DB_INIT_MPOOL | + DB_INIT_TXN | + DB_THREAD | + DB_PRIVATE, + S_IRUSR | S_IWUSR); + if (ret > 0) + throw runtime_error(strprintf("CDBEnv::MakeMock(): error %d opening database environment", ret)); + + fDbEnvInit = true; + fMockDb = true; +} + +CDBEnv::VerifyResult CDBEnv::Verify(std::string strFile, bool (*recoverFunc)(CDBEnv& dbenv, std::string strFile)) +{ + LOCK(cs_db); + assert(mapFileUseCount.count(strFile) == 0); + + Db db(&dbenv, 0); + int result = db.verify(strFile.c_str(), NULL, NULL, 0); + if (result == 0) + return VERIFY_OK; + else if (recoverFunc == NULL) + return RECOVER_FAIL; + + // Try to recover: + bool fRecovered = (*recoverFunc)(*this, strFile); + return (fRecovered ? RECOVER_OK : RECOVER_FAIL); +} + +bool CDBEnv::Salvage(std::string strFile, bool fAggressive, + std::vector& vResult) +{ + LOCK(cs_db); + assert(mapFileUseCount.count(strFile) == 0); + + u_int32_t flags = DB_SALVAGE; + if (fAggressive) flags |= DB_AGGRESSIVE; + + stringstream strDump; + + Db db(&dbenv, 0); + int result = db.verify(strFile.c_str(), NULL, &strDump, flags); + if (result != 0) + { + printf("ERROR: db salvage failed\n"); + return false; + } + + // Format of bdb dump is ascii lines: + // header lines... + // HEADER=END + // hexadecimal key + // hexadecimal value + // ... repeated + // DATA=END + + string strLine; + while (!strDump.eof() && strLine != "HEADER=END") + getline(strDump, strLine); // Skip past header + + std::string keyHex, valueHex; + while (!strDump.eof() && keyHex != "DATA=END") + { + getline(strDump, keyHex); + if (keyHex != "DATA_END") + { + getline(strDump, valueHex); + vResult.push_back(make_pair(ParseHex(keyHex),ParseHex(valueHex))); + } + } + + return (result == 0); +} + + +void CDBEnv::CheckpointLSN(std::string strFile) +{ + dbenv.txn_checkpoint(0, 0, 0); + if (fMockDb) + return; + dbenv.lsn_reset(strFile.c_str(), 0); +} + + +CDB::CDB(const char *pszFile, const char* pszMode) : + pdb(NULL), activeTxn(NULL) +{ + int ret; + if (pszFile == NULL) + return; + + fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w')); + bool fCreate = strchr(pszMode, 'c') != NULL; + unsigned int nFlags = DB_THREAD; + if (fCreate) + nFlags |= DB_CREATE; + + { + LOCK(bitdb.cs_db); + if (!bitdb.Open(GetDataDir())) + throw runtime_error("env open failed"); + + strFile = pszFile; + ++bitdb.mapFileUseCount[strFile]; + pdb = bitdb.mapDb[strFile]; + if (pdb == NULL) + { + pdb = new Db(&bitdb.dbenv, 0); + + bool fMockDb = bitdb.IsMock(); + if (fMockDb) + { + DbMpoolFile*mpf = pdb->get_mpf(); + ret = mpf->set_flags(DB_MPOOL_NOFILE, 1); + if (ret != 0) + throw runtime_error(strprintf("CDB() : failed to configure for no temp file backing for database %s", pszFile)); + } + + ret = pdb->open(NULL, // Txn pointer + fMockDb ? NULL : pszFile, // Filename + "main", // Logical db name + DB_BTREE, // Database type + nFlags, // Flags + 0); + + if (ret != 0) + { + delete pdb; + pdb = NULL; + --bitdb.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(CLIENT_VERSION); + fReadOnly = fTmp; + } + + bitdb.mapDb[strFile] = pdb; + } + } +} + +static bool IsChainFile(std::string strFile) +{ + if (strFile == "blkindex.dat") + return true; + + return false; +} + +void CDB::Close() +{ + if (!pdb) + return; + if (activeTxn) + activeTxn->abort(); + activeTxn = NULL; + pdb = NULL; + + // Flush database activity from memory pool to disk log + unsigned int nMinutes = 0; + if (fReadOnly) + nMinutes = 1; + if (IsChainFile(strFile)) + nMinutes = 2; + if (IsChainFile(strFile) && IsInitialBlockDownload()) + nMinutes = 5; + + bitdb.dbenv.txn_checkpoint(nMinutes ? GetArgUInt("-dblogsize", 100)*1024 : 0, nMinutes, 0); + + { + LOCK(bitdb.cs_db); + --bitdb.mapFileUseCount[strFile]; + } +} + +void CDBEnv::CloseDb(const string& strFile) +{ + { + LOCK(cs_db); + if (mapDb[strFile] != NULL) + { + // Close the database handle + Db* pdb = mapDb[strFile]; + pdb->close(0); + delete pdb; + mapDb[strFile] = NULL; + } + } +} + +bool CDBEnv::RemoveDb(const string& strFile) +{ + this->CloseDb(strFile); + + LOCK(cs_db); + int rc = dbenv.dbremove(NULL, strFile.c_str(), NULL, DB_AUTO_COMMIT); + return (rc == 0); +} + +bool CDB::Rewrite(const string& strFile, const char* pszSkip) +{ + while (!fShutdown) + { + { + LOCK(bitdb.cs_db); + if (!bitdb.mapFileUseCount.count(strFile) || bitdb.mapFileUseCount[strFile] == 0) + { + // Flush log data to the dat file + bitdb.CloseDb(strFile); + bitdb.CheckpointLSN(strFile); + bitdb.mapFileUseCount.erase(strFile); + + bool fSuccess = true; + printf("Rewriting %s...\n", strFile.c_str()); + string strFileRes = strFile + ".rewrite"; + { // surround usage of db with extra {} + CDB db(strFile.c_str(), "r"); + Db* pdbCopy = new Db(&bitdb.dbenv, 0); + + int ret = pdbCopy->open(NULL, // Txn pointer + strFileRes.c_str(), // Filename + "main", // Logical db name + DB_BTREE, // Database type + DB_CREATE, // Flags + 0); + if (ret > 0) + { + printf("Cannot create database file %s\n", strFileRes.c_str()); + fSuccess = false; + } + + Dbc* pcursor = db.GetCursor(); + if (pcursor) + while (fSuccess) + { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + int ret = db.ReadAtCursor(pcursor, ssKey, ssValue, DB_NEXT); + if (ret == DB_NOTFOUND) + { + pcursor->close(); + break; + } + else if (ret != 0) + { + pcursor->close(); + fSuccess = false; + break; + } + if (pszSkip && + strncmp(&ssKey[0], pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0) + continue; + if (strncmp(&ssKey[0], "\x07version", 8) == 0) + { + // Update version: + ssValue.clear(); + ssValue << CLIENT_VERSION; + } + Dbt datKey(&ssKey[0], ssKey.size()); + Dbt datValue(&ssValue[0], ssValue.size()); + int ret2 = pdbCopy->put(NULL, &datKey, &datValue, DB_NOOVERWRITE); + if (ret2 > 0) + fSuccess = false; + } + if (fSuccess) + { + db.Close(); + bitdb.CloseDb(strFile); + if (pdbCopy->close(0)) + fSuccess = false; + delete pdbCopy; + } + } + if (fSuccess) + { + Db dbA(&bitdb.dbenv, 0); + if (dbA.remove(strFile.c_str(), NULL, 0)) + fSuccess = false; + Db dbB(&bitdb.dbenv, 0); + if (dbB.rename(strFileRes.c_str(), NULL, strFile.c_str(), 0)) + fSuccess = false; + } + if (!fSuccess) + printf("Rewriting of %s FAILED!\n", strFileRes.c_str()); + return fSuccess; + } + } + Sleep(100); + } + return false; +} + + +void CDBEnv::Flush(bool fShutdown) +{ + int64_t nStart = GetTimeMillis(); + // Flush log data to the actual data file + // on all files that are not in use + printf("Flush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started"); + if (!fDbEnvInit) + return; + { + LOCK(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); + printf("%s checkpoint\n", strFile.c_str()); + dbenv.txn_checkpoint(0, 0, 0); + if (!IsChainFile(strFile) || fDetachDB) { + printf("%s detach\n", strFile.c_str()); + if (!fMockDb) + dbenv.lsn_reset(strFile.c_str(), 0); + } + printf("%s closed\n", strFile.c_str()); + mapFileUseCount.erase(mi++); + } + else + mi++; + } + printf("DBFlush(%s)%s ended %15" PRId64 "ms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started", GetTimeMillis() - nStart); + if (fShutdown) + { + char** listp; + if (mapFileUseCount.empty()) + { + dbenv.log_archive(&listp, DB_ARCH_REMOVE); + Close(); + } + } + } +} + + +// +// CAddrDB +// + + +CAddrDB::CAddrDB() +{ + pathAddr = GetDataDir() / "peers.dat"; +} + +bool CAddrDB::Write(const CAddrMan& addr) +{ + // Generate random temporary filename + unsigned short randv = 0; + RAND_bytes((unsigned char *)&randv, sizeof(randv)); + std::string tmpfn = strprintf("peers.dat.%04x", randv); + + // serialize addresses, checksum data up to that point, then append csum + CDataStream ssPeers(SER_DISK, CLIENT_VERSION); + ssPeers << FLATDATA(pchMessageStart); + ssPeers << addr; + uint256 hash = Hash(ssPeers.begin(), ssPeers.end()); + ssPeers << hash; + + // open temp output file, and associate with CAutoFile + boost::filesystem::path pathTmp = GetDataDir() / tmpfn; + FILE *file = fopen(pathTmp.string().c_str(), "wb"); + CAutoFile fileout = CAutoFile(file, SER_DISK, CLIENT_VERSION); + if (!fileout) + return error("CAddrman::Write() : open failed"); + + // Write and commit header, data + try { + fileout << ssPeers; + } + catch (const std::exception&) { + return error("CAddrman::Write() : I/O error"); + } + FileCommit(fileout); + fileout.fclose(); + + // replace existing peers.dat, if any, with new peers.dat.XXXX + if (!RenameOver(pathTmp, pathAddr)) + return error("CAddrman::Write() : Rename-into-place failed"); + + return true; +} + +bool CAddrDB::Read(CAddrMan& addr) +{ + // open input file, and associate with CAutoFile + FILE *file = fopen(pathAddr.string().c_str(), "rb"); + CAutoFile filein = CAutoFile(file, SER_DISK, CLIENT_VERSION); + if (!filein) + return error("CAddrman::Read() : open failed"); + + // use file size to size memory buffer + int fileSize = GetFilesize(filein); + int dataSize = fileSize - sizeof(uint256); + //Don't try to resize to a negative number if file is small + if ( dataSize < 0 ) dataSize = 0; + vector vchData; + vchData.resize(dataSize); + uint256 hashIn; + + // read data and checksum from file + try { + filein.read((char *)&vchData[0], dataSize); + filein >> hashIn; + } + catch (const std::exception&) { + return error("CAddrman::Read() 2 : I/O error or stream data corrupted"); + } + filein.fclose(); + + CDataStream ssPeers(vchData, SER_DISK, CLIENT_VERSION); + + // verify stored checksum matches input data + uint256 hashTmp = Hash(ssPeers.begin(), ssPeers.end()); + if (hashIn != hashTmp) + return error("CAddrman::Read() : checksum mismatch; data corrupted"); + + unsigned char pchMsgTmp[4]; + try { + // de-serialize file header (pchMessageStart magic number) and + ssPeers >> FLATDATA(pchMsgTmp); + + // verify the network matches ours + if (memcmp(pchMsgTmp, pchMessageStart, sizeof(pchMsgTmp))) + return error("CAddrman::Read() : invalid network magic number"); + + // de-serialize address data into one CAddrMan object + ssPeers >> addr; + } + catch (const std::exception&) { + return error("CAddrman::Read() : I/O error or stream data corrupted"); + } + + return true; +} + diff --git a/src/db.h b/src/db.h new file mode 100644 index 00000000..993b7a7b --- /dev/null +++ b/src/db.h @@ -0,0 +1,328 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_DB_H +#define BITCOIN_DB_H + +#include "main.h" + +#include +#include +#include + +#include + +class CAddress; +class CAddrMan; +class CBlockLocator; +class CDiskBlockIndex; +class CDiskTxPos; +class CMasterKey; +class COutPoint; +class CTxIndex; +class CWallet; +class CWalletTx; + +extern unsigned int nWalletDBUpdated; + +void ThreadFlushWalletDB(void* parg); +bool BackupWallet(const CWallet& wallet, const std::string& strDest); +bool DumpWallet(CWallet* pwallet, const std::string& strDest); +bool ImportWallet(CWallet* pwallet, const std::string& strLocation); + +class CDBEnv +{ +private: + bool fDetachDB; + bool fDbEnvInit; + bool fMockDb; + boost::filesystem::path pathEnv; + std::string strPath; + + void EnvShutdown(); + +public: + mutable CCriticalSection cs_db; + DbEnv dbenv; + std::map mapFileUseCount; + std::map mapDb; + + CDBEnv(); + ~CDBEnv(); + void MakeMock(); + bool IsMock() { return fMockDb; }; + + /* + * Verify that database file strFile is OK. If it is not, + * call the callback to try to recover. + * This must be called BEFORE strFile is opened. + * Returns true if strFile is OK. + */ + enum VerifyResult { VERIFY_OK, RECOVER_OK, RECOVER_FAIL }; + VerifyResult Verify(std::string strFile, bool (*recoverFunc)(CDBEnv& dbenv, std::string strFile)); + /* + * Salvage data from a file that Verify says is bad. + * fAggressive sets the DB_AGGRESSIVE flag (see berkeley DB->verify() method documentation). + * Appends binary key/value pairs to vResult, returns true if successful. + * NOTE: reads the entire database into memory, so cannot be used + * for huge databases. + */ + typedef std::pair, std::vector > KeyValPair; + bool Salvage(std::string strFile, bool fAggressive, std::vector& vResult); + + bool Open(boost::filesystem::path pathEnv_); + void Close(); + void Flush(bool fShutdown); + void CheckpointLSN(std::string strFile); + void SetDetach(bool fDetachDB_) { fDetachDB = fDetachDB_; } + bool GetDetach() { return fDetachDB; } + + void CloseDb(const std::string& strFile); + bool RemoveDb(const std::string& strFile); + + DbTxn *TxnBegin(int flags=DB_TXN_WRITE_NOSYNC) + { + DbTxn* ptxn = NULL; + int ret = dbenv.txn_begin(NULL, &ptxn, flags); + if (!ptxn || ret != 0) + return NULL; + return ptxn; + } +}; + +extern CDBEnv bitdb; + + +/** RAII class that provides access to a Berkeley database */ +class CDB +{ +protected: + Db* pdb; + std::string strFile; + DbTxn *activeTxn; + 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, CLIENT_VERSION); + ssKey.reserve(1000); + ssKey << key; + Dbt datKey(&ssKey[0], (uint32_t)ssKey.size()); + + // Read + Dbt datValue; + datValue.set_flags(DB_DBT_MALLOC); + int ret = pdb->get(activeTxn, &datKey, &datValue, 0); + memset(datKey.get_data(), 0, datKey.get_size()); + if (datValue.get_data() == NULL) + return false; + + // Unserialize value + try { + CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK, CLIENT_VERSION); + ssValue >> value; + } + catch (std::exception &e) { + (void)e; + return false; + } + + // 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"); + + // Key + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(1000); + ssKey << key; + Dbt datKey(&ssKey[0], (uint32_t)ssKey.size()); + + // Value + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + ssValue.reserve(10000); + ssValue << value; + Dbt datValue(&ssValue[0], (uint32_t)ssValue.size()); + + // Write + int ret = pdb->put(activeTxn, &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"); + + // Key + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(1000); + ssKey << key; + Dbt datKey(&ssKey[0], (uint32_t)ssKey.size()); + + // Erase + int ret = pdb->del(activeTxn, &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, CLIENT_VERSION); + ssKey.reserve(1000); + ssKey << key; + Dbt datKey(&ssKey[0], (uint32_t)ssKey.size()); + + // Exists + int ret = pdb->exists(activeTxn, &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((uint32_t)ssKey.size()); + } + Dbt datValue; + if (fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) + { + datValue.set_data(&ssValue[0]); + datValue.set_size((uint32_t)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; + } + +public: + bool TxnBegin() + { + if (!pdb || activeTxn) + return false; + DbTxn* ptxn = bitdb.TxnBegin(); + if (!ptxn) + return false; + activeTxn = ptxn; + return true; + } + + bool TxnCommit() + { + if (!pdb || !activeTxn) + return false; + int ret = activeTxn->commit(0); + activeTxn = NULL; + return (ret == 0); + } + + bool TxnAbort() + { + if (!pdb || !activeTxn) + return false; + int ret = activeTxn->abort(); + activeTxn = NULL; + 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); + } + + bool static Rewrite(const std::string& strFile, const char* pszSkip = NULL); +}; + + +/** Access to the (IP) address database (peers.dat) */ +class CAddrDB +{ +private: + boost::filesystem::path pathAddr; +public: + CAddrDB(); + bool Write(const CAddrMan& addr); + bool Read(CAddrMan& addr); +}; + +#endif // BITCOIN_DB_H diff --git a/src/hash.h b/src/hash.h new file mode 100644 index 00000000..7dbf1b64 --- /dev/null +++ b/src/hash.h @@ -0,0 +1,139 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_HASH_H +#define BITCOIN_HASH_H + +#include "serialize.h" +#include "uint256.h" +#include "version.h" + +#include + +#include +#include + +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; +} + +class CHashWriter +{ +private: + SHA256_CTX ctx; + +public: + int nType; + int nVersion; + + void Init() { + SHA256_Init(&ctx); + } + + CHashWriter(int nTypeIn, int nVersionIn) : nType(nTypeIn), nVersion(nVersionIn) { + Init(); + } + + CHashWriter& write(const char *pch, size_t size) { + SHA256_Update(&ctx, pch, size); + return (*this); + } + + // invalidates the object + uint256 GetHash() { + uint256 hash1; + SHA256_Final((unsigned char*)&hash1, &ctx); + uint256 hash2; + SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); + return hash2; + } + + template + CHashWriter& operator<<(const T& obj) { + // Serialize to this stream + ::Serialize(*this, obj, nType, nVersion); + return (*this); + } +}; + + +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=PROTOCOL_VERSION) +{ + CHashWriter ss(nType, nVersion); + ss << obj; + return ss.GetHash(); +} + +template +inline uint160 Hash160(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); + uint160 hash2; + RIPEMD160((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); + return hash2; +} + +inline uint160 Hash160(const std::vector& vch) +{ + return Hash160(vch.begin(), vch.end()); +} + +unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector& vDataToHash); + +typedef struct +{ + SHA512_CTX ctxInner; + SHA512_CTX ctxOuter; +} HMAC_SHA512_CTX; + +int HMAC_SHA512_Init(HMAC_SHA512_CTX *pctx, const void *pkey, size_t len); +int HMAC_SHA512_Update(HMAC_SHA512_CTX *pctx, const void *pdata, size_t len); +int HMAC_SHA512_Final(unsigned char *pmd, HMAC_SHA512_CTX *pctx); + +#endif diff --git a/src/init.cpp b/src/init.cpp new file mode 100644 index 00000000..899220eb --- /dev/null +++ b/src/init.cpp @@ -0,0 +1,1012 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "txdb.h" +#include "walletdb.h" +#include "bitcoinrpc.h" +#include "net.h" +#include "init.h" +#include "util.h" +#include "ui_interface.h" +#include "checkpoints.h" +#include +#include +#include +#include +#include +#include +#include + +#ifndef WIN32 +#include +#endif + + +using namespace std; +using namespace boost; + +CWallet* pwalletMain; +CClientUIInterface uiInterface; +std::string strWalletFileName; +bool fConfChange; +unsigned int nNodeLifespan; +bool fUseFastIndex; +bool fUseMemoryLog; +enum Checkpoints::CPMode CheckpointsMode; + +// Ping and address broadcast intervals +extern int64_t nPingInterval; +extern int64_t nBroadcastInterval; +extern int64_t nReserveBalance; + +////////////////////////////////////////////////////////////////////////////// +// +// Shutdown +// + +void ExitTimeout(void* parg) +{ +#ifdef WIN32 + Sleep(5000); + ExitProcess(0); +#endif +} + +void StartShutdown() +{ +#ifdef QT_GUI + // ensure we leave the Qt main loop for a clean GUI exit (Shutdown() is called in bitcoin.cpp afterwards) + uiInterface.QueueShutdown(); +#else + // Without UI, Shutdown() can simply be started in a new thread + NewThread(Shutdown, NULL); +#endif +} + +void Shutdown(void* parg) +{ + static CCriticalSection cs_Shutdown; + static bool fTaken; + + // Make this thread recognisable as the shutdown thread + RenameThread("XP-shutoff"); + + bool fFirstThread = false; + { + TRY_LOCK(cs_Shutdown, lockShutdown); + if (lockShutdown) + { + fFirstThread = !fTaken; + fTaken = true; + } + } + static bool fExit; + if (fFirstThread) + { + fShutdown = true; + fRequestShutdown = true; + nTransactionsUpdated++; +// CTxDB().Close(); + bitdb.Flush(false); + StopNode(); + bitdb.Flush(true); + boost::filesystem::remove(GetPidFile()); + UnregisterWallet(pwalletMain); + delete pwalletMain; + NewThread(ExitTimeout, NULL); + Sleep(50); + printf("XP exited\n\n"); + fExit = true; +#ifndef QT_GUI + // ensure non-UI client gets exited here, but let Bitcoin-Qt reach 'return 0;' in bitcoin.cpp + exit(0); +#endif + } + else + { + while (!fExit) + Sleep(500); + Sleep(100); + ExitThread(0); + } +} + +void HandleSIGTERM(int) +{ + fRequestShutdown = true; +} + +void HandleSIGHUP(int) +{ + fReopenDebugLog = true; +} + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// Start +// +#if !defined(QT_GUI) +bool AppInit(int argc, char* argv[]) +{ + bool fRet = false; + try + { + // + // Parameters + // + // If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main() + ParseParameters(argc, argv); + if (!boost::filesystem::is_directory(GetDataDir(false))) + { + fprintf(stderr, "Error: Specified directory does not exist\n"); + Shutdown(NULL); + } + ReadConfigFile(mapArgs, mapMultiArgs); + + if (mapArgs.count("-?") || mapArgs.count("--help")) + { + // First part of help message is specific to bitcoind / RPC client + std::string strUsage = _("XP version") + " " + FormatFullVersion() + "\n\n" + + _("Usage:") + "\n" + + " XPd [options] " + "\n" + + " XPd [options] [params] " + _("Send command to -server or XPd") + "\n" + + " XPd [options] help " + _("List commands") + "\n" + + " XPd [options] help " + _("Get help for a command") + "\n"; + + strUsage += "\n" + HelpMessage(); + + fprintf(stdout, "%s", strUsage.c_str()); + return false; + } + + // Command-line RPC + for (int i = 1; i < argc; i++) + if (!IsSwitchChar(argv[i][0]) && !boost::algorithm::istarts_with(argv[i], "XP:")) + fCommandLine = true; + + if (fCommandLine) + { + int ret = CommandLineRPC(argc, argv); + exit(ret); + } + + fRet = AppInit2(); + } + catch (std::exception& e) { + PrintException(&e, "AppInit()"); + } catch (...) { + PrintException(NULL, "AppInit()"); + } + if (!fRet) + Shutdown(NULL); + return fRet; +} + +extern void noui_connect(); +int main(int argc, char* argv[]) +{ + + // Connect bitcoind signal handlers + noui_connect(); + + bool fRet = AppInit(argc, argv); + + if (fRet && fDaemon) + return 0; + + return 1; +} +#endif + +bool static InitError(const std::string &str) +{ + uiInterface.ThreadSafeMessageBox(str, _("XP"), CClientUIInterface::OK | CClientUIInterface::MODAL); + return false; +} + +bool static InitWarning(const std::string &str) +{ + uiInterface.ThreadSafeMessageBox(str, _("XP"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION | CClientUIInterface::MODAL); + return true; +} + + +bool static Bind(const CService &addr, bool fError = true) { + if (IsLimited(addr)) + return false; + std::string strError; + if (!BindListenPort(addr, strError)) { + if (fError) + return InitError(strError); + return false; + } + return true; +} + +// Core-specific options shared between UI and daemon +std::string HelpMessage() +{ + string strUsage = _("Options:") + "\n" + + " -? " + _("This help message") + "\n" + + " -conf= " + _("Specify configuration file (default: XP.conf)") + "\n" + + " -pid= " + _("Specify pid file (default: XPd.pid)") + "\n" + + " -datadir= " + _("Specify data directory") + "\n" + + " -wallet= " + _("Specify wallet file (within data directory)") + "\n" + + " -dbcache= " + _("Set database cache size in megabytes (default: 25)") + "\n" + + " -dblogsize= " + _("Set database disk log size in megabytes (default: 100)") + "\n" + + " -timeout= " + _("Specify connection timeout in milliseconds (default: 5000)") + "\n" + + " -proxy= " + _("Connect through socks proxy") + "\n" + + " -socks= " + _("Select the version of socks proxy to use (4-5, default: 5)") + "\n" + + " -tor= " + _("Use proxy to reach tor hidden services (default: same as -proxy)") + "\n" + " -torname= " + _("Send the specified hidden service name when connecting to Tor nodes (default: none)") + "\n" + " -dns " + _("Allow DNS lookups for -addnode, -seednode and -connect") + "\n" + + " -port= " + _("Listen for connections on (default: 28192 or testnet: 17778)") + "\n" + + " -maxconnections= " + _("Maintain at most connections to peers (default: 125)") + "\n" + + " -addnode= " + _("Add a node to connect to and attempt to keep the connection open") + "\n" + + " -connect= " + _("Connect only to the specified node(s)") + "\n" + + " -seednode= " + _("Connect to a node to retrieve peer addresses, and disconnect") + "\n" + + " -externalip= " + _("Specify your own public address") + "\n" + + " -onlynet= " + _("Only connect to nodes in network (IPv4, IPv6 or Onion)") + "\n" + + " -discover " + _("Discover own IP address (default: 1 when listening and no -externalip)") + "\n" + + " -irc " + _("Find peers using internet relay chat (default: 1)") + "\n" + + " -listen " + _("Accept connections from outside (default: 1 if no -proxy or -connect)") + "\n" + + " -bind= " + _("Bind to given address. Use [host]:port notation for IPv6") + "\n" + + " -dnsseed " + _("Find peers using DNS lookup (default: 1)") + "\n" + + " -cppolicy " + _("Sync checkpoints policy (default: strict)") + "\n" + + " -banscore= " + _("Threshold for disconnecting misbehaving peers (default: 100)") + "\n" + + " -bantime= " + _("Number of seconds to keep misbehaving peers from reconnecting (default: 86400)") + "\n" + + " -maxreceivebuffer= " + _("Maximum per-connection receive buffer, *1000 bytes (default: 5000)") + "\n" + + " -maxsendbuffer= " + _("Maximum per-connection send buffer, *1000 bytes (default: 1000)") + "\n" + + " -detachdb " + _("Detach block and address databases. Increases shutdown time (default: 0)") + "\n" + + +#ifdef DB_LOG_IN_MEMORY + " -memorylog " + _("Use in-memory logging for block index database (default: 1)") + "\n" + +#endif + + " -paytxfee= " + _("Fee per KB to add to transactions you send") + "\n" + + " -mininput= " + str(boost::format(_("When creating transactions, ignore inputs with value less than this (default: %s)")) % FormatMoney(MIN_TXOUT_AMOUNT)) + "\n" + +#ifdef QT_GUI + " -server " + _("Accept command line and JSON-RPC commands") + "\n" + +#endif +#if !defined(WIN32) && !defined(QT_GUI) + " -daemon " + _("Run in the background as a daemon and accept commands") + "\n" + +#endif + " -testnet " + _("Use the test network") + "\n" + + " -debug " + _("Output extra debugging information. Implies all other -debug* options") + "\n" + + " -debugnet " + _("Output extra network debugging information") + "\n" + + " -logtimestamps " + _("Prepend debug output with timestamp") + "\n" + + " -shrinkdebugfile " + _("Shrink debug.log file on client startup (default: 1 when no -debug)") + "\n" + + " -printtoconsole " + _("Send trace/debug info to console instead of debug.log file") + "\n" + +#ifdef WIN32 + " -printtodebugger " + _("Send trace/debug info to debugger") + "\n" + +#endif + " -rpcuser= " + _("Username for JSON-RPC connections") + "\n" + + " -rpcpassword= " + _("Password for JSON-RPC connections") + "\n" + + " -rpcport= " + _("Listen for JSON-RPC connections on (default: 28191 or testnet: 18345)") + "\n" + + " -rpcallowip= " + _("Allow JSON-RPC connections from specified IP address") + "\n" + + " -rpcconnect= " + _("Send commands to node running on (default: 127.0.0.1)") + "\n" + + " -blocknotify= " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n" + + " -walletnotify= " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n" + + " -confchange " + _("Require a confirmations for change (default: 0)") + "\n" + + " -upgradewallet " + _("Upgrade wallet to latest format") + "\n" + + " -keypool= " + _("Set key pool size to (default: 100)") + "\n" + + " -rescan " + _("Rescan the block chain for missing wallet transactions") + "\n" + + " -zapwallettxes " + _("Clear list of wallet transactions (diagnostic tool; implies -rescan)") + "\n" + + " -salvagewallet " + _("Attempt to recover private keys from a corrupt wallet.dat") + "\n" + + " -checkblocks= " + _("How many blocks to check at startup (default: 2500, 0 = all)") + "\n" + + " -checklevel= " + _("How thorough the block verification is (0-6, default: 1)") + "\n" + + " -par=N " + _("Set the number of script verification threads (1-16, 0=auto, default: 0)") + "\n" + + " -loadblock= " + _("Imports blocks from external blk000?.dat file") + "\n" + + + "\n" + _("Block creation options:") + "\n" + + " -blockminsize= " + _("Set minimum block size in bytes (default: 0)") + "\n" + + " -blockmaxsize= " + _("Set maximum block size in bytes (default: 250000)") + "\n" + + " -blockprioritysize= " + _("Set maximum size of high-priority/low-fee transactions in bytes (default: 27000)") + "\n" + + + "\n" + _("SSL options: (see the Bitcoin Wiki for SSL setup instructions)") + "\n" + + " -rpcssl " + _("Use OpenSSL (https) for JSON-RPC connections") + "\n" + + " -rpcsslcertificatechainfile= " + _("Server certificate file (default: server.cert)") + "\n" + + " -rpcsslprivatekeyfile= " + _("Server private key (default: server.pem)") + "\n" + + " -rpcsslciphers= " + _("Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)") + "\n"; + + return strUsage; +} + +/** Initialize bitcoin. + * @pre Parameters should be parsed and config file should be read. + */ +bool AppInit2() +{ + // ********************************************************* Step 1: setup +#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 +#ifdef WIN32 + // Enable Data Execution Prevention (DEP) + // Minimum supported OS versions: WinXP SP3, WinVista >= SP1, Win Server 2008 + // A failure is non-critical and needs no further attention! +#ifndef PROCESS_DEP_ENABLE +// We define this here, because GCCs winbase.h limits this to _WIN32_WINNT >= 0x0601 (Windows 7), +// which is not correct. Can be removed, when GCCs winbase.h is fixed! +#define PROCESS_DEP_ENABLE 0x00000001 +#endif + typedef BOOL (WINAPI *PSETPROCDEPPOL)(DWORD); + PSETPROCDEPPOL setProcDEPPol = (PSETPROCDEPPOL)GetProcAddress(GetModuleHandleA("Kernel32.dll"), "SetProcessDEPPolicy"); + if (setProcDEPPol != NULL) setProcDEPPol(PROCESS_DEP_ENABLE); + + // Initialize Windows Sockets + WSADATA wsadata; + int ret = WSAStartup(MAKEWORD(2,2), &wsadata); + if (ret != NO_ERROR) + { + return InitError(strprintf("Error: TCP/IP socket library failed to start (WSAStartup returned error %d)", ret)); + } +#endif +#ifndef WIN32 + umask(077); + + // 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); + + // Reopen debug.log on SIGHUP + struct sigaction sa_hup; + sa_hup.sa_handler = HandleSIGHUP; + sigemptyset(&sa_hup.sa_mask); + sa_hup.sa_flags = 0; + sigaction(SIGHUP, &sa_hup, NULL); +#endif + + // ********************************************************* Step 2: parameter interactions + + nNodeLifespan = (unsigned int)(GetArg("-addrlifespan", 7)); + fUseFastIndex = GetBoolArg("-fastindex", true); + fUseMemoryLog = GetBoolArg("-memorylog", true); + + // Ping and address broadcast intervals + nPingInterval = max(10 * 60, GetArg("-keepalive", 30 * 60)); + nBroadcastInterval = max(6 * nOneHour, GetArg("-addrsetlifetime", nOneDay)); + + CheckpointsMode = Checkpoints::STRICT; + std::string strCpMode = GetArg("-cppolicy", "strict"); + + if(strCpMode == "strict") { + CheckpointsMode = Checkpoints::STRICT; + } + + if(strCpMode == "advisory") { + CheckpointsMode = Checkpoints::ADVISORY; + } + + if(strCpMode == "permissive") { + CheckpointsMode = Checkpoints::PERMISSIVE; + } + + fTestNet = GetBoolArg("-testnet"); + //if (fTestNet) { + SoftSetBoolArg("-irc", true); // We want IRC on by default + //} + + if (mapArgs.count("-bind")) { + // when specifying an explicit binding address, you want to listen on it + // even when -connect or -proxy is specified + SoftSetBoolArg("-listen", true); + } + + if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) { + // when only connecting to trusted nodes, do not seed via DNS, or listen by default + SoftSetBoolArg("-dnsseed", false); + SoftSetBoolArg("-listen", false); + } + + if (mapArgs.count("-proxy")) { + // to protect privacy, do not listen by default if a proxy server is specified + SoftSetBoolArg("-listen", false); + } + + if (!GetBoolArg("-listen", true)) { + // do not map ports or try to retrieve public IP when not listening (pointless) + SoftSetBoolArg("-discover", false); + } + + if (mapArgs.count("-externalip")) { + // if an explicit public IP is specified, do not try to find others + SoftSetBoolArg("-discover", false); + } + + if (GetBoolArg("-salvagewallet")) { + // Rewrite just private keys: rescan to find transactions + SoftSetBoolArg("-rescan", true); + } + + if (GetBoolArg("-zapwallettxes", false)) { + // -zapwallettx implies a rescan + if (SoftSetBoolArg("-rescan", true)) + printf("AppInit2 : parameter interaction: -zapwallettxes=1 -> setting -rescan=1\n"); + } + + // ********************************************************* Step 3: parameter-to-internal-flags + + // -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency + nScriptCheckThreads = GetArgInt("-par", 0); + if (nScriptCheckThreads == 0) + nScriptCheckThreads = boost::thread::hardware_concurrency(); + if (nScriptCheckThreads <= 1) + nScriptCheckThreads = 0; + else if (nScriptCheckThreads > MAX_SCRIPTCHECK_THREADS) + nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS; + + fDebug = GetBoolArg("-debug"); + + // -debug implies fDebug* + if (fDebug) + fDebugNet = true; + else + fDebugNet = GetBoolArg("-debugnet"); + + bitdb.SetDetach(GetBoolArg("-detachdb", false)); + +#if !defined(WIN32) && !defined(QT_GUI) + fDaemon = GetBoolArg("-daemon"); +#else + fDaemon = false; +#endif + + if (fDaemon) + fServer = true; + else + fServer = GetBoolArg("-server"); + + /* force fServer when running without GUI */ +#if !defined(QT_GUI) + fServer = true; +#endif + fPrintToConsole = GetBoolArg("-printtoconsole"); + fPrintToDebugger = GetBoolArg("-printtodebugger"); + fLogTimestamps = GetBoolArg("-logtimestamps"); + + if (mapArgs.count("-timeout")) + { + int nNewTimeout = GetArgInt("-timeout", 5000); + if (nNewTimeout > 0 && nNewTimeout < 600000) + nConnectTimeout = nNewTimeout; + } + + // Put client version data into coinbase flags. + COINBASE_FLAGS << PROTOCOL_VERSION << DISPLAY_VERSION_MAJOR << DISPLAY_VERSION_MINOR << DISPLAY_VERSION_REVISION; + + if (mapArgs.count("-paytxfee")) + { + if (!ParseMoney(mapArgs["-paytxfee"], nTransactionFee)) + return InitError(strprintf(_("Invalid amount for -paytxfee=: '%s'"), mapArgs["-paytxfee"].c_str())); + if (nTransactionFee > 0.25 * COIN) + InitWarning(_("Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction.")); + } + + fConfChange = GetBoolArg("-confchange", false); + + if (mapArgs.count("-mininput")) + { + if (!ParseMoney(mapArgs["-mininput"], nMinimumInputValue)) + return InitError(strprintf(_("Invalid amount for -mininput=: '%s'"), mapArgs["-mininput"].c_str())); + } + + // ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log + + std::string strDataDir = GetDataDir().string(); + strWalletFileName = GetArg("-wallet", "wallet.dat"); + + // strWalletFileName must be a plain filename without a directory + if (strWalletFileName != boost::filesystem::basename(strWalletFileName) + boost::filesystem::extension(strWalletFileName)) + return InitError(strprintf(_("Wallet %s resides outside data directory %s."), strWalletFileName.c_str(), strDataDir.c_str())); + + // Make sure only a single Bitcoin process is using the data directory. + boost::filesystem::path pathLockFile = GetDataDir() / ".lock"; + FILE* file = fopen(pathLockFile.string().c_str(), "a"); // empty lock file; created if it doesn't exist. + if (file) fclose(file); + static boost::interprocess::file_lock lock(pathLockFile.string().c_str()); + if (!lock.try_lock()) + return InitError(strprintf(_("Cannot obtain a lock on data directory %s. XP is probably already running."), strDataDir.c_str())); + +#if !defined(WIN32) && !defined(QT_GUI) + 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 (GetBoolArg("-shrinkdebugfile", !fDebug)) + ShrinkDebugFile(); + printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); + printf("XP version %s (%s)\n", FormatFullVersion().c_str(), CLIENT_DATE.c_str()); + printf("Using OpenSSL version %s\n", SSLeay_version(SSLEAY_VERSION)); + if (!fLogTimestamps) + printf("Startup time: %s\n", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); + printf("Default data directory %s\n", GetDefaultDataDir().string().c_str()); + printf("Used data directory %s\n", strDataDir.c_str()); + std::ostringstream strErrors; + + if (fDaemon) + fprintf(stdout, "XP server starting\n"); + + if (nScriptCheckThreads) { + printf("Using %u threads for script verification\n", nScriptCheckThreads); + for (int i=0; i nets; + BOOST_FOREACH(std::string snet, mapMultiArgs["-onlynet"]) { + enum Network net = ParseNetwork(snet); + if (net == NET_UNROUTABLE) + return InitError(strprintf(_("Unknown network specified in -onlynet: '%s'"), snet.c_str())); + nets.insert(net); + } + for (int n = 0; n < NET_MAX; n++) { + enum Network net = (enum Network)n; + if (!nets.count(net)) + SetLimited(net); + } + } +#if defined(USE_IPV6) +#if ! USE_IPV6 + else + SetLimited(NET_IPV6); +#endif +#endif + + CService addrProxy; + bool fProxy = false; + if (mapArgs.count("-proxy")) { + addrProxy = CService(mapArgs["-proxy"], nSocksDefault); + if (!addrProxy.IsValid()) + return InitError(strprintf(_("Invalid -proxy address: '%s'"), mapArgs["-proxy"].c_str())); + + if (!IsLimited(NET_IPV4)) + SetProxy(NET_IPV4, addrProxy, nSocksVersion); + if (nSocksVersion > 4) { +#ifdef USE_IPV6 + if (!IsLimited(NET_IPV6)) + SetProxy(NET_IPV6, addrProxy, nSocksVersion); +#endif + SetNameProxy(addrProxy, nSocksVersion); + } + fProxy = true; + } + + // -tor can override normal proxy, -notor disables tor entirely + if (!(mapArgs.count("-tor") && mapArgs["-tor"] == "0") && (fProxy || mapArgs.count("-tor"))) { + CService addrOnion; + if (!mapArgs.count("-tor")) + addrOnion = addrProxy; + else + addrOnion = CService(mapArgs["-tor"], nSocksDefault); + if (!addrOnion.IsValid()) + return InitError(strprintf(_("Invalid -tor address: '%s'"), mapArgs["-tor"].c_str())); + SetProxy(NET_TOR, addrOnion, 5); + SetReachable(NET_TOR); + } + + // see Step 2: parameter interactions for more information about these + if (!IsLimited(NET_IPV4) || !IsLimited(NET_IPV6)) + { + fNoListen = !GetBoolArg("-listen", true); + fDiscover = GetBoolArg("-discover", true); + fNameLookup = GetBoolArg("-dns", true); + } else { + // Don't listen, discover addresses or search for nodes if IPv4 and IPv6 networking is disabled. + fNoListen = true; + fDiscover = fNameLookup = false; + SoftSetBoolArg("-irc", false); + SoftSetBoolArg("-dnsseed", false); + } + + bool fBound = false; + if (!fNoListen) + { + std::string strError; + if (mapArgs.count("-bind")) { + BOOST_FOREACH(std::string strBind, mapMultiArgs["-bind"]) { + CService addrBind; + if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false)) + return InitError(strprintf(_("Cannot resolve -bind address: '%s'"), strBind.c_str())); + fBound |= Bind(addrBind); + } + } else { + struct in_addr inaddr_any; + inaddr_any.s_addr = INADDR_ANY; +#ifdef USE_IPV6 + if (!IsLimited(NET_IPV6)) + fBound |= Bind(CService(in6addr_any, GetListenPort()), false); +#endif + if (!IsLimited(NET_IPV4)) + fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound); + + } + if (!fBound) + return InitError(_("Failed to listen on any port. Use -listen=0 if you want this.")); + } + + // If Tor is reachable then listen on loopback interface, + // to allow allow other users reach you through the hidden service + if (!IsLimited(NET_TOR) && mapArgs.count("-torname")) { + std::string strError; + struct in_addr inaddr_loopback; + inaddr_loopback.s_addr = htonl(INADDR_LOOPBACK); + +#ifdef USE_IPV6 + if (!BindListenPort(CService(in6addr_loopback, GetListenPort()), strError)) + return InitError(strError); +#endif + if (!BindListenPort(CService(inaddr_loopback, GetListenPort()), strError)) + return InitError(strError); + } + + if (mapArgs.count("-externalip")) + { + BOOST_FOREACH(string strAddr, mapMultiArgs["-externalip"]) { + CService addrLocal(strAddr, GetListenPort(), fNameLookup); + if (!addrLocal.IsValid()) + return InitError(strprintf(_("Cannot resolve -externalip address: '%s'"), strAddr.c_str())); + AddLocal(CService(strAddr, GetListenPort(), fNameLookup), LOCAL_MANUAL); + } + } + + if (mapArgs.count("-reservebalance")) // ppcoin: reserve balance amount + { + if (!ParseMoney(mapArgs["-reservebalance"], nReserveBalance)) + { + InitError(_("Invalid amount for -reservebalance=")); + return false; + } + } + + if (mapArgs.count("-checkpointkey")) // ppcoin: checkpoint master priv key + { + if (!Checkpoints::SetCheckpointPrivKey(GetArg("-checkpointkey", ""))) + InitError(_("Unable to sign checkpoint, wrong checkpointkey?\n")); + } + + BOOST_FOREACH(string strDest, mapMultiArgs["-seednode"]) + AddOneShot(strDest); + + // ********************************************************* Step 7: load blockchain + + if (!bitdb.Open(GetDataDir())) + { + string msg = strprintf(_("Error initializing database environment %s!" + " To recover, BACKUP THAT DIRECTORY, then remove" + " everything from it except for wallet.dat."), strDataDir.c_str()); + return InitError(msg); + } + + if (GetBoolArg("-loadblockindextest")) + { + CTxDB txdb("r"); + txdb.LoadBlockIndex(); + PrintBlockTree(); + return false; + } + + + printf("Loading block index...\n"); + bool fLoaded = false; + while (!fLoaded) { + std::string strLoadError; + uiInterface.InitMessage(_("Loading block index...")); + + nStart = GetTimeMillis(); + do { + try { + UnloadBlockIndex(); + + if (!LoadBlockIndex()) { + strLoadError = _("Error loading block database"); + break; + } + } catch(std::exception &e) { + (void)e; + strLoadError = _("Error opening block database"); + break; + } + + fLoaded = true; + } while(false); + + if (!fLoaded) { + // TODO: suggest reindex here + return InitError(strLoadError); + } + } + + // as LoadBlockIndex can take several minutes, it's possible the user + // requested to kill bitcoin-qt during the last operation. If so, exit. + // As the program has not fully started yet, Shutdown() is possibly overkill. + if (fRequestShutdown) + { + printf("Shutdown requested. Exiting.\n"); + return false; + } + printf(" block index %15" PRId64 "ms\n", GetTimeMillis() - nStart); + + 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; + } + + // ********************************************************* Step 8: load wallet + + if (GetBoolArg("-zapwallettxes", false)) { + uiInterface.InitMessage(_("Zapping all transactions from wallet...")); + + pwalletMain = new CWallet(strWalletFileName); + DBErrors nZapWalletRet = pwalletMain->ZapWalletTx(); + if (nZapWalletRet != DB_LOAD_OK) { + uiInterface.InitMessage(_("Error loading wallet.dat: Wallet corrupted")); + return false; + } + delete pwalletMain; + pwalletMain = NULL; + } + + uiInterface.InitMessage(_("Loading wallet...")); + printf("Loading wallet...\n"); + nStart = GetTimeMillis(); + bool fFirstRun = true; + pwalletMain = new CWallet(strWalletFileName); + DBErrors nLoadWalletRet = pwalletMain->LoadWallet(fFirstRun); + if (nLoadWalletRet != DB_LOAD_OK) + { + if (nLoadWalletRet == DB_CORRUPT) + strErrors << _("Error loading wallet.dat: Wallet corrupted") << "\n"; + else if (nLoadWalletRet == DB_NONCRITICAL_ERROR) + { + string msg(_("Warning: error reading wallet.dat! All keys read correctly, but transaction data" + " or address book entries might be missing or incorrect.")); + uiInterface.ThreadSafeMessageBox(msg, _("XP"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION | CClientUIInterface::MODAL); + } + else if (nLoadWalletRet == DB_TOO_NEW) + strErrors << _("Error loading wallet.dat: Wallet requires newer version of XP") << "\n"; + else if (nLoadWalletRet == DB_NEED_REWRITE) + { + strErrors << _("Wallet needed to be rewritten: restart XP to complete") << "\n"; + printf("%s", strErrors.str().c_str()); + return InitError(strErrors.str()); + } + else + strErrors << _("Error loading wallet.dat") << "\n"; + } + + if (GetBoolArg("-upgradewallet", fFirstRun)) + { + int nMaxVersion = GetArgInt("-upgradewallet", 0); + if (nMaxVersion == 0) // the -upgradewallet without argument case + { + printf("Performing wallet upgrade to %i\n", FEATURE_LATEST); + nMaxVersion = CLIENT_VERSION; + pwalletMain->SetMinVersion(FEATURE_LATEST); // permanently upgrade the wallet immediately + } + else + printf("Allowing wallet upgrade up to %i\n", nMaxVersion); + if (nMaxVersion < pwalletMain->GetVersion()) + strErrors << _("Cannot downgrade wallet") << "\n"; + pwalletMain->SetMaxVersion(nMaxVersion); + } + + if (fFirstRun) + { + // Create new keyUser and set as default key + RandAddSeedPerfmon(); + + CPubKey newDefaultKey; + if (!pwalletMain->GetKeyFromPool(newDefaultKey, false)) + strErrors << _("Cannot initialize keypool") << "\n"; + pwalletMain->SetDefaultKey(newDefaultKey); + if (!pwalletMain->SetAddressBookName(pwalletMain->vchDefaultKey.GetID(), "")) + strErrors << _("Cannot write default address") << "\n"; + } + + printf("%s", strErrors.str().c_str()); + printf(" wallet %15" PRId64 "ms\n", GetTimeMillis() - nStart); + + RegisterWallet(pwalletMain); + + CBlockIndex *pindexRescan = pindexBest; + if (GetBoolArg("-rescan")) + pindexRescan = pindexGenesisBlock; + else + { + CWalletDB walletdb(strWalletFileName); + CBlockLocator locator; + if (walletdb.ReadBestBlock(locator)) + pindexRescan = locator.GetBlockIndex(); + } + if (pindexBest != pindexRescan && pindexBest && pindexRescan && pindexBest->nHeight > pindexRescan->nHeight) + { + uiInterface.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" PRId64 "ms\n", GetTimeMillis() - nStart); + } + + // ********************************************************* Step 9: import blocks + + if (mapArgs.count("-loadblock")) + { + uiInterface.InitMessage(_("Importing blockchain data file.")); + + BOOST_FOREACH(string strFile, mapMultiArgs["-loadblock"]) + { + FILE *file = fopen(strFile.c_str(), "rb"); + if (file) + LoadExternalBlockFile(file); + } + StartShutdown(); + } + + filesystem::path pathBootstrap = GetDataDir() / "bootstrap.dat"; + if (filesystem::exists(pathBootstrap)) { + uiInterface.InitMessage(_("Importing bootstrap blockchain data file.")); + + FILE *file = fopen(pathBootstrap.string().c_str(), "rb"); + if (file) { + filesystem::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old"; + LoadExternalBlockFile(file); + RenameOver(pathBootstrap, pathBootstrapOld); + } + } + + // ********************************************************* Step 10: load peers + + uiInterface.InitMessage(_("Loading addresses...")); + printf("Loading addresses...\n"); + nStart = GetTimeMillis(); + + { + CAddrDB adb; + if (!adb.Read(addrman)) + printf("Invalid or missing peers.dat; recreating\n"); + } + + printf("Loaded %i addresses from peers.dat %" PRId64 "ms\n", + addrman.size(), GetTimeMillis() - nStart); + + // ********************************************************* Step 11: start node + + if (!CheckDiskSpace()) + return false; + + RandAddSeedPerfmon(); + + //// debug print + printf("mapBlockIndex.size() = %" PRIszu "\n", mapBlockIndex.size()); + printf("nBestHeight = %d\n", nBestHeight); + printf("setKeyPool.size() = %" PRIszu "\n", pwalletMain->setKeyPool.size()); + printf("mapWallet.size() = %" PRIszu "\n", pwalletMain->mapWallet.size()); + printf("mapAddressBook.size() = %" PRIszu "\n", pwalletMain->mapAddressBook.size()); + + if (!NewThread(StartNode, NULL)) + InitError(_("Error: could not start node")); + + if (fServer) + NewThread(ThreadRPCServer, NULL); + + // ********************************************************* Step 12: finished + + uiInterface.InitMessage(_("Done loading")); + printf("Done loading\n"); + + if (!strErrors.str().empty()) + return InitError(strErrors.str()); + + // Add wallet transactions that aren't already in a block to mapTransactions + pwalletMain->ReacceptWalletTransactions(); + +#if !defined(QT_GUI) + // Loop until process is exit()ed from shutdown() function, + // called from ThreadRPCServer thread when a "stop" command is received. + while (1) + Sleep(5000); +#endif + + return true; +} diff --git a/src/init.h b/src/init.h new file mode 100644 index 00000000..84f2712e --- /dev/null +++ b/src/init.h @@ -0,0 +1,17 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_INIT_H +#define BITCOIN_INIT_H + +#include "wallet.h" + +extern CWallet* pwalletMain; +extern std::string strWalletFileName; +void StartShutdown(); +void Shutdown(void* parg); +bool AppInit2(); +std::string HelpMessage(); + +#endif diff --git a/src/irc.cpp b/src/irc.cpp new file mode 100644 index 00000000..b22b164d --- /dev/null +++ b/src/irc.cpp @@ -0,0 +1,407 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "irc.h" +#include "base58.h" +#include "net.h" + +#include // for startswith() and endswith() + +using namespace std; +using namespace boost; + +int nGotIRCAddresses = 0; + +void ThreadIRCSeed2(void* parg); + + + + +#pragma pack(push, 1) +struct ircaddr +{ + struct in_addr ip; + unsigned short port; +}; +#pragma pack(pop) + +string EncodeAddress(const CService& addr) +{ + struct ircaddr tmp; + if (addr.GetInAddr(&tmp.ip)) + { + tmp.port = htons(addr.GetPort()); + + vector vch(UBEGIN(tmp), UEND(tmp)); + return string("u") + EncodeBase58Check(vch); + } + return ""; +} + +bool DecodeAddress(string str, CService& 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 = CService(tmp.ip, ntohs(tmp.port)); + 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 RecvLineIRC(SOCKET hSocket, string& strLine) +{ + while (true) + { + 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) +{ + while (true) + { + string strLine; + strLine.reserve(10000); + if (!RecvLineIRC(hSocket, strLine)) + return 0; + printf("IRC %s\n", strLine.c_str()); + if (psz1 && strLine.find(psz1) != string::npos) + return 1; + if (psz2 && strLine.find(psz2) != string::npos) + return 2; + if (psz3 && strLine.find(psz3) != string::npos) + return 3; + if (psz4 && strLine.find(psz4) != string::npos) + 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(); + while (true) + { + 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, CNetAddr& 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()); + CNetAddr addr(strHost, true); + if (!addr.IsValid()) + return false; + ipRet = addr; + + return true; +} + + + +void ThreadIRCSeed(void* parg) +{ + // Make this thread recognisable as the IRC seeding thread + RenameThread("XP-ircseed"); + + printf("ThreadIRCSeed started\n"); + + try + { + ThreadIRCSeed2(parg); + } + catch (std::exception& e) { + PrintExceptionContinue(&e, "ThreadIRCSeed()"); + } catch (...) { + PrintExceptionContinue(NULL, "ThreadIRCSeed()"); + } + printf("ThreadIRCSeed exited\n"); +} + +void ThreadIRCSeed2(void* parg) +{ + // Don't connect to IRC if we won't use IPv4 connections. + if (IsLimited(NET_IPV4)) + return; + + // ... or if we won't make outbound connections and won't accept inbound ones. + if (mapArgs.count("-connect") && fNoListen) + return; + + // ... or if IRC is not enabled. + if (!GetBoolArg("-irc", true)) + return; + + printf("ThreadIRCSeed trying to connect...\n"); + + int nErrorWait = 10; + int nRetryWait = 10; + int nNameRetry = 0; + + while (!fShutdown) + { + const uint16_t nIrcPort = 6667; + CService addrConnect("92.243.23.21", nIrcPort); // irc.lfnet.org + + CService addrIRC("irc.lfnet.org", nIrcPort, 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; + } + + CNetAddr addrIPv4("1.2.3.4"); // arbitrary IPv4 address to make GetLocal prefer IPv4 addresses + CService addrLocal; + string strMyName; + // Don't use our IP as our nick if we're not listening + // or if it keeps failing because the nick is already in use. + if (!fNoListen && GetLocal(addrLocal, &addrIPv4) && nNameRetry<3) + strMyName = EncodeAddress(GetLocalAddress(&addrConnect)); + if (strMyName == "") + strMyName = strprintf("x%" PRIu64 "", 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"); + nNameRetry++; + Wait(10); + continue; + } + nErrorWait = nErrorWait * 11 / 10; + if (Wait(nErrorWait += 60)) + continue; + else + return; + } + nNameRetry = 0; + Sleep(500); + + // Get our external IP from the IRC server and re-nick before joining the channel + CNetAddr addrFromIRC; + if (GetIPFromIRC(hSocket, strMyName, addrFromIRC)) + { + printf("GetIPFromIRC() returned %s\n", addrFromIRC.ToString().c_str()); + // Don't use our IP as our nick if we're not listening + if (!fNoListen && addrFromIRC.IsRoutable()) + { + // IRC lets you to re-nick + AddLocal(addrFromIRC, LOCAL_IRC); + strMyName = EncodeAddress(GetLocalAddress(&addrConnect)); + Send(hSocket, strprintf("NICK %s\r", strMyName.c_str()).c_str()); + } + } + + if (fTestNet) { + Send(hSocket, "JOIN #XPTEST2\r"); + Send(hSocket, "WHO #XPTEST2\r"); + } else { + // randomly join #XP00-#XP05 + // int channel_number = GetRandInt(5); + + // Channel number is always 0 for initial release + int channel_number = 0; + Send(hSocket, strprintf("JOIN #XP%02d\r", channel_number).c_str()); + Send(hSocket, strprintf("WHO #XP%02d\r", channel_number).c_str()); + } + + int64_t 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; + + std::string strName; + + 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 + strName = vWords[7].c_str(); + printf("IRC got who\n"); + } + + if (vWords[1] == "JOIN" && vWords[0].size() > 1) + { + // :username!username@50000007.F000000B.90000002.IP JOIN :#channelname + strName = vWords[0].substr(1, vWords[0].find('!', 1) - 1); + printf("IRC got join\n"); + } + + if (boost::algorithm::starts_with(strName, "u")) + { + CAddress addr; + if (DecodeAddress(strName, addr)) + { + addr.nTime = GetAdjustedTime(); + if (addrman.Add(addr, addrConnect, 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; + + if (GetTime() - nStart > 20 * 60) + { + nErrorWait /= 3; + nRetryWait /= 3; + } + + nRetryWait = nRetryWait * 11 / 10; + if (!Wait(nRetryWait += 60)) + return; + } +} + + + + + + + + + + +#ifdef TEST +int main(int argc, char *argv[]) +{ + WSADATA wsadata; + if (WSAStartup(MAKEWORD(2,2), &wsadata) != NO_ERROR) + { + printf("Error at WSAStartup()\n"); + return false; + } + + ThreadIRCSeed(NULL); + + WSACleanup(); + return 0; +} +#endif diff --git a/src/irc.h b/src/irc.h new file mode 100644 index 00000000..119aeb3f --- /dev/null +++ b/src/irc.h @@ -0,0 +1,12 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_IRC_H +#define BITCOIN_IRC_H + +void ThreadIRCSeed(void* parg); + +extern int nGotIRCAddresses; + +#endif diff --git a/src/json/LICENSE.txt b/src/json/LICENSE.txt new file mode 100644 index 00000000..797d5363 --- /dev/null +++ b/src/json/LICENSE.txt @@ -0,0 +1,24 @@ +The MIT License + +Copyright (c) 2007 - 2009 John W. Wilkinson + +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. diff --git a/src/json/json_spirit.h b/src/json/json_spirit.h new file mode 100644 index 00000000..ac1879d5 --- /dev/null +++ b/src/json/json_spirit.h @@ -0,0 +1,18 @@ +#ifndef JSON_SPIRIT +#define JSON_SPIRIT + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include "json_spirit_value.h" +#include "json_spirit_reader.h" +#include "json_spirit_writer.h" +#include "json_spirit_utils.h" + +#endif diff --git a/src/json/json_spirit_error_position.h b/src/json/json_spirit_error_position.h new file mode 100644 index 00000000..17208507 --- /dev/null +++ b/src/json/json_spirit_error_position.h @@ -0,0 +1,54 @@ +#ifndef JSON_SPIRIT_ERROR_POSITION +#define JSON_SPIRIT_ERROR_POSITION + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include + +namespace json_spirit +{ + // An Error_position exception is thrown by the "read_or_throw" functions below on finding an error. + // Note the "read_or_throw" functions are around 3 times slower than the standard functions "read" + // functions that return a bool. + // + struct Error_position + { + Error_position(); + Error_position( unsigned int line, unsigned int column, const std::string& reason ); + bool operator==( const Error_position& lhs ) const; + unsigned int line_; + unsigned int column_; + std::string reason_; + }; + + inline Error_position::Error_position() + : line_( 0 ) + , column_( 0 ) + { + } + + inline Error_position::Error_position( unsigned int line, unsigned int column, const std::string& reason ) + : line_( line ) + , column_( column ) + , reason_( reason ) + { + } + + inline bool Error_position::operator==( const Error_position& lhs ) const + { + if( this == &lhs ) return true; + + return ( reason_ == lhs.reason_ ) && + ( line_ == lhs.line_ ) && + ( column_ == lhs.column_ ); +} +} + +#endif diff --git a/src/json/json_spirit_reader.cpp b/src/json/json_spirit_reader.cpp new file mode 100644 index 00000000..aa4f6372 --- /dev/null +++ b/src/json/json_spirit_reader.cpp @@ -0,0 +1,137 @@ +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#include "json_spirit_reader.h" +#include "json_spirit_reader_template.h" + +using namespace json_spirit; + +bool json_spirit::read( const std::string& s, Value& value ) +{ + return read_string( s, value ); +} + +void json_spirit::read_or_throw( const std::string& s, Value& value ) +{ + read_string_or_throw( s, value ); +} + +bool json_spirit::read( std::istream& is, Value& value ) +{ + return read_stream( is, value ); +} + +void json_spirit::read_or_throw( std::istream& is, Value& value ) +{ + read_stream_or_throw( is, value ); +} + +bool json_spirit::read( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ) +{ + return read_range( begin, end, value ); +} + +void json_spirit::read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ) +{ + begin = read_range_or_throw( begin, end, value ); +} + +#ifndef BOOST_NO_STD_WSTRING + +bool json_spirit::read( const std::wstring& s, wValue& value ) +{ + return read_string( s, value ); +} + +void json_spirit::read_or_throw( const std::wstring& s, wValue& value ) +{ + read_string_or_throw( s, value ); +} + +bool json_spirit::read( std::wistream& is, wValue& value ) +{ + return read_stream( is, value ); +} + +void json_spirit::read_or_throw( std::wistream& is, wValue& value ) +{ + read_stream_or_throw( is, value ); +} + +bool json_spirit::read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ) +{ + return read_range( begin, end, value ); +} + +void json_spirit::read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ) +{ + begin = read_range_or_throw( begin, end, value ); +} + +#endif + +bool json_spirit::read( const std::string& s, mValue& value ) +{ + return read_string( s, value ); +} + +void json_spirit::read_or_throw( const std::string& s, mValue& value ) +{ + read_string_or_throw( s, value ); +} + +bool json_spirit::read( std::istream& is, mValue& value ) +{ + return read_stream( is, value ); +} + +void json_spirit::read_or_throw( std::istream& is, mValue& value ) +{ + read_stream_or_throw( is, value ); +} + +bool json_spirit::read( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ) +{ + return read_range( begin, end, value ); +} + +void json_spirit::read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ) +{ + begin = read_range_or_throw( begin, end, value ); +} + +#ifndef BOOST_NO_STD_WSTRING + +bool json_spirit::read( const std::wstring& s, wmValue& value ) +{ + return read_string( s, value ); +} + +void json_spirit::read_or_throw( const std::wstring& s, wmValue& value ) +{ + read_string_or_throw( s, value ); +} + +bool json_spirit::read( std::wistream& is, wmValue& value ) +{ + return read_stream( is, value ); +} + +void json_spirit::read_or_throw( std::wistream& is, wmValue& value ) +{ + read_stream_or_throw( is, value ); +} + +bool json_spirit::read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ) +{ + return read_range( begin, end, value ); +} + +void json_spirit::read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ) +{ + begin = read_range_or_throw( begin, end, value ); +} + +#endif diff --git a/src/json/json_spirit_reader.h b/src/json/json_spirit_reader.h new file mode 100644 index 00000000..96494a97 --- /dev/null +++ b/src/json/json_spirit_reader.h @@ -0,0 +1,62 @@ +#ifndef JSON_SPIRIT_READER +#define JSON_SPIRIT_READER + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include "json_spirit_value.h" +#include "json_spirit_error_position.h" +#include + +namespace json_spirit +{ + // functions to reads a JSON values + + bool read( const std::string& s, Value& value ); + bool read( std::istream& is, Value& value ); + bool read( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ); + + void read_or_throw( const std::string& s, Value& value ); + void read_or_throw( std::istream& is, Value& value ); + void read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ); + +#ifndef BOOST_NO_STD_WSTRING + + bool read( const std::wstring& s, wValue& value ); + bool read( std::wistream& is, wValue& value ); + bool read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ); + + void read_or_throw( const std::wstring& s, wValue& value ); + void read_or_throw( std::wistream& is, wValue& value ); + void read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ); + +#endif + + bool read( const std::string& s, mValue& value ); + bool read( std::istream& is, mValue& value ); + bool read( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ); + + void read_or_throw( const std::string& s, mValue& value ); + void read_or_throw( std::istream& is, mValue& value ); + void read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ); + +#ifndef BOOST_NO_STD_WSTRING + + bool read( const std::wstring& s, wmValue& value ); + bool read( std::wistream& is, wmValue& value ); + bool read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ); + + void read_or_throw( const std::wstring& s, wmValue& value ); + void read_or_throw( std::wistream& is, wmValue& value ); + void read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ); + +#endif +} + +#endif diff --git a/src/json/json_spirit_reader_template.h b/src/json/json_spirit_reader_template.h new file mode 100644 index 00000000..46f5892f --- /dev/null +++ b/src/json/json_spirit_reader_template.h @@ -0,0 +1,612 @@ +#ifndef JSON_SPIRIT_READER_TEMPLATE +#define JSON_SPIRIT_READER_TEMPLATE + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#include "json_spirit_value.h" +#include "json_spirit_error_position.h" + +//#define BOOST_SPIRIT_THREADSAFE // uncomment for multithreaded use, requires linking to boost.thread + +#include +#include +#include + +#if BOOST_VERSION >= 103800 + #include + #include + #include + #include + #include + #define spirit_namespace boost::spirit::classic +#else + #include + #include + #include + #include + #include + #define spirit_namespace boost::spirit +#endif + +namespace json_spirit +{ + const spirit_namespace::int_parser < int64_t > int64_p = spirit_namespace::int_parser < int64_t >(); + const spirit_namespace::uint_parser< uint64_t > uint64_p = spirit_namespace::uint_parser< 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( int64_t i ) + { + add_to_current( i ); + } + + void new_uint64( 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( int64_t ) > Int_action; + typedef boost::function< void( uint64_t ) > Uint64_action; + + Char_action begin_obj ( boost::bind( &Semantic_actions_t::begin_obj, &self.actions_, _1 ) ); + Char_action end_obj ( boost::bind( &Semantic_actions_t::end_obj, &self.actions_, _1 ) ); + Char_action begin_array( boost::bind( &Semantic_actions_t::begin_array, &self.actions_, _1 ) ); + Char_action end_array ( boost::bind( &Semantic_actions_t::end_array, &self.actions_, _1 ) ); + Str_action new_name ( boost::bind( &Semantic_actions_t::new_name, &self.actions_, _1, _2 ) ); + Str_action new_str ( boost::bind( &Semantic_actions_t::new_str, &self.actions_, _1, _2 ) ); + Str_action new_true ( boost::bind( &Semantic_actions_t::new_true, &self.actions_, _1, _2 ) ); + Str_action new_false ( boost::bind( &Semantic_actions_t::new_false, &self.actions_, _1, _2 ) ); + Str_action new_null ( boost::bind( &Semantic_actions_t::new_null, &self.actions_, _1, _2 ) ); + Real_action new_real ( boost::bind( &Semantic_actions_t::new_real, &self.actions_, _1 ) ); + Int_action new_int ( boost::bind( &Semantic_actions_t::new_int, &self.actions_, _1 ) ); + Uint64_action new_uint64 ( boost::bind( &Semantic_actions_t::new_uint64, &self.actions_, _1 ) ); + + // actual grammer + + json_ + = value_ | eps_p[ &throw_not_value ] + ; + + value_ + = string_[ new_str ] + | number_ + | object_ + | array_ + | str_p( "true" ) [ new_true ] + | str_p( "false" )[ new_false ] + | str_p( "null" ) [ new_null ] + ; + + object_ + = ch_p('{')[ begin_obj ] + >> !members_ + >> ( ch_p('}')[ end_obj ] | eps_p[ &throw_not_object ] ) + ; + + members_ + = pair_ >> *( ',' >> pair_ ) + ; + + pair_ + = string_[ new_name ] + >> ( ':' | eps_p[ &throw_not_colon ] ) + >> ( value_ | eps_p[ &throw_not_value ] ) + ; + + array_ + = ch_p('[')[ begin_array ] + >> !elements_ + >> ( ch_p(']')[ end_array ] | eps_p[ &throw_not_array ] ) + ; + + elements_ + = value_ >> *( ',' >> value_ ) + ; + + string_ + = lexeme_d // this causes white space inside a string to be retained + [ + confix_p + ( + '"', + *lex_escape_ch_p, + '"' + ) + ] + ; + + number_ + = strict_real_p[ new_real ] + | int64_p [ new_int ] + | uint64_p [ new_uint64 ] + ; + } + + spirit_namespace::rule< ScannerT > json_, object_, members_, pair_, array_, elements_, value_, string_, number_; + + const spirit_namespace::rule< ScannerT >& start() const { return json_; } + }; + + private: + + Json_grammer& operator=( const Json_grammer& ); // to prevent "assignment operator could not be generated" warning + + Semantic_actions_t& actions_; + }; + + template< class Iter_type, class Value_type > + Iter_type read_range_or_throw( Iter_type begin, Iter_type end, Value_type& value ) + { + Semantic_actions< Value_type, Iter_type > semantic_actions( value ); + + const spirit_namespace::parse_info< Iter_type > info = + spirit_namespace::parse( begin, end, + Json_grammer< Value_type, Iter_type >( semantic_actions ), + spirit_namespace::space_p ); + + if( !info.hit ) + { + assert( false ); // in theory exception should already have been thrown + throw_error( info.stop, "error" ); + } + + return info.stop; + } + + template< class Iter_type, class Value_type > + void add_posn_iter_and_read_range_or_throw( Iter_type begin, Iter_type end, Value_type& value ) + { + typedef spirit_namespace::position_iterator< Iter_type > Posn_iter_t; + + const Posn_iter_t posn_begin( begin, end ); + const Posn_iter_t posn_end( end, end ); + + read_range_or_throw( posn_begin, posn_end, value ); + } + + template< class Iter_type, class Value_type > + bool read_range( Iter_type& begin, Iter_type end, Value_type& value ) + { + try + { + begin = read_range_or_throw( begin, end, value ); + + return true; + } + catch( ... ) + { + return false; + } + } + + template< class String_type, class Value_type > + void read_string_or_throw( const String_type& s, Value_type& value ) + { + add_posn_iter_and_read_range_or_throw( s.begin(), s.end(), value ); + } + + template< class String_type, class Value_type > + bool read_string( const String_type& s, Value_type& value ) + { + typename String_type::const_iterator begin = s.begin(); + + return read_range( begin, s.end(), value ); + } + + template< class Istream_type > + struct Multi_pass_iters + { + typedef typename Istream_type::char_type Char_type; + typedef std::istream_iterator< Char_type, Char_type > istream_iter; + typedef spirit_namespace::multi_pass< istream_iter > Mp_iter; + + Multi_pass_iters( Istream_type& is ) + { + is.unsetf( std::ios::skipws ); + + begin_ = spirit_namespace::make_multi_pass( istream_iter( is ) ); + end_ = spirit_namespace::make_multi_pass( istream_iter() ); + } + + Mp_iter begin_; + Mp_iter end_; + }; + + template< class Istream_type, class Value_type > + bool read_stream( Istream_type& is, Value_type& value ) + { + Multi_pass_iters< Istream_type > mp_iters( is ); + + return read_range( mp_iters.begin_, mp_iters.end_, value ); + } + + template< class Istream_type, class Value_type > + void read_stream_or_throw( Istream_type& is, Value_type& value ) + { + const Multi_pass_iters< Istream_type > mp_iters( is ); + + add_posn_iter_and_read_range_or_throw( mp_iters.begin_, mp_iters.end_, value ); + } +} + +#endif diff --git a/src/json/json_spirit_stream_reader.h b/src/json/json_spirit_stream_reader.h new file mode 100644 index 00000000..7e59c9ad --- /dev/null +++ b/src/json/json_spirit_stream_reader.h @@ -0,0 +1,70 @@ +#ifndef JSON_SPIRIT_READ_STREAM +#define JSON_SPIRIT_READ_STREAM + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include "json_spirit_reader_template.h" + +namespace json_spirit +{ + // these classes allows you to read multiple top level contiguous values from a stream, + // the normal stream read functions have a bug that prevent multiple top level values + // from being read unless they are separated by spaces + + template< class Istream_type, class Value_type > + class Stream_reader + { + public: + + Stream_reader( Istream_type& is ) + : iters_( is ) + { + } + + bool read_next( Value_type& value ) + { + return read_range( iters_.begin_, iters_.end_, value ); + } + + private: + + typedef Multi_pass_iters< Istream_type > Mp_iters; + + Mp_iters iters_; + }; + + template< class Istream_type, class Value_type > + class Stream_reader_thrower + { + public: + + Stream_reader_thrower( Istream_type& is ) + : iters_( is ) + , posn_begin_( iters_.begin_, iters_.end_ ) + , posn_end_( iters_.end_, iters_.end_ ) + { + } + + void read_next( Value_type& value ) + { + posn_begin_ = read_range_or_throw( posn_begin_, posn_end_, value ); + } + + private: + + typedef Multi_pass_iters< Istream_type > Mp_iters; + typedef spirit_namespace::position_iterator< typename Mp_iters::Mp_iter > Posn_iter_t; + + Mp_iters iters_; + Posn_iter_t posn_begin_, posn_end_; + }; +} + +#endif diff --git a/src/json/json_spirit_utils.h b/src/json/json_spirit_utils.h new file mode 100644 index 00000000..553e3b96 --- /dev/null +++ b/src/json/json_spirit_utils.h @@ -0,0 +1,61 @@ +#ifndef JSON_SPIRIT_UTILS +#define JSON_SPIRIT_UTILS + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include "json_spirit_value.h" +#include + +namespace json_spirit +{ + template< class Obj_t, class Map_t > + void obj_to_map( const Obj_t& obj, Map_t& mp_obj ) + { + mp_obj.clear(); + + for( typename Obj_t::const_iterator i = obj.begin(); i != obj.end(); ++i ) + { + mp_obj[ i->name_ ] = i->value_; + } + } + + template< class Obj_t, class Map_t > + void map_to_obj( const Map_t& mp_obj, Obj_t& obj ) + { + obj.clear(); + + for( typename Map_t::const_iterator i = mp_obj.begin(); i != mp_obj.end(); ++i ) + { + obj.push_back( typename Obj_t::value_type( i->first, i->second ) ); + } + } + + typedef std::map< std::string, Value > Mapped_obj; + +#ifndef BOOST_NO_STD_WSTRING + typedef std::map< std::wstring, wValue > wMapped_obj; +#endif + + template< class Object_type, class String_type > + const typename Object_type::value_type::Value_type& find_value( const Object_type& obj, const String_type& name ) + { + for( typename Object_type::const_iterator i = obj.begin(); i != obj.end(); ++i ) + { + if( i->name_ == name ) + { + return i->value_; + } + } + + return Object_type::value_type::Value_type::null; + } +} + +#endif diff --git a/src/json/json_spirit_value.cpp b/src/json/json_spirit_value.cpp new file mode 100644 index 00000000..44d2f06a --- /dev/null +++ b/src/json/json_spirit_value.cpp @@ -0,0 +1,8 @@ +/* Copyright (c) 2007 John W Wilkinson + + This source code can be used for any purpose as long as + this comment is retained. */ + +// json spirit version 2.00 + +#include "json_spirit_value.h" diff --git a/src/json/json_spirit_value.h b/src/json/json_spirit_value.h new file mode 100644 index 00000000..3b864f68 --- /dev/null +++ b/src/json/json_spirit_value.h @@ -0,0 +1,533 @@ +#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 + +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( int64_t value ); + Value_impl( 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, 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< int64_t >( value ) ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( int64_t value ) + : type_( int_type ) + , v_( value ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( 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 > + int64_t Value_impl< Config >::get_int64() const + { + check_type( int_type ); + + return boost::get< int64_t >( v_ ); + } + + template< class Config > + uint64_t Value_impl< Config >::get_uint64() const + { + check_type( int_type ); + + return static_cast< 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 > + int64_t get_value( const Value& value, Type_to_type< int64_t > ) + { + return value.get_int64(); + } + + template< class Value > + uint64_t get_value( const Value& value, Type_to_type< uint64_t > ) + { + return value.get_uint64(); + } + + template< class Value > + double get_value( const Value& value, Type_to_type< double > ) + { + return value.get_real(); + } + + template< class Value > + typename Value::String_type get_value( const Value& value, Type_to_type< typename Value::String_type > ) + { + return value.get_str(); + } + + template< class Value > + typename Value::Array get_value( const Value& value, Type_to_type< typename Value::Array > ) + { + return value.get_array(); + } + + template< class Value > + typename Value::Object get_value( const Value& value, Type_to_type< typename Value::Object > ) + { + return value.get_obj(); + } + + template< class Value > + bool get_value( const Value& value, Type_to_type< bool > ) + { + return value.get_bool(); + } + } + + template< class Config > + template< typename T > + T Value_impl< Config >::get_value() const + { + return internal_::get_value( *this, internal_::Type_to_type< T >() ); + } +} + +#endif diff --git a/src/json/json_spirit_writer.cpp b/src/json/json_spirit_writer.cpp new file mode 100644 index 00000000..d24a632c --- /dev/null +++ b/src/json/json_spirit_writer.cpp @@ -0,0 +1,95 @@ +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#include "json_spirit_writer.h" +#include "json_spirit_writer_template.h" + +void json_spirit::write( const Value& value, std::ostream& os ) +{ + write_stream( value, os, false ); +} + +void json_spirit::write_formatted( const Value& value, std::ostream& os ) +{ + write_stream( value, os, true ); +} + +std::string json_spirit::write( const Value& value ) +{ + return write_string( value, false ); +} + +std::string json_spirit::write_formatted( const Value& value ) +{ + return write_string( value, true ); +} + +#ifndef BOOST_NO_STD_WSTRING + +void json_spirit::write( const wValue& value, std::wostream& os ) +{ + write_stream( value, os, false ); +} + +void json_spirit::write_formatted( const wValue& value, std::wostream& os ) +{ + write_stream( value, os, true ); +} + +std::wstring json_spirit::write( const wValue& value ) +{ + return write_string( value, false ); +} + +std::wstring json_spirit::write_formatted( const wValue& value ) +{ + return write_string( value, true ); +} + +#endif + +void json_spirit::write( const mValue& value, std::ostream& os ) +{ + write_stream( value, os, false ); +} + +void json_spirit::write_formatted( const mValue& value, std::ostream& os ) +{ + write_stream( value, os, true ); +} + +std::string json_spirit::write( const mValue& value ) +{ + return write_string( value, false ); +} + +std::string json_spirit::write_formatted( const mValue& value ) +{ + return write_string( value, true ); +} + +#ifndef BOOST_NO_STD_WSTRING + +void json_spirit::write( const wmValue& value, std::wostream& os ) +{ + write_stream( value, os, false ); +} + +void json_spirit::write_formatted( const wmValue& value, std::wostream& os ) +{ + write_stream( value, os, true ); +} + +std::wstring json_spirit::write( const wmValue& value ) +{ + return write_string( value, false ); +} + +std::wstring json_spirit::write_formatted( const wmValue& value ) +{ + return write_string( value, true ); +} + +#endif diff --git a/src/json/json_spirit_writer.h b/src/json/json_spirit_writer.h new file mode 100644 index 00000000..52e14068 --- /dev/null +++ b/src/json/json_spirit_writer.h @@ -0,0 +1,50 @@ +#ifndef JSON_SPIRIT_WRITER +#define JSON_SPIRIT_WRITER + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include "json_spirit_value.h" +#include + +namespace json_spirit +{ + // functions to convert JSON Values to text, + // the "formatted" versions add whitespace to format the output nicely + + void write ( const Value& value, std::ostream& os ); + void write_formatted( const Value& value, std::ostream& os ); + std::string write ( const Value& value ); + std::string write_formatted( const Value& value ); + +#ifndef BOOST_NO_STD_WSTRING + + void write ( const wValue& value, std::wostream& os ); + void write_formatted( const wValue& value, std::wostream& os ); + std::wstring write ( const wValue& value ); + std::wstring write_formatted( const wValue& value ); + +#endif + + void write ( const mValue& value, std::ostream& os ); + void write_formatted( const mValue& value, std::ostream& os ); + std::string write ( const mValue& value ); + std::string write_formatted( const mValue& value ); + +#ifndef BOOST_NO_STD_WSTRING + + void write ( const wmValue& value, std::wostream& os ); + void write_formatted( const wmValue& value, std::wostream& os ); + std::wstring write ( const wmValue& value ); + std::wstring write_formatted( const wmValue& value ); + +#endif +} + +#endif diff --git a/src/json/json_spirit_writer_template.h b/src/json/json_spirit_writer_template.h new file mode 100644 index 00000000..8e7a1682 --- /dev/null +++ b/src/json/json_spirit_writer_template.h @@ -0,0 +1,247 @@ +#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 ) + { + 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; + + // FIXME: This comparison is always true on some platforms + const wint_t unsigned_c( ( c >= 0 ) ? c : 256 + c ); + + if( iswprint( unsigned_c ) ) + { + result += c; + } + else + { + result += non_printable_to_string< String_type >( unsigned_c ); + } + } + + return result; + } + + // this class generates the JSON text, + // it keeps track of the indentation level etc. + // + template< class Value_type, class Ostream_type > + class Generator + { + typedef typename Value_type::Config_type Config_type; + typedef typename Config_type::String_type String_type; + typedef typename Config_type::Object_type Object_type; + typedef typename Config_type::Array_type Array_type; + typedef typename String_type::value_type Char_type; + typedef typename Object_type::value_type Obj_member_type; + + public: + + Generator( const Value_type& value, Ostream_type& os, bool pretty ) + : os_( os ) + , indentation_level_( 0 ) + , pretty_( pretty ) + { + output( value ); + } + + private: + + void output( const Value_type& value ) + { + switch( value.type() ) + { + case obj_type: output( value.get_obj() ); break; + case array_type: output( value.get_array() ); break; + case str_type: output( value.get_str() ); break; + case bool_type: output( value.get_bool() ); break; + case int_type: output_int( value ); break; + + /// Bitcoin: Added std::fixed and changed precision from 16 to 8 + case real_type: os_ << std::showpoint << std::fixed << std::setprecision(8) + << value.get_real(); break; + + case null_type: os_ << "null"; break; + default: assert( false ); + } + } + + void output( const Object_type& obj ) + { + output_array_or_obj( obj, '{', '}' ); + } + + void output( const Array_type& arr ) + { + output_array_or_obj( arr, '[', ']' ); + } + + void output( const Obj_member_type& member ) + { + output( Config_type::get_name( member ) ); space(); + os_ << ':'; space(); + output( Config_type::get_value( member ) ); + } + + void output_int( const Value_type& value ) + { + if( value.is_uint64() ) + { + os_ << value.get_uint64(); + } + else + { + os_ << value.get_int64(); + } + } + + void output( const String_type& s ) + { + os_ << '"' << add_esc_chars( s ) << '"'; + } + + void output( bool b ) + { + os_ << to_str< String_type >( b ? "true" : "false" ); + } + + template< class T > + void output_array_or_obj( const T& t, Char_type start_char, Char_type end_char ) + { + os_ << start_char; new_line(); + + ++indentation_level_; + + for( typename T::const_iterator i = t.begin(); i != t.end(); ++i ) + { + indent(); output( *i ); + + typename T::const_iterator next = i; + + if( ++next != t.end()) + { + os_ << ','; + } + + new_line(); + } + + --indentation_level_; + + indent(); os_ << end_char; + } + + void indent() + { + if( !pretty_ ) return; + + for( int i = 0; i < indentation_level_; ++i ) + { + os_ << " "; + } + } + + void space() + { + if( pretty_ ) os_ << ' '; + } + + void new_line() + { + if( pretty_ ) os_ << '\n'; + } + + Generator& operator=( const Generator& ); // to prevent "assignment operator could not be generated" warning + + Ostream_type& os_; + int indentation_level_; + bool pretty_; + }; + + template< class Value_type, class Ostream_type > + void write_stream( const Value_type& value, Ostream_type& os, bool pretty ) + { + Generator< Value_type, Ostream_type >( value, os, pretty ); + } + + template< class Value_type > + typename Value_type::String_type write_string( const Value_type& value, bool pretty ) + { + typedef typename Value_type::String_type::value_type Char_type; + + std::basic_ostringstream< Char_type > os; + + write_stream( value, os, pretty ); + + return os.str(); + } +} + +#endif diff --git a/src/kernel.cpp b/src/kernel.cpp new file mode 100644 index 00000000..59f28e45 --- /dev/null +++ b/src/kernel.cpp @@ -0,0 +1,523 @@ +// Copyright (c) 2012-2013 The PPCoin developers +// Copyright (c) 2013-2015 The XP developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include "kernel.h" +#include "kernel_worker.h" +#include "txdb.h" + +extern unsigned int nStakeMaxAge; +extern unsigned int nStakeTargetSpacing; + +using namespace std; + + +// Protocol switch time for fixed kernel modifier interval +unsigned int nModifierSwitchTime = 1413763200; // Mon, 20 Oct 2014 00:00:00 GMT +unsigned int nModifierTestSwitchTime = 1397520000; // Tue, 15 Apr 2014 00:00:00 GMT + +// Note: user must upgrade before the protocol switch deadline, otherwise it's required to +// re-download the blockchain. The timestamp of upgrade is recorded in the blockchain +// database. +unsigned int nModifierUpgradeTime = 0; + +typedef std::map MapModifierCheckpoints; + +// Hard checkpoints of stake modifiers to ensure they are deterministic +static std::map mapStakeModifierCheckpoints = + boost::assign::map_list_of + ( 1999999999, 0x0e00670bu ) + ; + +// Hard checkpoints of stake modifiers to ensure they are deterministic (testNet) +static std::map mapStakeModifierCheckpointsTestNet = + boost::assign::map_list_of + ( 1999999999, 0x0e00670bu ) + ; + +// Pregenerated entropy bits table (from genesis to #9689) +// +// Bits are packed into array of 256 bit integers: +// +// * array index calculated as nHeight / 256 +// * position of bit is calculated as nHeight & 0xFF. +// +const uint256 entropyStore[] = { + uint256("0x4555b4dcc1d690ddd9b810c90c66e82b18bf4f43cc887246c418383ec120a5ab"), + uint256("0xaa6d1198412fa77608addf6549c9198a22155e8afd7a9ded6179f6b7cfc66b0c"), + uint256("0x9442fabfa4116fb14a9769c2eea003845a1f5c3a0260f36b497d68f3a3cd4078"), + uint256("0x0e769042a9a98e42388195d699574b822d06515f7053ad884c53d7ee059f05b1"), + uint256("0x7005aac20baf70251aebfe3f1b95987d83ef1e3e6963de8fed601d4dd07bf7cf"), + uint256("0x58952c5c3de188f2e33c38d3f53d7bf44f9bc545a4289d266696273fa821be66"), + uint256("0x50b6c2ed780c08aaec3f7665b1b6004206243e3866456fc910b83b52d07eeb63"), + uint256("0x563841eefca85ba3384986c58100408ae3f1ba2ac727e1ac910ce154a06c702f"), + uint256("0x79275b03938b3e27a9b01a7f7953c6c487c58355f5d4169accfbb800213ffd13"), + uint256("0xd783f2538b3ed18f135af90adc687c5646d93aeaeaabc6667be94f7aa0a2d366"), + uint256("0xb441d0c175c40c8e88b09d88ea008af79cbed2d28219427d2e72fda682974db8"), + uint256("0x3204c43bd41f2e19628af3b0c9aca3db15bca4c8705d51056e7b17a319c04715"), + uint256("0x7e80e6ab7857d8f2f261a0a49c783bd800b365b8c9b85cc0e13f73904b0dcaa9"), + uint256("0xefaaee60ed82d2ad145c0e347941fdb131eb8fd289a45eef07121a93f283c5f1"), + uint256("0x3efc86e4334da332c1fd4c12513c40cff689f3efdc7f9913230822adacdda4f9"), + uint256("0xf0d6b8f38599a017fa35d1fbbf9ef51eca5ebc5b286aadba40c4c3e1d9bace0c"), + uint256("0x286a67f27323486036a0a92d35382fc8963c0c00bad331723318b4b9fdb2b56e"), + uint256("0xecbfaaa6567c54f08c4d5bd0118a2d7b58740f42cbfc73aa1536c1f5f76de87c"), + uint256("0xf9a4de1c5c46520de5aaf10d3796cf0e27ddce98b3398357f5726a949664e308"), + uint256("0xd75e6c4dc4be08401e3478d2467d9ab96a62af4f255c04a82c41af0de0a487bb"), + uint256("0x1a82c3bc6ad6047294c16571b5e2b7316c97bf8813e7da15798b9820d67e39f2"), + uint256("0xb49be0080de564e01829ded7e7971979565a741c5975dc9978dcc020192d396c"), + uint256("0x0d8eed113be67663b5a15a0625a9b49792b5ea59c005c4f405914877acab7000"), + uint256("0x8f9d46e2bc05a218ffa942965b747056197d393b097085523640cd59e07fe7c7"), + uint256("0x7a63ab40bc7f40ac2ebe9ede438d97b45fa6ed6f8419016da8d5f7a670111dda"), + uint256("0x63fbcc080448c43d6cf915c958314feff7a95a52ba43a68c05fc281d3a522d25"), + uint256("0xf834cf824c326d3ea861ea1e85dc3289265e37045981e28208e7344a7f8081d7"), + uint256("0xb4edc22ec98cc49b2f5af5bae3f52f5e6058280f74f2c432c2dd89ae49acceb8"), + uint256("0x0fe596037dcf81bf5c64f39755261c404ed088af5c8c31dd7549b6657ee92365"), + uint256("0xbbad51a0aeba254b01d18c328de9e932b9b859b61e622c325d64e2211b5e413d"), + uint256("0xabf0194cc787be938bc51c7fdf1cae4ec79e65ebab8fa8b8f40541c44ef384b0"), + uint256("0x83bc12d6fdbd3e854cb91c4ca7dfba3c38e8714121af88c8a8abdb33e5002438"), + uint256("0x71a2513026cabaedcbe55aeb6dc8049e5b763a3f54f10c33dd333624f764b38c"), + uint256("0xee6725632ff5c025dff6a18cd059875dcae20f399b03bccba13d9d5fcf6d9d9a"), + uint256("0xa168a2741d1e7e50cc74b79f695c25ffd3576e6bd61353c2a20e569fd63b2dac"), + uint256("0x6e462d2a87bfde9398b6747f94a8ed6a01e4d96c5b4372df5c910c106c48bd13"), + uint256("0x8eeb696181957c4b22434028990f49cb30006827c73860e77e2eecf5c38be99d"), + uint256("0x3188aaa65877b166f05cdc48f55b1f77a7d6fb221c395596d990ae5647e9ba96") +}; + +// Whether the given block is subject to new modifier protocol +bool IsFixedModifierInterval(unsigned int nTimeBlock) +{ + return (nTimeBlock >= (fTestNet? nModifierTestSwitchTime : nModifierSwitchTime)); +} + +// Get the last stake modifier and its generation time from a given block +static bool GetLastStakeModifier(const CBlockIndex* pindex, uint64_t& nStakeModifier, int64_t& nModifierTime) +{ + if (!pindex) + return error("GetLastStakeModifier: null pindex"); + while (pindex && pindex->pprev && !pindex->GeneratedStakeModifier()) + pindex = pindex->pprev; + if (!pindex->GeneratedStakeModifier()) + return error("GetLastStakeModifier: no generation at genesis block"); + nStakeModifier = pindex->nStakeModifier; + nModifierTime = pindex->GetBlockTime(); + return true; +} + +// Get selection interval section (in seconds) +static int64_t GetStakeModifierSelectionIntervalSection(int nSection) +{ + assert (nSection >= 0 && nSection < 64); + return (nModifierInterval * 63 / (63 + ((63 - nSection) * (MODIFIER_INTERVAL_RATIO - 1)))); +} + +// Get stake modifier selection interval (in seconds) +static int64_t GetStakeModifierSelectionInterval() +{ + int64_t nSelectionInterval = 0; + for (int nSection=0; nSection<64; nSection++) + nSelectionInterval += GetStakeModifierSelectionIntervalSection(nSection); + return nSelectionInterval; +} + +// select a block from the candidate blocks in vSortedByTimestamp, excluding +// already selected blocks in vSelectedBlocks, and with timestamp up to +// nSelectionIntervalStop. +static bool SelectBlockFromCandidates(vector >& vSortedByTimestamp, map& mapSelectedBlocks, + int64_t nSelectionIntervalStop, uint64_t nStakeModifierPrev, const CBlockIndex** pindexSelected) +{ + bool fSelected = false; + uint256 hashBest = 0; + *pindexSelected = (const CBlockIndex*) 0; + BOOST_FOREACH(const PAIRTYPE(int64_t, uint256)& item, vSortedByTimestamp) + { + if (!mapBlockIndex.count(item.second)) + return error("SelectBlockFromCandidates: failed to find block index for candidate block %s", item.second.ToString().c_str()); + const CBlockIndex* pindex = mapBlockIndex[item.second]; + if (fSelected && pindex->GetBlockTime() > nSelectionIntervalStop) + break; + if (mapSelectedBlocks.count(pindex->GetBlockHash()) > 0) + continue; + // compute the selection hash by hashing its proof-hash and the + // previous proof-of-stake modifier + uint256 hashProof = pindex->IsProofOfStake()? pindex->hashProofOfStake : pindex->GetBlockHash(); + CDataStream ss(SER_GETHASH, 0); + ss << hashProof << nStakeModifierPrev; + uint256 hashSelection = Hash(ss.begin(), ss.end()); + // the selection hash is divided by 2**32 so that proof-of-stake block + // is always favored over proof-of-work block. this is to preserve + // the energy efficiency property + if (pindex->IsProofOfStake()) + hashSelection >>= 32; + if (fSelected && hashSelection < hashBest) + { + hashBest = hashSelection; + *pindexSelected = (const CBlockIndex*) pindex; + } + else if (!fSelected) + { + fSelected = true; + hashBest = hashSelection; + *pindexSelected = (const CBlockIndex*) pindex; + } + } + if (fDebug && GetBoolArg("-printstakemodifier")) + printf("SelectBlockFromCandidates: selection hash=%s\n", hashBest.ToString().c_str()); + return fSelected; +} + +// Stake Modifier (hash modifier of proof-of-stake): +// The purpose of stake modifier is to prevent a txout (coin) owner from +// computing future proof-of-stake generated by this txout at the time +// of transaction confirmation. To meet kernel protocol, the txout +// must hash with a future stake modifier to generate the proof. +// Stake modifier consists of bits each of which is contributed from a +// selected block of a given block group in the past. +// The selection of a block is based on a hash of the block's proof-hash and +// the previous stake modifier. +// Stake modifier is recomputed at a fixed time interval instead of every +// block. This is to make it difficult for an attacker to gain control of +// additional bits in the stake modifier, even after generating a chain of +// blocks. +bool ComputeNextStakeModifier(const CBlockIndex* pindexCurrent, uint64_t& nStakeModifier, bool& fGeneratedStakeModifier) +{ + nStakeModifier = 0; + fGeneratedStakeModifier = false; + const CBlockIndex* pindexPrev = pindexCurrent->pprev; + if (!pindexPrev) + { + fGeneratedStakeModifier = true; + return true; // genesis block's modifier is 0 + } + + // First find current stake modifier and its generation block time + // if it's not old enough, return the same stake modifier + int64_t nModifierTime = 0; + if (!GetLastStakeModifier(pindexPrev, nStakeModifier, nModifierTime)) + return error("ComputeNextStakeModifier: unable to get last modifier"); + if (fDebug) + { + printf("ComputeNextStakeModifier: prev modifier=0x%016" PRIx64 " time=%s epoch=%u\n", nStakeModifier, DateTimeStrFormat(nModifierTime).c_str(), (unsigned int)nModifierTime); + } + if (nModifierTime / nModifierInterval >= pindexPrev->GetBlockTime() / nModifierInterval) + { + if (fDebug) + { + printf("ComputeNextStakeModifier: no new interval keep current modifier: pindexPrev nHeight=%d nTime=%u\n", pindexPrev->nHeight, (unsigned int)pindexPrev->GetBlockTime()); + } + return true; + } + if (nModifierTime / nModifierInterval >= pindexCurrent->GetBlockTime() / nModifierInterval) + { + // fixed interval protocol requires current block timestamp also be in a different modifier interval + if (IsFixedModifierInterval(pindexCurrent->nTime)) + { + if (fDebug) + { + printf("ComputeNextStakeModifier: no new interval keep current modifier: pindexCurrent nHeight=%d nTime=%u\n", pindexCurrent->nHeight, (unsigned int)pindexCurrent->GetBlockTime()); + } + return true; + } + else + { + if (fDebug) + { + printf("ComputeNextStakeModifier: old modifier at block %s not meeting fixed modifier interval: pindexCurrent nHeight=%d nTime=%u\n", pindexCurrent->GetBlockHash().ToString().c_str(), pindexCurrent->nHeight, (unsigned int)pindexCurrent->GetBlockTime()); + } + } + } + + // Sort candidate blocks by timestamp + vector > vSortedByTimestamp; + vSortedByTimestamp.reserve(64 * nModifierInterval / nStakeTargetSpacing); + int64_t nSelectionInterval = GetStakeModifierSelectionInterval(); + int64_t nSelectionIntervalStart = (pindexPrev->GetBlockTime() / nModifierInterval) * nModifierInterval - nSelectionInterval; + const CBlockIndex* pindex = pindexPrev; + while (pindex && pindex->GetBlockTime() >= nSelectionIntervalStart) + { + vSortedByTimestamp.push_back(make_pair(pindex->GetBlockTime(), pindex->GetBlockHash())); + pindex = pindex->pprev; + } + int nHeightFirstCandidate = pindex ? (pindex->nHeight + 1) : 0; + reverse(vSortedByTimestamp.begin(), vSortedByTimestamp.end()); + sort(vSortedByTimestamp.begin(), vSortedByTimestamp.end()); + + // Select 64 blocks from candidate blocks to generate stake modifier + uint64_t nStakeModifierNew = 0; + int64_t nSelectionIntervalStop = nSelectionIntervalStart; + map mapSelectedBlocks; + for (int nRound=0; nRoundGetStakeEntropyBit()) << nRound); + // add the selected block from candidates to selected list + mapSelectedBlocks.insert(make_pair(pindex->GetBlockHash(), pindex)); + if (fDebug && GetBoolArg("-printstakemodifier")) + printf("ComputeNextStakeModifier: selected round %d stop=%s height=%d bit=%d\n", nRound, DateTimeStrFormat(nSelectionIntervalStop).c_str(), pindex->nHeight, pindex->GetStakeEntropyBit()); + } + + // Print selection map for visualization of the selected blocks + if (fDebug && GetBoolArg("-printstakemodifier")) + { + string strSelectionMap = ""; + // '-' indicates proof-of-work blocks not selected + strSelectionMap.insert(0, pindexPrev->nHeight - nHeightFirstCandidate + 1, '-'); + pindex = pindexPrev; + while (pindex && pindex->nHeight >= nHeightFirstCandidate) + { + // '=' indicates proof-of-stake blocks not selected + if (pindex->IsProofOfStake()) + strSelectionMap.replace(pindex->nHeight - nHeightFirstCandidate, 1, "="); + pindex = pindex->pprev; + } + BOOST_FOREACH(const PAIRTYPE(uint256, const CBlockIndex*)& item, mapSelectedBlocks) + { + // 'S' indicates selected proof-of-stake blocks + // 'W' indicates selected proof-of-work blocks + strSelectionMap.replace(item.second->nHeight - nHeightFirstCandidate, 1, item.second->IsProofOfStake()? "S" : "W"); + } + printf("ComputeNextStakeModifier: selection height [%d, %d] map %s\n", nHeightFirstCandidate, pindexPrev->nHeight, strSelectionMap.c_str()); + } + if (fDebug) + { + printf("ComputeNextStakeModifier: new modifier=0x%016" PRIx64 " time=%s\n", nStakeModifierNew, DateTimeStrFormat(pindexPrev->GetBlockTime()).c_str()); + } + + nStakeModifier = nStakeModifierNew; + fGeneratedStakeModifier = true; + return true; +} + +// The stake modifier used to hash for a stake kernel is chosen as the stake +// modifier about a selection interval later than the coin generating the kernel +static bool GetKernelStakeModifier(uint256 hashBlockFrom, uint64_t& nStakeModifier, int& nStakeModifierHeight, int64_t& nStakeModifierTime, bool fPrintProofOfStake) +{ + nStakeModifier = 0; + if (!mapBlockIndex.count(hashBlockFrom)) + return error("GetKernelStakeModifier() : block not indexed"); + const CBlockIndex* pindexFrom = mapBlockIndex[hashBlockFrom]; + nStakeModifierHeight = pindexFrom->nHeight; + nStakeModifierTime = pindexFrom->GetBlockTime(); + int64_t nStakeModifierSelectionInterval = GetStakeModifierSelectionInterval(); + const CBlockIndex* pindex = pindexFrom; + // loop to find the stake modifier later by a selection interval + while (nStakeModifierTime < pindexFrom->GetBlockTime() + nStakeModifierSelectionInterval) + { + if (!pindex->pnext) + { // reached best block; may happen if node is behind on block chain + if (fPrintProofOfStake || (pindex->GetBlockTime() + nStakeMinAge - nStakeModifierSelectionInterval > GetAdjustedTime())) + return error("GetKernelStakeModifier() : reached best block %s at height %d from block %s", + pindex->GetBlockHash().ToString().c_str(), pindex->nHeight, hashBlockFrom.ToString().c_str()); + else + return false; + } + pindex = pindex->pnext; + if (pindex->GeneratedStakeModifier()) + { + nStakeModifierHeight = pindex->nHeight; + nStakeModifierTime = pindex->GetBlockTime(); + } + } + nStakeModifier = pindex->nStakeModifier; + return true; +} + +bool GetKernelStakeModifier(uint256 hashBlockFrom, uint64_t& nStakeModifier) +{ + int nStakeModifierHeight; + int64_t nStakeModifierTime; + + return GetKernelStakeModifier(hashBlockFrom, nStakeModifier, nStakeModifierHeight, nStakeModifierTime, false); +} + + +// ppcoin kernel protocol +// coinstake must meet hash target according to the protocol: +// kernel (input 0) must meet the formula +// hash(nStakeModifier + txPrev.block.nTime + txPrev.offset + txPrev.nTime + txPrev.vout.n + nTime) < bnTarget * nCoinDayWeight +// this ensures that the chance of getting a coinstake is proportional to the +// amount of coin age one owns. +// The reason this hash is chosen is the following: +// nStakeModifier: scrambles computation to make it very difficult to precompute +// future proof-of-stake at the time of the coin's confirmation +// txPrev.block.nTime: prevent nodes from guessing a good timestamp to +// generate transaction for future advantage +// txPrev.offset: offset of txPrev inside block, to reduce the chance of +// nodes generating coinstake at the same time +// txPrev.nTime: reduce the chance of nodes generating coinstake at the same +// time +// txPrev.vout.n: output number of txPrev, to reduce the chance of nodes +// generating coinstake at the same time +// block/tx hash should not be used here as they can be generated in vast +// quantities so as to generate blocks faster, degrading the system back into +// a proof-of-work situation. +// +bool CheckStakeKernelHash(uint32_t nBits, const CBlock& blockFrom, uint32_t nTxPrevOffset, const CTransaction& txPrev, const COutPoint& prevout, uint32_t nTimeTx, uint256& hashProofOfStake, uint256& targetProofOfStake, bool fPrintProofOfStake) +{ + if (nTimeTx < txPrev.nTime) // Transaction timestamp violation + return error("CheckStakeKernelHash() : nTime violation"); + + uint32_t nTimeBlockFrom = blockFrom.GetBlockTime(); + if (nTimeBlockFrom + nStakeMinAge > nTimeTx) // Min age requirement + return error("CheckStakeKernelHash() : min age violation"); + + CBigNum bnTargetPerCoinDay; + bnTargetPerCoinDay.SetCompact(nBits); + int64_t nValueIn = txPrev.vout[prevout.n].nValue; + + uint256 hashBlockFrom = blockFrom.GetHash(); + + CBigNum bnCoinDayWeight = CBigNum(nValueIn) * GetWeight((int64_t)txPrev.nTime, (int64_t)nTimeTx) / COIN / nOneDay; + targetProofOfStake = (bnCoinDayWeight * bnTargetPerCoinDay).getuint256(); + + // Calculate hash + CDataStream ss(SER_GETHASH, 0); + uint64_t nStakeModifier = 0; + int nStakeModifierHeight = 0; + int64_t nStakeModifierTime = 0; + + if (!GetKernelStakeModifier(hashBlockFrom, nStakeModifier, nStakeModifierHeight, nStakeModifierTime, fPrintProofOfStake)) + return false; + ss << nStakeModifier; + + ss << nTimeBlockFrom << nTxPrevOffset << txPrev.nTime << prevout.n << nTimeTx; + hashProofOfStake = Hash(ss.begin(), ss.end()); + if (fPrintProofOfStake) + { + printf("CheckStakeKernelHash() : using modifier 0x%016" PRIx64 " at height=%d timestamp=%s for block from height=%d timestamp=%s\n", + nStakeModifier, nStakeModifierHeight, + DateTimeStrFormat(nStakeModifierTime).c_str(), + mapBlockIndex[hashBlockFrom]->nHeight, + DateTimeStrFormat(blockFrom.GetBlockTime()).c_str()); + printf("CheckStakeKernelHash() : check modifier=0x%016" PRIx64 " nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashTarget=%s hashProof=%s\n", + nStakeModifier, + nTimeBlockFrom, nTxPrevOffset, txPrev.nTime, prevout.n, nTimeTx, + targetProofOfStake.ToString().c_str(), hashProofOfStake.ToString().c_str()); + } + + // Now check if proof-of-stake hash meets target protocol + if (CBigNum(hashProofOfStake) > bnCoinDayWeight * bnTargetPerCoinDay) + return false; + if (fDebug && !fPrintProofOfStake) + { + printf("CheckStakeKernelHash() : using modifier 0x%016" PRIx64 " at height=%d timestamp=%s for block from height=%d timestamp=%s\n", + nStakeModifier, nStakeModifierHeight, + DateTimeStrFormat(nStakeModifierTime).c_str(), + mapBlockIndex[hashBlockFrom]->nHeight, + DateTimeStrFormat(blockFrom.GetBlockTime()).c_str()); + printf("CheckStakeKernelHash() : pass modifier=0x%016" PRIx64 " nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashTarget=%s hashProof=%s\n", + nStakeModifier, + nTimeBlockFrom, nTxPrevOffset, txPrev.nTime, prevout.n, nTimeTx, + targetProofOfStake.ToString().c_str(), hashProofOfStake.ToString().c_str()); + } + return true; +} + +// Scan given kernel for solution +bool ScanKernelForward(unsigned char *kernel, uint32_t nBits, uint32_t nInputTxTime, int64_t nValueIn, std::pair &SearchInterval, std::vector > &solutions) +{ + // TODO: custom threads amount + + uint32_t nThreads = boost::thread::hardware_concurrency(); + uint32_t nPart = (SearchInterval.second - SearchInterval.first) / nThreads; + + + KernelWorker *workers = new KernelWorker[nThreads]; + + boost::thread_group group; + for(size_t i = 0; i < nThreads; i++) + { + uint32_t nBegin = SearchInterval.first + nPart * i; + uint32_t nEnd = SearchInterval.first + nPart * (i + 1); + workers[i] = KernelWorker(kernel, nBits, nInputTxTime, nValueIn, nBegin, nEnd); + boost::function workerFnc = boost::bind(&KernelWorker::Do, &workers[i]); + group.create_thread(workerFnc); + } + + group.join_all(); + solutions.clear(); + + for(size_t i = 0; i < nThreads; i++) + { + std::vector > ws = workers[i].GetSolutions(); + solutions.insert(solutions.end(), ws.begin(), ws.end()); + } + + delete [] workers; + + if (solutions.size() == 0) + { + // no solutions + return false; + } + + return true; +} + +// Check kernel hash target and coinstake signature +bool CheckProofOfStake(const CTransaction& tx, unsigned int nBits, uint256& hashProofOfStake, uint256& targetProofOfStake) +{ + if (!tx.IsCoinStake()) + return error("CheckProofOfStake() : called on non-coinstake %s", tx.GetHash().ToString().c_str()); + + // Kernel (input 0) must match the stake hash target per coin age (nBits) + const CTxIn& txin = tx.vin[0]; + + // First try finding the previous transaction in database + CTxDB txdb("r"); + CTransaction txPrev; + CTxIndex txindex; + if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex)) + return tx.DoS(1, error("CheckProofOfStake() : INFO: read txPrev failed")); // previous transaction not in main chain, may occur during initial download + +#ifndef USE_LEVELDB + txdb.Close(); +#endif + + // Verify signature + if (!VerifySignature(txPrev, tx, 0, MANDATORY_SCRIPT_VERIFY_FLAGS, 0)) + return tx.DoS(100, error("CheckProofOfStake() : VerifySignature failed on coinstake %s", tx.GetHash().ToString().c_str())); + + // Read block header + CBlock block; + if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) + return fDebug? error("CheckProofOfStake() : read block failed") : false; // unable to read block of previous transaction + + if (!CheckStakeKernelHash(nBits, block, txindex.pos.nTxPos - txindex.pos.nBlockPos, txPrev, txin.prevout, tx.nTime, hashProofOfStake, targetProofOfStake, fDebug)) + return tx.DoS(1, error("CheckProofOfStake() : INFO: check kernel failed on coinstake %s, hashProof=%s", tx.GetHash().ToString().c_str(), hashProofOfStake.ToString().c_str())); // may occur during initial download or if behind on block chain sync + + return true; +} + +// Get stake modifier checksum +uint32_t GetStakeModifierChecksum(const CBlockIndex* pindex) +{ + assert (pindex->pprev || pindex->GetBlockHash() == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)); + // Hash previous checksum with flags, hashProofOfStake and nStakeModifier + CDataStream ss(SER_GETHASH, 0); + if (pindex->pprev) + ss << pindex->pprev->nStakeModifierChecksum; + ss << pindex->nFlags << pindex->hashProofOfStake << pindex->nStakeModifier; + uint256 hashChecksum = Hash(ss.begin(), ss.end()); + hashChecksum >>= (256 - 32); + return static_cast(hashChecksum.Get64()); +} + +// Check stake modifier hard checkpoints +bool CheckStakeModifierCheckpoints(int nHeight, uint32_t nStakeModifierChecksum) +{ + MapModifierCheckpoints& checkpoints = (fTestNet ? mapStakeModifierCheckpointsTestNet : mapStakeModifierCheckpoints); + + if (checkpoints.count(nHeight)) + return nStakeModifierChecksum == checkpoints[nHeight]; + return true; +} diff --git a/src/kernel.h b/src/kernel.h new file mode 100644 index 00000000..c76c00d4 --- /dev/null +++ b/src/kernel.h @@ -0,0 +1,62 @@ +// Copyright (c) 2012-2013 The PPCoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef PPCOIN_KERNEL_H +#define PPCOIN_KERNEL_H + +#include "main.h" +#include "wallet.h" + +using namespace std; + +// ChainDB upgrade time +extern unsigned int nModifierUpgradeTime; + +// MODIFIER_INTERVAL: time to elapse before new modifier is computed +extern unsigned int nModifierInterval; + +// MODIFIER_INTERVAL_RATIO: +// ratio of group interval length between the last group and the first group +static const int MODIFIER_INTERVAL_RATIO = 3; + +// Whether the given block is subject to new modifier protocol +bool IsFixedModifierInterval(unsigned int nTimeBlock); + +// Compute the hash modifier for proof-of-stake +bool ComputeNextStakeModifier(const CBlockIndex* pindexCurrent, uint64_t& nStakeModifier, bool& fGeneratedStakeModifier); + +// The stake modifier used to hash for a stake kernel is chosen as the stake +// modifier about a selection interval later than the coin generating the kernel +bool GetKernelStakeModifier(uint256 hashBlockFrom, uint64_t& nStakeModifier); + +// Check whether stake kernel meets hash target +// Sets hashProofOfStake on success return +bool CheckStakeKernelHash(unsigned int nBits, const CBlock& blockFrom, uint32_t nTxPrevOffset, const CTransaction& txPrev, const COutPoint& prevout, uint32_t nTimeTx, uint256& hashProofOfStake, uint256& targetProofOfStake, bool fPrintProofOfStake=false); + +// Scan given kernel for solutions +bool ScanKernelForward(unsigned char *kernel, uint32_t nBits, uint32_t nInputTxTime, int64_t nValueIn, std::pair &SearchInterval, std::vector > &solutions); + +// Check kernel hash target and coinstake signature +// Sets hashProofOfStake on success return +bool CheckProofOfStake(const CTransaction& tx, unsigned int nBits, uint256& hashProofOfStake, uint256& targetProofOfStake); + +// Get stake modifier checksum +uint32_t GetStakeModifierChecksum(const CBlockIndex* pindex); + +// Check stake modifier hard checkpoints +bool CheckStakeModifierCheckpoints(int nHeight, uint32_t nStakeModifierChecksum); + +// Get time weight using supplied timestamps +inline int64_t GetWeight(int64_t nIntervalBeginning, int64_t nIntervalEnd) +{ + // Kernel hash weight starts from 0 at the 30-day min age + // this change increases active coins participating the hash and helps + // to secure the network when proof-of-stake difficulty is low + // + // Maximum TimeWeight is 90 days. + + return min(nIntervalEnd - nIntervalBeginning - nStakeMinAge, (int64_t)nStakeMaxAge); +} + + +#endif // PPCOIN_KERNEL_H diff --git a/src/kernel_worker.cpp b/src/kernel_worker.cpp new file mode 100644 index 00000000..29809520 --- /dev/null +++ b/src/kernel_worker.cpp @@ -0,0 +1,646 @@ +#include +#include + +#include "uint256.h" +#include "bignum.h" +#include "kernel.h" +#include "kernel_worker.h" + +using namespace std; + +#ifdef USE_ASM + +#ifdef _MSC_VER +#include +#define __builtin_bswap32 _byteswap_ulong +#endif + +#if defined(__i386__) || defined(__x86_64__) +#include +#endif + +#ifndef __i386__ +// kernel padding +static const uint32_t block1_suffix[9] = { 0x80000000, 0, 0, 0, 0, 0, 0, 0, 0x000000e0 }; +// hash padding +static const uint32_t block2_suffix[8] = { 0x80000000, 0, 0, 0, 0, 0, 0, 0x00000100 }; + +// Sha256 initial state +static const uint32_t sha256_initial[8] = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 }; + +extern "C" void sha256_transform(uint32_t *state, const uint32_t *block, int swap); +#endif + +// 4-way kernel padding +static const uint32_t block1_suffix_4way[4 * 9] = { + 0x80000000, 0x80000000, 0x80000000, 0x80000000, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0x000000e0, 0x000000e0, 0x000000e0, 0x000000e0 +}; + +// 4-way hash padding +static const uint32_t block2_suffix_4way[4 * 8] = { + 0x80000000, 0x80000000, 0x80000000, 0x80000000, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0x00000100, 0x00000100, 0x00000100, 0x00000100 +}; + +extern "C" int sha256_use_4way(); +extern "C" void sha256_init_4way(uint32_t *state); +extern "C" void sha256_transform_4way(uint32_t *state, const uint32_t *block, int swap); +bool fUse4Way = sha256_use_4way() != 0; + +#ifdef __x86_64__ +// 8-way kernel padding +static const uint32_t block1_suffix_8way[8 * 9] = { + 0x80000000, 0x80000000, 0x80000000, 0x80000000, 0x80000000, 0x80000000, 0x80000000, 0x80000000, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0x000000e0, 0x000000e0, 0x000000e0, 0x000000e0, 0x000000e0, 0x000000e0, 0x000000e0, 0x000000e0 +}; + +// 8-way hash padding +static const uint32_t block2_suffix_8way[8 * 8] = { + 0x80000000, 0x80000000, 0x80000000, 0x80000000, 0x80000000, 0x80000000, 0x80000000, 0x80000000, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0x000000e0, 0x000000e0, 0x000000e0, 0x000000e0, 0x000000e0, 0x000000e0, 0x000000e0, 0x000000e0 +}; + +extern "C" int sha256_use_8way(); +extern "C" void sha256_init_8way(uint32_t *state); +extern "C" void sha256_transform_8way(uint32_t *state, const uint32_t *block, int swap); +bool fUse8Way = sha256_use_8way() != 0; + +inline void copyrow8_swap32(uint32_t *to, uint32_t *from) +{ + // There are no AVX2 CPUs without SSSE3 support, so we don't need any conditions here. + __m128i mask = _mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3); + _mm_storeu_si128((__m128i *)&to[0], _mm_shuffle_epi8(_mm_loadu_si128((__m128i *)&from[0]), mask)); + _mm_storeu_si128((__m128i *)&to[4], _mm_shuffle_epi8(_mm_loadu_si128((__m128i *)&from[4]), mask)); +} +#endif + +#if defined(__i386__) || defined(__x86_64__) +extern "C" int sha256_use_ssse3(); +bool fUseSSSE3 = sha256_use_ssse3() != 0; + +inline void copyrow4_swap32(uint32_t *to, uint32_t *from) +{ + if (!fUseSSSE3) + { + for (int i = 0; i < 4; i++) + to[i] = __builtin_bswap32(from[i]); + } + else + { + __m128i mask = _mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3); + _mm_storeu_si128((__m128i *)&to[0], _mm_shuffle_epi8(_mm_loadu_si128((__m128i *)&from[0]), mask)); + } +} +#else +inline void copyrow4_swap32(uint32_t *to, uint32_t *from) +{ + for (int i = 0; i < 4; i++) + to[i] = __builtin_bswap32(from[i]); +} +#endif +#endif + +KernelWorker::KernelWorker(unsigned char *kernel, uint32_t nBits, uint32_t nInputTxTime, int64_t nValueIn, uint32_t nIntervalBegin, uint32_t nIntervalEnd) + : kernel(kernel), nBits(nBits), nInputTxTime(nInputTxTime), bnValueIn(nValueIn), nIntervalBegin(nIntervalBegin), nIntervalEnd(nIntervalEnd) + { + solutions = vector >(); + } + +#ifdef USE_ASM +#ifdef __x86_64__ +void KernelWorker::Do_8way() +{ + SetThreadPriority(THREAD_PRIORITY_LOWEST); + + // Compute maximum possible target to filter out majority of obviously insufficient hashes + CBigNum bnTargetPerCoinDay; + bnTargetPerCoinDay.SetCompact(nBits); + uint256 nMaxTarget = (bnTargetPerCoinDay * bnValueIn * nStakeMaxAge / COIN / nOneDay).getuint256(); + +#ifdef _MSC_VER + __declspec(align(16)) uint32_t blocks1[8 * 16]; + __declspec(align(16)) uint32_t blocks2[8 * 16]; + __declspec(align(16)) uint32_t candidates[8 * 8]; +#else + uint32_t blocks1[8 * 16] __attribute__((aligned(16))); + uint32_t blocks2[8 * 16] __attribute__((aligned(16))); + uint32_t candidates[8 * 8] __attribute__((aligned(16))); +#endif + + vector vRow = vector(8); + uint32_t *pnKernel = (uint32_t *) kernel; + + for(int i = 0; i < 7; i++) + { + fill(vRow.begin(), vRow.end(), pnKernel[i]); + copyrow8_swap32(&blocks1[i*8], &vRow[0]); + } + + memcpy(&blocks1[56], &block1_suffix_8way[0], 36*8); // sha256 padding + memcpy(&blocks2[64], &block2_suffix_8way[0], 32*8); + + uint32_t nHashes[8]; + uint32_t nTimeStamps[8]; + + // Search forward in time from the given timestamp + // Stopping search in case of shutting down + for (uint32_t nTimeTx=nIntervalBegin, nMaxTarget32 = nMaxTarget.Get32(7); nTimeTx= CBigNum(nHashProofOfStake)) + solutions.push_back(std::pair(nHashProofOfStake, nTimeStamps[nResult])); + } + } + } +} +#endif + +void KernelWorker::Do_4way() +{ + SetThreadPriority(THREAD_PRIORITY_LOWEST); + + // Compute maximum possible target to filter out majority of obviously insufficient hashes + CBigNum bnTargetPerCoinDay; + bnTargetPerCoinDay.SetCompact(nBits); + uint256 nMaxTarget = (bnTargetPerCoinDay * bnValueIn * nStakeMaxAge / COIN / nOneDay).getuint256(); + +#ifdef _MSC_VER + __declspec(align(16)) uint32_t blocks1[4 * 16]; + __declspec(align(16)) uint32_t blocks2[4 * 16]; + __declspec(align(16)) uint32_t candidates[4 * 8]; +#else + uint32_t blocks1[4 * 16] __attribute__((aligned(16))); + uint32_t blocks2[4 * 16] __attribute__((aligned(16))); + uint32_t candidates[4 * 8] __attribute__((aligned(16))); +#endif + + vector vRow = vector(4); + uint32_t *pnKernel = (uint32_t *) kernel; + + for(int i = 0; i < 7; i++) + { + fill(vRow.begin(), vRow.end(), pnKernel[i]); + copyrow4_swap32(&blocks1[i*4], &vRow[0]); + } + + memcpy(&blocks1[28], &block1_suffix_4way[0], 36*4); // sha256 padding + memcpy(&blocks2[32], &block2_suffix_4way[0], 32*4); + + uint32_t nHashes[4]; + uint32_t nTimeStamps[4]; + + // Search forward in time from the given timestamp + // Stopping search in case of shutting down + for (uint32_t nTimeTx=nIntervalBegin, nMaxTarget32 = nMaxTarget.Get32(7); nTimeTx= CBigNum(nHashProofOfStake)) + solutions.push_back(std::pair(nHashProofOfStake, nTimeStamps[nResult])); + } + } + } +} +#endif + +void KernelWorker::Do_generic() +{ + SetThreadPriority(THREAD_PRIORITY_LOWEST); + + // Compute maximum possible target to filter out majority of obviously insufficient hashes + CBigNum bnTargetPerCoinDay; + bnTargetPerCoinDay.SetCompact(nBits); + uint256 nMaxTarget = (bnTargetPerCoinDay * bnValueIn * nStakeMaxAge / COIN / nOneDay).getuint256(); + +#if !defined(USE_ASM) || defined(__i386__) + SHA256_CTX ctx, workerCtx; + // Init new sha256 context and update it + // with first 24 bytes of kernel + SHA256_Init(&ctx); + SHA256_Update(&ctx, kernel, 8 + 16); + workerCtx = ctx; // save context + + // Sha256 result buffer + uint32_t hashProofOfStake[8]; + uint256 *pnHashProofOfStake = (uint256 *)&hashProofOfStake; + + // Search forward in time from the given timestamp + // Stopping search in case of shutting down + for (uint32_t nTimeTx=nIntervalBegin, nMaxTarget32 = nMaxTarget.Get32(7); nTimeTx nMaxTarget32) + continue; + + CBigNum bnCoinDayWeight = bnValueIn * GetWeight((int64_t)nInputTxTime, (int64_t)nTimeTx) / COIN / nOneDay; + CBigNum bnTargetProofOfStake = bnCoinDayWeight * bnTargetPerCoinDay; + + if (bnTargetProofOfStake >= CBigNum(*pnHashProofOfStake)) + solutions.push_back(std::pair(*pnHashProofOfStake, nTimeTx)); + } +#else + +#ifdef _MSC_VER + __declspec(align(16)) uint32_t block1[16]; + __declspec(align(16)) uint32_t block2[16]; + __declspec(align(16)) uint32_t candidate[8]; +#else + uint32_t block1[16] __attribute__((aligned(16))); + uint32_t block2[16] __attribute__((aligned(16))); + uint32_t candidate[8] __attribute__((aligned(16))); +#endif + + memcpy(&block1[7], &block1_suffix[0], 36); // sha256 padding + memcpy(&block2[8], &block2_suffix[0], 32); + + uint32_t *pnKernel = (uint32_t *) kernel; + + for (int i = 0; i < 6; i++) + block1[i] = __builtin_bswap32(pnKernel[i]); + + // Search forward in time from the given timestamp + // Stopping search in case of shutting down + for (uint32_t nTimeTx=nIntervalBegin, nMaxTarget32 = nMaxTarget.Get32(7); nTimeTx nMaxTarget32) + continue; + + uint256 nHashProofOfStake; + uint32_t *pnHashProofOfStake = (uint32_t *) &nHashProofOfStake; + + for (int i = 0; i < 7; i++) + pnHashProofOfStake[i] = __builtin_bswap32(candidate[i]); + pnHashProofOfStake[7] = nHash7; + + CBigNum bnCoinDayWeight = bnValueIn * GetWeight((int64_t)nInputTxTime, (int64_t)nTimeTx) / COIN / nOneDay; + CBigNum bnTargetProofOfStake = bnCoinDayWeight * bnTargetPerCoinDay; + + if (bnTargetProofOfStake >= CBigNum(nHashProofOfStake)) + solutions.push_back(std::pair(nHashProofOfStake, nTimeTx)); + } +#endif +} + +void KernelWorker::Do() +{ +#ifdef USE_ASM +#ifdef __x86_64__ + if (false && fUse8Way) // disable for now + { + Do_8way(); + return; + } +#endif + if (fUse4Way) + { + Do_4way(); + return; + } +#endif + + Do_generic(); +} + +vector >& KernelWorker::GetSolutions() +{ + return solutions; +} + +// Scan given kernel for solutions +#ifdef USE_ASM + +#ifdef __x86_64__ +bool ScanKernelBackward_8Way(unsigned char *kernel, uint32_t nBits, uint32_t nInputTxTime, int64_t nValueIn, std::pair &SearchInterval, std::pair &solution) +{ + CBigNum bnTargetPerCoinDay; + bnTargetPerCoinDay.SetCompact(nBits); + + CBigNum bnValueIn(nValueIn); + + // Get maximum possible target to filter out the majority of obviously insufficient hashes + uint256 nMaxTarget = (bnTargetPerCoinDay * bnValueIn * nStakeMaxAge / COIN / nOneDay).getuint256(); + +#ifdef _MSC_VER + __declspec(align(16)) uint32_t blocks1[8 * 16]; + __declspec(align(16)) uint32_t blocks2[8 * 16]; + __declspec(align(16)) uint32_t candidates[8 * 8]; +#else + uint32_t blocks1[8 * 16] __attribute__((aligned(16))); + uint32_t blocks2[8 * 16] __attribute__((aligned(16))); + uint32_t candidates[8 * 8] __attribute__((aligned(16))); +#endif + + vector vRow = vector(8); + uint32_t *pnKernel = (uint32_t *) kernel; + + for(int i = 0; i < 7; i++) + { + fill(vRow.begin(), vRow.end(), pnKernel[i]); + copyrow8_swap32(&blocks1[i*8], &vRow[0]); + } + + memcpy(&blocks1[56], &block1_suffix_8way[0], 36*8); // sha256 padding + memcpy(&blocks2[64], &block2_suffix_8way[0], 32*8); + + uint32_t nHashes[8]; + uint32_t nTimeStamps[8]; + + // Search forward in time from the given timestamp + // Stopping search in case of shutting down + for (uint32_t nTimeTx=SearchInterval.first, nMaxTarget32 = nMaxTarget.Get32(7); nTimeTx>SearchInterval.second && !fShutdown; nTimeTx -=8) + { + sha256_init_8way(blocks2); + sha256_init_8way(candidates); + + nTimeStamps[0] = nTimeTx; + nTimeStamps[1] = nTimeTx-1; + nTimeStamps[2] = nTimeTx-2; + nTimeStamps[3] = nTimeTx-3; + nTimeStamps[4] = nTimeTx-4; + nTimeStamps[5] = nTimeTx-5; + nTimeStamps[6] = nTimeTx-6; + nTimeStamps[7] = nTimeTx-7; + + copyrow8_swap32(&blocks1[24], &nTimeStamps[0]); // Kernel timestamps + sha256_transform_8way(&blocks2[0], &blocks1[0], 0); // first hashing + sha256_transform_8way(&candidates[0], &blocks2[0], 0); // second hashing + copyrow8_swap32(&nHashes[0], &candidates[56]); + + for(int nResult = 0; nResult < 8; nResult++) + { + if (nHashes[nResult] <= nMaxTarget32) // Possible hit + { + uint256 nHashProofOfStake = 0; + uint32_t *pnHashProofOfStake = (uint32_t *) &nHashProofOfStake; + + for (int i = 0; i < 7; i++) + pnHashProofOfStake[i] = __builtin_bswap32(candidates[(i*8) + nResult]); + pnHashProofOfStake[7] = nHashes[nResult]; + + CBigNum bnCoinDayWeight = bnValueIn * GetWeight((int64_t)nInputTxTime, (int64_t)nTimeStamps[nResult]) / COIN / nOneDay; + CBigNum bnTargetProofOfStake = bnCoinDayWeight * bnTargetPerCoinDay; + + if (bnTargetProofOfStake >= CBigNum(nHashProofOfStake)) + { + solution.first = nHashProofOfStake; + solution.second = nTimeStamps[nResult]; + + return true; + } + } + } + } + + return false; +} +#endif + +bool ScanKernelBackward_4Way(unsigned char *kernel, uint32_t nBits, uint32_t nInputTxTime, int64_t nValueIn, std::pair &SearchInterval, std::pair &solution) +{ + CBigNum bnTargetPerCoinDay; + bnTargetPerCoinDay.SetCompact(nBits); + + CBigNum bnValueIn(nValueIn); + + // Get maximum possible target to filter out the majority of obviously insufficient hashes + uint256 nMaxTarget = (bnTargetPerCoinDay * bnValueIn * nStakeMaxAge / COIN / nOneDay).getuint256(); + +#ifdef _MSC_VER + __declspec(align(16)) uint32_t blocks1[4 * 16]; + __declspec(align(16)) uint32_t blocks2[4 * 16]; + __declspec(align(16)) uint32_t candidates[4 * 8]; +#else + uint32_t blocks1[4 * 16] __attribute__((aligned(16))); + uint32_t blocks2[4 * 16] __attribute__((aligned(16))); + uint32_t candidates[4 * 8] __attribute__((aligned(16))); +#endif + + vector vRow = vector(4); + uint32_t *pnKernel = (uint32_t *) kernel; + + for(int i = 0; i < 7; i++) + { + fill(vRow.begin(), vRow.end(), pnKernel[i]); + copyrow4_swap32(&blocks1[i*4], &vRow[0]); + } + + memcpy(&blocks1[28], &block1_suffix_4way[0], 36*4); // sha256 padding + memcpy(&blocks2[32], &block2_suffix_4way[0], 32*4); + + uint32_t nHashes[4]; + uint32_t nTimeStamps[4]; + + // Search forward in time from the given timestamp + // Stopping search in case of shutting down + for (uint32_t nTimeTx=SearchInterval.first, nMaxTarget32 = nMaxTarget.Get32(7); nTimeTx>SearchInterval.second && !fShutdown; nTimeTx -=4) + { + sha256_init_4way(blocks2); + sha256_init_4way(candidates); + + nTimeStamps[0] = nTimeTx; + nTimeStamps[1] = nTimeTx-1; + nTimeStamps[2] = nTimeTx-2; + nTimeStamps[3] = nTimeTx-3; + + copyrow4_swap32(&blocks1[24], &nTimeStamps[0]); // Kernel timestamps + sha256_transform_4way(&blocks2[0], &blocks1[0], 0); // first hashing + sha256_transform_4way(&candidates[0], &blocks2[0], 0); // second hashing + copyrow4_swap32(&nHashes[0], &candidates[28]); + + for(int nResult = 0; nResult < 4; nResult++) + { + if (nHashes[nResult] <= nMaxTarget32) // Possible hit + { + uint256 nHashProofOfStake = 0; + uint32_t *pnHashProofOfStake = (uint32_t *) &nHashProofOfStake; + + for (int i = 0; i < 7; i++) + pnHashProofOfStake[i] = __builtin_bswap32(candidates[(i*4) + nResult]); + pnHashProofOfStake[7] = nHashes[nResult]; + + CBigNum bnCoinDayWeight = bnValueIn * GetWeight((int64_t)nInputTxTime, (int64_t)nTimeStamps[nResult]) / COIN / nOneDay; + CBigNum bnTargetProofOfStake = bnCoinDayWeight * bnTargetPerCoinDay; + + if (bnTargetProofOfStake >= CBigNum(nHashProofOfStake)) + { + solution.first = nHashProofOfStake; + solution.second = nTimeStamps[nResult]; + + return true; + } + } + } + } + + return false; +} +#endif + +bool ScanKernelBackward(unsigned char *kernel, uint32_t nBits, uint32_t nInputTxTime, int64_t nValueIn, std::pair &SearchInterval, std::pair &solution) +{ +#ifdef USE_ASM +#ifdef __x86_64__ + if (false && fUse8Way) // disable for now + { + return ScanKernelBackward_8Way(kernel, nBits, nInputTxTime, nValueIn, SearchInterval, solution); + } +#endif + if (fUse4Way) + { + return ScanKernelBackward_4Way(kernel, nBits, nInputTxTime, nValueIn, SearchInterval, solution); + } +#endif + + CBigNum bnTargetPerCoinDay; + bnTargetPerCoinDay.SetCompact(nBits); + + CBigNum bnValueIn(nValueIn); + + // Get maximum possible target to filter out the majority of obviously insufficient hashes + uint256 nMaxTarget = (bnTargetPerCoinDay * bnValueIn * nStakeMaxAge / COIN / nOneDay).getuint256(); + + SHA256_CTX ctx, workerCtx; + // Init new sha256 context and update it + // with first 24 bytes of kernel + SHA256_Init(&ctx); + SHA256_Update(&ctx, kernel, 8 + 16); + workerCtx = ctx; // save context + + // Search backward in time from the given timestamp + // Stopping search in case of shutting down + for (uint32_t nTimeTx=SearchInterval.first; nTimeTx>SearchInterval.second && !fShutdown; nTimeTx--) + { + // Complete first hashing iteration + uint256 hash1; + SHA256_Update(&ctx, (unsigned char*)&nTimeTx, 4); + SHA256_Final((unsigned char*)&hash1, &ctx); + + // Restore context + ctx = workerCtx; + + // Finally, calculate kernel hash + uint256 hashProofOfStake; + SHA256((unsigned char*)&hash1, sizeof(hashProofOfStake), (unsigned char*)&hashProofOfStake); + + // Skip if hash doesn't satisfy the maximum target + if (hashProofOfStake > nMaxTarget) + continue; + + CBigNum bnCoinDayWeight = bnValueIn * GetWeight((int64_t)nInputTxTime, (int64_t)nTimeTx) / COIN / nOneDay; + CBigNum bnTargetProofOfStake = bnCoinDayWeight * bnTargetPerCoinDay; + + if (bnTargetProofOfStake >= CBigNum(hashProofOfStake)) + { + solution.first = hashProofOfStake; + solution.second = nTimeTx; + + return true; + } + } + + return false; +} diff --git a/src/kernel_worker.h b/src/kernel_worker.h new file mode 100644 index 00000000..7bea4231 --- /dev/null +++ b/src/kernel_worker.h @@ -0,0 +1,46 @@ +#ifndef BITCOIN_HERNELWORKER_H +#define BITCOIN_HERNELWORKER_H + +#include + +using namespace std; + +class KernelWorker +{ +public: + KernelWorker() + { } + KernelWorker(unsigned char *kernel, uint32_t nBits, uint32_t nInputTxTime, int64_t nValueIn, uint32_t nIntervalBegin, uint32_t nIntervalEnd); + void Do(); + vector >& GetSolutions(); + +private: +#ifdef USE_ASM +#ifdef __x86_64__ + // AVX2 CPUs: 8-way hashing. + void Do_8way(); +#endif + // SSE2, Neon: 4-way hashing. + void Do_4way(); +#endif + // One way hashing. + void Do_generic(); + + // Kernel solutions. + vector > solutions; + + // Kernel metadaya + uint8_t *kernel; + uint32_t nBits; + uint32_t nInputTxTime; + CBigNum bnValueIn; + + // Interval boundaries. + uint32_t nIntervalBegin; + uint32_t nIntervalEnd; +}; + +// Scan given kernel for solutions +bool ScanKernelBackward(unsigned char *kernel, uint32_t nBits, uint32_t nInputTxTime, int64_t nValueIn, pair &SearchInterval, pair &solution); + +#endif diff --git a/src/kernelrecord.cpp b/src/kernelrecord.cpp new file mode 100644 index 00000000..bf05268e --- /dev/null +++ b/src/kernelrecord.cpp @@ -0,0 +1,143 @@ +#include "kernelrecord.h" + +#include "wallet.h" +#include "base58.h" + +#ifdef _MSC_VER +#pragma warning( disable : 4345) +#endif + +using namespace std; + +bool KernelRecord::showTransaction(const CWalletTx &wtx) +{ + if (wtx.IsCoinBase()) + { + if (wtx.GetDepthInMainChain() < 2) + { + return false; + } + } + + if(!wtx.IsTrusted()) + { + return false; + } + + return true; +} + +/* + * Decompose CWallet transaction to model kernel records. + */ +vector KernelRecord::decomposeOutput(const CWallet *wallet, const CWalletTx &wtx) +{ + vector parts; + int64_t nTime = wtx.GetTxTime(); + uint256 hash = wtx.GetHash(); + std::map mapValue = wtx.mapValue; + int64_t nDayWeight = (min((GetAdjustedTime() - nTime), (int64_t)(nStakeMaxAge+nStakeMinAge)) - nStakeMinAge); // DayWeight * 86400, чтобы был + // правильный расчёт CoinAge + if (showTransaction(wtx)) + { + for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++) + { + CTxOut txOut = wtx.vout[nOut]; + if( wallet->IsMine(txOut) ) { + CTxDestination address; + std::string addrStr; + + uint64_t coinAge = max( (txOut.nValue * nDayWeight) / (COIN * 86400), (int64_t)0); + + if (ExtractDestination(txOut.scriptPubKey, address)) + { + // Sent to Bitcoin Address + addrStr = CBitcoinAddress(address).ToString(); + } + else + { + // Sent to IP, or other non-address transaction like OP_EVAL + addrStr = mapValue["to"]; + } + + parts.push_back(KernelRecord(hash, nTime, addrStr, txOut.nValue, wtx.IsSpent(nOut), coinAge)); + } + } + } + + return parts; +} + +std::string KernelRecord::getTxID() +{ + return hash.ToString() + strprintf("-%03d", idx); +} + +int64_t KernelRecord::getAge() const +{ + return (GetAdjustedTime() - nTime) / 86400; +} + +uint64_t KernelRecord::getCoinDay() const +{ + int64_t nWeight = GetAdjustedTime() - nTime - nStakeMinAge; + if( nWeight < 0) + return 0; + nWeight = min(nWeight, (int64_t)nStakeMaxAge); + uint64_t coinAge = (nValue * nWeight ) / (COIN * 86400); + return coinAge; +} + +int64_t KernelRecord::getPoSReward(int nBits, int minutes) +{ + int64_t PoSReward; + int64_t nWeight = GetAdjustedTime() - nTime + minutes * 60; + if( nWeight < nStakeMinAge) + return 0; + uint64_t coinAge = (nValue * nWeight ) / (COIN * 86400); + PoSReward = GetProofOfStakeReward(coinAge, nBits, GetAdjustedTime() + minutes * 60); + return PoSReward; +} + +double KernelRecord::getProbToMintStake(double difficulty, int timeOffset) const +{ + //double maxTarget = pow(static_cast(2), 224); + //double target = maxTarget / difficulty; + //int dayWeight = (min((GetAdjustedTime() - nTime) + timeOffset, (int64_t)(nStakeMinAge+nStakeMaxAge)) - nStakeMinAge) / 86400; + //uint64_t coinAge = max(nValue * dayWeight / COIN, (int64_t)0); + //return target * coinAge / pow(static_cast(2), 256); + int64_t Weight = (min((GetAdjustedTime() - nTime) + timeOffset, (int64_t)(nStakeMinAge+nStakeMaxAge)) - nStakeMinAge); + uint64_t coinAge = max(nValue * Weight / (COIN * 86400), (int64_t)0); + return coinAge / (pow(static_cast(2),32) * difficulty); +} + +double KernelRecord::getProbToMintWithinNMinutes(double difficulty, int minutes) +{ + if(difficulty != prevDifficulty || minutes != prevMinutes) + { + double prob = 1; + double p; + int d = minutes / (60 * 24); // Number of full days + int m = minutes % (60 * 24); // Number of minutes in the last day + int i, timeOffset; + + // Probabilities for the first d days + for(i = 0; i < d; i++) + { + timeOffset = i * 86400; + p = pow(1 - getProbToMintStake(difficulty, timeOffset), 86400); + prob *= p; + } + + // Probability for the m minutes of the last day + timeOffset = d * 86400; + p = pow(1 - getProbToMintStake(difficulty, timeOffset), 60 * m); + prob *= p; + + prob = 1 - prob; + prevProbability = prob; + prevDifficulty = difficulty; + prevMinutes = minutes; + } + return prevProbability; +} diff --git a/src/kernelrecord.h b/src/kernelrecord.h new file mode 100644 index 00000000..8f328164 --- /dev/null +++ b/src/kernelrecord.h @@ -0,0 +1,54 @@ +#ifndef KERNELRECORD_H +#define KERNELRECORD_H + +#include "uint256.h" + +class CWallet; +class CWalletTx; + +class KernelRecord +{ +public: + KernelRecord(): + hash(), nTime(0), address(""), nValue(0), idx(0), spent(false), coinAge(0), prevMinutes(0), prevDifficulty(0), prevProbability(0) + { + } + + KernelRecord(uint256 hash, int64_t nTime): + hash(hash), nTime(nTime), address(""), nValue(0), idx(0), spent(false), coinAge(0), prevMinutes(0), prevDifficulty(0), prevProbability(0) + { + } + + KernelRecord(uint256 hash, int64_t nTime, + const std::string &address, + int64_t nValue, bool spent, int64_t coinAge): + hash(hash), nTime(nTime), address(address), nValue(nValue), + idx(0), spent(spent), coinAge(coinAge), prevMinutes(0), prevDifficulty(0), prevProbability(0) + { + } + + static bool showTransaction(const CWalletTx &wtx); + static std::vector decomposeOutput(const CWallet *wallet, const CWalletTx &wtx); + + + uint256 hash; + int64_t nTime; + std::string address; + int64_t nValue; + int idx; + bool spent; + int64_t coinAge; + + std::string getTxID(); + int64_t getAge() const; + uint64_t getCoinDay() const; + double getProbToMintStake(double difficulty, int timeOffset = 0) const; + double getProbToMintWithinNMinutes(double difficulty, int minutes); + int64_t getPoSReward(int nBits, int timeOffset); +protected: + int prevMinutes; + double prevDifficulty; + double prevProbability; +}; + +#endif // KERNELRECORD_H diff --git a/src/key.cpp b/src/key.cpp new file mode 100644 index 00000000..e95fb176 --- /dev/null +++ b/src/key.cpp @@ -0,0 +1,534 @@ +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include + +#include "key.h" + +// Generate a private key from just the secret parameter +int EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key) +{ + int ok = 0; + BN_CTX *ctx = NULL; + EC_POINT *pub_key = NULL; + + if (!eckey) return 0; + + const EC_GROUP *group = EC_KEY_get0_group(eckey); + + if ((ctx = BN_CTX_new()) == NULL) + goto err; + + pub_key = EC_POINT_new(group); + + if (pub_key == NULL) + goto err; + + if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, ctx)) + goto err; + + EC_KEY_set_private_key(eckey,priv_key); + EC_KEY_set_public_key(eckey,pub_key); + + ok = 1; + +err: + + if (pub_key) + EC_POINT_free(pub_key); + if (ctx != NULL) + BN_CTX_free(ctx); + + return(ok); +} + +// Perform ECDSA key recovery (see SEC1 4.1.6) for curves over (mod p)-fields +// recid selects which key is recovered +// if check is non-zero, additional checks are performed +int ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned char *msg, int msglen, int recid, int check) +{ + if (!eckey) return 0; + + int ret = 0; + BN_CTX *ctx = NULL; + + BIGNUM *x = NULL; + BIGNUM *e = NULL; + BIGNUM *order = NULL; + BIGNUM *sor = NULL; + BIGNUM *eor = NULL; + BIGNUM *field = NULL; + EC_POINT *R = NULL; + EC_POINT *O = NULL; + EC_POINT *Q = NULL; + BIGNUM *rr = NULL; + BIGNUM *zero = NULL; + int n = 0; + int i = recid / 2; + + const EC_GROUP *group = EC_KEY_get0_group(eckey); + if ((ctx = BN_CTX_new()) == NULL) { ret = -1; goto err; } + BN_CTX_start(ctx); + order = BN_CTX_get(ctx); + if (!EC_GROUP_get_order(group, order, ctx)) { ret = -2; goto err; } + x = BN_CTX_get(ctx); + if (!BN_copy(x, order)) { ret=-1; goto err; } + if (!BN_mul_word(x, i)) { ret=-1; goto err; } + if (!BN_add(x, x, ecsig->r)) { ret=-1; goto err; } + field = BN_CTX_get(ctx); + if (!EC_GROUP_get_curve_GFp(group, field, NULL, NULL, ctx)) { ret=-2; goto err; } + if (BN_cmp(x, field) >= 0) { ret=0; goto err; } + if ((R = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } + if (!EC_POINT_set_compressed_coordinates_GFp(group, R, x, recid % 2, ctx)) { ret=0; goto err; } + if (check) + { + if ((O = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } + if (!EC_POINT_mul(group, O, NULL, R, order, ctx)) { ret=-2; goto err; } + if (!EC_POINT_is_at_infinity(group, O)) { ret = 0; goto err; } + } + if ((Q = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } + n = EC_GROUP_get_degree(group); + e = BN_CTX_get(ctx); + if (!BN_bin2bn(msg, msglen, e)) { ret=-1; goto err; } + if (8*msglen > n) BN_rshift(e, e, 8-(n & 7)); + zero = BN_CTX_get(ctx); + if (!BN_zero(zero)) { ret=-1; goto err; } + if (!BN_mod_sub(e, zero, e, order, ctx)) { ret=-1; goto err; } + rr = BN_CTX_get(ctx); + if (!BN_mod_inverse(rr, ecsig->r, order, ctx)) { ret=-1; goto err; } + sor = BN_CTX_get(ctx); + if (!BN_mod_mul(sor, ecsig->s, rr, order, ctx)) { ret=-1; goto err; } + eor = BN_CTX_get(ctx); + if (!BN_mod_mul(eor, e, rr, order, ctx)) { ret=-1; goto err; } + if (!EC_POINT_mul(group, Q, eor, R, sor, ctx)) { ret=-2; goto err; } + if (!EC_KEY_set_public_key(eckey, Q)) { ret=-2; goto err; } + + ret = 1; + +err: + if (ctx) { + BN_CTX_end(ctx); + BN_CTX_free(ctx); + } + if (R != NULL) EC_POINT_free(R); + if (O != NULL) EC_POINT_free(O); + if (Q != NULL) EC_POINT_free(Q); + return ret; +} + +int CompareBigEndian(const unsigned char *c1, size_t c1len, const unsigned char *c2, size_t c2len) { + while (c1len > c2len) { + if (*c1) + return 1; + c1++; + c1len--; + } + while (c2len > c1len) { + if (*c2) + return -1; + c2++; + c2len--; + } + while (c1len > 0) { + if (*c1 > *c2) + return 1; + if (*c2 > *c1) + return -1; + c1++; + c2++; + c1len--; + } + return 0; +} + +// Order of secp256k1's generator minus 1. +const unsigned char vchMaxModOrder[32] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE, + 0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B, + 0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x40 +}; + +// Half of the order of secp256k1's generator minus 1. +const unsigned char vchMaxModHalfOrder[32] = { + 0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0x5D,0x57,0x6E,0x73,0x57,0xA4,0x50,0x1D, + 0xDF,0xE9,0x2F,0x46,0x68,0x1B,0x20,0xA0 +}; + +const unsigned char *vchZero = NULL; + + + +void CKey::SetCompressedPubKey() +{ + EC_KEY_set_conv_form(pkey, POINT_CONVERSION_COMPRESSED); + fCompressedPubKey = true; +} + +void CKey::Reset() +{ + fCompressedPubKey = false; + if (pkey != NULL) + EC_KEY_free(pkey); + 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::CKey() +{ + pkey = NULL; + Reset(); +} + +CKey::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; + fCompressedPubKey = b.fCompressedPubKey; +} + +CKey& 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; + fCompressedPubKey = b.fCompressedPubKey; + return (*this); +} + +CKey::~CKey() +{ + EC_KEY_free(pkey); +} + +bool CKey::IsNull() const +{ + return !fSet; +} + +bool CKey::IsCompressed() const +{ + return fCompressedPubKey; +} + +bool CKey::CheckSignatureElement(const unsigned char *vch, int len, bool half) { + return CompareBigEndian(vch, len, vchZero, 0) > 0 && + CompareBigEndian(vch, len, half ? vchMaxModHalfOrder : vchMaxModOrder, 32) <= 0; +} + +bool CKey::ReserealizeSignature(std::vector& vchSig) +{ + if (vchSig.empty()) + return false; + + unsigned char *pos = &vchSig[0]; + ECDSA_SIG *sig = d2i_ECDSA_SIG(NULL, (const unsigned char **)&pos, vchSig.size()); + if (sig == NULL) + return false; + + bool ret = false; + int nSize = i2d_ECDSA_SIG(sig, NULL); + if (nSize > 0) { + vchSig.resize(nSize); // grow or shrink as needed + + pos = &vchSig[0]; + i2d_ECDSA_SIG(sig, &pos); + + ret = true; + } + + ECDSA_SIG_free(sig); + + return ret; +} + +void CKey::MakeNewKey(bool fCompressed) +{ + if (!EC_KEY_generate_key(pkey)) + throw key_error("CKey::MakeNewKey() : EC_KEY_generate_key failed"); + if (fCompressed) + SetCompressedPubKey(); + fSet = true; +} + +bool CKey::SetPrivKey(const CPrivKey& vchPrivKey) +{ + const unsigned char* pbegin = &vchPrivKey[0]; + if (d2i_ECPrivateKey(&pkey, &pbegin, vchPrivKey.size())) + { + // In testing, d2i_ECPrivateKey can return true + // but fill in pkey with a key that fails + // EC_KEY_check_key, so: + if (EC_KEY_check_key(pkey)) + { + fSet = true; + return true; + } + } + // If vchPrivKey data is bad d2i_ECPrivateKey() can + // leave pkey in a state where calling EC_KEY_free() + // crashes. To avoid that, set pkey to NULL and + // leak the memory (a leak is better than a crash) + pkey = NULL; + Reset(); + return false; +} + +bool CKey::SetSecret(const CSecret& vchSecret, bool fCompressed) +{ + EC_KEY_free(pkey); + pkey = EC_KEY_new_by_curve_name(NID_secp256k1); + if (pkey == NULL) + throw key_error("CKey::SetSecret() : EC_KEY_new_by_curve_name failed"); + if (vchSecret.size() != 32) + throw key_error("CKey::SetSecret() : secret must be 32 bytes"); + BIGNUM *bn = BN_bin2bn(&vchSecret[0],32,BN_new()); + if (bn == NULL) + throw key_error("CKey::SetSecret() : BN_bin2bn failed"); + if (!EC_KEY_regenerate_key(pkey,bn)) + { + BN_clear_free(bn); + throw key_error("CKey::SetSecret() : EC_KEY_regenerate_key failed"); + } + BN_clear_free(bn); + fSet = true; + if (fCompressed || fCompressedPubKey) + SetCompressedPubKey(); + return true; +} + +CSecret CKey::GetSecret(bool &fCompressed) const +{ + CSecret vchRet; + vchRet.resize(32); + const BIGNUM *bn = EC_KEY_get0_private_key(pkey); + int nBytes = BN_num_bytes(bn); + if (bn == NULL) + throw key_error("CKey::GetSecret() : EC_KEY_get0_private_key failed"); + int n=BN_bn2bin(bn,&vchRet[32 - nBytes]); + if (n != nBytes) + throw key_error("CKey::GetSecret(): BN_bn2bin failed"); + fCompressed = fCompressedPubKey; + return vchRet; +} + +CPrivKey CKey::GetPrivKey() const +{ + 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 CKey::SetPubKey(const CPubKey& vchPubKey) +{ + const unsigned char* pbegin = &vchPubKey.vchPubKey[0]; + if (o2i_ECPublicKey(&pkey, &pbegin, vchPubKey.vchPubKey.size())) + { + fSet = true; + if (vchPubKey.vchPubKey.size() == 33) + SetCompressedPubKey(); + return true; + } + pkey = NULL; + Reset(); + return false; +} + +CPubKey CKey::GetPubKey() const +{ + int nSize = i2o_ECPublicKey(pkey, NULL); + if (!nSize) + throw key_error("CKey::GetPubKey() : i2o_ECPublicKey failed"); + std::vector vchPubKey(nSize, 0); + unsigned char* pbegin = &vchPubKey[0]; + if (i2o_ECPublicKey(pkey, &pbegin) != nSize) + throw key_error("CKey::GetPubKey() : i2o_ECPublicKey returned unexpected size"); + return CPubKey(vchPubKey); +} + +bool CKey::Sign(uint256 hash, std::vector& vchSig) +{ + vchSig.clear(); + ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey); + if (sig==NULL) + return false; + const EC_GROUP *group = EC_KEY_get0_group(pkey); + CBigNum order, halforder; + EC_GROUP_get_order(group, &order, NULL); + BN_rshift1(&halforder, &order); + // enforce low S values, by negating the value (modulo the order) if above order/2. + if (BN_cmp(sig->s, &halforder) > 0) { + BN_sub(sig->s, &order, sig->s); + } + unsigned int nSize = ECDSA_size(pkey); + vchSig.resize(nSize); // Make sure it is big enough + unsigned char *pos = &vchSig[0]; + nSize = i2d_ECDSA_SIG(sig, &pos); + ECDSA_SIG_free(sig); + vchSig.resize(nSize); // Shrink to fit actual size + // Testing our new signature + if (ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], vchSig.size(), pkey) != 1) { + vchSig.clear(); + return false; + } + return true; +} + +// create a compact signature (65 bytes), which allows reconstructing the used public key +// The format is one header byte, followed by two times 32 bytes for the serialized r and s values. +// The header byte: 0x1B = first key with even y, 0x1C = first key with odd y, +// 0x1D = second key with even y, 0x1E = second key with odd y +bool CKey::SignCompact(uint256 hash, std::vector& vchSig) +{ + bool fOk = false; + ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey); + if (sig==NULL) + return false; + const EC_GROUP *group = EC_KEY_get0_group(pkey); + CBigNum order, halforder; + EC_GROUP_get_order(group, &order, NULL); + BN_rshift1(&halforder, &order); + // enforce low S values, by negating the value (modulo the order) if above order/2. + if (BN_cmp(sig->s, &halforder) > 0) { + BN_sub(sig->s, &order, sig->s); + } + vchSig.clear(); + vchSig.resize(65,0); + int nBitsR = BN_num_bits(sig->r); + int nBitsS = BN_num_bits(sig->s); + if (nBitsR <= 256 && nBitsS <= 256) + { + int8_t nRecId = -1; + for (int8_t i=0; i<4; i++) + { + CKey keyRec; + keyRec.fSet = true; + if (fCompressedPubKey) + keyRec.SetCompressedPubKey(); + if (ECDSA_SIG_recover_key_GFp(keyRec.pkey, sig, (unsigned char*)&hash, sizeof(hash), i, 1) == 1) + if (keyRec.GetPubKey() == this->GetPubKey()) + { + nRecId = i; + break; + } + } + + if (nRecId == -1) + { + ECDSA_SIG_free(sig); + throw key_error("CKey::SignCompact() : unable to construct recoverable key"); + } + + vchSig[0] = nRecId+27+(fCompressedPubKey ? 4 : 0); + BN_bn2bin(sig->r,&vchSig[33-(nBitsR+7)/8]); + BN_bn2bin(sig->s,&vchSig[65-(nBitsS+7)/8]); + fOk = true; + } + ECDSA_SIG_free(sig); + return fOk; +} + +// reconstruct public key from a compact signature +// This is only slightly more CPU intensive than just verifying it. +// If this function succeeds, the recovered public key is guaranteed to be valid +// (the signature is a valid signature of the given data for that key) +bool CKey::SetCompactSignature(uint256 hash, const std::vector& vchSig) +{ + if (vchSig.size() != 65) + return false; + int nV = vchSig[0]; + if (nV<27 || nV>=35) + return false; + ECDSA_SIG *sig = ECDSA_SIG_new(); + BN_bin2bn(&vchSig[1],32,sig->r); + BN_bin2bn(&vchSig[33],32,sig->s); + + EC_KEY_free(pkey); + pkey = EC_KEY_new_by_curve_name(NID_secp256k1); + if (nV >= 31) + { + SetCompressedPubKey(); + nV -= 4; + } + if (ECDSA_SIG_recover_key_GFp(pkey, sig, (unsigned char*)&hash, sizeof(hash), nV - 27, 0) == 1) + { + fSet = true; + ECDSA_SIG_free(sig); + return true; + } + ECDSA_SIG_free(sig); + return false; +} + +bool CKey::Verify(uint256 hash, const std::vector& vchSig) +{ + if (vchSig.empty()) + return false; + + // New versions of OpenSSL will reject non-canonical DER signatures. de/re-serialize first. + unsigned char *norm_der = NULL; + ECDSA_SIG *norm_sig = ECDSA_SIG_new(); + const unsigned char* sigptr = &vchSig[0]; + assert(norm_sig); + if (d2i_ECDSA_SIG(&norm_sig, &sigptr, vchSig.size()) == NULL) + { + /* As of OpenSSL 1.0.0p d2i_ECDSA_SIG frees and nulls the pointer on + * error. But OpenSSL's own use of this function redundantly frees the + * result. As ECDSA_SIG_free(NULL) is a no-op, and in the absence of a + * clear contract for the function behaving the same way is more + * conservative. + */ + ECDSA_SIG_free(norm_sig); + return false; + } + int derlen = i2d_ECDSA_SIG(norm_sig, &norm_der); + ECDSA_SIG_free(norm_sig); + if (derlen <= 0) + return false; + + // -1 = error, 0 = bad sig, 1 = good + bool ret = ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), norm_der, derlen, pkey) == 1; + OPENSSL_free(norm_der); + return ret; +} + +bool CKey::VerifyCompact(uint256 hash, const std::vector& vchSig) +{ + CKey key; + if (!key.SetCompactSignature(hash, vchSig)) + return false; + if (GetPubKey() != key.GetPubKey()) + return false; + + return true; +} + +bool CKey::IsValid() +{ + if (!fSet) + return false; + + if (!EC_KEY_check_key(pkey)) + return false; + + bool fCompr; + CSecret secret = GetSecret(fCompr); + CKey key2; + key2.SetSecret(secret, fCompr); + return GetPubKey() == key2.GetPubKey(); +} diff --git a/src/key.h b/src/key.h new file mode 100644 index 00000000..e329cf04 --- /dev/null +++ b/src/key.h @@ -0,0 +1,169 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_KEY_H +#define BITCOIN_KEY_H + +#include +#include + +#include "allocators.h" +#include "serialize.h" +#include "uint256.h" +#include "hash.h" +#include "bignum.h" + +#include // for EC_KEY definition + +// 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) {} +}; + +/** A reference to a CKey: the Hash160 of its serialized public key */ +class CKeyID : public uint160 +{ +public: + CKeyID() : uint160(0) { } + CKeyID(const uint160 &in) : uint160(in) { } +}; + +/** A reference to a CScript: the Hash160 of its serialization (see script.h) */ +class CScriptID : public uint160 +{ +public: + CScriptID() : uint160(0) { } + CScriptID(const uint160 &in) : uint160(in) { } +}; + +/** An encapsulated public key. */ +class CPubKey { +private: + std::vector vchPubKey; + friend class CKey; + +public: + CPubKey() { } + CPubKey(const std::vector &vchPubKeyIn) : vchPubKey(vchPubKeyIn) { } + friend bool operator==(const CPubKey &a, const CPubKey &b) { return a.vchPubKey == b.vchPubKey; } + friend bool operator!=(const CPubKey &a, const CPubKey &b) { return a.vchPubKey != b.vchPubKey; } + friend bool operator<(const CPubKey &a, const CPubKey &b) { return a.vchPubKey < b.vchPubKey; } + + IMPLEMENT_SERIALIZE( + READWRITE(vchPubKey); + ) + + CKeyID GetID() const { + return CKeyID(Hash160(vchPubKey)); + } + + uint256 GetHash() const { + return Hash(vchPubKey.begin(), vchPubKey.end()); + } + + bool IsValid() const { + return vchPubKey.size() == 33 || vchPubKey.size() == 65; + } + + bool IsCompressed() const { + return vchPubKey.size() == 33; + } + + std::vector Raw() const { + return vchPubKey; + } +}; + + +// secure_allocator is defined in allocators.h +// CPrivKey is a serialized private key, with all parameters included (279 bytes) +typedef std::vector > CPrivKey; +// CSecret is a serialization of just the secret parameter (32 bytes) +typedef std::vector > CSecret; + +/** An encapsulated OpenSSL Elliptic Curve key (public and/or private) */ +class CKey +{ +protected: + EC_KEY* pkey; + bool fSet; + bool fCompressedPubKey; + + void SetCompressedPubKey(); + +public: + + void Reset(); + + CKey(); + CKey(const CKey& b); + + CKey& operator=(const CKey& b); + + ~CKey(); + + bool IsNull() const; + bool IsCompressed() const; + + void MakeNewKey(bool fCompressed); + bool SetPrivKey(const CPrivKey& vchPrivKey); + bool SetSecret(const CSecret& vchSecret, bool fCompressed = false); + CSecret GetSecret(bool &fCompressed) const; + CPrivKey GetPrivKey() const; + bool SetPubKey(const CPubKey& vchPubKey); + CPubKey GetPubKey() const; + + bool Sign(uint256 hash, std::vector& vchSig); + + // create a compact signature (65 bytes), which allows reconstructing the used public key + // The format is one header byte, followed by two times 32 bytes for the serialized r and s values. + // The header byte: 0x1B = first key with even y, 0x1C = first key with odd y, + // 0x1D = second key with even y, 0x1E = second key with odd y + bool SignCompact(uint256 hash, std::vector& vchSig); + + // reconstruct public key from a compact signature + // This is only slightly more CPU intensive than just verifying it. + // If this function succeeds, the recovered public key is guaranteed to be valid + // (the signature is a valid signature of the given data for that key) + bool SetCompactSignature(uint256 hash, const std::vector& vchSig); + + bool Verify(uint256 hash, const std::vector& vchSig); + + // Verify a compact signature + bool VerifyCompact(uint256 hash, const std::vector& vchSig); + + bool IsValid(); + + // Check whether an element of a signature (r or s) is valid. + static bool CheckSignatureElement(const unsigned char *vch, int len, bool half); + + // Reserialize to DER + static bool ReserealizeSignature(std::vector& vchSig); +}; + +#endif diff --git a/src/keystore.cpp b/src/keystore.cpp new file mode 100644 index 00000000..ef617484 --- /dev/null +++ b/src/keystore.cpp @@ -0,0 +1,301 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "keystore.h" +#include "script.h" +#include "base58.h" + +extern bool fWalletUnlockMintOnly; + +bool CKeyStore::GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const +{ + CKey key; + if (!GetKey(address, key)) + return false; + vchPubKeyOut = key.GetPubKey(); + return true; +} + +bool CBasicKeyStore::AddKey(const CKey& key) +{ + bool fCompressed = false; + CSecret secret = key.GetSecret(fCompressed); + { + LOCK(cs_KeyStore); + mapKeys[key.GetPubKey().GetID()] = make_pair(secret, fCompressed); + } + return true; +} + +bool CBasicKeyStore::AddCScript(const CScript& redeemScript) +{ + if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE) + return error("CBasicKeyStore::AddCScript() : redeemScripts > %i bytes are invalid", MAX_SCRIPT_ELEMENT_SIZE); + + { + LOCK(cs_KeyStore); + mapScripts[redeemScript.GetID()] = redeemScript; + } + return true; +} + +bool CBasicKeyStore::HaveCScript(const CScriptID& hash) const +{ + bool result; + { + LOCK(cs_KeyStore); + result = (mapScripts.count(hash) > 0); + } + return result; +} + + +bool CBasicKeyStore::GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const +{ + { + LOCK(cs_KeyStore); + ScriptMap::const_iterator mi = mapScripts.find(hash); + if (mi != mapScripts.end()) + { + redeemScriptOut = (*mi).second; + return true; + } + } + return false; +} + +bool CBasicKeyStore::AddWatchOnly(const CScript &dest) +{ + LOCK(cs_KeyStore); + + CTxDestination address; + if (ExtractDestination(dest, address)) { + CKeyID keyID; + CBitcoinAddress(address).GetKeyID(keyID); + if (HaveKey(keyID)) + return false; + } + + setWatchOnly.insert(dest); + return true; +} + + +bool CBasicKeyStore::RemoveWatchOnly(const CScript &dest) +{ + LOCK(cs_KeyStore); + setWatchOnly.erase(dest); + return true; +} + +bool CBasicKeyStore::HaveWatchOnly(const CScript &dest) const +{ + LOCK(cs_KeyStore); + return setWatchOnly.count(dest) > 0; +} + +bool CBasicKeyStore::HaveWatchOnly() const +{ + LOCK(cs_KeyStore); + return (!setWatchOnly.empty()); +} + +bool CCryptoKeyStore::SetCrypted() +{ + { + LOCK(cs_KeyStore); + if (fUseCrypto) + return true; + if (!mapKeys.empty()) + return false; + fUseCrypto = true; + } + return true; +} + +bool CCryptoKeyStore::Lock() +{ + if (!SetCrypted()) + return false; + + { + LOCK(cs_KeyStore); + vMasterKey.clear(); + fWalletUnlockMintOnly = false; + } + + NotifyStatusChanged(this); + return true; +} + +bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) +{ + { + LOCK(cs_KeyStore); + if (!SetCrypted()) + return false; + + CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin(); + for (; mi != mapCryptedKeys.end(); ++mi) + { + const CPubKey &vchPubKey = (*mi).second.first; + const std::vector &vchCryptedSecret = (*mi).second.second; + CSecret vchSecret; + if(!DecryptSecret(vMasterKeyIn, vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) + return false; + if (vchSecret.size() != 32) + return false; + CKey key; + key.SetPubKey(vchPubKey); + key.SetSecret(vchSecret); + if (key.GetPubKey() == vchPubKey) + break; + return false; + } + vMasterKey = vMasterKeyIn; + } + NotifyStatusChanged(this); + return true; +} + +bool CCryptoKeyStore::AddKey(const CKey& key) +{ + { + LOCK(cs_KeyStore); + + CScript script; + script.SetDestination(key.GetPubKey().GetID()); + + if (HaveWatchOnly(script)) + return false; + + if (!IsCrypted()) + return CBasicKeyStore::AddKey(key); + + if (IsLocked()) + return false; + + std::vector vchCryptedSecret; + CPubKey vchPubKey = key.GetPubKey(); + bool fCompressed; + if (!EncryptSecret(vMasterKey, key.GetSecret(fCompressed), vchPubKey.GetHash(), vchCryptedSecret)) + return false; + + if (!AddCryptedKey(key.GetPubKey(), vchCryptedSecret)) + return false; + } + return true; +} + + +bool CCryptoKeyStore::AddCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret) +{ + { + LOCK(cs_KeyStore); + if (!SetCrypted()) + return false; + + mapCryptedKeys[vchPubKey.GetID()] = make_pair(vchPubKey, vchCryptedSecret); + } + return true; +} + +bool CCryptoKeyStore::GetKey(const CKeyID &address, CKey& keyOut) const +{ + { + LOCK(cs_KeyStore); + if (!IsCrypted()) + return CBasicKeyStore::GetKey(address, keyOut); + + CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); + if (mi != mapCryptedKeys.end()) + { + const CPubKey &vchPubKey = (*mi).second.first; + const std::vector &vchCryptedSecret = (*mi).second.second; + CSecret vchSecret; + if (!DecryptSecret(vMasterKey, vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) + return false; + if (vchSecret.size() != 32) + return false; + keyOut.SetPubKey(vchPubKey); + keyOut.SetSecret(vchSecret); + return true; + } + } + return false; +} + +bool CCryptoKeyStore::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const +{ + { + LOCK(cs_KeyStore); + if (!IsCrypted()) + return CKeyStore::GetPubKey(address, vchPubKeyOut); + + CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); + if (mi != mapCryptedKeys.end()) + { + vchPubKeyOut = (*mi).second.first; + return true; + } + } + return false; +} + +bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn) +{ + { + LOCK(cs_KeyStore); + if (!mapCryptedKeys.empty() || IsCrypted()) + return false; + + fUseCrypto = true; + BOOST_FOREACH(KeyMap::value_type& mKey, mapKeys) + { + CKey key; + if (!key.SetSecret(mKey.second.first, mKey.second.second)) + return false; + const CPubKey vchPubKey = key.GetPubKey(); + std::vector vchCryptedSecret; + bool fCompressed; + if (!EncryptSecret(vMasterKeyIn, key.GetSecret(fCompressed), vchPubKey.GetHash(), vchCryptedSecret)) + return false; + if (!AddCryptedKey(vchPubKey, vchCryptedSecret)) + return false; + } + mapKeys.clear(); + } + return true; +} + +bool CCryptoKeyStore::DecryptKeys(const CKeyingMaterial& vMasterKeyIn) +{ + { + LOCK(cs_KeyStore); + if (!IsCrypted()) + return false; + + CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin(); + for (; mi != mapCryptedKeys.end(); ++mi) + { + const CPubKey &vchPubKey = (*mi).second.first; + const std::vector &vchCryptedSecret = (*mi).second.second; + CSecret vchSecret; + if(!DecryptSecret(vMasterKeyIn, vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) + return false; + if (vchSecret.size() != 32) + return false; + CKey key; + key.SetPubKey(vchPubKey); + key.SetSecret(vchSecret); + if (!CBasicKeyStore::AddKey(key)) + return false; + } + + mapCryptedKeys.clear(); + } + + return true; +} diff --git a/src/keystore.h b/src/keystore.h new file mode 100644 index 00000000..96285fb8 --- /dev/null +++ b/src/keystore.h @@ -0,0 +1,213 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_KEYSTORE_H +#define BITCOIN_KEYSTORE_H + +#include "crypter.h" +#include "sync.h" +#include +#include + +class CScript; + +class CNoDestination { +public: + friend bool operator==(const CNoDestination &a, const CNoDestination &b) { return true; } + friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; } +}; + +/** A txout script template with a specific destination. It is either: + * CNoDestination: no destination set + * CKeyID: TX_PUBKEYHASH destination + * CScriptID: TX_SCRIPTHASH destination + * + * A CTxDestination is the internal data type encoded in a CBitcoinAddress. + */ +typedef boost::variant CTxDestination; + +/** A virtual base class for key stores */ +class CKeyStore +{ +protected: + mutable CCriticalSection cs_KeyStore; + +public: + virtual ~CKeyStore() {} + + // Add a key to the store. + virtual bool AddKey(const CKey& key) =0; + + // Check whether a key corresponding to a given address is present in the store. + virtual bool HaveKey(const CKeyID &address) const =0; + virtual bool GetKey(const CKeyID &address, CKey& keyOut) const =0; + virtual void GetKeys(std::set &setAddress) const =0; + virtual bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const; + + // Support for BIP 0013 : see https://en.bitcoin.it/wiki/BIP_0013 + virtual bool AddCScript(const CScript& redeemScript) =0; + virtual bool HaveCScript(const CScriptID &hash) const =0; + virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const =0; + + // Support for Watch-only addresses + virtual bool AddWatchOnly(const CScript &dest) =0; + virtual bool RemoveWatchOnly(const CScript &dest) =0; + virtual bool HaveWatchOnly(const CScript &dest) const =0; + virtual bool HaveWatchOnly() const =0; + + virtual bool GetSecret(const CKeyID &address, CSecret& vchSecret, bool &fCompressed) const + { + CKey key; + if (!GetKey(address, key)) + return false; + vchSecret = key.GetSecret(fCompressed); + return true; + } +}; + +typedef std::map > KeyMap; +typedef std::map ScriptMap; +typedef std::set WatchOnlySet; + +/** Basic key store, that keeps keys in an address->secret map */ +class CBasicKeyStore : public CKeyStore +{ +protected: + KeyMap mapKeys; + ScriptMap mapScripts; + WatchOnlySet setWatchOnly; + +public: + bool AddKey(const CKey& key); + bool HaveKey(const CKeyID &address) const + { + bool result; + { + LOCK(cs_KeyStore); + result = (mapKeys.count(address) > 0); + } + return result; + } + void GetKeys(std::set &setAddress) const + { + setAddress.clear(); + { + LOCK(cs_KeyStore); + KeyMap::const_iterator mi = mapKeys.begin(); + while (mi != mapKeys.end()) + { + setAddress.insert((*mi).first); + mi++; + } + } + } + bool GetKey(const CKeyID &address, CKey &keyOut) const + { + { + LOCK(cs_KeyStore); + KeyMap::const_iterator mi = mapKeys.find(address); + if (mi != mapKeys.end()) + { + keyOut.Reset(); + keyOut.SetSecret((*mi).second.first, (*mi).second.second); + return true; + } + } + return false; + } + virtual bool AddCScript(const CScript& redeemScript); + virtual bool HaveCScript(const CScriptID &hash) const; + virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const; + + virtual bool AddWatchOnly(const CScript &dest); + virtual bool RemoveWatchOnly(const CScript &dest); + virtual bool HaveWatchOnly(const CScript &dest) const; + virtual bool HaveWatchOnly() const; +}; + +typedef std::map > > CryptedKeyMap; + +/** Keystore which keeps the private keys encrypted. + * It derives from the basic key store, which is used if no encryption is active. + */ +class CCryptoKeyStore : public CBasicKeyStore +{ +private: + CryptedKeyMap mapCryptedKeys; + + CKeyingMaterial vMasterKey; + + // if fUseCrypto is true, mapKeys must be empty + // if fUseCrypto is false, vMasterKey must be empty + bool fUseCrypto; + +protected: + bool SetCrypted(); + + // will encrypt previously unencrypted keys + bool EncryptKeys(CKeyingMaterial& vMasterKeyIn); + bool DecryptKeys(const CKeyingMaterial& vMasterKeyIn); + + bool Unlock(const CKeyingMaterial& vMasterKeyIn); + +public: + CCryptoKeyStore() : fUseCrypto(false) + { + } + + bool IsCrypted() const + { + return fUseCrypto; + } + + bool IsLocked() const + { + if (!IsCrypted()) + return false; + bool result; + { + LOCK(cs_KeyStore); + result = vMasterKey.empty(); + } + return result; + } + + bool Lock(); + + virtual bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret); + bool AddKey(const CKey& key); + bool HaveKey(const CKeyID &address) const + { + { + LOCK(cs_KeyStore); + if (!IsCrypted()) + return CBasicKeyStore::HaveKey(address); + return mapCryptedKeys.count(address) > 0; + } + } + bool GetKey(const CKeyID &address, CKey& keyOut) const; + bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const; + void GetKeys(std::set &setAddress) const + { + if (!IsCrypted()) + { + CBasicKeyStore::GetKeys(setAddress); + return; + } + setAddress.clear(); + CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin(); + while (mi != mapCryptedKeys.end()) + { + setAddress.insert((*mi).first); + mi++; + } + } + + /* Wallet status (encrypted, locked) changed. + * Note: Called without locks held. + */ + boost::signals2::signal NotifyStatusChanged; +}; + +#endif diff --git a/src/leveldb/.gitignore b/src/leveldb/.gitignore new file mode 100644 index 00000000..71d87a4e --- /dev/null +++ b/src/leveldb/.gitignore @@ -0,0 +1,13 @@ +build_config.mk +*.a +*.o +*.dylib* +*.so +*.so.* +*_test +db_bench +leveldbutil +Release +Debug +Benchmark +vs2010.* diff --git a/src/leveldb/AUTHORS b/src/leveldb/AUTHORS new file mode 100644 index 00000000..2439d7a4 --- /dev/null +++ b/src/leveldb/AUTHORS @@ -0,0 +1,12 @@ +# Names should be added to this file like so: +# Name or Organization + +Google Inc. + +# Initial version authors: +Jeffrey Dean +Sanjay Ghemawat + +# Partial list of contributors: +Kevin Regan +Johan Bilien diff --git a/src/leveldb/CONTRIBUTING.md b/src/leveldb/CONTRIBUTING.md new file mode 100644 index 00000000..cd600ff4 --- /dev/null +++ b/src/leveldb/CONTRIBUTING.md @@ -0,0 +1,36 @@ +# Contributing + +We'd love to accept your code patches! However, before we can take them, we +have to jump a couple of legal hurdles. + +## Contributor License Agreements + +Please fill out either the individual or corporate Contributor License +Agreement as appropriate. + +* If you are an individual writing original source code and you're sure you +own the intellectual property, then sign an [individual CLA](https://developers.google.com/open-source/cla/individual). +* If you work for a company that wants to allow you to contribute your work, +then sign a [corporate CLA](https://developers.google.com/open-source/cla/corporate). + +Follow either of the two links above to access the appropriate CLA and +instructions for how to sign and return it. + +## Submitting a Patch + +1. Sign the contributors license agreement above. +2. Decide which code you want to submit. A submission should be a set of changes +that addresses one issue in the [issue tracker](https://github.com/google/leveldb/issues). +Please don't mix more than one logical change per submission, because it makes +the history hard to follow. If you want to make a change +(e.g. add a sample or feature) that doesn't have a corresponding issue in the +issue tracker, please create one. +3. **Submitting**: When you are ready to submit, send us a Pull Request. Be +sure to include the issue number you fixed and the name you used to sign +the CLA. + +## Writing Code ## + +If your contribution contains code, please make sure that it follows +[the style guide](http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml). +Otherwise we will have to ask you to make changes, and that's no fun for anyone. diff --git a/src/leveldb/LICENSE b/src/leveldb/LICENSE new file mode 100644 index 00000000..8e80208c --- /dev/null +++ b/src/leveldb/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2011 The LevelDB Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/leveldb/Makefile b/src/leveldb/Makefile new file mode 100644 index 00000000..20b725e3 --- /dev/null +++ b/src/leveldb/Makefile @@ -0,0 +1,227 @@ +# Copyright (c) 2011 The LevelDB Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. See the AUTHORS file for names of contributors. + +#----------------------------------------------- +# Uncomment exactly one of the lines labelled (A), (B), and (C) below +# to switch between compilation modes. + +# (A) Production use (optimized mode) +OPT ?= -O2 -DNDEBUG +# (B) Debug mode, w/ full line-level debugging symbols +# OPT ?= -g2 +# (C) Profiling mode: opt, but w/debugging symbols +# OPT ?= -O2 -g2 -DNDEBUG +#----------------------------------------------- + +# detect what platform we're building on +$(shell CC="$(CC)" CXX="$(CXX)" TARGET_OS="$(TARGET_OS)" \ + sh ./build_detect_platform build_config.mk ./) +# this file is generated by the previous line to set build flags and sources +include build_config.mk + +CFLAGS += -I. -I./include $(PLATFORM_CCFLAGS) $(OPT) +CXXFLAGS += -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT) + +LDFLAGS += $(PLATFORM_LDFLAGS) +LIBS += $(PLATFORM_LIBS) + +LIBOBJECTS = $(SOURCES:.cc=.o) +MEMENVOBJECTS = $(MEMENV_SOURCES:.cc=.o) + +TESTUTIL = ./util/testutil.o +TESTHARNESS = ./util/testharness.o $(TESTUTIL) + +# Note: iOS should probably be using libtool, not ar. +ifeq ($(PLATFORM), IOS) +AR=xcrun ar +endif + +TESTS = \ + arena_test \ + autocompact_test \ + bloom_test \ + c_test \ + cache_test \ + coding_test \ + corruption_test \ + crc32c_test \ + db_test \ + dbformat_test \ + env_test \ + filename_test \ + filter_block_test \ + hash_test \ + issue178_test \ + issue200_test \ + log_test \ + memenv_test \ + skiplist_test \ + table_test \ + version_edit_test \ + version_set_test \ + write_batch_test + +PROGRAMS = db_bench leveldbutil $(TESTS) +BENCHMARKS = db_bench_sqlite3 db_bench_tree_db + +LIBRARY = libleveldb.a +MEMENVLIBRARY = libmemenv.a + +default: all + +# Should we build shared libraries? +ifneq ($(PLATFORM_SHARED_EXT),) + +ifneq ($(PLATFORM_SHARED_VERSIONED),true) +SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT) +SHARED2 = $(SHARED1) +SHARED3 = $(SHARED1) +SHARED = $(SHARED1) +else +# Update db.h if you change these. +SHARED_MAJOR = 1 +SHARED_MINOR = 18 +SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT) +SHARED2 = $(SHARED1).$(SHARED_MAJOR) +SHARED3 = $(SHARED1).$(SHARED_MAJOR).$(SHARED_MINOR) +SHARED = $(SHARED1) $(SHARED2) $(SHARED3) +$(SHARED1): $(SHARED3) + ln -fs $(SHARED3) $(SHARED1) +$(SHARED2): $(SHARED3) + ln -fs $(SHARED3) $(SHARED2) +endif + +$(SHARED3): + $(CXX) $(LDFLAGS) $(PLATFORM_SHARED_LDFLAGS)$(SHARED2) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SOURCES) -o $(SHARED3) $(LIBS) + +endif # PLATFORM_SHARED_EXT + +all: $(SHARED) $(LIBRARY) + +check: all $(PROGRAMS) $(TESTS) + for t in $(TESTS); do echo "***** Running $$t"; ./$$t || exit 1; done + +clean: + -rm -f $(PROGRAMS) $(BENCHMARKS) $(LIBRARY) $(SHARED) $(MEMENVLIBRARY) */*.o */*/*.o ios-x86/*/*.o ios-arm/*/*.o build_config.mk + -rm -rf ios-x86/* ios-arm/* + +$(LIBRARY): $(LIBOBJECTS) + rm -f $@ + $(AR) -rs $@ $(LIBOBJECTS) + +db_bench: db/db_bench.o $(LIBOBJECTS) $(TESTUTIL) + $(CXX) $(LDFLAGS) db/db_bench.o $(LIBOBJECTS) $(TESTUTIL) -o $@ $(LIBS) + +db_bench_sqlite3: doc/bench/db_bench_sqlite3.o $(LIBOBJECTS) $(TESTUTIL) + $(CXX) $(LDFLAGS) doc/bench/db_bench_sqlite3.o $(LIBOBJECTS) $(TESTUTIL) -o $@ -lsqlite3 $(LIBS) + +db_bench_tree_db: doc/bench/db_bench_tree_db.o $(LIBOBJECTS) $(TESTUTIL) + $(CXX) $(LDFLAGS) doc/bench/db_bench_tree_db.o $(LIBOBJECTS) $(TESTUTIL) -o $@ -lkyotocabinet $(LIBS) + +leveldbutil: db/leveldb_main.o $(LIBOBJECTS) + $(CXX) $(LDFLAGS) db/leveldb_main.o $(LIBOBJECTS) -o $@ $(LIBS) + +arena_test: util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +autocompact_test: db/autocompact_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/autocompact_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +bloom_test: util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +c_test: db/c_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +cache_test: util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +coding_test: util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +corruption_test: db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +crc32c_test: util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +db_test: db/db_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/db_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +dbformat_test: db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +env_test: util/env_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) util/env_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +filename_test: db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +filter_block_test: table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +hash_test: util/hash_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) util/hash_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +issue178_test: issues/issue178_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) issues/issue178_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +issue200_test: issues/issue200_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) issues/issue200_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +log_test: db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +table_test: table/table_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) table/table_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +skiplist_test: db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +version_edit_test: db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +version_set_test: db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +write_batch_test: db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +$(MEMENVLIBRARY) : $(MEMENVOBJECTS) + rm -f $@ + $(AR) -rs $@ $(MEMENVOBJECTS) + +memenv_test : helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS) + $(CXX) $(LDFLAGS) helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS) -o $@ $(LIBS) + +ifeq ($(PLATFORM), IOS) +# For iOS, create universal object files to be used on both the simulator and +# a device. +PLATFORMSROOT=/Applications/Xcode.app/Contents/Developer/Platforms +SIMULATORROOT=$(PLATFORMSROOT)/iPhoneSimulator.platform/Developer +DEVICEROOT=$(PLATFORMSROOT)/iPhoneOS.platform/Developer +IOSVERSION=$(shell defaults read $(PLATFORMSROOT)/iPhoneOS.platform/version CFBundleShortVersionString) +IOSARCH=-arch armv6 -arch armv7 -arch armv7s -arch arm64 + +.cc.o: + mkdir -p ios-x86/$(dir $@) + xcrun -sdk iphonesimulator $(CXX) $(CXXFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -arch x86_64 -c $< -o ios-x86/$@ + mkdir -p ios-arm/$(dir $@) + xcrun -sdk iphoneos $(CXX) $(CXXFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk $(IOSARCH) -c $< -o ios-arm/$@ + xcrun lipo ios-x86/$@ ios-arm/$@ -create -output $@ + +.c.o: + mkdir -p ios-x86/$(dir $@) + xcrun -sdk iphonesimulator $(CC) $(CFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -arch x86_64 -c $< -o ios-x86/$@ + mkdir -p ios-arm/$(dir $@) + xcrun -sdk iphoneos $(CC) $(CFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk $(IOSARCH) -c $< -o ios-arm/$@ + xcrun lipo ios-x86/$@ ios-arm/$@ -create -output $@ + +else +.cc.o: + $(CXX) $(CXXFLAGS) -c $< -o $@ + +.c.o: + $(CC) $(CFLAGS) -c $< -o $@ +endif diff --git a/src/leveldb/NEWS b/src/leveldb/NEWS new file mode 100644 index 00000000..3fd99242 --- /dev/null +++ b/src/leveldb/NEWS @@ -0,0 +1,17 @@ +Release 1.2 2011-05-16 +---------------------- + +Fixes for larger databases (tested up to one billion 100-byte entries, +i.e., ~100GB). + +(1) Place hard limit on number of level-0 files. This fixes errors +of the form "too many open files". + +(2) Fixed memtable management. Before the fix, a heavy write burst +could cause unbounded memory usage. + +A fix for a logging bug where the reader would incorrectly complain +about corruption. + +Allow public access to WriteBatch contents so that users can easily +wrap a DB. diff --git a/src/leveldb/README b/src/leveldb/README new file mode 100644 index 00000000..3618adee --- /dev/null +++ b/src/leveldb/README @@ -0,0 +1,51 @@ +leveldb: A key-value store +Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com) + +The code under this directory implements a system for maintaining a +persistent key/value store. + +See doc/index.html for more explanation. +See doc/impl.html for a brief overview of the implementation. + +The public interface is in include/*.h. Callers should not include or +rely on the details of any other header files in this package. Those +internal APIs may be changed without warning. + +Guide to header files: + +include/db.h + Main interface to the DB: Start here + +include/options.h + Control over the behavior of an entire database, and also + control over the behavior of individual reads and writes. + +include/comparator.h + Abstraction for user-specified comparison function. If you want + just bytewise comparison of keys, you can use the default comparator, + but clients can write their own comparator implementations if they + want custom ordering (e.g. to handle different character + encodings, etc.) + +include/iterator.h + Interface for iterating over data. You can get an iterator + from a DB object. + +include/write_batch.h + Interface for atomically applying multiple updates to a database. + +include/slice.h + A simple module for maintaining a pointer and a length into some + other byte array. + +include/status.h + Status is returned from many of the public interfaces and is used + to report success and various kinds of errors. + +include/env.h + Abstraction of the OS environment. A posix implementation of + this interface is in util/env_posix.cc + +include/table.h +include/table_builder.h + Lower-level modules that most clients probably won't use directly diff --git a/src/leveldb/README.md b/src/leveldb/README.md new file mode 100644 index 00000000..480affb5 --- /dev/null +++ b/src/leveldb/README.md @@ -0,0 +1,138 @@ +**LevelDB is a fast key-value storage library written at Google that provides an ordered mapping from string keys to string values.** + +Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com) + +# Features + * Keys and values are arbitrary byte arrays. + * Data is stored sorted by key. + * Callers can provide a custom comparison function to override the sort order. + * The basic operations are `Put(key,value)`, `Get(key)`, `Delete(key)`. + * Multiple changes can be made in one atomic batch. + * Users can create a transient snapshot to get a consistent view of data. + * Forward and backward iteration is supported over the data. + * Data is automatically compressed using the [Snappy compression library](http://code.google.com/p/snappy). + * External activity (file system operations etc.) is relayed through a virtual interface so users can customize the operating system interactions. + * [Detailed documentation](http://htmlpreview.github.io/?https://github.com/google/leveldb/blob/master/doc/index.html) about how to use the library is included with the source code. + + +# Limitations + * This is not a SQL database. It does not have a relational data model, it does not support SQL queries, and it has no support for indexes. + * Only a single process (possibly multi-threaded) can access a particular database at a time. + * There is no client-server support builtin to the library. An application that needs such support will have to wrap their own server around the library. + +# Performance + +Here is a performance report (with explanations) from the run of the +included db_bench program. The results are somewhat noisy, but should +be enough to get a ballpark performance estimate. + +## Setup + +We use a database with a million entries. Each entry has a 16 byte +key, and a 100 byte value. Values used by the benchmark compress to +about half their original size. + + LevelDB: version 1.1 + Date: Sun May 1 12:11:26 2011 + CPU: 4 x Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz + CPUCache: 4096 KB + Keys: 16 bytes each + Values: 100 bytes each (50 bytes after compression) + Entries: 1000000 + Raw Size: 110.6 MB (estimated) + File Size: 62.9 MB (estimated) + +## Write performance + +The "fill" benchmarks create a brand new database, in either +sequential, or random order. The "fillsync" benchmark flushes data +from the operating system to the disk after every operation; the other +write operations leave the data sitting in the operating system buffer +cache for a while. The "overwrite" benchmark does random writes that +update existing keys in the database. + + fillseq : 1.765 micros/op; 62.7 MB/s + fillsync : 268.409 micros/op; 0.4 MB/s (10000 ops) + fillrandom : 2.460 micros/op; 45.0 MB/s + overwrite : 2.380 micros/op; 46.5 MB/s + +Each "op" above corresponds to a write of a single key/value pair. +I.e., a random write benchmark goes at approximately 400,000 writes per second. + +Each "fillsync" operation costs much less (0.3 millisecond) +than a disk seek (typically 10 milliseconds). We suspect that this is +because the hard disk itself is buffering the update in its memory and +responding before the data has been written to the platter. This may +or may not be safe based on whether or not the hard disk has enough +power to save its memory in the event of a power failure. + +## Read performance + +We list the performance of reading sequentially in both the forward +and reverse direction, and also the performance of a random lookup. +Note that the database created by the benchmark is quite small. +Therefore the report characterizes the performance of leveldb when the +working set fits in memory. The cost of reading a piece of data that +is not present in the operating system buffer cache will be dominated +by the one or two disk seeks needed to fetch the data from disk. +Write performance will be mostly unaffected by whether or not the +working set fits in memory. + + readrandom : 16.677 micros/op; (approximately 60,000 reads per second) + readseq : 0.476 micros/op; 232.3 MB/s + readreverse : 0.724 micros/op; 152.9 MB/s + +LevelDB compacts its underlying storage data in the background to +improve read performance. The results listed above were done +immediately after a lot of random writes. The results after +compactions (which are usually triggered automatically) are better. + + readrandom : 11.602 micros/op; (approximately 85,000 reads per second) + readseq : 0.423 micros/op; 261.8 MB/s + readreverse : 0.663 micros/op; 166.9 MB/s + +Some of the high cost of reads comes from repeated decompression of blocks +read from disk. If we supply enough cache to the leveldb so it can hold the +uncompressed blocks in memory, the read performance improves again: + + readrandom : 9.775 micros/op; (approximately 100,000 reads per second before compaction) + readrandom : 5.215 micros/op; (approximately 190,000 reads per second after compaction) + +## Repository contents + +See doc/index.html for more explanation. See doc/impl.html for a brief overview of the implementation. + +The public interface is in include/*.h. Callers should not include or +rely on the details of any other header files in this package. Those +internal APIs may be changed without warning. + +Guide to header files: + +* **include/db.h**: Main interface to the DB: Start here + +* **include/options.h**: Control over the behavior of an entire database, +and also control over the behavior of individual reads and writes. + +* **include/comparator.h**: Abstraction for user-specified comparison function. +If you want just bytewise comparison of keys, you can use the default +comparator, but clients can write their own comparator implementations if they +want custom ordering (e.g. to handle different character encodings, etc.) + +* **include/iterator.h**: Interface for iterating over data. You can get +an iterator from a DB object. + +* **include/write_batch.h**: Interface for atomically applying multiple +updates to a database. + +* **include/slice.h**: A simple module for maintaining a pointer and a +length into some other byte array. + +* **include/status.h**: Status is returned from many of the public interfaces +and is used to report success and various kinds of errors. + +* **include/env.h**: +Abstraction of the OS environment. A posix implementation of this interface is +in util/env_posix.cc + +* **include/table.h, include/table_builder.h**: Lower-level modules that most +clients probably won't use directly diff --git a/src/leveldb/TODO b/src/leveldb/TODO new file mode 100644 index 00000000..e603c071 --- /dev/null +++ b/src/leveldb/TODO @@ -0,0 +1,14 @@ +ss +- Stats + +db +- Maybe implement DB::BulkDeleteForRange(start_key, end_key) + that would blow away files whose ranges are entirely contained + within [start_key..end_key]? For Chrome, deletion of obsolete + object stores, etc. can be done in the background anyway, so + probably not that important. +- There have been requests for MultiGet. + +After a range is completely deleted, what gets rid of the +corresponding files if we do no future changes to that range. Make +the conditions for triggering compactions fire in more situations? diff --git a/src/leveldb/WINDOWS.md b/src/leveldb/WINDOWS.md new file mode 100644 index 00000000..5b76c244 --- /dev/null +++ b/src/leveldb/WINDOWS.md @@ -0,0 +1,39 @@ +# Building LevelDB On Windows + +## Prereqs + +Install the [Windows Software Development Kit version 7.1](http://www.microsoft.com/downloads/dlx/en-us/listdetailsview.aspx?FamilyID=6b6c21d2-2006-4afa-9702-529fa782d63b). + +Download and extract the [Snappy source distribution](http://snappy.googlecode.com/files/snappy-1.0.5.tar.gz) + +1. Open the "Windows SDK 7.1 Command Prompt" : + Start Menu -> "Microsoft Windows SDK v7.1" > "Windows SDK 7.1 Command Prompt" +2. Change the directory to the leveldb project + +## Building the Static lib + +* 32 bit Version + + setenv /x86 + msbuild.exe /p:Configuration=Release /p:Platform=Win32 /p:Snappy=..\snappy-1.0.5 + +* 64 bit Version + + setenv /x64 + msbuild.exe /p:Configuration=Release /p:Platform=x64 /p:Snappy=..\snappy-1.0.5 + + +## Building and Running the Benchmark app + +* 32 bit Version + + setenv /x86 + msbuild.exe /p:Configuration=Benchmark /p:Platform=Win32 /p:Snappy=..\snappy-1.0.5 + Benchmark\leveldb.exe + +* 64 bit Version + + setenv /x64 + msbuild.exe /p:Configuration=Benchmark /p:Platform=x64 /p:Snappy=..\snappy-1.0.5 + x64\Benchmark\leveldb.exe + diff --git a/src/leveldb/build_detect_platform b/src/leveldb/build_detect_platform new file mode 100644 index 00000000..336bf1d4 --- /dev/null +++ b/src/leveldb/build_detect_platform @@ -0,0 +1,219 @@ +#!/bin/sh +# +# Detects OS we're compiling on and outputs a file specified by the first +# argument, which in turn gets read while processing Makefile. +# +# The output will set the following variables: +# CC C Compiler path +# CXX C++ Compiler path +# PLATFORM_LDFLAGS Linker flags +# PLATFORM_LIBS Libraries flags +# PLATFORM_SHARED_EXT Extension for shared libraries +# PLATFORM_SHARED_LDFLAGS Flags for building shared library +# This flag is embedded just before the name +# of the shared library without intervening spaces +# PLATFORM_SHARED_CFLAGS Flags for compiling objects for shared library +# PLATFORM_CCFLAGS C compiler flags +# PLATFORM_CXXFLAGS C++ compiler flags. Will contain: +# PLATFORM_SHARED_VERSIONED Set to 'true' if platform supports versioned +# shared libraries, empty otherwise. +# +# The PLATFORM_CCFLAGS and PLATFORM_CXXFLAGS might include the following: +# +# -DLEVELDB_ATOMIC_PRESENT if is present +# -DLEVELDB_PLATFORM_POSIX for Posix-based platforms +# -DSNAPPY if the Snappy library is present +# + +OUTPUT=$1 +PREFIX=$2 +if test -z "$OUTPUT" || test -z "$PREFIX"; then + echo "usage: $0 " >&2 + exit 1 +fi + +# Delete existing output, if it exists +rm -f $OUTPUT +touch $OUTPUT + +if test -z "$CC"; then + CC=cc +fi + +if test -z "$CXX"; then + CXX=g++ +fi + +if test -z "$TMPDIR"; then + TMPDIR=/tmp +fi + +# Detect OS +if test -z "$TARGET_OS"; then + TARGET_OS=`uname -s` +fi + +COMMON_FLAGS= +CROSS_COMPILE= +PLATFORM_CCFLAGS= +PLATFORM_CXXFLAGS= +PLATFORM_LDFLAGS= +PLATFORM_LIBS= +PLATFORM_SHARED_EXT="so" +PLATFORM_SHARED_LDFLAGS="-shared -Wl,-soname -Wl," +PLATFORM_SHARED_CFLAGS="-fPIC" +PLATFORM_SHARED_VERSIONED=true + +MEMCMP_FLAG= +if [ "$CXX" = "g++" ]; then + # Use libc's memcmp instead of GCC's memcmp. This results in ~40% + # performance improvement on readrandom under gcc 4.4.3 on Linux/x86. + MEMCMP_FLAG="-fno-builtin-memcmp" +fi + +case "$TARGET_OS" in + CYGWIN_*) + PLATFORM=OS_LINUX + COMMON_FLAGS="$MEMCMP_FLAG -lpthread -DOS_LINUX -DCYGWIN" + PLATFORM_LDFLAGS="-lpthread" + PORT_FILE=port/port_posix.cc + ;; + Darwin) + PLATFORM=OS_MACOSX + COMMON_FLAGS="$MEMCMP_FLAG -DOS_MACOSX" + PLATFORM_SHARED_EXT=dylib + [ -z "$INSTALL_PATH" ] && INSTALL_PATH=`pwd` + PLATFORM_SHARED_LDFLAGS="-dynamiclib -install_name $INSTALL_PATH/" + PORT_FILE=port/port_posix.cc + ;; + Linux) + PLATFORM=OS_LINUX + COMMON_FLAGS="$MEMCMP_FLAG -pthread -DOS_LINUX" + PLATFORM_LDFLAGS="-pthread" + PORT_FILE=port/port_posix.cc + ;; + SunOS) + PLATFORM=OS_SOLARIS + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_SOLARIS" + PLATFORM_LIBS="-lpthread -lrt" + PORT_FILE=port/port_posix.cc + ;; + FreeBSD) + PLATFORM=OS_FREEBSD + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_FREEBSD" + PLATFORM_LIBS="-lpthread" + PORT_FILE=port/port_posix.cc + ;; + GNU/kFreeBSD) + PLATFORM=OS_KFREEBSD + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_KFREEBSD" + PLATFORM_LIBS="-lpthread" + PORT_FILE=port/port_posix.cc + ;; + NetBSD) + PLATFORM=OS_NETBSD + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_NETBSD" + PLATFORM_LIBS="-lpthread -lgcc_s" + PORT_FILE=port/port_posix.cc + ;; + OpenBSD) + PLATFORM=OS_OPENBSD + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_OPENBSD" + PLATFORM_LDFLAGS="-pthread" + PORT_FILE=port/port_posix.cc + ;; + DragonFly) + PLATFORM=OS_DRAGONFLYBSD + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_DRAGONFLYBSD" + PLATFORM_LIBS="-lpthread" + PORT_FILE=port/port_posix.cc + ;; + OS_ANDROID_CROSSCOMPILE) + PLATFORM=OS_ANDROID + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_ANDROID -DLEVELDB_PLATFORM_POSIX" + PLATFORM_LDFLAGS="" # All pthread features are in the Android C library + PORT_FILE=port/port_posix.cc + CROSS_COMPILE=true + ;; + HP-UX) + PLATFORM=OS_HPUX + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_HPUX" + PLATFORM_LDFLAGS="-pthread" + PORT_FILE=port/port_posix.cc + # man ld: +h internal_name + PLATFORM_SHARED_LDFLAGS="-shared -Wl,+h -Wl," + ;; + IOS) + PLATFORM=IOS + COMMON_FLAGS="$MEMCMP_FLAG -DOS_MACOSX" + [ -z "$INSTALL_PATH" ] && INSTALL_PATH=`pwd` + PORT_FILE=port/port_posix.cc + PLATFORM_SHARED_EXT= + PLATFORM_SHARED_LDFLAGS= + PLATFORM_SHARED_CFLAGS= + PLATFORM_SHARED_VERSIONED= + ;; + OS_WINDOWS_CROSSCOMPILE | NATIVE_WINDOWS) + PLATFORM=OS_WINDOWS + COMMON_FLAGS="-fno-builtin-memcmp -D_REENTRANT -DOS_WINDOWS -DLEVELDB_PLATFORM_WINDOWS -DWINVER=0x0500 -D__USE_MINGW_ANSI_STDIO=1" + PLATFORM_SOURCES="util/env_win.cc" + PLATFORM_LIBS="-lshlwapi" + PORT_FILE=port/port_win.cc + CROSS_COMPILE=true + ;; + *) + echo "Unknown platform!" >&2 + exit 1 +esac + +# We want to make a list of all cc files within util, db, table, and helpers +# except for the test and benchmark files. By default, find will output a list +# of all files matching either rule, so we need to append -print to make the +# prune take effect. +DIRS="$PREFIX/db $PREFIX/util $PREFIX/table" + +set -f # temporarily disable globbing so that our patterns aren't expanded +PRUNE_TEST="-name *test*.cc -prune" +PRUNE_BENCH="-name *_bench.cc -prune" +PRUNE_TOOL="-name leveldb_main.cc -prune" +PORTABLE_FILES=`find $DIRS $PRUNE_TEST -o $PRUNE_BENCH -o $PRUNE_TOOL -o -name '*.cc' -print | sort | sed "s,^$PREFIX/,," | tr "\n" " "` + +set +f # re-enable globbing + +# The sources consist of the portable files, plus the platform-specific port +# file. +echo "SOURCES=$PORTABLE_FILES $PORT_FILE" >> $OUTPUT +echo "MEMENV_SOURCES=helpers/memenv/memenv.cc" >> $OUTPUT + +if [ "$CROSS_COMPILE" = "true" ]; then + # Cross-compiling; do not try any compilation tests. + true +else + CXXOUTPUT="${TMPDIR}/leveldb_build_detect_platform-cxx.$$" + COMMON_FLAGS="$COMMON_FLAGS -DLEVELDB_PLATFORM_POSIX" + + # Test whether tcmalloc is available + $CXX $CXXFLAGS -x c++ - -o $CXXOUTPUT -ltcmalloc 2>/dev/null </dev/null +fi + +PLATFORM_CCFLAGS="$PLATFORM_CCFLAGS $COMMON_FLAGS" +PLATFORM_CXXFLAGS="$PLATFORM_CXXFLAGS $COMMON_FLAGS" + +echo "CC=$CC" >> $OUTPUT +echo "CXX=$CXX" >> $OUTPUT +echo "PLATFORM=$PLATFORM" >> $OUTPUT +echo "PLATFORM_LDFLAGS=$PLATFORM_LDFLAGS" >> $OUTPUT +echo "PLATFORM_LIBS=$PLATFORM_LIBS" >> $OUTPUT +echo "PLATFORM_CCFLAGS=$PLATFORM_CCFLAGS" >> $OUTPUT +echo "PLATFORM_CXXFLAGS=$PLATFORM_CXXFLAGS" >> $OUTPUT +echo "PLATFORM_SHARED_CFLAGS=$PLATFORM_SHARED_CFLAGS" >> $OUTPUT +echo "PLATFORM_SHARED_EXT=$PLATFORM_SHARED_EXT" >> $OUTPUT +echo "PLATFORM_SHARED_LDFLAGS=$PLATFORM_SHARED_LDFLAGS" >> $OUTPUT +echo "PLATFORM_SHARED_VERSIONED=$PLATFORM_SHARED_VERSIONED" >> $OUTPUT diff --git a/src/leveldb/db/autocompact_test.cc b/src/leveldb/db/autocompact_test.cc new file mode 100644 index 00000000..d20a2362 --- /dev/null +++ b/src/leveldb/db/autocompact_test.cc @@ -0,0 +1,118 @@ +// Copyright (c) 2013 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/db.h" +#include "db/db_impl.h" +#include "leveldb/cache.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +class AutoCompactTest { + public: + std::string dbname_; + Cache* tiny_cache_; + Options options_; + DB* db_; + + AutoCompactTest() { + dbname_ = test::TmpDir() + "/autocompact_test"; + tiny_cache_ = NewLRUCache(100); + options_.block_cache = tiny_cache_; + DestroyDB(dbname_, options_); + options_.create_if_missing = true; + options_.compression = kNoCompression; + ASSERT_OK(DB::Open(options_, dbname_, &db_)); + } + + ~AutoCompactTest() { + delete db_; + DestroyDB(dbname_, Options()); + delete tiny_cache_; + } + + std::string Key(int i) { + char buf[100]; + snprintf(buf, sizeof(buf), "key%06d", i); + return std::string(buf); + } + + uint64_t Size(const Slice& start, const Slice& limit) { + Range r(start, limit); + uint64_t size; + db_->GetApproximateSizes(&r, 1, &size); + return size; + } + + void DoReads(int n); +}; + +static const int kValueSize = 200 * 1024; +static const int kTotalSize = 100 * 1024 * 1024; +static const int kCount = kTotalSize / kValueSize; + +// Read through the first n keys repeatedly and check that they get +// compacted (verified by checking the size of the key space). +void AutoCompactTest::DoReads(int n) { + std::string value(kValueSize, 'x'); + DBImpl* dbi = reinterpret_cast(db_); + + // Fill database + for (int i = 0; i < kCount; i++) { + ASSERT_OK(db_->Put(WriteOptions(), Key(i), value)); + } + ASSERT_OK(dbi->TEST_CompactMemTable()); + + // Delete everything + for (int i = 0; i < kCount; i++) { + ASSERT_OK(db_->Delete(WriteOptions(), Key(i))); + } + ASSERT_OK(dbi->TEST_CompactMemTable()); + + // Get initial measurement of the space we will be reading. + const int64_t initial_size = Size(Key(0), Key(n)); + const int64_t initial_other_size = Size(Key(n), Key(kCount)); + + // Read until size drops significantly. + std::string limit_key = Key(n); + for (int read = 0; true; read++) { + ASSERT_LT(read, 100) << "Taking too long to compact"; + Iterator* iter = db_->NewIterator(ReadOptions()); + for (iter->SeekToFirst(); + iter->Valid() && iter->key().ToString() < limit_key; + iter->Next()) { + // Drop data + } + delete iter; + // Wait a little bit to allow any triggered compactions to complete. + Env::Default()->SleepForMicroseconds(1000000); + uint64_t size = Size(Key(0), Key(n)); + fprintf(stderr, "iter %3d => %7.3f MB [other %7.3f MB]\n", + read+1, size/1048576.0, Size(Key(n), Key(kCount))/1048576.0); + if (size <= initial_size/10) { + break; + } + } + + // Verify that the size of the key space not touched by the reads + // is pretty much unchanged. + const int64_t final_other_size = Size(Key(n), Key(kCount)); + ASSERT_LE(final_other_size, initial_other_size + 1048576); + ASSERT_GE(final_other_size, initial_other_size/5 - 1048576); +} + +TEST(AutoCompactTest, ReadAll) { + DoReads(kCount); +} + +TEST(AutoCompactTest, ReadHalf) { + DoReads(kCount/2); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/builder.cc b/src/leveldb/db/builder.cc new file mode 100644 index 00000000..f4198821 --- /dev/null +++ b/src/leveldb/db/builder.cc @@ -0,0 +1,88 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/builder.h" + +#include "db/filename.h" +#include "db/dbformat.h" +#include "db/table_cache.h" +#include "db/version_edit.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/iterator.h" + +namespace leveldb { + +Status BuildTable(const std::string& dbname, + Env* env, + const Options& options, + TableCache* table_cache, + Iterator* iter, + FileMetaData* meta) { + Status s; + meta->file_size = 0; + iter->SeekToFirst(); + + std::string fname = TableFileName(dbname, meta->number); + if (iter->Valid()) { + WritableFile* file; + s = env->NewWritableFile(fname, &file); + if (!s.ok()) { + return s; + } + + TableBuilder* builder = new TableBuilder(options, file); + meta->smallest.DecodeFrom(iter->key()); + for (; iter->Valid(); iter->Next()) { + Slice key = iter->key(); + meta->largest.DecodeFrom(key); + builder->Add(key, iter->value()); + } + + // Finish and check for builder errors + if (s.ok()) { + s = builder->Finish(); + if (s.ok()) { + meta->file_size = builder->FileSize(); + assert(meta->file_size > 0); + } + } else { + builder->Abandon(); + } + delete builder; + + // Finish and check for file errors + if (s.ok()) { + s = file->Sync(); + } + if (s.ok()) { + s = file->Close(); + } + delete file; + file = NULL; + + if (s.ok()) { + // Verify that the table is usable + Iterator* it = table_cache->NewIterator(ReadOptions(), + meta->number, + meta->file_size); + s = it->status(); + delete it; + } + } + + // Check for input iterator errors + if (!iter->status().ok()) { + s = iter->status(); + } + + if (s.ok() && meta->file_size > 0) { + // Keep it + } else { + env->DeleteFile(fname); + } + return s; +} + +} // namespace leveldb diff --git a/src/leveldb/db/builder.h b/src/leveldb/db/builder.h new file mode 100644 index 00000000..62431fcf --- /dev/null +++ b/src/leveldb/db/builder.h @@ -0,0 +1,34 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_BUILDER_H_ +#define STORAGE_LEVELDB_DB_BUILDER_H_ + +#include "leveldb/status.h" + +namespace leveldb { + +struct Options; +struct FileMetaData; + +class Env; +class Iterator; +class TableCache; +class VersionEdit; + +// Build a Table file from the contents of *iter. The generated file +// will be named according to meta->number. On success, the rest of +// *meta will be filled with metadata about the generated table. +// If no data is present in *iter, meta->file_size will be set to +// zero, and no Table file will be produced. +extern Status BuildTable(const std::string& dbname, + Env* env, + const Options& options, + TableCache* table_cache, + Iterator* iter, + FileMetaData* meta); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_BUILDER_H_ diff --git a/src/leveldb/db/c.cc b/src/leveldb/db/c.cc new file mode 100644 index 00000000..66f83915 --- /dev/null +++ b/src/leveldb/db/c.cc @@ -0,0 +1,597 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/c.h" + +#include +#ifndef _MSC_VER +#include +#endif +#include "leveldb/cache.h" +#include "leveldb/comparator.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/filter_policy.h" +#include "leveldb/iterator.h" +#include "leveldb/options.h" +#include "leveldb/status.h" +#include "leveldb/write_batch.h" + +using leveldb::Cache; +using leveldb::Comparator; +using leveldb::CompressionType; +using leveldb::DB; +using leveldb::Env; +using leveldb::FileLock; +using leveldb::FilterPolicy; +using leveldb::Iterator; +using leveldb::kMajorVersion; +using leveldb::kMinorVersion; +using leveldb::Logger; +using leveldb::NewBloomFilterPolicy; +using leveldb::NewLRUCache; +using leveldb::Options; +using leveldb::RandomAccessFile; +using leveldb::Range; +using leveldb::ReadOptions; +using leveldb::SequentialFile; +using leveldb::Slice; +using leveldb::Snapshot; +using leveldb::Status; +using leveldb::WritableFile; +using leveldb::WriteBatch; +using leveldb::WriteOptions; + +extern "C" { + +struct leveldb_t { DB* rep; }; +struct leveldb_iterator_t { Iterator* rep; }; +struct leveldb_writebatch_t { WriteBatch rep; }; +struct leveldb_snapshot_t { const Snapshot* rep; }; +struct leveldb_readoptions_t { ReadOptions rep; }; +struct leveldb_writeoptions_t { WriteOptions rep; }; +struct leveldb_options_t { Options rep; }; +struct leveldb_cache_t { Cache* rep; }; +struct leveldb_seqfile_t { SequentialFile* rep; }; +struct leveldb_randomfile_t { RandomAccessFile* rep; }; +struct leveldb_writablefile_t { WritableFile* rep; }; +struct leveldb_logger_t { Logger* rep; }; +struct leveldb_filelock_t { FileLock* rep; }; + +struct leveldb_comparator_t : public Comparator { + void* state_; + void (*destructor_)(void*); + int (*compare_)( + void*, + const char* a, size_t alen, + const char* b, size_t blen); + const char* (*name_)(void*); + + virtual ~leveldb_comparator_t() { + (*destructor_)(state_); + } + + virtual int Compare(const Slice& a, const Slice& b) const { + return (*compare_)(state_, a.data(), a.size(), b.data(), b.size()); + } + + virtual const char* Name() const { + return (*name_)(state_); + } + + // No-ops since the C binding does not support key shortening methods. + virtual void FindShortestSeparator(std::string*, const Slice&) const { } + virtual void FindShortSuccessor(std::string* key) const { } +}; + +struct leveldb_filterpolicy_t : public FilterPolicy { + void* state_; + void (*destructor_)(void*); + const char* (*name_)(void*); + char* (*create_)( + void*, + const char* const* key_array, const size_t* key_length_array, + int num_keys, + size_t* filter_length); + unsigned char (*key_match_)( + void*, + const char* key, size_t length, + const char* filter, size_t filter_length); + + virtual ~leveldb_filterpolicy_t() { + (*destructor_)(state_); + } + + virtual const char* Name() const { + return (*name_)(state_); + } + + virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const { + std::vector key_pointers(n); + std::vector key_sizes(n); + for (int i = 0; i < n; i++) { + key_pointers[i] = keys[i].data(); + key_sizes[i] = keys[i].size(); + } + size_t len; + char* filter = (*create_)(state_, &key_pointers[0], &key_sizes[0], n, &len); + dst->append(filter, len); + free(filter); + } + + virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const { + return (*key_match_)(state_, key.data(), key.size(), + filter.data(), filter.size()); + } +}; + +struct leveldb_env_t { + Env* rep; + bool is_default; +}; + +static bool SaveError(char** errptr, const Status& s) { + assert(errptr != NULL); + if (s.ok()) { + return false; + } else if (*errptr == NULL) { + *errptr = strdup(s.ToString().c_str()); + } else { + // TODO(sanjay): Merge with existing error? + free(*errptr); + *errptr = strdup(s.ToString().c_str()); + } + return true; +} + +static char* CopyString(const std::string& str) { + char* result = reinterpret_cast(malloc(sizeof(char) * str.size())); + memcpy(result, str.data(), sizeof(char) * str.size()); + return result; +} + +leveldb_t* leveldb_open( + const leveldb_options_t* options, + const char* name, + char** errptr) { + DB* db; + if (SaveError(errptr, DB::Open(options->rep, std::string(name), &db))) { + return NULL; + } + leveldb_t* result = new leveldb_t; + result->rep = db; + return result; +} + +void leveldb_close(leveldb_t* db) { + delete db->rep; + delete db; +} + +void leveldb_put( + leveldb_t* db, + const leveldb_writeoptions_t* options, + const char* key, size_t keylen, + const char* val, size_t vallen, + char** errptr) { + SaveError(errptr, + db->rep->Put(options->rep, Slice(key, keylen), Slice(val, vallen))); +} + +void leveldb_delete( + leveldb_t* db, + const leveldb_writeoptions_t* options, + const char* key, size_t keylen, + char** errptr) { + SaveError(errptr, db->rep->Delete(options->rep, Slice(key, keylen))); +} + + +void leveldb_write( + leveldb_t* db, + const leveldb_writeoptions_t* options, + leveldb_writebatch_t* batch, + char** errptr) { + SaveError(errptr, db->rep->Write(options->rep, &batch->rep)); +} + +char* leveldb_get( + leveldb_t* db, + const leveldb_readoptions_t* options, + const char* key, size_t keylen, + size_t* vallen, + char** errptr) { + char* result = NULL; + std::string tmp; + Status s = db->rep->Get(options->rep, Slice(key, keylen), &tmp); + if (s.ok()) { + *vallen = tmp.size(); + result = CopyString(tmp); + } else { + *vallen = 0; + if (!s.IsNotFound()) { + SaveError(errptr, s); + } + } + return result; +} + +leveldb_iterator_t* leveldb_create_iterator( + leveldb_t* db, + const leveldb_readoptions_t* options) { + leveldb_iterator_t* result = new leveldb_iterator_t; + result->rep = db->rep->NewIterator(options->rep); + return result; +} + +const leveldb_snapshot_t* leveldb_create_snapshot( + leveldb_t* db) { + leveldb_snapshot_t* result = new leveldb_snapshot_t; + result->rep = db->rep->GetSnapshot(); + return result; +} + +void leveldb_release_snapshot( + leveldb_t* db, + const leveldb_snapshot_t* snapshot) { + db->rep->ReleaseSnapshot(snapshot->rep); + delete snapshot; +} + +char* leveldb_property_value( + leveldb_t* db, + const char* propname) { + std::string tmp; + if (db->rep->GetProperty(Slice(propname), &tmp)) { + // We use strdup() since we expect human readable output. + return strdup(tmp.c_str()); + } else { + return NULL; + } +} + +void leveldb_approximate_sizes( + leveldb_t* db, + int num_ranges, + const char* const* range_start_key, const size_t* range_start_key_len, + const char* const* range_limit_key, const size_t* range_limit_key_len, + uint64_t* sizes) { + Range* ranges = new Range[num_ranges]; + for (int i = 0; i < num_ranges; i++) { + ranges[i].start = Slice(range_start_key[i], range_start_key_len[i]); + ranges[i].limit = Slice(range_limit_key[i], range_limit_key_len[i]); + } + db->rep->GetApproximateSizes(ranges, num_ranges, sizes); + delete[] ranges; +} + +void leveldb_compact_range( + leveldb_t* db, + const char* start_key, size_t start_key_len, + const char* limit_key, size_t limit_key_len) { + Slice a, b; + db->rep->CompactRange( + // Pass NULL Slice if corresponding "const char*" is NULL + (start_key ? (a = Slice(start_key, start_key_len), &a) : NULL), + (limit_key ? (b = Slice(limit_key, limit_key_len), &b) : NULL)); +} + +void leveldb_destroy_db( + const leveldb_options_t* options, + const char* name, + char** errptr) { + SaveError(errptr, DestroyDB(name, options->rep)); +} + +void leveldb_repair_db( + const leveldb_options_t* options, + const char* name, + char** errptr) { + SaveError(errptr, RepairDB(name, options->rep)); +} + +void leveldb_iter_destroy(leveldb_iterator_t* iter) { + delete iter->rep; + delete iter; +} + +unsigned char leveldb_iter_valid(const leveldb_iterator_t* iter) { + return iter->rep->Valid(); +} + +void leveldb_iter_seek_to_first(leveldb_iterator_t* iter) { + iter->rep->SeekToFirst(); +} + +void leveldb_iter_seek_to_last(leveldb_iterator_t* iter) { + iter->rep->SeekToLast(); +} + +void leveldb_iter_seek(leveldb_iterator_t* iter, const char* k, size_t klen) { + iter->rep->Seek(Slice(k, klen)); +} + +void leveldb_iter_next(leveldb_iterator_t* iter) { + iter->rep->Next(); +} + +void leveldb_iter_prev(leveldb_iterator_t* iter) { + iter->rep->Prev(); +} + +const char* leveldb_iter_key(const leveldb_iterator_t* iter, size_t* klen) { + Slice s = iter->rep->key(); + *klen = s.size(); + return s.data(); +} + +const char* leveldb_iter_value(const leveldb_iterator_t* iter, size_t* vlen) { + Slice s = iter->rep->value(); + *vlen = s.size(); + return s.data(); +} + +void leveldb_iter_get_error(const leveldb_iterator_t* iter, char** errptr) { + SaveError(errptr, iter->rep->status()); +} + +leveldb_writebatch_t* leveldb_writebatch_create() { + return new leveldb_writebatch_t; +} + +void leveldb_writebatch_destroy(leveldb_writebatch_t* b) { + delete b; +} + +void leveldb_writebatch_clear(leveldb_writebatch_t* b) { + b->rep.Clear(); +} + +void leveldb_writebatch_put( + leveldb_writebatch_t* b, + const char* key, size_t klen, + const char* val, size_t vlen) { + b->rep.Put(Slice(key, klen), Slice(val, vlen)); +} + +void leveldb_writebatch_delete( + leveldb_writebatch_t* b, + const char* key, size_t klen) { + b->rep.Delete(Slice(key, klen)); +} + +void leveldb_writebatch_iterate( + leveldb_writebatch_t* b, + void* state, + void (*put)(void*, const char* k, size_t klen, const char* v, size_t vlen), + void (*deleted)(void*, const char* k, size_t klen)) { + class H : public WriteBatch::Handler { + public: + void* state_; + void (*put_)(void*, const char* k, size_t klen, const char* v, size_t vlen); + void (*deleted_)(void*, const char* k, size_t klen); + virtual void Put(const Slice& key, const Slice& value) { + (*put_)(state_, key.data(), key.size(), value.data(), value.size()); + } + virtual void Delete(const Slice& key) { + (*deleted_)(state_, key.data(), key.size()); + } + }; + H handler; + handler.state_ = state; + handler.put_ = put; + handler.deleted_ = deleted; + b->rep.Iterate(&handler); +} + +leveldb_options_t* leveldb_options_create() { + return new leveldb_options_t; +} + +void leveldb_options_destroy(leveldb_options_t* options) { + delete options; +} + +void leveldb_options_set_comparator( + leveldb_options_t* opt, + leveldb_comparator_t* cmp) { + opt->rep.comparator = cmp; +} + +void leveldb_options_set_filter_policy( + leveldb_options_t* opt, + leveldb_filterpolicy_t* policy) { + opt->rep.filter_policy = policy; +} + +void leveldb_options_set_create_if_missing( + leveldb_options_t* opt, unsigned char v) { + opt->rep.create_if_missing = v; +} + +void leveldb_options_set_error_if_exists( + leveldb_options_t* opt, unsigned char v) { + opt->rep.error_if_exists = v; +} + +void leveldb_options_set_paranoid_checks( + leveldb_options_t* opt, unsigned char v) { + opt->rep.paranoid_checks = v; +} + +void leveldb_options_set_env(leveldb_options_t* opt, leveldb_env_t* env) { + opt->rep.env = (env ? env->rep : NULL); +} + +void leveldb_options_set_info_log(leveldb_options_t* opt, leveldb_logger_t* l) { + opt->rep.info_log = (l ? l->rep : NULL); +} + +void leveldb_options_set_write_buffer_size(leveldb_options_t* opt, size_t s) { + opt->rep.write_buffer_size = s; +} + +void leveldb_options_set_max_open_files(leveldb_options_t* opt, int n) { + opt->rep.max_open_files = n; +} + +void leveldb_options_set_cache(leveldb_options_t* opt, leveldb_cache_t* c) { + opt->rep.block_cache = c->rep; +} + +void leveldb_options_set_block_size(leveldb_options_t* opt, size_t s) { + opt->rep.block_size = s; +} + +void leveldb_options_set_block_restart_interval(leveldb_options_t* opt, int n) { + opt->rep.block_restart_interval = n; +} + +void leveldb_options_set_compression(leveldb_options_t* opt, int t) { + opt->rep.compression = static_cast(t); +} + +leveldb_comparator_t* leveldb_comparator_create( + void* state, + void (*destructor)(void*), + int (*compare)( + void*, + const char* a, size_t alen, + const char* b, size_t blen), + const char* (*name)(void*)) { + leveldb_comparator_t* result = new leveldb_comparator_t; + result->state_ = state; + result->destructor_ = destructor; + result->compare_ = compare; + result->name_ = name; + return result; +} + +void leveldb_comparator_destroy(leveldb_comparator_t* cmp) { + delete cmp; +} + +leveldb_filterpolicy_t* leveldb_filterpolicy_create( + void* state, + void (*destructor)(void*), + char* (*create_filter)( + void*, + const char* const* key_array, const size_t* key_length_array, + int num_keys, + size_t* filter_length), + unsigned char (*key_may_match)( + void*, + const char* key, size_t length, + const char* filter, size_t filter_length), + const char* (*name)(void*)) { + leveldb_filterpolicy_t* result = new leveldb_filterpolicy_t; + result->state_ = state; + result->destructor_ = destructor; + result->create_ = create_filter; + result->key_match_ = key_may_match; + result->name_ = name; + return result; +} + +void leveldb_filterpolicy_destroy(leveldb_filterpolicy_t* filter) { + delete filter; +} + +leveldb_filterpolicy_t* leveldb_filterpolicy_create_bloom(int bits_per_key) { + // Make a leveldb_filterpolicy_t, but override all of its methods so + // they delegate to a NewBloomFilterPolicy() instead of user + // supplied C functions. + struct Wrapper : public leveldb_filterpolicy_t { + const FilterPolicy* rep_; + ~Wrapper() { delete rep_; } + const char* Name() const { return rep_->Name(); } + void CreateFilter(const Slice* keys, int n, std::string* dst) const { + return rep_->CreateFilter(keys, n, dst); + } + bool KeyMayMatch(const Slice& key, const Slice& filter) const { + return rep_->KeyMayMatch(key, filter); + } + static void DoNothing(void*) { } + }; + Wrapper* wrapper = new Wrapper; + wrapper->rep_ = NewBloomFilterPolicy(bits_per_key); + wrapper->state_ = NULL; + wrapper->destructor_ = &Wrapper::DoNothing; + return wrapper; +} + +leveldb_readoptions_t* leveldb_readoptions_create() { + return new leveldb_readoptions_t; +} + +void leveldb_readoptions_destroy(leveldb_readoptions_t* opt) { + delete opt; +} + +void leveldb_readoptions_set_verify_checksums( + leveldb_readoptions_t* opt, + unsigned char v) { + opt->rep.verify_checksums = v; +} + +void leveldb_readoptions_set_fill_cache( + leveldb_readoptions_t* opt, unsigned char v) { + opt->rep.fill_cache = v; +} + +void leveldb_readoptions_set_snapshot( + leveldb_readoptions_t* opt, + const leveldb_snapshot_t* snap) { + opt->rep.snapshot = (snap ? snap->rep : NULL); +} + +leveldb_writeoptions_t* leveldb_writeoptions_create() { + return new leveldb_writeoptions_t; +} + +void leveldb_writeoptions_destroy(leveldb_writeoptions_t* opt) { + delete opt; +} + +void leveldb_writeoptions_set_sync( + leveldb_writeoptions_t* opt, unsigned char v) { + opt->rep.sync = v; +} + +leveldb_cache_t* leveldb_cache_create_lru(size_t capacity) { + leveldb_cache_t* c = new leveldb_cache_t; + c->rep = NewLRUCache(capacity); + return c; +} + +void leveldb_cache_destroy(leveldb_cache_t* cache) { + delete cache->rep; + delete cache; +} + +leveldb_env_t* leveldb_create_default_env() { + leveldb_env_t* result = new leveldb_env_t; + result->rep = Env::Default(); + result->is_default = true; + return result; +} + +void leveldb_env_destroy(leveldb_env_t* env) { + if (!env->is_default) delete env->rep; + delete env; +} + +void leveldb_free(void* ptr) { + free(ptr); +} + +int leveldb_major_version() { + return kMajorVersion; +} + +int leveldb_minor_version() { + return kMinorVersion; +} + +} // end extern "C" diff --git a/src/leveldb/db/c_test.c b/src/leveldb/db/c_test.c new file mode 100644 index 00000000..7cd5ee02 --- /dev/null +++ b/src/leveldb/db/c_test.c @@ -0,0 +1,390 @@ +/* Copyright (c) 2011 The LevelDB Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. See the AUTHORS file for names of contributors. */ + +#include "leveldb/c.h" + +#include +#include +#include +#include +#include +#include + +const char* phase = ""; +static char dbname[200]; + +static void StartPhase(const char* name) { + fprintf(stderr, "=== Test %s\n", name); + phase = name; +} + +static const char* GetTempDir(void) { + const char* ret = getenv("TEST_TMPDIR"); + if (ret == NULL || ret[0] == '\0') + ret = "/tmp"; + return ret; +} + +#define CheckNoError(err) \ + if ((err) != NULL) { \ + fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, (err)); \ + abort(); \ + } + +#define CheckCondition(cond) \ + if (!(cond)) { \ + fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, #cond); \ + abort(); \ + } + +static void CheckEqual(const char* expected, const char* v, size_t n) { + if (expected == NULL && v == NULL) { + // ok + } else if (expected != NULL && v != NULL && n == strlen(expected) && + memcmp(expected, v, n) == 0) { + // ok + return; + } else { + fprintf(stderr, "%s: expected '%s', got '%s'\n", + phase, + (expected ? expected : "(null)"), + (v ? v : "(null")); + abort(); + } +} + +static void Free(char** ptr) { + if (*ptr) { + free(*ptr); + *ptr = NULL; + } +} + +static void CheckGet( + leveldb_t* db, + const leveldb_readoptions_t* options, + const char* key, + const char* expected) { + char* err = NULL; + size_t val_len; + char* val; + val = leveldb_get(db, options, key, strlen(key), &val_len, &err); + CheckNoError(err); + CheckEqual(expected, val, val_len); + Free(&val); +} + +static void CheckIter(leveldb_iterator_t* iter, + const char* key, const char* val) { + size_t len; + const char* str; + str = leveldb_iter_key(iter, &len); + CheckEqual(key, str, len); + str = leveldb_iter_value(iter, &len); + CheckEqual(val, str, len); +} + +// Callback from leveldb_writebatch_iterate() +static void CheckPut(void* ptr, + const char* k, size_t klen, + const char* v, size_t vlen) { + int* state = (int*) ptr; + CheckCondition(*state < 2); + switch (*state) { + case 0: + CheckEqual("bar", k, klen); + CheckEqual("b", v, vlen); + break; + case 1: + CheckEqual("box", k, klen); + CheckEqual("c", v, vlen); + break; + } + (*state)++; +} + +// Callback from leveldb_writebatch_iterate() +static void CheckDel(void* ptr, const char* k, size_t klen) { + int* state = (int*) ptr; + CheckCondition(*state == 2); + CheckEqual("bar", k, klen); + (*state)++; +} + +static void CmpDestroy(void* arg) { } + +static int CmpCompare(void* arg, const char* a, size_t alen, + const char* b, size_t blen) { + int n = (alen < blen) ? alen : blen; + int r = memcmp(a, b, n); + if (r == 0) { + if (alen < blen) r = -1; + else if (alen > blen) r = +1; + } + return r; +} + +static const char* CmpName(void* arg) { + return "foo"; +} + +// Custom filter policy +static unsigned char fake_filter_result = 1; +static void FilterDestroy(void* arg) { } +static const char* FilterName(void* arg) { + return "TestFilter"; +} +static char* FilterCreate( + void* arg, + const char* const* key_array, const size_t* key_length_array, + int num_keys, + size_t* filter_length) { + *filter_length = 4; + char* result = malloc(4); + memcpy(result, "fake", 4); + return result; +} +unsigned char FilterKeyMatch( + void* arg, + const char* key, size_t length, + const char* filter, size_t filter_length) { + CheckCondition(filter_length == 4); + CheckCondition(memcmp(filter, "fake", 4) == 0); + return fake_filter_result; +} + +int main(int argc, char** argv) { + leveldb_t* db; + leveldb_comparator_t* cmp; + leveldb_cache_t* cache; + leveldb_env_t* env; + leveldb_options_t* options; + leveldb_readoptions_t* roptions; + leveldb_writeoptions_t* woptions; + char* err = NULL; + int run = -1; + + CheckCondition(leveldb_major_version() >= 1); + CheckCondition(leveldb_minor_version() >= 1); + + snprintf(dbname, sizeof(dbname), + "%s/leveldb_c_test-%d", + GetTempDir(), + ((int) geteuid())); + + StartPhase("create_objects"); + cmp = leveldb_comparator_create(NULL, CmpDestroy, CmpCompare, CmpName); + env = leveldb_create_default_env(); + cache = leveldb_cache_create_lru(100000); + + options = leveldb_options_create(); + leveldb_options_set_comparator(options, cmp); + leveldb_options_set_error_if_exists(options, 1); + leveldb_options_set_cache(options, cache); + leveldb_options_set_env(options, env); + leveldb_options_set_info_log(options, NULL); + leveldb_options_set_write_buffer_size(options, 100000); + leveldb_options_set_paranoid_checks(options, 1); + leveldb_options_set_max_open_files(options, 10); + leveldb_options_set_block_size(options, 1024); + leveldb_options_set_block_restart_interval(options, 8); + leveldb_options_set_compression(options, leveldb_no_compression); + + roptions = leveldb_readoptions_create(); + leveldb_readoptions_set_verify_checksums(roptions, 1); + leveldb_readoptions_set_fill_cache(roptions, 0); + + woptions = leveldb_writeoptions_create(); + leveldb_writeoptions_set_sync(woptions, 1); + + StartPhase("destroy"); + leveldb_destroy_db(options, dbname, &err); + Free(&err); + + StartPhase("open_error"); + db = leveldb_open(options, dbname, &err); + CheckCondition(err != NULL); + Free(&err); + + StartPhase("leveldb_free"); + db = leveldb_open(options, dbname, &err); + CheckCondition(err != NULL); + leveldb_free(err); + err = NULL; + + StartPhase("open"); + leveldb_options_set_create_if_missing(options, 1); + db = leveldb_open(options, dbname, &err); + CheckNoError(err); + CheckGet(db, roptions, "foo", NULL); + + StartPhase("put"); + leveldb_put(db, woptions, "foo", 3, "hello", 5, &err); + CheckNoError(err); + CheckGet(db, roptions, "foo", "hello"); + + StartPhase("compactall"); + leveldb_compact_range(db, NULL, 0, NULL, 0); + CheckGet(db, roptions, "foo", "hello"); + + StartPhase("compactrange"); + leveldb_compact_range(db, "a", 1, "z", 1); + CheckGet(db, roptions, "foo", "hello"); + + StartPhase("writebatch"); + { + leveldb_writebatch_t* wb = leveldb_writebatch_create(); + leveldb_writebatch_put(wb, "foo", 3, "a", 1); + leveldb_writebatch_clear(wb); + leveldb_writebatch_put(wb, "bar", 3, "b", 1); + leveldb_writebatch_put(wb, "box", 3, "c", 1); + leveldb_writebatch_delete(wb, "bar", 3); + leveldb_write(db, woptions, wb, &err); + CheckNoError(err); + CheckGet(db, roptions, "foo", "hello"); + CheckGet(db, roptions, "bar", NULL); + CheckGet(db, roptions, "box", "c"); + int pos = 0; + leveldb_writebatch_iterate(wb, &pos, CheckPut, CheckDel); + CheckCondition(pos == 3); + leveldb_writebatch_destroy(wb); + } + + StartPhase("iter"); + { + leveldb_iterator_t* iter = leveldb_create_iterator(db, roptions); + CheckCondition(!leveldb_iter_valid(iter)); + leveldb_iter_seek_to_first(iter); + CheckCondition(leveldb_iter_valid(iter)); + CheckIter(iter, "box", "c"); + leveldb_iter_next(iter); + CheckIter(iter, "foo", "hello"); + leveldb_iter_prev(iter); + CheckIter(iter, "box", "c"); + leveldb_iter_prev(iter); + CheckCondition(!leveldb_iter_valid(iter)); + leveldb_iter_seek_to_last(iter); + CheckIter(iter, "foo", "hello"); + leveldb_iter_seek(iter, "b", 1); + CheckIter(iter, "box", "c"); + leveldb_iter_get_error(iter, &err); + CheckNoError(err); + leveldb_iter_destroy(iter); + } + + StartPhase("approximate_sizes"); + { + int i; + int n = 20000; + char keybuf[100]; + char valbuf[100]; + uint64_t sizes[2]; + const char* start[2] = { "a", "k00000000000000010000" }; + size_t start_len[2] = { 1, 21 }; + const char* limit[2] = { "k00000000000000010000", "z" }; + size_t limit_len[2] = { 21, 1 }; + leveldb_writeoptions_set_sync(woptions, 0); + for (i = 0; i < n; i++) { + snprintf(keybuf, sizeof(keybuf), "k%020d", i); + snprintf(valbuf, sizeof(valbuf), "v%020d", i); + leveldb_put(db, woptions, keybuf, strlen(keybuf), valbuf, strlen(valbuf), + &err); + CheckNoError(err); + } + leveldb_approximate_sizes(db, 2, start, start_len, limit, limit_len, sizes); + CheckCondition(sizes[0] > 0); + CheckCondition(sizes[1] > 0); + } + + StartPhase("property"); + { + char* prop = leveldb_property_value(db, "nosuchprop"); + CheckCondition(prop == NULL); + prop = leveldb_property_value(db, "leveldb.stats"); + CheckCondition(prop != NULL); + Free(&prop); + } + + StartPhase("snapshot"); + { + const leveldb_snapshot_t* snap; + snap = leveldb_create_snapshot(db); + leveldb_delete(db, woptions, "foo", 3, &err); + CheckNoError(err); + leveldb_readoptions_set_snapshot(roptions, snap); + CheckGet(db, roptions, "foo", "hello"); + leveldb_readoptions_set_snapshot(roptions, NULL); + CheckGet(db, roptions, "foo", NULL); + leveldb_release_snapshot(db, snap); + } + + StartPhase("repair"); + { + leveldb_close(db); + leveldb_options_set_create_if_missing(options, 0); + leveldb_options_set_error_if_exists(options, 0); + leveldb_repair_db(options, dbname, &err); + CheckNoError(err); + db = leveldb_open(options, dbname, &err); + CheckNoError(err); + CheckGet(db, roptions, "foo", NULL); + CheckGet(db, roptions, "bar", NULL); + CheckGet(db, roptions, "box", "c"); + leveldb_options_set_create_if_missing(options, 1); + leveldb_options_set_error_if_exists(options, 1); + } + + StartPhase("filter"); + for (run = 0; run < 2; run++) { + // First run uses custom filter, second run uses bloom filter + CheckNoError(err); + leveldb_filterpolicy_t* policy; + if (run == 0) { + policy = leveldb_filterpolicy_create( + NULL, FilterDestroy, FilterCreate, FilterKeyMatch, FilterName); + } else { + policy = leveldb_filterpolicy_create_bloom(10); + } + + // Create new database + leveldb_close(db); + leveldb_destroy_db(options, dbname, &err); + leveldb_options_set_filter_policy(options, policy); + db = leveldb_open(options, dbname, &err); + CheckNoError(err); + leveldb_put(db, woptions, "foo", 3, "foovalue", 8, &err); + CheckNoError(err); + leveldb_put(db, woptions, "bar", 3, "barvalue", 8, &err); + CheckNoError(err); + leveldb_compact_range(db, NULL, 0, NULL, 0); + + fake_filter_result = 1; + CheckGet(db, roptions, "foo", "foovalue"); + CheckGet(db, roptions, "bar", "barvalue"); + if (phase == 0) { + // Must not find value when custom filter returns false + fake_filter_result = 0; + CheckGet(db, roptions, "foo", NULL); + CheckGet(db, roptions, "bar", NULL); + fake_filter_result = 1; + + CheckGet(db, roptions, "foo", "foovalue"); + CheckGet(db, roptions, "bar", "barvalue"); + } + leveldb_options_set_filter_policy(options, NULL); + leveldb_filterpolicy_destroy(policy); + } + + StartPhase("cleanup"); + leveldb_close(db); + leveldb_options_destroy(options); + leveldb_readoptions_destroy(roptions); + leveldb_writeoptions_destroy(woptions); + leveldb_cache_destroy(cache); + leveldb_comparator_destroy(cmp); + leveldb_env_destroy(env); + + fprintf(stderr, "PASS\n"); + return 0; +} diff --git a/src/leveldb/db/corruption_test.cc b/src/leveldb/db/corruption_test.cc new file mode 100644 index 00000000..96afc689 --- /dev/null +++ b/src/leveldb/db/corruption_test.cc @@ -0,0 +1,374 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/db.h" + +#include +#include +#include +#include +#include "leveldb/cache.h" +#include "leveldb/env.h" +#include "leveldb/table.h" +#include "leveldb/write_batch.h" +#include "db/db_impl.h" +#include "db/filename.h" +#include "db/log_format.h" +#include "db/version_set.h" +#include "util/logging.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +static const int kValueSize = 1000; + +class CorruptionTest { + public: + test::ErrorEnv env_; + std::string dbname_; + Cache* tiny_cache_; + Options options_; + DB* db_; + + CorruptionTest() { + tiny_cache_ = NewLRUCache(100); + options_.env = &env_; + options_.block_cache = tiny_cache_; + dbname_ = test::TmpDir() + "/db_test"; + DestroyDB(dbname_, options_); + + db_ = NULL; + options_.create_if_missing = true; + Reopen(); + options_.create_if_missing = false; + } + + ~CorruptionTest() { + delete db_; + DestroyDB(dbname_, Options()); + delete tiny_cache_; + } + + Status TryReopen() { + delete db_; + db_ = NULL; + return DB::Open(options_, dbname_, &db_); + } + + void Reopen() { + ASSERT_OK(TryReopen()); + } + + void RepairDB() { + delete db_; + db_ = NULL; + ASSERT_OK(::leveldb::RepairDB(dbname_, options_)); + } + + void Build(int n) { + std::string key_space, value_space; + WriteBatch batch; + for (int i = 0; i < n; i++) { + //if ((i % 100) == 0) fprintf(stderr, "@ %d of %d\n", i, n); + Slice key = Key(i, &key_space); + batch.Clear(); + batch.Put(key, Value(i, &value_space)); + WriteOptions options; + // Corrupt() doesn't work without this sync on windows; stat reports 0 for + // the file size. + if (i == n - 1) { + options.sync = true; + } + ASSERT_OK(db_->Write(options, &batch)); + } + } + + void Check(int min_expected, int max_expected) { + int next_expected = 0; + int missed = 0; + int bad_keys = 0; + int bad_values = 0; + int correct = 0; + std::string value_space; + Iterator* iter = db_->NewIterator(ReadOptions()); + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + uint64_t key; + Slice in(iter->key()); + if (in == "" || in == "~") { + // Ignore boundary keys. + continue; + } + if (!ConsumeDecimalNumber(&in, &key) || + !in.empty() || + key < next_expected) { + bad_keys++; + continue; + } + missed += (key - next_expected); + next_expected = key + 1; + if (iter->value() != Value(key, &value_space)) { + bad_values++; + } else { + correct++; + } + } + delete iter; + + fprintf(stderr, + "expected=%d..%d; got=%d; bad_keys=%d; bad_values=%d; missed=%d\n", + min_expected, max_expected, correct, bad_keys, bad_values, missed); + ASSERT_LE(min_expected, correct); + ASSERT_GE(max_expected, correct); + } + + void Corrupt(FileType filetype, int offset, int bytes_to_corrupt) { + // Pick file to corrupt + std::vector filenames; + ASSERT_OK(env_.GetChildren(dbname_, &filenames)); + uint64_t number; + FileType type; + std::string fname; + int picked_number = -1; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type) && + type == filetype && + int(number) > picked_number) { // Pick latest file + fname = dbname_ + "/" + filenames[i]; + picked_number = number; + } + } + ASSERT_TRUE(!fname.empty()) << filetype; + + struct stat sbuf; + if (stat(fname.c_str(), &sbuf) != 0) { + const char* msg = strerror(errno); + ASSERT_TRUE(false) << fname << ": " << msg; + } + + if (offset < 0) { + // Relative to end of file; make it absolute + if (-offset > sbuf.st_size) { + offset = 0; + } else { + offset = sbuf.st_size + offset; + } + } + if (offset > sbuf.st_size) { + offset = sbuf.st_size; + } + if (offset + bytes_to_corrupt > sbuf.st_size) { + bytes_to_corrupt = sbuf.st_size - offset; + } + + // Do it + std::string contents; + Status s = ReadFileToString(Env::Default(), fname, &contents); + ASSERT_TRUE(s.ok()) << s.ToString(); + for (int i = 0; i < bytes_to_corrupt; i++) { + contents[i + offset] ^= 0x80; + } + s = WriteStringToFile(Env::Default(), contents, fname); + ASSERT_TRUE(s.ok()) << s.ToString(); + } + + int Property(const std::string& name) { + std::string property; + int result; + if (db_->GetProperty(name, &property) && + sscanf(property.c_str(), "%d", &result) == 1) { + return result; + } else { + return -1; + } + } + + // Return the ith key + Slice Key(int i, std::string* storage) { + char buf[100]; + snprintf(buf, sizeof(buf), "%016d", i); + storage->assign(buf, strlen(buf)); + return Slice(*storage); + } + + // Return the value to associate with the specified key + Slice Value(int k, std::string* storage) { + Random r(k); + return test::RandomString(&r, kValueSize, storage); + } +}; + +TEST(CorruptionTest, Recovery) { + Build(100); + Check(100, 100); + Corrupt(kLogFile, 19, 1); // WriteBatch tag for first record + Corrupt(kLogFile, log::kBlockSize + 1000, 1); // Somewhere in second block + Reopen(); + + // The 64 records in the first two log blocks are completely lost. + Check(36, 36); +} + +TEST(CorruptionTest, RecoverWriteError) { + env_.writable_file_error_ = true; + Status s = TryReopen(); + ASSERT_TRUE(!s.ok()); +} + +TEST(CorruptionTest, NewFileErrorDuringWrite) { + // Do enough writing to force minor compaction + env_.writable_file_error_ = true; + const int num = 3 + (Options().write_buffer_size / kValueSize); + std::string value_storage; + Status s; + for (int i = 0; s.ok() && i < num; i++) { + WriteBatch batch; + batch.Put("a", Value(100, &value_storage)); + s = db_->Write(WriteOptions(), &batch); + } + ASSERT_TRUE(!s.ok()); + ASSERT_GE(env_.num_writable_file_errors_, 1); + env_.writable_file_error_ = false; + Reopen(); +} + +TEST(CorruptionTest, TableFile) { + Build(100); + DBImpl* dbi = reinterpret_cast(db_); + dbi->TEST_CompactMemTable(); + dbi->TEST_CompactRange(0, NULL, NULL); + dbi->TEST_CompactRange(1, NULL, NULL); + + Corrupt(kTableFile, 100, 1); + Check(90, 99); +} + +TEST(CorruptionTest, TableFileRepair) { + options_.block_size = 2 * kValueSize; // Limit scope of corruption + options_.paranoid_checks = true; + Reopen(); + Build(100); + DBImpl* dbi = reinterpret_cast(db_); + dbi->TEST_CompactMemTable(); + dbi->TEST_CompactRange(0, NULL, NULL); + dbi->TEST_CompactRange(1, NULL, NULL); + + Corrupt(kTableFile, 100, 1); + RepairDB(); + Reopen(); + Check(95, 99); +} + +TEST(CorruptionTest, TableFileIndexData) { + Build(10000); // Enough to build multiple Tables + DBImpl* dbi = reinterpret_cast(db_); + dbi->TEST_CompactMemTable(); + + Corrupt(kTableFile, -2000, 500); + Reopen(); + Check(5000, 9999); +} + +TEST(CorruptionTest, MissingDescriptor) { + Build(1000); + RepairDB(); + Reopen(); + Check(1000, 1000); +} + +TEST(CorruptionTest, SequenceNumberRecovery) { + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v1")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v2")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v3")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v4")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v5")); + RepairDB(); + Reopen(); + std::string v; + ASSERT_OK(db_->Get(ReadOptions(), "foo", &v)); + ASSERT_EQ("v5", v); + // Write something. If sequence number was not recovered properly, + // it will be hidden by an earlier write. + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v6")); + ASSERT_OK(db_->Get(ReadOptions(), "foo", &v)); + ASSERT_EQ("v6", v); + Reopen(); + ASSERT_OK(db_->Get(ReadOptions(), "foo", &v)); + ASSERT_EQ("v6", v); +} + +TEST(CorruptionTest, CorruptedDescriptor) { + ASSERT_OK(db_->Put(WriteOptions(), "foo", "hello")); + DBImpl* dbi = reinterpret_cast(db_); + dbi->TEST_CompactMemTable(); + dbi->TEST_CompactRange(0, NULL, NULL); + + Corrupt(kDescriptorFile, 0, 1000); + Status s = TryReopen(); + ASSERT_TRUE(!s.ok()); + + RepairDB(); + Reopen(); + std::string v; + ASSERT_OK(db_->Get(ReadOptions(), "foo", &v)); + ASSERT_EQ("hello", v); +} + +TEST(CorruptionTest, CompactionInputError) { + Build(10); + DBImpl* dbi = reinterpret_cast(db_); + dbi->TEST_CompactMemTable(); + const int last = config::kMaxMemCompactLevel; + ASSERT_EQ(1, Property("leveldb.num-files-at-level" + NumberToString(last))); + + Corrupt(kTableFile, 100, 1); + Check(5, 9); + + // Force compactions by writing lots of values + Build(10000); + Check(10000, 10000); +} + +TEST(CorruptionTest, CompactionInputErrorParanoid) { + options_.paranoid_checks = true; + options_.write_buffer_size = 512 << 10; + Reopen(); + DBImpl* dbi = reinterpret_cast(db_); + + // Make multiple inputs so we need to compact. + for (int i = 0; i < 2; i++) { + Build(10); + dbi->TEST_CompactMemTable(); + Corrupt(kTableFile, 100, 1); + env_.SleepForMicroseconds(100000); + } + dbi->CompactRange(NULL, NULL); + + // Write must fail because of corrupted table + std::string tmp1, tmp2; + Status s = db_->Put(WriteOptions(), Key(5, &tmp1), Value(5, &tmp2)); + ASSERT_TRUE(!s.ok()) << "write did not fail in corrupted paranoid db"; +} + +TEST(CorruptionTest, UnrelatedKeys) { + Build(10); + DBImpl* dbi = reinterpret_cast(db_); + dbi->TEST_CompactMemTable(); + Corrupt(kTableFile, 100, 1); + + std::string tmp1, tmp2; + ASSERT_OK(db_->Put(WriteOptions(), Key(1000, &tmp1), Value(1000, &tmp2))); + std::string v; + ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v)); + ASSERT_EQ(Value(1000, &tmp2).ToString(), v); + dbi->TEST_CompactMemTable(); + ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v)); + ASSERT_EQ(Value(1000, &tmp2).ToString(), v); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/db_bench.cc b/src/leveldb/db/db_bench.cc new file mode 100644 index 00000000..705a170a --- /dev/null +++ b/src/leveldb/db/db_bench.cc @@ -0,0 +1,978 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include +#include "db/db_impl.h" +#include "db/version_set.h" +#include "leveldb/cache.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/write_batch.h" +#include "port/port.h" +#include "util/crc32c.h" +#include "util/histogram.h" +#include "util/mutexlock.h" +#include "util/random.h" +#include "util/testutil.h" + +// Comma-separated list of operations to run in the specified order +// Actual benchmarks: +// fillseq -- write N values in sequential key order in async mode +// fillrandom -- write N values in random key order in async mode +// overwrite -- overwrite N values in random key order in async mode +// fillsync -- write N/100 values in random key order in sync mode +// fill100K -- write N/1000 100K values in random order in async mode +// deleteseq -- delete N keys in sequential order +// deleterandom -- delete N keys in random order +// readseq -- read N times sequentially +// readreverse -- read N times in reverse order +// readrandom -- read N times in random order +// readmissing -- read N missing keys in random order +// readhot -- read N times in random order from 1% section of DB +// seekrandom -- N random seeks +// crc32c -- repeated crc32c of 4K of data +// acquireload -- load N*1000 times +// Meta operations: +// compact -- Compact the entire DB +// stats -- Print DB stats +// sstables -- Print sstable info +// heapprofile -- Dump a heap profile (if supported by this port) +static const char* FLAGS_benchmarks = + "fillseq," + "fillsync," + "fillrandom," + "overwrite," + "readrandom," + "readrandom," // Extra run to allow previous compactions to quiesce + "readseq," + "readreverse," + "compact," + "readrandom," + "readseq," + "readreverse," + "fill100K," + "crc32c," + "snappycomp," + "snappyuncomp," + "acquireload," + ; + +// Number of key/values to place in database +static int FLAGS_num = 1000000; + +// Number of read operations to do. If negative, do FLAGS_num reads. +static int FLAGS_reads = -1; + +// Number of concurrent threads to run. +static int FLAGS_threads = 1; + +// Size of each value +static int FLAGS_value_size = 100; + +// Arrange to generate values that shrink to this fraction of +// their original size after compression +static double FLAGS_compression_ratio = 0.5; + +// Print histogram of operation timings +static bool FLAGS_histogram = false; + +// Number of bytes to buffer in memtable before compacting +// (initialized to default value by "main") +static int FLAGS_write_buffer_size = 0; + +// Number of bytes to use as a cache of uncompressed data. +// Negative means use default settings. +static int FLAGS_cache_size = -1; + +// Maximum number of files to keep open at the same time (use default if == 0) +static int FLAGS_open_files = 0; + +// Bloom filter bits per key. +// Negative means use default settings. +static int FLAGS_bloom_bits = -1; + +// If true, do not destroy the existing database. If you set this +// flag and also specify a benchmark that wants a fresh database, that +// benchmark will fail. +static bool FLAGS_use_existing_db = false; + +// Use the db with the following name. +static const char* FLAGS_db = NULL; + +namespace leveldb { + +namespace { + +// Helper for quickly generating random data. +class RandomGenerator { + private: + std::string data_; + int pos_; + + public: + RandomGenerator() { + // We use a limited amount of data over and over again and ensure + // that it is larger than the compression window (32KB), and also + // large enough to serve all typical value sizes we want to write. + Random rnd(301); + std::string piece; + while (data_.size() < 1048576) { + // Add a short fragment that is as compressible as specified + // by FLAGS_compression_ratio. + test::CompressibleString(&rnd, FLAGS_compression_ratio, 100, &piece); + data_.append(piece); + } + pos_ = 0; + } + + Slice Generate(size_t len) { + if (pos_ + len > data_.size()) { + pos_ = 0; + assert(len < data_.size()); + } + pos_ += len; + return Slice(data_.data() + pos_ - len, len); + } +}; + +static Slice TrimSpace(Slice s) { + size_t start = 0; + while (start < s.size() && isspace(s[start])) { + start++; + } + size_t limit = s.size(); + while (limit > start && isspace(s[limit-1])) { + limit--; + } + return Slice(s.data() + start, limit - start); +} + +static void AppendWithSpace(std::string* str, Slice msg) { + if (msg.empty()) return; + if (!str->empty()) { + str->push_back(' '); + } + str->append(msg.data(), msg.size()); +} + +class Stats { + private: + double start_; + double finish_; + double seconds_; + int done_; + int next_report_; + int64_t bytes_; + double last_op_finish_; + Histogram hist_; + std::string message_; + + public: + Stats() { Start(); } + + void Start() { + next_report_ = 100; + last_op_finish_ = start_; + hist_.Clear(); + done_ = 0; + bytes_ = 0; + seconds_ = 0; + start_ = Env::Default()->NowMicros(); + finish_ = start_; + message_.clear(); + } + + void Merge(const Stats& other) { + hist_.Merge(other.hist_); + done_ += other.done_; + bytes_ += other.bytes_; + seconds_ += other.seconds_; + if (other.start_ < start_) start_ = other.start_; + if (other.finish_ > finish_) finish_ = other.finish_; + + // Just keep the messages from one thread + if (message_.empty()) message_ = other.message_; + } + + void Stop() { + finish_ = Env::Default()->NowMicros(); + seconds_ = (finish_ - start_) * 1e-6; + } + + void AddMessage(Slice msg) { + AppendWithSpace(&message_, msg); + } + + void FinishedSingleOp() { + if (FLAGS_histogram) { + double now = Env::Default()->NowMicros(); + double micros = now - last_op_finish_; + hist_.Add(micros); + if (micros > 20000) { + fprintf(stderr, "long op: %.1f micros%30s\r", micros, ""); + fflush(stderr); + } + last_op_finish_ = now; + } + + done_++; + if (done_ >= next_report_) { + if (next_report_ < 1000) next_report_ += 100; + else if (next_report_ < 5000) next_report_ += 500; + else if (next_report_ < 10000) next_report_ += 1000; + else if (next_report_ < 50000) next_report_ += 5000; + else if (next_report_ < 100000) next_report_ += 10000; + else if (next_report_ < 500000) next_report_ += 50000; + else next_report_ += 100000; + fprintf(stderr, "... finished %d ops%30s\r", done_, ""); + fflush(stderr); + } + } + + void AddBytes(int64_t n) { + bytes_ += n; + } + + void Report(const Slice& name) { + // Pretend at least one op was done in case we are running a benchmark + // that does not call FinishedSingleOp(). + if (done_ < 1) done_ = 1; + + std::string extra; + if (bytes_ > 0) { + // Rate is computed on actual elapsed time, not the sum of per-thread + // elapsed times. + double elapsed = (finish_ - start_) * 1e-6; + char rate[100]; + snprintf(rate, sizeof(rate), "%6.1f MB/s", + (bytes_ / 1048576.0) / elapsed); + extra = rate; + } + AppendWithSpace(&extra, message_); + + fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", + name.ToString().c_str(), + seconds_ * 1e6 / done_, + (extra.empty() ? "" : " "), + extra.c_str()); + if (FLAGS_histogram) { + fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str()); + } + fflush(stdout); + } +}; + +// State shared by all concurrent executions of the same benchmark. +struct SharedState { + port::Mutex mu; + port::CondVar cv; + int total; + + // Each thread goes through the following states: + // (1) initializing + // (2) waiting for others to be initialized + // (3) running + // (4) done + + int num_initialized; + int num_done; + bool start; + + SharedState() : cv(&mu) { } +}; + +// Per-thread state for concurrent executions of the same benchmark. +struct ThreadState { + int tid; // 0..n-1 when running in n threads + Random rand; // Has different seeds for different threads + Stats stats; + SharedState* shared; + + ThreadState(int index) + : tid(index), + rand(1000 + index) { + } +}; + +} // namespace + +class Benchmark { + private: + Cache* cache_; + const FilterPolicy* filter_policy_; + DB* db_; + int num_; + int value_size_; + int entries_per_batch_; + WriteOptions write_options_; + int reads_; + int heap_counter_; + + void PrintHeader() { + const int kKeySize = 16; + PrintEnvironment(); + fprintf(stdout, "Keys: %d bytes each\n", kKeySize); + fprintf(stdout, "Values: %d bytes each (%d bytes after compression)\n", + FLAGS_value_size, + static_cast(FLAGS_value_size * FLAGS_compression_ratio + 0.5)); + fprintf(stdout, "Entries: %d\n", num_); + fprintf(stdout, "RawSize: %.1f MB (estimated)\n", + ((static_cast(kKeySize + FLAGS_value_size) * num_) + / 1048576.0)); + fprintf(stdout, "FileSize: %.1f MB (estimated)\n", + (((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_) + / 1048576.0)); + PrintWarnings(); + fprintf(stdout, "------------------------------------------------\n"); + } + + void PrintWarnings() { +#if defined(__GNUC__) && !defined(__OPTIMIZE__) + fprintf(stdout, + "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n" + ); +#endif +#ifndef NDEBUG + fprintf(stdout, + "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n"); +#endif + + // See if snappy is working by attempting to compress a compressible string + const char text[] = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"; + std::string compressed; + if (!port::Snappy_Compress(text, sizeof(text), &compressed)) { + fprintf(stdout, "WARNING: Snappy compression is not enabled\n"); + } else if (compressed.size() >= sizeof(text)) { + fprintf(stdout, "WARNING: Snappy compression is not effective\n"); + } + } + + void PrintEnvironment() { + fprintf(stderr, "LevelDB: version %d.%d\n", + kMajorVersion, kMinorVersion); + +#if defined(__linux) + time_t now = time(NULL); + fprintf(stderr, "Date: %s", ctime(&now)); // ctime() adds newline + + FILE* cpuinfo = fopen("/proc/cpuinfo", "r"); + if (cpuinfo != NULL) { + char line[1000]; + int num_cpus = 0; + std::string cpu_type; + std::string cache_size; + while (fgets(line, sizeof(line), cpuinfo) != NULL) { + const char* sep = strchr(line, ':'); + if (sep == NULL) { + continue; + } + Slice key = TrimSpace(Slice(line, sep - 1 - line)); + Slice val = TrimSpace(Slice(sep + 1)); + if (key == "model name") { + ++num_cpus; + cpu_type = val.ToString(); + } else if (key == "cache size") { + cache_size = val.ToString(); + } + } + fclose(cpuinfo); + fprintf(stderr, "CPU: %d * %s\n", num_cpus, cpu_type.c_str()); + fprintf(stderr, "CPUCache: %s\n", cache_size.c_str()); + } +#endif + } + + public: + Benchmark() + : cache_(FLAGS_cache_size >= 0 ? NewLRUCache(FLAGS_cache_size) : NULL), + filter_policy_(FLAGS_bloom_bits >= 0 + ? NewBloomFilterPolicy(FLAGS_bloom_bits) + : NULL), + db_(NULL), + num_(FLAGS_num), + value_size_(FLAGS_value_size), + entries_per_batch_(1), + reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), + heap_counter_(0) { + std::vector files; + Env::Default()->GetChildren(FLAGS_db, &files); + for (size_t i = 0; i < files.size(); i++) { + if (Slice(files[i]).starts_with("heap-")) { + Env::Default()->DeleteFile(std::string(FLAGS_db) + "/" + files[i]); + } + } + if (!FLAGS_use_existing_db) { + DestroyDB(FLAGS_db, Options()); + } + } + + ~Benchmark() { + delete db_; + delete cache_; + delete filter_policy_; + } + + void Run() { + PrintHeader(); + Open(); + + const char* benchmarks = FLAGS_benchmarks; + while (benchmarks != NULL) { + const char* sep = strchr(benchmarks, ','); + Slice name; + if (sep == NULL) { + name = benchmarks; + benchmarks = NULL; + } else { + name = Slice(benchmarks, sep - benchmarks); + benchmarks = sep + 1; + } + + // Reset parameters that may be overridden below + num_ = FLAGS_num; + reads_ = (FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads); + value_size_ = FLAGS_value_size; + entries_per_batch_ = 1; + write_options_ = WriteOptions(); + + void (Benchmark::*method)(ThreadState*) = NULL; + bool fresh_db = false; + int num_threads = FLAGS_threads; + + if (name == Slice("fillseq")) { + fresh_db = true; + method = &Benchmark::WriteSeq; + } else if (name == Slice("fillbatch")) { + fresh_db = true; + entries_per_batch_ = 1000; + method = &Benchmark::WriteSeq; + } else if (name == Slice("fillrandom")) { + fresh_db = true; + method = &Benchmark::WriteRandom; + } else if (name == Slice("overwrite")) { + fresh_db = false; + method = &Benchmark::WriteRandom; + } else if (name == Slice("fillsync")) { + fresh_db = true; + num_ /= 1000; + write_options_.sync = true; + method = &Benchmark::WriteRandom; + } else if (name == Slice("fill100K")) { + fresh_db = true; + num_ /= 1000; + value_size_ = 100 * 1000; + method = &Benchmark::WriteRandom; + } else if (name == Slice("readseq")) { + method = &Benchmark::ReadSequential; + } else if (name == Slice("readreverse")) { + method = &Benchmark::ReadReverse; + } else if (name == Slice("readrandom")) { + method = &Benchmark::ReadRandom; + } else if (name == Slice("readmissing")) { + method = &Benchmark::ReadMissing; + } else if (name == Slice("seekrandom")) { + method = &Benchmark::SeekRandom; + } else if (name == Slice("readhot")) { + method = &Benchmark::ReadHot; + } else if (name == Slice("readrandomsmall")) { + reads_ /= 1000; + method = &Benchmark::ReadRandom; + } else if (name == Slice("deleteseq")) { + method = &Benchmark::DeleteSeq; + } else if (name == Slice("deleterandom")) { + method = &Benchmark::DeleteRandom; + } else if (name == Slice("readwhilewriting")) { + num_threads++; // Add extra thread for writing + method = &Benchmark::ReadWhileWriting; + } else if (name == Slice("compact")) { + method = &Benchmark::Compact; + } else if (name == Slice("crc32c")) { + method = &Benchmark::Crc32c; + } else if (name == Slice("acquireload")) { + method = &Benchmark::AcquireLoad; + } else if (name == Slice("snappycomp")) { + method = &Benchmark::SnappyCompress; + } else if (name == Slice("snappyuncomp")) { + method = &Benchmark::SnappyUncompress; + } else if (name == Slice("heapprofile")) { + HeapProfile(); + } else if (name == Slice("stats")) { + PrintStats("leveldb.stats"); + } else if (name == Slice("sstables")) { + PrintStats("leveldb.sstables"); + } else { + if (name != Slice()) { // No error message for empty name + fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str()); + } + } + + if (fresh_db) { + if (FLAGS_use_existing_db) { + fprintf(stdout, "%-12s : skipped (--use_existing_db is true)\n", + name.ToString().c_str()); + method = NULL; + } else { + delete db_; + db_ = NULL; + DestroyDB(FLAGS_db, Options()); + Open(); + } + } + + if (method != NULL) { + RunBenchmark(num_threads, name, method); + } + } + } + + private: + struct ThreadArg { + Benchmark* bm; + SharedState* shared; + ThreadState* thread; + void (Benchmark::*method)(ThreadState*); + }; + + static void ThreadBody(void* v) { + ThreadArg* arg = reinterpret_cast(v); + SharedState* shared = arg->shared; + ThreadState* thread = arg->thread; + { + MutexLock l(&shared->mu); + shared->num_initialized++; + if (shared->num_initialized >= shared->total) { + shared->cv.SignalAll(); + } + while (!shared->start) { + shared->cv.Wait(); + } + } + + thread->stats.Start(); + (arg->bm->*(arg->method))(thread); + thread->stats.Stop(); + + { + MutexLock l(&shared->mu); + shared->num_done++; + if (shared->num_done >= shared->total) { + shared->cv.SignalAll(); + } + } + } + + void RunBenchmark(int n, Slice name, + void (Benchmark::*method)(ThreadState*)) { + SharedState shared; + shared.total = n; + shared.num_initialized = 0; + shared.num_done = 0; + shared.start = false; + + ThreadArg* arg = new ThreadArg[n]; + for (int i = 0; i < n; i++) { + arg[i].bm = this; + arg[i].method = method; + arg[i].shared = &shared; + arg[i].thread = new ThreadState(i); + arg[i].thread->shared = &shared; + Env::Default()->StartThread(ThreadBody, &arg[i]); + } + + shared.mu.Lock(); + while (shared.num_initialized < n) { + shared.cv.Wait(); + } + + shared.start = true; + shared.cv.SignalAll(); + while (shared.num_done < n) { + shared.cv.Wait(); + } + shared.mu.Unlock(); + + for (int i = 1; i < n; i++) { + arg[0].thread->stats.Merge(arg[i].thread->stats); + } + arg[0].thread->stats.Report(name); + + for (int i = 0; i < n; i++) { + delete arg[i].thread; + } + delete[] arg; + } + + void Crc32c(ThreadState* thread) { + // Checksum about 500MB of data total + const int size = 4096; + const char* label = "(4K per op)"; + std::string data(size, 'x'); + int64_t bytes = 0; + uint32_t crc = 0; + while (bytes < 500 * 1048576) { + crc = crc32c::Value(data.data(), size); + thread->stats.FinishedSingleOp(); + bytes += size; + } + // Print so result is not dead + fprintf(stderr, "... crc=0x%x\r", static_cast(crc)); + + thread->stats.AddBytes(bytes); + thread->stats.AddMessage(label); + } + + void AcquireLoad(ThreadState* thread) { + int dummy; + port::AtomicPointer ap(&dummy); + int count = 0; + void *ptr = NULL; + thread->stats.AddMessage("(each op is 1000 loads)"); + while (count < 100000) { + for (int i = 0; i < 1000; i++) { + ptr = ap.Acquire_Load(); + } + count++; + thread->stats.FinishedSingleOp(); + } + if (ptr == NULL) exit(1); // Disable unused variable warning. + } + + void SnappyCompress(ThreadState* thread) { + RandomGenerator gen; + Slice input = gen.Generate(Options().block_size); + int64_t bytes = 0; + int64_t produced = 0; + bool ok = true; + std::string compressed; + while (ok && bytes < 1024 * 1048576) { // Compress 1G + ok = port::Snappy_Compress(input.data(), input.size(), &compressed); + produced += compressed.size(); + bytes += input.size(); + thread->stats.FinishedSingleOp(); + } + + if (!ok) { + thread->stats.AddMessage("(snappy failure)"); + } else { + char buf[100]; + snprintf(buf, sizeof(buf), "(output: %.1f%%)", + (produced * 100.0) / bytes); + thread->stats.AddMessage(buf); + thread->stats.AddBytes(bytes); + } + } + + void SnappyUncompress(ThreadState* thread) { + RandomGenerator gen; + Slice input = gen.Generate(Options().block_size); + std::string compressed; + bool ok = port::Snappy_Compress(input.data(), input.size(), &compressed); + int64_t bytes = 0; + char* uncompressed = new char[input.size()]; + while (ok && bytes < 1024 * 1048576) { // Compress 1G + ok = port::Snappy_Uncompress(compressed.data(), compressed.size(), + uncompressed); + bytes += input.size(); + thread->stats.FinishedSingleOp(); + } + delete[] uncompressed; + + if (!ok) { + thread->stats.AddMessage("(snappy failure)"); + } else { + thread->stats.AddBytes(bytes); + } + } + + void Open() { + assert(db_ == NULL); + Options options; + options.create_if_missing = !FLAGS_use_existing_db; + options.block_cache = cache_; + options.write_buffer_size = FLAGS_write_buffer_size; + options.max_open_files = FLAGS_open_files; + options.filter_policy = filter_policy_; + Status s = DB::Open(options, FLAGS_db, &db_); + if (!s.ok()) { + fprintf(stderr, "open error: %s\n", s.ToString().c_str()); + exit(1); + } + } + + void WriteSeq(ThreadState* thread) { + DoWrite(thread, true); + } + + void WriteRandom(ThreadState* thread) { + DoWrite(thread, false); + } + + void DoWrite(ThreadState* thread, bool seq) { + if (num_ != FLAGS_num) { + char msg[100]; + snprintf(msg, sizeof(msg), "(%d ops)", num_); + thread->stats.AddMessage(msg); + } + + RandomGenerator gen; + WriteBatch batch; + Status s; + int64_t bytes = 0; + for (int i = 0; i < num_; i += entries_per_batch_) { + batch.Clear(); + for (int j = 0; j < entries_per_batch_; j++) { + const int k = seq ? i+j : (thread->rand.Next() % FLAGS_num); + char key[100]; + snprintf(key, sizeof(key), "%016d", k); + batch.Put(key, gen.Generate(value_size_)); + bytes += value_size_ + strlen(key); + thread->stats.FinishedSingleOp(); + } + s = db_->Write(write_options_, &batch); + if (!s.ok()) { + fprintf(stderr, "put error: %s\n", s.ToString().c_str()); + exit(1); + } + } + thread->stats.AddBytes(bytes); + } + + void ReadSequential(ThreadState* thread) { + Iterator* iter = db_->NewIterator(ReadOptions()); + int i = 0; + int64_t bytes = 0; + for (iter->SeekToFirst(); i < reads_ && iter->Valid(); iter->Next()) { + bytes += iter->key().size() + iter->value().size(); + thread->stats.FinishedSingleOp(); + ++i; + } + delete iter; + thread->stats.AddBytes(bytes); + } + + void ReadReverse(ThreadState* thread) { + Iterator* iter = db_->NewIterator(ReadOptions()); + int i = 0; + int64_t bytes = 0; + for (iter->SeekToLast(); i < reads_ && iter->Valid(); iter->Prev()) { + bytes += iter->key().size() + iter->value().size(); + thread->stats.FinishedSingleOp(); + ++i; + } + delete iter; + thread->stats.AddBytes(bytes); + } + + void ReadRandom(ThreadState* thread) { + ReadOptions options; + std::string value; + int found = 0; + for (int i = 0; i < reads_; i++) { + char key[100]; + const int k = thread->rand.Next() % FLAGS_num; + snprintf(key, sizeof(key), "%016d", k); + if (db_->Get(options, key, &value).ok()) { + found++; + } + thread->stats.FinishedSingleOp(); + } + char msg[100]; + snprintf(msg, sizeof(msg), "(%d of %d found)", found, num_); + thread->stats.AddMessage(msg); + } + + void ReadMissing(ThreadState* thread) { + ReadOptions options; + std::string value; + for (int i = 0; i < reads_; i++) { + char key[100]; + const int k = thread->rand.Next() % FLAGS_num; + snprintf(key, sizeof(key), "%016d.", k); + db_->Get(options, key, &value); + thread->stats.FinishedSingleOp(); + } + } + + void ReadHot(ThreadState* thread) { + ReadOptions options; + std::string value; + const int range = (FLAGS_num + 99) / 100; + for (int i = 0; i < reads_; i++) { + char key[100]; + const int k = thread->rand.Next() % range; + snprintf(key, sizeof(key), "%016d", k); + db_->Get(options, key, &value); + thread->stats.FinishedSingleOp(); + } + } + + void SeekRandom(ThreadState* thread) { + ReadOptions options; + int found = 0; + for (int i = 0; i < reads_; i++) { + Iterator* iter = db_->NewIterator(options); + char key[100]; + const int k = thread->rand.Next() % FLAGS_num; + snprintf(key, sizeof(key), "%016d", k); + iter->Seek(key); + if (iter->Valid() && iter->key() == key) found++; + delete iter; + thread->stats.FinishedSingleOp(); + } + char msg[100]; + snprintf(msg, sizeof(msg), "(%d of %d found)", found, num_); + thread->stats.AddMessage(msg); + } + + void DoDelete(ThreadState* thread, bool seq) { + RandomGenerator gen; + WriteBatch batch; + Status s; + for (int i = 0; i < num_; i += entries_per_batch_) { + batch.Clear(); + for (int j = 0; j < entries_per_batch_; j++) { + const int k = seq ? i+j : (thread->rand.Next() % FLAGS_num); + char key[100]; + snprintf(key, sizeof(key), "%016d", k); + batch.Delete(key); + thread->stats.FinishedSingleOp(); + } + s = db_->Write(write_options_, &batch); + if (!s.ok()) { + fprintf(stderr, "del error: %s\n", s.ToString().c_str()); + exit(1); + } + } + } + + void DeleteSeq(ThreadState* thread) { + DoDelete(thread, true); + } + + void DeleteRandom(ThreadState* thread) { + DoDelete(thread, false); + } + + void ReadWhileWriting(ThreadState* thread) { + if (thread->tid > 0) { + ReadRandom(thread); + } else { + // Special thread that keeps writing until other threads are done. + RandomGenerator gen; + while (true) { + { + MutexLock l(&thread->shared->mu); + if (thread->shared->num_done + 1 >= thread->shared->num_initialized) { + // Other threads have finished + break; + } + } + + const int k = thread->rand.Next() % FLAGS_num; + char key[100]; + snprintf(key, sizeof(key), "%016d", k); + Status s = db_->Put(write_options_, key, gen.Generate(value_size_)); + if (!s.ok()) { + fprintf(stderr, "put error: %s\n", s.ToString().c_str()); + exit(1); + } + } + + // Do not count any of the preceding work/delay in stats. + thread->stats.Start(); + } + } + + void Compact(ThreadState* thread) { + db_->CompactRange(NULL, NULL); + } + + void PrintStats(const char* key) { + std::string stats; + if (!db_->GetProperty(key, &stats)) { + stats = "(failed)"; + } + fprintf(stdout, "\n%s\n", stats.c_str()); + } + + static void WriteToFile(void* arg, const char* buf, int n) { + reinterpret_cast(arg)->Append(Slice(buf, n)); + } + + void HeapProfile() { + char fname[100]; + snprintf(fname, sizeof(fname), "%s/heap-%04d", FLAGS_db, ++heap_counter_); + WritableFile* file; + Status s = Env::Default()->NewWritableFile(fname, &file); + if (!s.ok()) { + fprintf(stderr, "%s\n", s.ToString().c_str()); + return; + } + bool ok = port::GetHeapProfile(WriteToFile, file); + delete file; + if (!ok) { + fprintf(stderr, "heap profiling not supported\n"); + Env::Default()->DeleteFile(fname); + } + } +}; + +} // namespace leveldb + +int main(int argc, char** argv) { + FLAGS_write_buffer_size = leveldb::Options().write_buffer_size; + FLAGS_open_files = leveldb::Options().max_open_files; + std::string default_db_path; + + for (int i = 1; i < argc; i++) { + double d; + int n; + char junk; + if (leveldb::Slice(argv[i]).starts_with("--benchmarks=")) { + FLAGS_benchmarks = argv[i] + strlen("--benchmarks="); + } else if (sscanf(argv[i], "--compression_ratio=%lf%c", &d, &junk) == 1) { + FLAGS_compression_ratio = d; + } else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_histogram = n; + } else if (sscanf(argv[i], "--use_existing_db=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_use_existing_db = n; + } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) { + FLAGS_num = n; + } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) { + FLAGS_reads = n; + } else if (sscanf(argv[i], "--threads=%d%c", &n, &junk) == 1) { + FLAGS_threads = n; + } else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) { + FLAGS_value_size = n; + } else if (sscanf(argv[i], "--write_buffer_size=%d%c", &n, &junk) == 1) { + FLAGS_write_buffer_size = n; + } else if (sscanf(argv[i], "--cache_size=%d%c", &n, &junk) == 1) { + FLAGS_cache_size = n; + } else if (sscanf(argv[i], "--bloom_bits=%d%c", &n, &junk) == 1) { + FLAGS_bloom_bits = n; + } else if (sscanf(argv[i], "--open_files=%d%c", &n, &junk) == 1) { + FLAGS_open_files = n; + } else if (strncmp(argv[i], "--db=", 5) == 0) { + FLAGS_db = argv[i] + 5; + } else { + fprintf(stderr, "Invalid flag '%s'\n", argv[i]); + exit(1); + } + } + + // Choose a location for the test database if none given with --db= + if (FLAGS_db == NULL) { + leveldb::Env::Default()->GetTestDirectory(&default_db_path); + default_db_path += "/dbbench"; + FLAGS_db = default_db_path.c_str(); + } + + leveldb::Benchmark benchmark; + benchmark.Run(); + return 0; +} diff --git a/src/leveldb/db/db_impl.cc b/src/leveldb/db/db_impl.cc new file mode 100644 index 00000000..49b95953 --- /dev/null +++ b/src/leveldb/db/db_impl.cc @@ -0,0 +1,1513 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/db_impl.h" + +#include +#include +#include +#include +#include +#include +#include "db/builder.h" +#include "db/db_iter.h" +#include "db/dbformat.h" +#include "db/filename.h" +#include "db/log_reader.h" +#include "db/log_writer.h" +#include "db/memtable.h" +#include "db/table_cache.h" +#include "db/version_set.h" +#include "db/write_batch_internal.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/status.h" +#include "leveldb/table.h" +#include "leveldb/table_builder.h" +#include "port/port.h" +#include "table/block.h" +#include "table/merger.h" +#include "table/two_level_iterator.h" +#include "util/coding.h" +#include "util/logging.h" +#include "util/mutexlock.h" + +namespace leveldb { + +const int kNumNonTableCacheFiles = 10; + +// Information kept for every waiting writer +struct DBImpl::Writer { + Status status; + WriteBatch* batch; + bool sync; + bool done; + port::CondVar cv; + + explicit Writer(port::Mutex* mu) : cv(mu) { } +}; + +struct DBImpl::CompactionState { + Compaction* const compaction; + + // Sequence numbers < smallest_snapshot are not significant since we + // will never have to service a snapshot below smallest_snapshot. + // Therefore if we have seen a sequence number S <= smallest_snapshot, + // we can drop all entries for the same key with sequence numbers < S. + SequenceNumber smallest_snapshot; + + // Files produced by compaction + struct Output { + uint64_t number; + uint64_t file_size; + InternalKey smallest, largest; + }; + std::vector outputs; + + // State kept for output being generated + WritableFile* outfile; + TableBuilder* builder; + + uint64_t total_bytes; + + Output* current_output() { return &outputs[outputs.size()-1]; } + + explicit CompactionState(Compaction* c) + : compaction(c), + outfile(NULL), + builder(NULL), + total_bytes(0) { + } +}; + +// Fix user-supplied options to be reasonable +template +static void ClipToRange(T* ptr, V minvalue, V maxvalue) { + if (static_cast(*ptr) > maxvalue) *ptr = maxvalue; + if (static_cast(*ptr) < minvalue) *ptr = minvalue; +} +Options SanitizeOptions(const std::string& dbname, + const InternalKeyComparator* icmp, + const InternalFilterPolicy* ipolicy, + const Options& src) { + Options result = src; + result.comparator = icmp; + result.filter_policy = (src.filter_policy != NULL) ? ipolicy : NULL; + ClipToRange(&result.max_open_files, 64 + kNumNonTableCacheFiles, 50000); + ClipToRange(&result.write_buffer_size, 64<<10, 1<<30); + ClipToRange(&result.block_size, 1<<10, 4<<20); + if (result.info_log == NULL) { + // Open a log file in the same directory as the db + src.env->CreateDir(dbname); // In case it does not exist + src.env->RenameFile(InfoLogFileName(dbname), OldInfoLogFileName(dbname)); + Status s = src.env->NewLogger(InfoLogFileName(dbname), &result.info_log); + if (!s.ok()) { + // No place suitable for logging + result.info_log = NULL; + } + } + if (result.block_cache == NULL) { + result.block_cache = NewLRUCache(8 << 20); + } + return result; +} + +DBImpl::DBImpl(const Options& raw_options, const std::string& dbname) + : env_(raw_options.env), + internal_comparator_(raw_options.comparator), + internal_filter_policy_(raw_options.filter_policy), + options_(SanitizeOptions(dbname, &internal_comparator_, + &internal_filter_policy_, raw_options)), + owns_info_log_(options_.info_log != raw_options.info_log), + owns_cache_(options_.block_cache != raw_options.block_cache), + dbname_(dbname), + db_lock_(NULL), + shutting_down_(NULL), + bg_cv_(&mutex_), + mem_(new MemTable(internal_comparator_)), + imm_(NULL), + logfile_(NULL), + logfile_number_(0), + log_(NULL), + seed_(0), + tmp_batch_(new WriteBatch), + bg_compaction_scheduled_(false), + manual_compaction_(NULL) { + mem_->Ref(); + has_imm_.Release_Store(NULL); + + // Reserve ten files or so for other uses and give the rest to TableCache. + const int table_cache_size = options_.max_open_files - kNumNonTableCacheFiles; + table_cache_ = new TableCache(dbname_, &options_, table_cache_size); + + versions_ = new VersionSet(dbname_, &options_, table_cache_, + &internal_comparator_); +} + +DBImpl::~DBImpl() { + // Wait for background work to finish + mutex_.Lock(); + shutting_down_.Release_Store(this); // Any non-NULL value is ok + while (bg_compaction_scheduled_) { + bg_cv_.Wait(); + } + mutex_.Unlock(); + + if (db_lock_ != NULL) { + env_->UnlockFile(db_lock_); + } + + delete versions_; + if (mem_ != NULL) mem_->Unref(); + if (imm_ != NULL) imm_->Unref(); + delete tmp_batch_; + delete log_; + delete logfile_; + delete table_cache_; + + if (owns_info_log_) { + delete options_.info_log; + } + if (owns_cache_) { + delete options_.block_cache; + } +} + +Status DBImpl::NewDB() { + VersionEdit new_db; + new_db.SetComparatorName(user_comparator()->Name()); + new_db.SetLogNumber(0); + new_db.SetNextFile(2); + new_db.SetLastSequence(0); + + const std::string manifest = DescriptorFileName(dbname_, 1); + WritableFile* file; + Status s = env_->NewWritableFile(manifest, &file); + if (!s.ok()) { + return s; + } + { + log::Writer log(file); + std::string record; + new_db.EncodeTo(&record); + s = log.AddRecord(record); + if (s.ok()) { + s = file->Close(); + } + } + delete file; + if (s.ok()) { + // Make "CURRENT" file that points to the new manifest file. + s = SetCurrentFile(env_, dbname_, 1); + } else { + env_->DeleteFile(manifest); + } + return s; +} + +void DBImpl::MaybeIgnoreError(Status* s) const { + if (s->ok() || options_.paranoid_checks) { + // No change needed + } else { + Log(options_.info_log, "Ignoring error %s", s->ToString().c_str()); + *s = Status::OK(); + } +} + +void DBImpl::DeleteObsoleteFiles() { + if (!bg_error_.ok()) { + // After a background error, we don't know whether a new version may + // or may not have been committed, so we cannot safely garbage collect. + return; + } + + // Make a set of all of the live files + std::set live = pending_outputs_; + versions_->AddLiveFiles(&live); + + std::vector filenames; + env_->GetChildren(dbname_, &filenames); // Ignoring errors on purpose + uint64_t number; + FileType type; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type)) { + bool keep = true; + switch (type) { + case kLogFile: + keep = ((number >= versions_->LogNumber()) || + (number == versions_->PrevLogNumber())); + break; + case kDescriptorFile: + // Keep my manifest file, and any newer incarnations' + // (in case there is a race that allows other incarnations) + keep = (number >= versions_->ManifestFileNumber()); + break; + case kTableFile: + keep = (live.find(number) != live.end()); + break; + case kTempFile: + // Any temp files that are currently being written to must + // be recorded in pending_outputs_, which is inserted into "live" + keep = (live.find(number) != live.end()); + break; + case kCurrentFile: + case kDBLockFile: + case kInfoLogFile: + keep = true; + break; + } + + if (!keep) { + if (type == kTableFile) { + table_cache_->Evict(number); + } + Log(options_.info_log, "Delete type=%d #%lld\n", + int(type), + static_cast(number)); + env_->DeleteFile(dbname_ + "/" + filenames[i]); + } + } + } +} + +Status DBImpl::Recover(VersionEdit* edit) { + mutex_.AssertHeld(); + + // Ignore error from CreateDir since the creation of the DB is + // committed only when the descriptor is created, and this directory + // may already exist from a previous failed creation attempt. + env_->CreateDir(dbname_); + assert(db_lock_ == NULL); + Status s = env_->LockFile(LockFileName(dbname_), &db_lock_); + if (!s.ok()) { + return s; + } + + if (!env_->FileExists(CurrentFileName(dbname_))) { + if (options_.create_if_missing) { + s = NewDB(); + if (!s.ok()) { + return s; + } + } else { + return Status::InvalidArgument( + dbname_, "does not exist (create_if_missing is false)"); + } + } else { + if (options_.error_if_exists) { + return Status::InvalidArgument( + dbname_, "exists (error_if_exists is true)"); + } + } + + s = versions_->Recover(); + if (s.ok()) { + SequenceNumber max_sequence(0); + + // Recover from all newer log files than the ones named in the + // descriptor (new log files may have been added by the previous + // incarnation without registering them in the descriptor). + // + // Note that PrevLogNumber() is no longer used, but we pay + // attention to it in case we are recovering a database + // produced by an older version of leveldb. + const uint64_t min_log = versions_->LogNumber(); + const uint64_t prev_log = versions_->PrevLogNumber(); + std::vector filenames; + s = env_->GetChildren(dbname_, &filenames); + if (!s.ok()) { + return s; + } + std::set expected; + versions_->AddLiveFiles(&expected); + uint64_t number; + FileType type; + std::vector logs; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type)) { + expected.erase(number); + if (type == kLogFile && ((number >= min_log) || (number == prev_log))) + logs.push_back(number); + } + } + if (!expected.empty()) { + char buf[50]; + snprintf(buf, sizeof(buf), "%d missing files; e.g.", + static_cast(expected.size())); + return Status::Corruption(buf, TableFileName(dbname_, *(expected.begin()))); + } + + // Recover in the order in which the logs were generated + std::sort(logs.begin(), logs.end()); + for (size_t i = 0; i < logs.size(); i++) { + s = RecoverLogFile(logs[i], edit, &max_sequence); + + // The previous incarnation may not have written any MANIFEST + // records after allocating this log number. So we manually + // update the file number allocation counter in VersionSet. + versions_->MarkFileNumberUsed(logs[i]); + } + + if (s.ok()) { + if (versions_->LastSequence() < max_sequence) { + versions_->SetLastSequence(max_sequence); + } + } + } + + return s; +} + +Status DBImpl::RecoverLogFile(uint64_t log_number, + VersionEdit* edit, + SequenceNumber* max_sequence) { + struct LogReporter : public log::Reader::Reporter { + Env* env; + Logger* info_log; + const char* fname; + Status* status; // NULL if options_.paranoid_checks==false + virtual void Corruption(size_t bytes, const Status& s) { + Log(info_log, "%s%s: dropping %d bytes; %s", + (this->status == NULL ? "(ignoring error) " : ""), + fname, static_cast(bytes), s.ToString().c_str()); + if (this->status != NULL && this->status->ok()) *this->status = s; + } + }; + + mutex_.AssertHeld(); + + // Open the log file + std::string fname = LogFileName(dbname_, log_number); + SequentialFile* file; + Status status = env_->NewSequentialFile(fname, &file); + if (!status.ok()) { + MaybeIgnoreError(&status); + return status; + } + + // Create the log reader. + LogReporter reporter; + reporter.env = env_; + reporter.info_log = options_.info_log; + reporter.fname = fname.c_str(); + reporter.status = (options_.paranoid_checks ? &status : NULL); + // We intentionally make log::Reader do checksumming even if + // paranoid_checks==false so that corruptions cause entire commits + // to be skipped instead of propagating bad information (like overly + // large sequence numbers). + log::Reader reader(file, &reporter, true/*checksum*/, + 0/*initial_offset*/); + Log(options_.info_log, "Recovering log #%llu", + (unsigned long long) log_number); + + // Read all the records and add to a memtable + std::string scratch; + Slice record; + WriteBatch batch; + MemTable* mem = NULL; + while (reader.ReadRecord(&record, &scratch) && + status.ok()) { + if (record.size() < 12) { + reporter.Corruption( + record.size(), Status::Corruption("log record too small")); + continue; + } + WriteBatchInternal::SetContents(&batch, record); + + if (mem == NULL) { + mem = new MemTable(internal_comparator_); + mem->Ref(); + } + status = WriteBatchInternal::InsertInto(&batch, mem); + MaybeIgnoreError(&status); + if (!status.ok()) { + break; + } + const SequenceNumber last_seq = + WriteBatchInternal::Sequence(&batch) + + WriteBatchInternal::Count(&batch) - 1; + if (last_seq > *max_sequence) { + *max_sequence = last_seq; + } + + if (mem->ApproximateMemoryUsage() > options_.write_buffer_size) { + status = WriteLevel0Table(mem, edit, NULL); + if (!status.ok()) { + // Reflect errors immediately so that conditions like full + // file-systems cause the DB::Open() to fail. + break; + } + mem->Unref(); + mem = NULL; + } + } + + if (status.ok() && mem != NULL) { + status = WriteLevel0Table(mem, edit, NULL); + // Reflect errors immediately so that conditions like full + // file-systems cause the DB::Open() to fail. + } + + if (mem != NULL) mem->Unref(); + delete file; + return status; +} + +Status DBImpl::WriteLevel0Table(MemTable* mem, VersionEdit* edit, + Version* base) { + mutex_.AssertHeld(); + const uint64_t start_micros = env_->NowMicros(); + FileMetaData meta; + meta.number = versions_->NewFileNumber(); + pending_outputs_.insert(meta.number); + Iterator* iter = mem->NewIterator(); + Log(options_.info_log, "Level-0 table #%llu: started", + (unsigned long long) meta.number); + + Status s; + { + mutex_.Unlock(); + s = BuildTable(dbname_, env_, options_, table_cache_, iter, &meta); + mutex_.Lock(); + } + + Log(options_.info_log, "Level-0 table #%llu: %lld bytes %s", + (unsigned long long) meta.number, + (unsigned long long) meta.file_size, + s.ToString().c_str()); + delete iter; + pending_outputs_.erase(meta.number); + + + // Note that if file_size is zero, the file has been deleted and + // should not be added to the manifest. + int level = 0; + if (s.ok() && meta.file_size > 0) { + const Slice min_user_key = meta.smallest.user_key(); + const Slice max_user_key = meta.largest.user_key(); + if (base != NULL) { + level = base->PickLevelForMemTableOutput(min_user_key, max_user_key); + } + edit->AddFile(level, meta.number, meta.file_size, + meta.smallest, meta.largest); + } + + CompactionStats stats; + stats.micros = env_->NowMicros() - start_micros; + stats.bytes_written = meta.file_size; + stats_[level].Add(stats); + return s; +} + +void DBImpl::CompactMemTable() { + mutex_.AssertHeld(); + assert(imm_ != NULL); + + // Save the contents of the memtable as a new Table + VersionEdit edit; + Version* base = versions_->current(); + base->Ref(); + Status s = WriteLevel0Table(imm_, &edit, base); + base->Unref(); + + if (s.ok() && shutting_down_.Acquire_Load()) { + s = Status::IOError("Deleting DB during memtable compaction"); + } + + // Replace immutable memtable with the generated Table + if (s.ok()) { + edit.SetPrevLogNumber(0); + edit.SetLogNumber(logfile_number_); // Earlier logs no longer needed + s = versions_->LogAndApply(&edit, &mutex_); + } + + if (s.ok()) { + // Commit to the new state + imm_->Unref(); + imm_ = NULL; + has_imm_.Release_Store(NULL); + DeleteObsoleteFiles(); + } else { + RecordBackgroundError(s); + } +} + +void DBImpl::CompactRange(const Slice* begin, const Slice* end) { + int max_level_with_files = 1; + { + MutexLock l(&mutex_); + Version* base = versions_->current(); + for (int level = 1; level < config::kNumLevels; level++) { + if (base->OverlapInLevel(level, begin, end)) { + max_level_with_files = level; + } + } + } + TEST_CompactMemTable(); // TODO(sanjay): Skip if memtable does not overlap + for (int level = 0; level < max_level_with_files; level++) { + TEST_CompactRange(level, begin, end); + } +} + +void DBImpl::TEST_CompactRange(int level, const Slice* begin,const Slice* end) { + assert(level >= 0); + assert(level + 1 < config::kNumLevels); + + InternalKey begin_storage, end_storage; + + ManualCompaction manual; + manual.level = level; + manual.done = false; + if (begin == NULL) { + manual.begin = NULL; + } else { + begin_storage = InternalKey(*begin, kMaxSequenceNumber, kValueTypeForSeek); + manual.begin = &begin_storage; + } + if (end == NULL) { + manual.end = NULL; + } else { + end_storage = InternalKey(*end, 0, static_cast(0)); + manual.end = &end_storage; + } + + MutexLock l(&mutex_); + while (!manual.done && !shutting_down_.Acquire_Load() && bg_error_.ok()) { + if (manual_compaction_ == NULL) { // Idle + manual_compaction_ = &manual; + MaybeScheduleCompaction(); + } else { // Running either my compaction or another compaction. + bg_cv_.Wait(); + } + } + if (manual_compaction_ == &manual) { + // Cancel my manual compaction since we aborted early for some reason. + manual_compaction_ = NULL; + } +} + +Status DBImpl::TEST_CompactMemTable() { + // NULL batch means just wait for earlier writes to be done + Status s = Write(WriteOptions(), NULL); + if (s.ok()) { + // Wait until the compaction completes + MutexLock l(&mutex_); + while (imm_ != NULL && bg_error_.ok()) { + bg_cv_.Wait(); + } + if (imm_ != NULL) { + s = bg_error_; + } + } + return s; +} + +void DBImpl::RecordBackgroundError(const Status& s) { + mutex_.AssertHeld(); + if (bg_error_.ok()) { + bg_error_ = s; + bg_cv_.SignalAll(); + } +} + +void DBImpl::MaybeScheduleCompaction() { + mutex_.AssertHeld(); + if (bg_compaction_scheduled_) { + // Already scheduled + } else if (shutting_down_.Acquire_Load()) { + // DB is being deleted; no more background compactions + } else if (!bg_error_.ok()) { + // Already got an error; no more changes + } else if (imm_ == NULL && + manual_compaction_ == NULL && + !versions_->NeedsCompaction()) { + // No work to be done + } else { + bg_compaction_scheduled_ = true; + env_->Schedule(&DBImpl::BGWork, this); + } +} + +void DBImpl::BGWork(void* db) { + reinterpret_cast(db)->BackgroundCall(); +} + +void DBImpl::BackgroundCall() { + MutexLock l(&mutex_); + assert(bg_compaction_scheduled_); + if (shutting_down_.Acquire_Load()) { + // No more background work when shutting down. + } else if (!bg_error_.ok()) { + // No more background work after a background error. + } else { + BackgroundCompaction(); + } + + bg_compaction_scheduled_ = false; + + // Previous compaction may have produced too many files in a level, + // so reschedule another compaction if needed. + MaybeScheduleCompaction(); + bg_cv_.SignalAll(); +} + +void DBImpl::BackgroundCompaction() { + mutex_.AssertHeld(); + + if (imm_ != NULL) { + CompactMemTable(); + return; + } + + Compaction* c; + bool is_manual = (manual_compaction_ != NULL); + InternalKey manual_end; + if (is_manual) { + ManualCompaction* m = manual_compaction_; + c = versions_->CompactRange(m->level, m->begin, m->end); + m->done = (c == NULL); + if (c != NULL) { + manual_end = c->input(0, c->num_input_files(0) - 1)->largest; + } + Log(options_.info_log, + "Manual compaction at level-%d from %s .. %s; will stop at %s\n", + m->level, + (m->begin ? m->begin->DebugString().c_str() : "(begin)"), + (m->end ? m->end->DebugString().c_str() : "(end)"), + (m->done ? "(end)" : manual_end.DebugString().c_str())); + } else { + c = versions_->PickCompaction(); + } + + Status status; + if (c == NULL) { + // Nothing to do + } else if (!is_manual && c->IsTrivialMove()) { + // Move file to next level + assert(c->num_input_files(0) == 1); + FileMetaData* f = c->input(0, 0); + c->edit()->DeleteFile(c->level(), f->number); + c->edit()->AddFile(c->level() + 1, f->number, f->file_size, + f->smallest, f->largest); + status = versions_->LogAndApply(c->edit(), &mutex_); + if (!status.ok()) { + RecordBackgroundError(status); + } + VersionSet::LevelSummaryStorage tmp; + Log(options_.info_log, "Moved #%lld to level-%d %lld bytes %s: %s\n", + static_cast(f->number), + c->level() + 1, + static_cast(f->file_size), + status.ToString().c_str(), + versions_->LevelSummary(&tmp)); + } else { + CompactionState* compact = new CompactionState(c); + status = DoCompactionWork(compact); + if (!status.ok()) { + RecordBackgroundError(status); + } + CleanupCompaction(compact); + c->ReleaseInputs(); + DeleteObsoleteFiles(); + } + delete c; + + if (status.ok()) { + // Done + } else if (shutting_down_.Acquire_Load()) { + // Ignore compaction errors found during shutting down + } else { + Log(options_.info_log, + "Compaction error: %s", status.ToString().c_str()); + } + + if (is_manual) { + ManualCompaction* m = manual_compaction_; + if (!status.ok()) { + m->done = true; + } + if (!m->done) { + // We only compacted part of the requested range. Update *m + // to the range that is left to be compacted. + m->tmp_storage = manual_end; + m->begin = &m->tmp_storage; + } + manual_compaction_ = NULL; + } +} + +void DBImpl::CleanupCompaction(CompactionState* compact) { + mutex_.AssertHeld(); + if (compact->builder != NULL) { + // May happen if we get a shutdown call in the middle of compaction + compact->builder->Abandon(); + delete compact->builder; + } else { + assert(compact->outfile == NULL); + } + delete compact->outfile; + for (size_t i = 0; i < compact->outputs.size(); i++) { + const CompactionState::Output& out = compact->outputs[i]; + pending_outputs_.erase(out.number); + } + delete compact; +} + +Status DBImpl::OpenCompactionOutputFile(CompactionState* compact) { + assert(compact != NULL); + assert(compact->builder == NULL); + uint64_t file_number; + { + mutex_.Lock(); + file_number = versions_->NewFileNumber(); + pending_outputs_.insert(file_number); + CompactionState::Output out; + out.number = file_number; + out.smallest.Clear(); + out.largest.Clear(); + compact->outputs.push_back(out); + mutex_.Unlock(); + } + + // Make the output file + std::string fname = TableFileName(dbname_, file_number); + Status s = env_->NewWritableFile(fname, &compact->outfile); + if (s.ok()) { + compact->builder = new TableBuilder(options_, compact->outfile); + } + return s; +} + +Status DBImpl::FinishCompactionOutputFile(CompactionState* compact, + Iterator* input) { + assert(compact != NULL); + assert(compact->outfile != NULL); + assert(compact->builder != NULL); + + const uint64_t output_number = compact->current_output()->number; + assert(output_number != 0); + + // Check for iterator errors + Status s = input->status(); + const uint64_t current_entries = compact->builder->NumEntries(); + if (s.ok()) { + s = compact->builder->Finish(); + } else { + compact->builder->Abandon(); + } + const uint64_t current_bytes = compact->builder->FileSize(); + compact->current_output()->file_size = current_bytes; + compact->total_bytes += current_bytes; + delete compact->builder; + compact->builder = NULL; + + // Finish and check for file errors + if (s.ok()) { + s = compact->outfile->Sync(); + } + if (s.ok()) { + s = compact->outfile->Close(); + } + delete compact->outfile; + compact->outfile = NULL; + + if (s.ok() && current_entries > 0) { + // Verify that the table is usable + Iterator* iter = table_cache_->NewIterator(ReadOptions(), + output_number, + current_bytes); + s = iter->status(); + delete iter; + if (s.ok()) { + Log(options_.info_log, + "Generated table #%llu: %lld keys, %lld bytes", + (unsigned long long) output_number, + (unsigned long long) current_entries, + (unsigned long long) current_bytes); + } + } + return s; +} + + +Status DBImpl::InstallCompactionResults(CompactionState* compact) { + mutex_.AssertHeld(); + Log(options_.info_log, "Compacted %d@%d + %d@%d files => %lld bytes", + compact->compaction->num_input_files(0), + compact->compaction->level(), + compact->compaction->num_input_files(1), + compact->compaction->level() + 1, + static_cast(compact->total_bytes)); + + // Add compaction outputs + compact->compaction->AddInputDeletions(compact->compaction->edit()); + const int level = compact->compaction->level(); + for (size_t i = 0; i < compact->outputs.size(); i++) { + const CompactionState::Output& out = compact->outputs[i]; + compact->compaction->edit()->AddFile( + level + 1, + out.number, out.file_size, out.smallest, out.largest); + } + return versions_->LogAndApply(compact->compaction->edit(), &mutex_); +} + +Status DBImpl::DoCompactionWork(CompactionState* compact) { + const uint64_t start_micros = env_->NowMicros(); + int64_t imm_micros = 0; // Micros spent doing imm_ compactions + + Log(options_.info_log, "Compacting %d@%d + %d@%d files", + compact->compaction->num_input_files(0), + compact->compaction->level(), + compact->compaction->num_input_files(1), + compact->compaction->level() + 1); + + assert(versions_->NumLevelFiles(compact->compaction->level()) > 0); + assert(compact->builder == NULL); + assert(compact->outfile == NULL); + if (snapshots_.empty()) { + compact->smallest_snapshot = versions_->LastSequence(); + } else { + compact->smallest_snapshot = snapshots_.oldest()->number_; + } + + // Release mutex while we're actually doing the compaction work + mutex_.Unlock(); + + Iterator* input = versions_->MakeInputIterator(compact->compaction); + input->SeekToFirst(); + Status status; + ParsedInternalKey ikey; + std::string current_user_key; + bool has_current_user_key = false; + SequenceNumber last_sequence_for_key = kMaxSequenceNumber; + for (; input->Valid() && !shutting_down_.Acquire_Load(); ) { + // Prioritize immutable compaction work + if (has_imm_.NoBarrier_Load() != NULL) { + const uint64_t imm_start = env_->NowMicros(); + mutex_.Lock(); + if (imm_ != NULL) { + CompactMemTable(); + bg_cv_.SignalAll(); // Wakeup MakeRoomForWrite() if necessary + } + mutex_.Unlock(); + imm_micros += (env_->NowMicros() - imm_start); + } + + Slice key = input->key(); + if (compact->compaction->ShouldStopBefore(key) && + compact->builder != NULL) { + status = FinishCompactionOutputFile(compact, input); + if (!status.ok()) { + break; + } + } + + // Handle key/value, add to state, etc. + bool drop = false; + if (!ParseInternalKey(key, &ikey)) { + // Do not hide error keys + current_user_key.clear(); + has_current_user_key = false; + last_sequence_for_key = kMaxSequenceNumber; + } else { + if (!has_current_user_key || + user_comparator()->Compare(ikey.user_key, + Slice(current_user_key)) != 0) { + // First occurrence of this user key + current_user_key.assign(ikey.user_key.data(), ikey.user_key.size()); + has_current_user_key = true; + last_sequence_for_key = kMaxSequenceNumber; + } + + if (last_sequence_for_key <= compact->smallest_snapshot) { + // Hidden by an newer entry for same user key + drop = true; // (A) + } else if (ikey.type == kTypeDeletion && + ikey.sequence <= compact->smallest_snapshot && + compact->compaction->IsBaseLevelForKey(ikey.user_key)) { + // For this user key: + // (1) there is no data in higher levels + // (2) data in lower levels will have larger sequence numbers + // (3) data in layers that are being compacted here and have + // smaller sequence numbers will be dropped in the next + // few iterations of this loop (by rule (A) above). + // Therefore this deletion marker is obsolete and can be dropped. + drop = true; + } + + last_sequence_for_key = ikey.sequence; + } +#if 0 + Log(options_.info_log, + " Compact: %s, seq %d, type: %d %d, drop: %d, is_base: %d, " + "%d smallest_snapshot: %d", + ikey.user_key.ToString().c_str(), + (int)ikey.sequence, ikey.type, kTypeValue, drop, + compact->compaction->IsBaseLevelForKey(ikey.user_key), + (int)last_sequence_for_key, (int)compact->smallest_snapshot); +#endif + + if (!drop) { + // Open output file if necessary + if (compact->builder == NULL) { + status = OpenCompactionOutputFile(compact); + if (!status.ok()) { + break; + } + } + if (compact->builder->NumEntries() == 0) { + compact->current_output()->smallest.DecodeFrom(key); + } + compact->current_output()->largest.DecodeFrom(key); + compact->builder->Add(key, input->value()); + + // Close output file if it is big enough + if (compact->builder->FileSize() >= + compact->compaction->MaxOutputFileSize()) { + status = FinishCompactionOutputFile(compact, input); + if (!status.ok()) { + break; + } + } + } + + input->Next(); + } + + if (status.ok() && shutting_down_.Acquire_Load()) { + status = Status::IOError("Deleting DB during compaction"); + } + if (status.ok() && compact->builder != NULL) { + status = FinishCompactionOutputFile(compact, input); + } + if (status.ok()) { + status = input->status(); + } + delete input; + input = NULL; + + CompactionStats stats; + stats.micros = env_->NowMicros() - start_micros - imm_micros; + for (int which = 0; which < 2; which++) { + for (int i = 0; i < compact->compaction->num_input_files(which); i++) { + stats.bytes_read += compact->compaction->input(which, i)->file_size; + } + } + for (size_t i = 0; i < compact->outputs.size(); i++) { + stats.bytes_written += compact->outputs[i].file_size; + } + + mutex_.Lock(); + stats_[compact->compaction->level() + 1].Add(stats); + + if (status.ok()) { + status = InstallCompactionResults(compact); + } + if (!status.ok()) { + RecordBackgroundError(status); + } + VersionSet::LevelSummaryStorage tmp; + Log(options_.info_log, + "compacted to: %s", versions_->LevelSummary(&tmp)); + return status; +} + +namespace { +struct IterState { + port::Mutex* mu; + Version* version; + MemTable* mem; + MemTable* imm; +}; + +static void CleanupIteratorState(void* arg1, void* arg2) { + IterState* state = reinterpret_cast(arg1); + state->mu->Lock(); + state->mem->Unref(); + if (state->imm != NULL) state->imm->Unref(); + state->version->Unref(); + state->mu->Unlock(); + delete state; +} +} // namespace + +Iterator* DBImpl::NewInternalIterator(const ReadOptions& options, + SequenceNumber* latest_snapshot, + uint32_t* seed) { + IterState* cleanup = new IterState; + mutex_.Lock(); + *latest_snapshot = versions_->LastSequence(); + + // Collect together all needed child iterators + std::vector list; + list.push_back(mem_->NewIterator()); + mem_->Ref(); + if (imm_ != NULL) { + list.push_back(imm_->NewIterator()); + imm_->Ref(); + } + versions_->current()->AddIterators(options, &list); + Iterator* internal_iter = + NewMergingIterator(&internal_comparator_, &list[0], list.size()); + versions_->current()->Ref(); + + cleanup->mu = &mutex_; + cleanup->mem = mem_; + cleanup->imm = imm_; + cleanup->version = versions_->current(); + internal_iter->RegisterCleanup(CleanupIteratorState, cleanup, NULL); + + *seed = ++seed_; + mutex_.Unlock(); + return internal_iter; +} + +Iterator* DBImpl::TEST_NewInternalIterator() { + SequenceNumber ignored; + uint32_t ignored_seed; + return NewInternalIterator(ReadOptions(), &ignored, &ignored_seed); +} + +int64_t DBImpl::TEST_MaxNextLevelOverlappingBytes() { + MutexLock l(&mutex_); + return versions_->MaxNextLevelOverlappingBytes(); +} + +Status DBImpl::Get(const ReadOptions& options, + const Slice& key, + std::string* value) { + Status s; + MutexLock l(&mutex_); + SequenceNumber snapshot; + if (options.snapshot != NULL) { + snapshot = reinterpret_cast(options.snapshot)->number_; + } else { + snapshot = versions_->LastSequence(); + } + + MemTable* mem = mem_; + MemTable* imm = imm_; + Version* current = versions_->current(); + mem->Ref(); + if (imm != NULL) imm->Ref(); + current->Ref(); + + bool have_stat_update = false; + Version::GetStats stats; + + // Unlock while reading from files and memtables + { + mutex_.Unlock(); + // First look in the memtable, then in the immutable memtable (if any). + LookupKey lkey(key, snapshot); + if (mem->Get(lkey, value, &s)) { + // Done + } else if (imm != NULL && imm->Get(lkey, value, &s)) { + // Done + } else { + s = current->Get(options, lkey, value, &stats); + have_stat_update = true; + } + mutex_.Lock(); + } + + if (have_stat_update && current->UpdateStats(stats)) { + MaybeScheduleCompaction(); + } + mem->Unref(); + if (imm != NULL) imm->Unref(); + current->Unref(); + return s; +} + +Iterator* DBImpl::NewIterator(const ReadOptions& options) { + SequenceNumber latest_snapshot; + uint32_t seed; + Iterator* iter = NewInternalIterator(options, &latest_snapshot, &seed); + return NewDBIterator( + this, user_comparator(), iter, + (options.snapshot != NULL + ? reinterpret_cast(options.snapshot)->number_ + : latest_snapshot), + seed); +} + +void DBImpl::RecordReadSample(Slice key) { + MutexLock l(&mutex_); + if (versions_->current()->RecordReadSample(key)) { + MaybeScheduleCompaction(); + } +} + +const Snapshot* DBImpl::GetSnapshot() { + MutexLock l(&mutex_); + return snapshots_.New(versions_->LastSequence()); +} + +void DBImpl::ReleaseSnapshot(const Snapshot* s) { + MutexLock l(&mutex_); + snapshots_.Delete(reinterpret_cast(s)); +} + +// Convenience methods +Status DBImpl::Put(const WriteOptions& o, const Slice& key, const Slice& val) { + return DB::Put(o, key, val); +} + +Status DBImpl::Delete(const WriteOptions& options, const Slice& key) { + return DB::Delete(options, key); +} + +Status DBImpl::Write(const WriteOptions& options, WriteBatch* my_batch) { + Writer w(&mutex_); + w.batch = my_batch; + w.sync = options.sync; + w.done = false; + + MutexLock l(&mutex_); + writers_.push_back(&w); + while (!w.done && &w != writers_.front()) { + w.cv.Wait(); + } + if (w.done) { + return w.status; + } + + // May temporarily unlock and wait. + Status status = MakeRoomForWrite(my_batch == NULL); + uint64_t last_sequence = versions_->LastSequence(); + Writer* last_writer = &w; + if (status.ok() && my_batch != NULL) { // NULL batch is for compactions + WriteBatch* updates = BuildBatchGroup(&last_writer); + WriteBatchInternal::SetSequence(updates, last_sequence + 1); + last_sequence += WriteBatchInternal::Count(updates); + + // Add to log and apply to memtable. We can release the lock + // during this phase since &w is currently responsible for logging + // and protects against concurrent loggers and concurrent writes + // into mem_. + { + mutex_.Unlock(); + status = log_->AddRecord(WriteBatchInternal::Contents(updates)); + bool sync_error = false; + if (status.ok() && options.sync) { + status = logfile_->Sync(); + if (!status.ok()) { + sync_error = true; + } + } + if (status.ok()) { + status = WriteBatchInternal::InsertInto(updates, mem_); + } + mutex_.Lock(); + if (sync_error) { + // The state of the log file is indeterminate: the log record we + // just added may or may not show up when the DB is re-opened. + // So we force the DB into a mode where all future writes fail. + RecordBackgroundError(status); + } + } + if (updates == tmp_batch_) tmp_batch_->Clear(); + + versions_->SetLastSequence(last_sequence); + } + + while (true) { + Writer* ready = writers_.front(); + writers_.pop_front(); + if (ready != &w) { + ready->status = status; + ready->done = true; + ready->cv.Signal(); + } + if (ready == last_writer) break; + } + + // Notify new head of write queue + if (!writers_.empty()) { + writers_.front()->cv.Signal(); + } + + return status; +} + +// REQUIRES: Writer list must be non-empty +// REQUIRES: First writer must have a non-NULL batch +WriteBatch* DBImpl::BuildBatchGroup(Writer** last_writer) { + assert(!writers_.empty()); + Writer* first = writers_.front(); + WriteBatch* result = first->batch; + assert(result != NULL); + + size_t size = WriteBatchInternal::ByteSize(first->batch); + + // Allow the group to grow up to a maximum size, but if the + // original write is small, limit the growth so we do not slow + // down the small write too much. + size_t max_size = 1 << 20; + if (size <= (128<<10)) { + max_size = size + (128<<10); + } + + *last_writer = first; + std::deque::iterator iter = writers_.begin(); + ++iter; // Advance past "first" + for (; iter != writers_.end(); ++iter) { + Writer* w = *iter; + if (w->sync && !first->sync) { + // Do not include a sync write into a batch handled by a non-sync write. + break; + } + + if (w->batch != NULL) { + size += WriteBatchInternal::ByteSize(w->batch); + if (size > max_size) { + // Do not make batch too big + break; + } + + // Append to *result + if (result == first->batch) { + // Switch to temporary batch instead of disturbing caller's batch + result = tmp_batch_; + assert(WriteBatchInternal::Count(result) == 0); + WriteBatchInternal::Append(result, first->batch); + } + WriteBatchInternal::Append(result, w->batch); + } + *last_writer = w; + } + return result; +} + +// REQUIRES: mutex_ is held +// REQUIRES: this thread is currently at the front of the writer queue +Status DBImpl::MakeRoomForWrite(bool force) { + mutex_.AssertHeld(); + assert(!writers_.empty()); + bool allow_delay = !force; + Status s; + while (true) { + if (!bg_error_.ok()) { + // Yield previous error + s = bg_error_; + break; + } else if ( + allow_delay && + versions_->NumLevelFiles(0) >= config::kL0_SlowdownWritesTrigger) { + // We are getting close to hitting a hard limit on the number of + // L0 files. Rather than delaying a single write by several + // seconds when we hit the hard limit, start delaying each + // individual write by 1ms to reduce latency variance. Also, + // this delay hands over some CPU to the compaction thread in + // case it is sharing the same core as the writer. + mutex_.Unlock(); + env_->SleepForMicroseconds(1000); + allow_delay = false; // Do not delay a single write more than once + mutex_.Lock(); + } else if (!force && + (mem_->ApproximateMemoryUsage() <= options_.write_buffer_size)) { + // There is room in current memtable + break; + } else if (imm_ != NULL) { + // We have filled up the current memtable, but the previous + // one is still being compacted, so we wait. + Log(options_.info_log, "Current memtable full; waiting...\n"); + bg_cv_.Wait(); + } else if (versions_->NumLevelFiles(0) >= config::kL0_StopWritesTrigger) { + // There are too many level-0 files. + Log(options_.info_log, "Too many L0 files; waiting...\n"); + bg_cv_.Wait(); + } else { + // Attempt to switch to a new memtable and trigger compaction of old + assert(versions_->PrevLogNumber() == 0); + uint64_t new_log_number = versions_->NewFileNumber(); + WritableFile* lfile = NULL; + s = env_->NewWritableFile(LogFileName(dbname_, new_log_number), &lfile); + if (!s.ok()) { + // Avoid chewing through file number space in a tight loop. + versions_->ReuseFileNumber(new_log_number); + break; + } + delete log_; + delete logfile_; + logfile_ = lfile; + logfile_number_ = new_log_number; + log_ = new log::Writer(lfile); + imm_ = mem_; + has_imm_.Release_Store(imm_); + mem_ = new MemTable(internal_comparator_); + mem_->Ref(); + force = false; // Do not force another compaction if have room + MaybeScheduleCompaction(); + } + } + return s; +} + +bool DBImpl::GetProperty(const Slice& property, std::string* value) { + value->clear(); + + MutexLock l(&mutex_); + Slice in = property; + Slice prefix("leveldb."); + if (!in.starts_with(prefix)) return false; + in.remove_prefix(prefix.size()); + + if (in.starts_with("num-files-at-level")) { + in.remove_prefix(strlen("num-files-at-level")); + uint64_t level; + bool ok = ConsumeDecimalNumber(&in, &level) && in.empty(); + if (!ok || level >= config::kNumLevels) { + return false; + } else { + char buf[100]; + snprintf(buf, sizeof(buf), "%d", + versions_->NumLevelFiles(static_cast(level))); + *value = buf; + return true; + } + } else if (in == "stats") { + char buf[200]; + snprintf(buf, sizeof(buf), + " Compactions\n" + "Level Files Size(MB) Time(sec) Read(MB) Write(MB)\n" + "--------------------------------------------------\n" + ); + value->append(buf); + for (int level = 0; level < config::kNumLevels; level++) { + int files = versions_->NumLevelFiles(level); + if (stats_[level].micros > 0 || files > 0) { + snprintf( + buf, sizeof(buf), + "%3d %8d %8.0f %9.0f %8.0f %9.0f\n", + level, + files, + versions_->NumLevelBytes(level) / 1048576.0, + stats_[level].micros / 1e6, + stats_[level].bytes_read / 1048576.0, + stats_[level].bytes_written / 1048576.0); + value->append(buf); + } + } + return true; + } else if (in == "sstables") { + *value = versions_->current()->DebugString(); + return true; + } + + return false; +} + +void DBImpl::GetApproximateSizes( + const Range* range, int n, + uint64_t* sizes) { + // TODO(opt): better implementation + Version* v; + { + MutexLock l(&mutex_); + versions_->current()->Ref(); + v = versions_->current(); + } + + for (int i = 0; i < n; i++) { + // Convert user_key into a corresponding internal key. + InternalKey k1(range[i].start, kMaxSequenceNumber, kValueTypeForSeek); + InternalKey k2(range[i].limit, kMaxSequenceNumber, kValueTypeForSeek); + uint64_t start = versions_->ApproximateOffsetOf(v, k1); + uint64_t limit = versions_->ApproximateOffsetOf(v, k2); + sizes[i] = (limit >= start ? limit - start : 0); + } + + { + MutexLock l(&mutex_); + v->Unref(); + } +} + +// Default implementations of convenience methods that subclasses of DB +// can call if they wish +Status DB::Put(const WriteOptions& opt, const Slice& key, const Slice& value) { + WriteBatch batch; + batch.Put(key, value); + return Write(opt, &batch); +} + +Status DB::Delete(const WriteOptions& opt, const Slice& key) { + WriteBatch batch; + batch.Delete(key); + return Write(opt, &batch); +} + +DB::~DB() { } + +Status DB::Open(const Options& options, const std::string& dbname, + DB** dbptr) { + *dbptr = NULL; + + DBImpl* impl = new DBImpl(options, dbname); + impl->mutex_.Lock(); + VersionEdit edit; + Status s = impl->Recover(&edit); // Handles create_if_missing, error_if_exists + if (s.ok()) { + uint64_t new_log_number = impl->versions_->NewFileNumber(); + WritableFile* lfile; + s = options.env->NewWritableFile(LogFileName(dbname, new_log_number), + &lfile); + if (s.ok()) { + edit.SetLogNumber(new_log_number); + impl->logfile_ = lfile; + impl->logfile_number_ = new_log_number; + impl->log_ = new log::Writer(lfile); + s = impl->versions_->LogAndApply(&edit, &impl->mutex_); + } + if (s.ok()) { + impl->DeleteObsoleteFiles(); + impl->MaybeScheduleCompaction(); + } + } + impl->mutex_.Unlock(); + if (s.ok()) { + *dbptr = impl; + } else { + delete impl; + } + return s; +} + +Snapshot::~Snapshot() { +} + +Status DestroyDB(const std::string& dbname, const Options& options) { + Env* env = options.env; + std::vector filenames; + // Ignore error in case directory does not exist + env->GetChildren(dbname, &filenames); + if (filenames.empty()) { + return Status::OK(); + } + + FileLock* lock; + const std::string lockname = LockFileName(dbname); + Status result = env->LockFile(lockname, &lock); + if (result.ok()) { + uint64_t number; + FileType type; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type) && + type != kDBLockFile) { // Lock file will be deleted at end + Status del = env->DeleteFile(dbname + "/" + filenames[i]); + if (result.ok() && !del.ok()) { + result = del; + } + } + } + env->UnlockFile(lock); // Ignore error since state is already gone + env->DeleteFile(lockname); + env->DeleteDir(dbname); // Ignore error in case dir contains other files + } + return result; +} + +} // namespace leveldb diff --git a/src/leveldb/db/db_impl.h b/src/leveldb/db/db_impl.h new file mode 100644 index 00000000..cfc99816 --- /dev/null +++ b/src/leveldb/db/db_impl.h @@ -0,0 +1,211 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_DB_IMPL_H_ +#define STORAGE_LEVELDB_DB_DB_IMPL_H_ + +#include +#include +#include "db/dbformat.h" +#include "db/log_writer.h" +#include "db/snapshot.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "port/port.h" +#include "port/thread_annotations.h" + +namespace leveldb { + +class MemTable; +class TableCache; +class Version; +class VersionEdit; +class VersionSet; + +class DBImpl : public DB { + public: + DBImpl(const Options& options, const std::string& dbname); + virtual ~DBImpl(); + + // Implementations of the DB interface + virtual Status Put(const WriteOptions&, const Slice& key, const Slice& value); + virtual Status Delete(const WriteOptions&, const Slice& key); + virtual Status Write(const WriteOptions& options, WriteBatch* updates); + virtual Status Get(const ReadOptions& options, + const Slice& key, + std::string* value); + virtual Iterator* NewIterator(const ReadOptions&); + virtual const Snapshot* GetSnapshot(); + virtual void ReleaseSnapshot(const Snapshot* snapshot); + virtual bool GetProperty(const Slice& property, std::string* value); + virtual void GetApproximateSizes(const Range* range, int n, uint64_t* sizes); + virtual void CompactRange(const Slice* begin, const Slice* end); + + // Extra methods (for testing) that are not in the public DB interface + + // Compact any files in the named level that overlap [*begin,*end] + void TEST_CompactRange(int level, const Slice* begin, const Slice* end); + + // Force current memtable contents to be compacted. + Status TEST_CompactMemTable(); + + // Return an internal iterator over the current state of the database. + // The keys of this iterator are internal keys (see format.h). + // The returned iterator should be deleted when no longer needed. + Iterator* TEST_NewInternalIterator(); + + // Return the maximum overlapping data (in bytes) at next level for any + // file at a level >= 1. + int64_t TEST_MaxNextLevelOverlappingBytes(); + + // Record a sample of bytes read at the specified internal key. + // Samples are taken approximately once every config::kReadBytesPeriod + // bytes. + void RecordReadSample(Slice key); + + private: + friend class DB; + struct CompactionState; + struct Writer; + + Iterator* NewInternalIterator(const ReadOptions&, + SequenceNumber* latest_snapshot, + uint32_t* seed); + + Status NewDB(); + + // Recover the descriptor from persistent storage. May do a significant + // amount of work to recover recently logged updates. Any changes to + // be made to the descriptor are added to *edit. + Status Recover(VersionEdit* edit) EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + void MaybeIgnoreError(Status* s) const; + + // Delete any unneeded files and stale in-memory entries. + void DeleteObsoleteFiles(); + + // Compact the in-memory write buffer to disk. Switches to a new + // log-file/memtable and writes a new descriptor iff successful. + // Errors are recorded in bg_error_. + void CompactMemTable() EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + Status RecoverLogFile(uint64_t log_number, + VersionEdit* edit, + SequenceNumber* max_sequence) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + Status WriteLevel0Table(MemTable* mem, VersionEdit* edit, Version* base) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + Status MakeRoomForWrite(bool force /* compact even if there is room? */) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + WriteBatch* BuildBatchGroup(Writer** last_writer); + + void RecordBackgroundError(const Status& s); + + void MaybeScheduleCompaction() EXCLUSIVE_LOCKS_REQUIRED(mutex_); + static void BGWork(void* db); + void BackgroundCall(); + void BackgroundCompaction() EXCLUSIVE_LOCKS_REQUIRED(mutex_); + void CleanupCompaction(CompactionState* compact) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + Status DoCompactionWork(CompactionState* compact) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + Status OpenCompactionOutputFile(CompactionState* compact); + Status FinishCompactionOutputFile(CompactionState* compact, Iterator* input); + Status InstallCompactionResults(CompactionState* compact) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + // Constant after construction + Env* const env_; + const InternalKeyComparator internal_comparator_; + const InternalFilterPolicy internal_filter_policy_; + const Options options_; // options_.comparator == &internal_comparator_ + bool owns_info_log_; + bool owns_cache_; + const std::string dbname_; + + // table_cache_ provides its own synchronization + TableCache* table_cache_; + + // Lock over the persistent DB state. Non-NULL iff successfully acquired. + FileLock* db_lock_; + + // State below is protected by mutex_ + port::Mutex mutex_; + port::AtomicPointer shutting_down_; + port::CondVar bg_cv_; // Signalled when background work finishes + MemTable* mem_; + MemTable* imm_; // Memtable being compacted + port::AtomicPointer has_imm_; // So bg thread can detect non-NULL imm_ + WritableFile* logfile_; + uint64_t logfile_number_; + log::Writer* log_; + uint32_t seed_; // For sampling. + + // Queue of writers. + std::deque writers_; + WriteBatch* tmp_batch_; + + SnapshotList snapshots_; + + // Set of table files to protect from deletion because they are + // part of ongoing compactions. + std::set pending_outputs_; + + // Has a background compaction been scheduled or is running? + bool bg_compaction_scheduled_; + + // Information for a manual compaction + struct ManualCompaction { + int level; + bool done; + const InternalKey* begin; // NULL means beginning of key range + const InternalKey* end; // NULL means end of key range + InternalKey tmp_storage; // Used to keep track of compaction progress + }; + ManualCompaction* manual_compaction_; + + VersionSet* versions_; + + // Have we encountered a background error in paranoid mode? + Status bg_error_; + + // Per level compaction stats. stats_[level] stores the stats for + // compactions that produced data for the specified "level". + struct CompactionStats { + int64_t micros; + int64_t bytes_read; + int64_t bytes_written; + + CompactionStats() : micros(0), bytes_read(0), bytes_written(0) { } + + void Add(const CompactionStats& c) { + this->micros += c.micros; + this->bytes_read += c.bytes_read; + this->bytes_written += c.bytes_written; + } + }; + CompactionStats stats_[config::kNumLevels]; + + // No copying allowed + DBImpl(const DBImpl&); + void operator=(const DBImpl&); + + const Comparator* user_comparator() const { + return internal_comparator_.user_comparator(); + } +}; + +// Sanitize db options. The caller should delete result.info_log if +// it is not equal to src.info_log. +extern Options SanitizeOptions(const std::string& db, + const InternalKeyComparator* icmp, + const InternalFilterPolicy* ipolicy, + const Options& src); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_DB_IMPL_H_ diff --git a/src/leveldb/db/db_iter.cc b/src/leveldb/db/db_iter.cc new file mode 100644 index 00000000..aace0e98 --- /dev/null +++ b/src/leveldb/db/db_iter.cc @@ -0,0 +1,322 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/db_iter.h" + +#include "db/filename.h" +#include "db/db_impl.h" +#include "db/dbformat.h" +#include "leveldb/env.h" +#include "leveldb/iterator.h" +#include "port/port.h" +#include "util/logging.h" +#include "util/mutexlock.h" +#include "util/random.h" + +#ifdef _MSC_VER +#include +typedef SSIZE_T ssize_t; +#endif + +namespace leveldb { + +#if 0 +static void DumpInternalIter(Iterator* iter) { + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + ParsedInternalKey k; + if (!ParseInternalKey(iter->key(), &k)) { + fprintf(stderr, "Corrupt '%s'\n", EscapeString(iter->key()).c_str()); + } else { + fprintf(stderr, "@ '%s'\n", k.DebugString().c_str()); + } + } +} +#endif + +namespace { + +// Memtables and sstables that make the DB representation contain +// (userkey,seq,type) => uservalue entries. DBIter +// combines multiple entries for the same userkey found in the DB +// representation into a single entry while accounting for sequence +// numbers, deletion markers, overwrites, etc. +class DBIter: public Iterator { + public: + // Which direction is the iterator currently moving? + // (1) When moving forward, the internal iterator is positioned at + // the exact entry that yields this->key(), this->value() + // (2) When moving backwards, the internal iterator is positioned + // just before all entries whose user key == this->key(). + enum Direction { + kForward, + kReverse + }; + + DBIter(DBImpl* db, const Comparator* cmp, Iterator* iter, SequenceNumber s, + uint32_t seed) + : db_(db), + user_comparator_(cmp), + iter_(iter), + sequence_(s), + direction_(kForward), + valid_(false), + rnd_(seed), + bytes_counter_(RandomPeriod()) { + } + virtual ~DBIter() { + delete iter_; + } + virtual bool Valid() const { return valid_; } + virtual Slice key() const { + assert(valid_); + return (direction_ == kForward) ? ExtractUserKey(iter_->key()) : saved_key_; + } + virtual Slice value() const { + assert(valid_); + return (direction_ == kForward) ? iter_->value() : saved_value_; + } + virtual Status status() const { + if (status_.ok()) { + return iter_->status(); + } else { + return status_; + } + } + + virtual void Next(); + virtual void Prev(); + virtual void Seek(const Slice& target); + virtual void SeekToFirst(); + virtual void SeekToLast(); + + private: + void FindNextUserEntry(bool skipping, std::string* skip); + void FindPrevUserEntry(); + bool ParseKey(ParsedInternalKey* key); + + inline void SaveKey(const Slice& k, std::string* dst) { + dst->assign(k.data(), k.size()); + } + + inline void ClearSavedValue() { + if (saved_value_.capacity() > 1048576) { + std::string empty; + swap(empty, saved_value_); + } else { + saved_value_.clear(); + } + } + + // Pick next gap with average value of config::kReadBytesPeriod. + ssize_t RandomPeriod() { + return rnd_.Uniform(2*config::kReadBytesPeriod); + } + + DBImpl* db_; + const Comparator* const user_comparator_; + Iterator* const iter_; + SequenceNumber const sequence_; + + Status status_; + std::string saved_key_; // == current key when direction_==kReverse + std::string saved_value_; // == current raw value when direction_==kReverse + Direction direction_; + bool valid_; + + Random rnd_; + ssize_t bytes_counter_; + + // No copying allowed + DBIter(const DBIter&); + void operator=(const DBIter&); +}; + +inline bool DBIter::ParseKey(ParsedInternalKey* ikey) { + Slice k = iter_->key(); + ssize_t n = k.size() + iter_->value().size(); + bytes_counter_ -= n; + while (bytes_counter_ < 0) { + bytes_counter_ += RandomPeriod(); + db_->RecordReadSample(k); + } + if (!ParseInternalKey(k, ikey)) { + status_ = Status::Corruption("corrupted internal key in DBIter"); + return false; + } else { + return true; + } +} + +void DBIter::Next() { + assert(valid_); + + if (direction_ == kReverse) { // Switch directions? + direction_ = kForward; + // iter_ is pointing just before the entries for this->key(), + // so advance into the range of entries for this->key() and then + // use the normal skipping code below. + if (!iter_->Valid()) { + iter_->SeekToFirst(); + } else { + iter_->Next(); + } + if (!iter_->Valid()) { + valid_ = false; + saved_key_.clear(); + return; + } + // saved_key_ already contains the key to skip past. + } else { + // Store in saved_key_ the current key so we skip it below. + SaveKey(ExtractUserKey(iter_->key()), &saved_key_); + } + + FindNextUserEntry(true, &saved_key_); +} + +void DBIter::FindNextUserEntry(bool skipping, std::string* skip) { + // Loop until we hit an acceptable entry to yield + assert(iter_->Valid()); + assert(direction_ == kForward); + do { + ParsedInternalKey ikey; + if (ParseKey(&ikey) && ikey.sequence <= sequence_) { + switch (ikey.type) { + case kTypeDeletion: + // Arrange to skip all upcoming entries for this key since + // they are hidden by this deletion. + SaveKey(ikey.user_key, skip); + skipping = true; + break; + case kTypeValue: + if (skipping && + user_comparator_->Compare(ikey.user_key, *skip) <= 0) { + // Entry hidden + } else { + valid_ = true; + saved_key_.clear(); + return; + } + break; + } + } + iter_->Next(); + } while (iter_->Valid()); + saved_key_.clear(); + valid_ = false; +} + +void DBIter::Prev() { + assert(valid_); + + if (direction_ == kForward) { // Switch directions? + // iter_ is pointing at the current entry. Scan backwards until + // the key changes so we can use the normal reverse scanning code. + assert(iter_->Valid()); // Otherwise valid_ would have been false + SaveKey(ExtractUserKey(iter_->key()), &saved_key_); + while (true) { + iter_->Prev(); + if (!iter_->Valid()) { + valid_ = false; + saved_key_.clear(); + ClearSavedValue(); + return; + } + if (user_comparator_->Compare(ExtractUserKey(iter_->key()), + saved_key_) < 0) { + break; + } + } + direction_ = kReverse; + } + + FindPrevUserEntry(); +} + +void DBIter::FindPrevUserEntry() { + assert(direction_ == kReverse); + + ValueType value_type = kTypeDeletion; + if (iter_->Valid()) { + do { + ParsedInternalKey ikey; + if (ParseKey(&ikey) && ikey.sequence <= sequence_) { + if ((value_type != kTypeDeletion) && + user_comparator_->Compare(ikey.user_key, saved_key_) < 0) { + // We encountered a non-deleted value in entries for previous keys, + break; + } + value_type = ikey.type; + if (value_type == kTypeDeletion) { + saved_key_.clear(); + ClearSavedValue(); + } else { + Slice raw_value = iter_->value(); + if (saved_value_.capacity() > raw_value.size() + 1048576) { + std::string empty; + swap(empty, saved_value_); + } + SaveKey(ExtractUserKey(iter_->key()), &saved_key_); + saved_value_.assign(raw_value.data(), raw_value.size()); + } + } + iter_->Prev(); + } while (iter_->Valid()); + } + + if (value_type == kTypeDeletion) { + // End + valid_ = false; + saved_key_.clear(); + ClearSavedValue(); + direction_ = kForward; + } else { + valid_ = true; + } +} + +void DBIter::Seek(const Slice& target) { + direction_ = kForward; + ClearSavedValue(); + saved_key_.clear(); + AppendInternalKey( + &saved_key_, ParsedInternalKey(target, sequence_, kValueTypeForSeek)); + iter_->Seek(saved_key_); + if (iter_->Valid()) { + FindNextUserEntry(false, &saved_key_ /* temporary storage */); + } else { + valid_ = false; + } +} + +void DBIter::SeekToFirst() { + direction_ = kForward; + ClearSavedValue(); + iter_->SeekToFirst(); + if (iter_->Valid()) { + FindNextUserEntry(false, &saved_key_ /* temporary storage */); + } else { + valid_ = false; + } +} + +void DBIter::SeekToLast() { + direction_ = kReverse; + ClearSavedValue(); + iter_->SeekToLast(); + FindPrevUserEntry(); +} + +} // anonymous namespace + +Iterator* NewDBIterator( + DBImpl* db, + const Comparator* user_key_comparator, + Iterator* internal_iter, + SequenceNumber sequence, + uint32_t seed) { + return new DBIter(db, user_key_comparator, internal_iter, sequence, seed); +} + +} // namespace leveldb diff --git a/src/leveldb/db/db_iter.h b/src/leveldb/db/db_iter.h new file mode 100644 index 00000000..04927e93 --- /dev/null +++ b/src/leveldb/db/db_iter.h @@ -0,0 +1,28 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_DB_ITER_H_ +#define STORAGE_LEVELDB_DB_DB_ITER_H_ + +#include +#include "leveldb/db.h" +#include "db/dbformat.h" + +namespace leveldb { + +class DBImpl; + +// Return a new iterator that converts internal keys (yielded by +// "*internal_iter") that were live at the specified "sequence" number +// into appropriate user keys. +extern Iterator* NewDBIterator( + DBImpl* db, + const Comparator* user_key_comparator, + Iterator* internal_iter, + SequenceNumber sequence, + uint32_t seed); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_DB_ITER_H_ diff --git a/src/leveldb/db/db_test.cc b/src/leveldb/db/db_test.cc new file mode 100644 index 00000000..0fed9137 --- /dev/null +++ b/src/leveldb/db/db_test.cc @@ -0,0 +1,2128 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/db.h" +#include "leveldb/filter_policy.h" +#include "db/db_impl.h" +#include "db/filename.h" +#include "db/version_set.h" +#include "db/write_batch_internal.h" +#include "leveldb/cache.h" +#include "leveldb/env.h" +#include "leveldb/table.h" +#include "util/hash.h" +#include "util/logging.h" +#include "util/mutexlock.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +static std::string RandomString(Random* rnd, int len) { + std::string r; + test::RandomString(rnd, len, &r); + return r; +} + +namespace { +class AtomicCounter { + private: + port::Mutex mu_; + int count_; + public: + AtomicCounter() : count_(0) { } + void Increment() { + IncrementBy(1); + } + void IncrementBy(int count) { + MutexLock l(&mu_); + count_ += count; + } + int Read() { + MutexLock l(&mu_); + return count_; + } + void Reset() { + MutexLock l(&mu_); + count_ = 0; + } +}; + +void DelayMilliseconds(int millis) { + Env::Default()->SleepForMicroseconds(millis * 1000); +} +} + +// Special Env used to delay background operations +class SpecialEnv : public EnvWrapper { + public: + // sstable/log Sync() calls are blocked while this pointer is non-NULL. + port::AtomicPointer delay_data_sync_; + + // sstable/log Sync() calls return an error. + port::AtomicPointer data_sync_error_; + + // Simulate no-space errors while this pointer is non-NULL. + port::AtomicPointer no_space_; + + // Simulate non-writable file system while this pointer is non-NULL + port::AtomicPointer non_writable_; + + // Force sync of manifest files to fail while this pointer is non-NULL + port::AtomicPointer manifest_sync_error_; + + // Force write to manifest files to fail while this pointer is non-NULL + port::AtomicPointer manifest_write_error_; + + bool count_random_reads_; + AtomicCounter random_read_counter_; + + explicit SpecialEnv(Env* base) : EnvWrapper(base) { + delay_data_sync_.Release_Store(NULL); + data_sync_error_.Release_Store(NULL); + no_space_.Release_Store(NULL); + non_writable_.Release_Store(NULL); + count_random_reads_ = false; + manifest_sync_error_.Release_Store(NULL); + manifest_write_error_.Release_Store(NULL); + } + + Status NewWritableFile(const std::string& f, WritableFile** r) { + class DataFile : public WritableFile { + private: + SpecialEnv* env_; + WritableFile* base_; + + public: + DataFile(SpecialEnv* env, WritableFile* base) + : env_(env), + base_(base) { + } + ~DataFile() { delete base_; } + Status Append(const Slice& data) { + if (env_->no_space_.Acquire_Load() != NULL) { + // Drop writes on the floor + return Status::OK(); + } else { + return base_->Append(data); + } + } + Status Close() { return base_->Close(); } + Status Flush() { return base_->Flush(); } + Status Sync() { + if (env_->data_sync_error_.Acquire_Load() != NULL) { + return Status::IOError("simulated data sync error"); + } + while (env_->delay_data_sync_.Acquire_Load() != NULL) { + DelayMilliseconds(100); + } + return base_->Sync(); + } + }; + class ManifestFile : public WritableFile { + private: + SpecialEnv* env_; + WritableFile* base_; + public: + ManifestFile(SpecialEnv* env, WritableFile* b) : env_(env), base_(b) { } + ~ManifestFile() { delete base_; } + Status Append(const Slice& data) { + if (env_->manifest_write_error_.Acquire_Load() != NULL) { + return Status::IOError("simulated writer error"); + } else { + return base_->Append(data); + } + } + Status Close() { return base_->Close(); } + Status Flush() { return base_->Flush(); } + Status Sync() { + if (env_->manifest_sync_error_.Acquire_Load() != NULL) { + return Status::IOError("simulated sync error"); + } else { + return base_->Sync(); + } + } + }; + + if (non_writable_.Acquire_Load() != NULL) { + return Status::IOError("simulated write error"); + } + + Status s = target()->NewWritableFile(f, r); + if (s.ok()) { + if (strstr(f.c_str(), ".ldb") != NULL || + strstr(f.c_str(), ".log") != NULL) { + *r = new DataFile(this, *r); + } else if (strstr(f.c_str(), "MANIFEST") != NULL) { + *r = new ManifestFile(this, *r); + } + } + return s; + } + + Status NewRandomAccessFile(const std::string& f, RandomAccessFile** r) { + class CountingFile : public RandomAccessFile { + private: + RandomAccessFile* target_; + AtomicCounter* counter_; + public: + CountingFile(RandomAccessFile* target, AtomicCounter* counter) + : target_(target), counter_(counter) { + } + virtual ~CountingFile() { delete target_; } + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + counter_->Increment(); + return target_->Read(offset, n, result, scratch); + } + }; + + Status s = target()->NewRandomAccessFile(f, r); + if (s.ok() && count_random_reads_) { + *r = new CountingFile(*r, &random_read_counter_); + } + return s; + } +}; + +class DBTest { + private: + const FilterPolicy* filter_policy_; + + // Sequence of option configurations to try + enum OptionConfig { + kDefault, + kFilter, + kUncompressed, + kEnd + }; + int option_config_; + + public: + std::string dbname_; + SpecialEnv* env_; + DB* db_; + + Options last_options_; + + DBTest() : option_config_(kDefault), + env_(new SpecialEnv(Env::Default())) { + filter_policy_ = NewBloomFilterPolicy(10); + dbname_ = test::TmpDir() + "/db_test"; + DestroyDB(dbname_, Options()); + db_ = NULL; + Reopen(); + } + + ~DBTest() { + delete db_; + DestroyDB(dbname_, Options()); + delete env_; + delete filter_policy_; + } + + // Switch to a fresh database with the next option configuration to + // test. Return false if there are no more configurations to test. + bool ChangeOptions() { + option_config_++; + if (option_config_ >= kEnd) { + return false; + } else { + DestroyAndReopen(); + return true; + } + } + + // Return the current option configuration. + Options CurrentOptions() { + Options options; + switch (option_config_) { + case kFilter: + options.filter_policy = filter_policy_; + break; + case kUncompressed: + options.compression = kNoCompression; + break; + default: + break; + } + return options; + } + + DBImpl* dbfull() { + return reinterpret_cast(db_); + } + + void Reopen(Options* options = NULL) { + ASSERT_OK(TryReopen(options)); + } + + void Close() { + delete db_; + db_ = NULL; + } + + void DestroyAndReopen(Options* options = NULL) { + delete db_; + db_ = NULL; + DestroyDB(dbname_, Options()); + ASSERT_OK(TryReopen(options)); + } + + Status TryReopen(Options* options) { + delete db_; + db_ = NULL; + Options opts; + if (options != NULL) { + opts = *options; + } else { + opts = CurrentOptions(); + opts.create_if_missing = true; + } + last_options_ = opts; + + return DB::Open(opts, dbname_, &db_); + } + + Status Put(const std::string& k, const std::string& v) { + return db_->Put(WriteOptions(), k, v); + } + + Status Delete(const std::string& k) { + return db_->Delete(WriteOptions(), k); + } + + std::string Get(const std::string& k, const Snapshot* snapshot = NULL) { + ReadOptions options; + options.snapshot = snapshot; + std::string result; + Status s = db_->Get(options, k, &result); + if (s.IsNotFound()) { + result = "NOT_FOUND"; + } else if (!s.ok()) { + result = s.ToString(); + } + return result; + } + + // Return a string that contains all key,value pairs in order, + // formatted like "(k1->v1)(k2->v2)". + std::string Contents() { + std::vector forward; + std::string result; + Iterator* iter = db_->NewIterator(ReadOptions()); + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + std::string s = IterStatus(iter); + result.push_back('('); + result.append(s); + result.push_back(')'); + forward.push_back(s); + } + + // Check reverse iteration results are the reverse of forward results + size_t matched = 0; + for (iter->SeekToLast(); iter->Valid(); iter->Prev()) { + ASSERT_LT(matched, forward.size()); + ASSERT_EQ(IterStatus(iter), forward[forward.size() - matched - 1]); + matched++; + } + ASSERT_EQ(matched, forward.size()); + + delete iter; + return result; + } + + std::string AllEntriesFor(const Slice& user_key) { + Iterator* iter = dbfull()->TEST_NewInternalIterator(); + InternalKey target(user_key, kMaxSequenceNumber, kTypeValue); + iter->Seek(target.Encode()); + std::string result; + if (!iter->status().ok()) { + result = iter->status().ToString(); + } else { + result = "[ "; + bool first = true; + while (iter->Valid()) { + ParsedInternalKey ikey; + if (!ParseInternalKey(iter->key(), &ikey)) { + result += "CORRUPTED"; + } else { + if (last_options_.comparator->Compare(ikey.user_key, user_key) != 0) { + break; + } + if (!first) { + result += ", "; + } + first = false; + switch (ikey.type) { + case kTypeValue: + result += iter->value().ToString(); + break; + case kTypeDeletion: + result += "DEL"; + break; + } + } + iter->Next(); + } + if (!first) { + result += " "; + } + result += "]"; + } + delete iter; + return result; + } + + int NumTableFilesAtLevel(int level) { + std::string property; + ASSERT_TRUE( + db_->GetProperty("leveldb.num-files-at-level" + NumberToString(level), + &property)); + return atoi(property.c_str()); + } + + int TotalTableFiles() { + int result = 0; + for (int level = 0; level < config::kNumLevels; level++) { + result += NumTableFilesAtLevel(level); + } + return result; + } + + // Return spread of files per level + std::string FilesPerLevel() { + std::string result; + int last_non_zero_offset = 0; + for (int level = 0; level < config::kNumLevels; level++) { + int f = NumTableFilesAtLevel(level); + char buf[100]; + snprintf(buf, sizeof(buf), "%s%d", (level ? "," : ""), f); + result += buf; + if (f > 0) { + last_non_zero_offset = result.size(); + } + } + result.resize(last_non_zero_offset); + return result; + } + + int CountFiles() { + std::vector files; + env_->GetChildren(dbname_, &files); + return static_cast(files.size()); + } + + uint64_t Size(const Slice& start, const Slice& limit) { + Range r(start, limit); + uint64_t size; + db_->GetApproximateSizes(&r, 1, &size); + return size; + } + + void Compact(const Slice& start, const Slice& limit) { + db_->CompactRange(&start, &limit); + } + + // Do n memtable compactions, each of which produces an sstable + // covering the range [small,large]. + void MakeTables(int n, const std::string& small, const std::string& large) { + for (int i = 0; i < n; i++) { + Put(small, "begin"); + Put(large, "end"); + dbfull()->TEST_CompactMemTable(); + } + } + + // Prevent pushing of new sstables into deeper levels by adding + // tables that cover a specified range to all levels. + void FillLevels(const std::string& smallest, const std::string& largest) { + MakeTables(config::kNumLevels, smallest, largest); + } + + void DumpFileCounts(const char* label) { + fprintf(stderr, "---\n%s:\n", label); + fprintf(stderr, "maxoverlap: %lld\n", + static_cast( + dbfull()->TEST_MaxNextLevelOverlappingBytes())); + for (int level = 0; level < config::kNumLevels; level++) { + int num = NumTableFilesAtLevel(level); + if (num > 0) { + fprintf(stderr, " level %3d : %d files\n", level, num); + } + } + } + + std::string DumpSSTableList() { + std::string property; + db_->GetProperty("leveldb.sstables", &property); + return property; + } + + std::string IterStatus(Iterator* iter) { + std::string result; + if (iter->Valid()) { + result = iter->key().ToString() + "->" + iter->value().ToString(); + } else { + result = "(invalid)"; + } + return result; + } + + bool DeleteAnSSTFile() { + std::vector filenames; + ASSERT_OK(env_->GetChildren(dbname_, &filenames)); + uint64_t number; + FileType type; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type) && type == kTableFile) { + ASSERT_OK(env_->DeleteFile(TableFileName(dbname_, number))); + return true; + } + } + return false; + } + + // Returns number of files renamed. + int RenameLDBToSST() { + std::vector filenames; + ASSERT_OK(env_->GetChildren(dbname_, &filenames)); + uint64_t number; + FileType type; + int files_renamed = 0; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type) && type == kTableFile) { + const std::string from = TableFileName(dbname_, number); + const std::string to = SSTTableFileName(dbname_, number); + ASSERT_OK(env_->RenameFile(from, to)); + files_renamed++; + } + } + return files_renamed; + } +}; + +TEST(DBTest, Empty) { + do { + ASSERT_TRUE(db_ != NULL); + ASSERT_EQ("NOT_FOUND", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, ReadWrite) { + do { + ASSERT_OK(Put("foo", "v1")); + ASSERT_EQ("v1", Get("foo")); + ASSERT_OK(Put("bar", "v2")); + ASSERT_OK(Put("foo", "v3")); + ASSERT_EQ("v3", Get("foo")); + ASSERT_EQ("v2", Get("bar")); + } while (ChangeOptions()); +} + +TEST(DBTest, PutDeleteGet) { + do { + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v1")); + ASSERT_EQ("v1", Get("foo")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v2")); + ASSERT_EQ("v2", Get("foo")); + ASSERT_OK(db_->Delete(WriteOptions(), "foo")); + ASSERT_EQ("NOT_FOUND", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetFromImmutableLayer) { + do { + Options options = CurrentOptions(); + options.env = env_; + options.write_buffer_size = 100000; // Small write buffer + Reopen(&options); + + ASSERT_OK(Put("foo", "v1")); + ASSERT_EQ("v1", Get("foo")); + + env_->delay_data_sync_.Release_Store(env_); // Block sync calls + Put("k1", std::string(100000, 'x')); // Fill memtable + Put("k2", std::string(100000, 'y')); // Trigger compaction + ASSERT_EQ("v1", Get("foo")); + env_->delay_data_sync_.Release_Store(NULL); // Release sync calls + } while (ChangeOptions()); +} + +TEST(DBTest, GetFromVersions) { + do { + ASSERT_OK(Put("foo", "v1")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("v1", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetSnapshot) { + do { + // Try with both a short key and a long key + for (int i = 0; i < 2; i++) { + std::string key = (i == 0) ? std::string("foo") : std::string(200, 'x'); + ASSERT_OK(Put(key, "v1")); + const Snapshot* s1 = db_->GetSnapshot(); + ASSERT_OK(Put(key, "v2")); + ASSERT_EQ("v2", Get(key)); + ASSERT_EQ("v1", Get(key, s1)); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("v2", Get(key)); + ASSERT_EQ("v1", Get(key, s1)); + db_->ReleaseSnapshot(s1); + } + } while (ChangeOptions()); +} + +TEST(DBTest, GetLevel0Ordering) { + do { + // Check that we process level-0 files in correct order. The code + // below generates two level-0 files where the earlier one comes + // before the later one in the level-0 file list since the earlier + // one has a smaller "smallest" key. + ASSERT_OK(Put("bar", "b")); + ASSERT_OK(Put("foo", "v1")); + dbfull()->TEST_CompactMemTable(); + ASSERT_OK(Put("foo", "v2")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("v2", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetOrderedByLevels) { + do { + ASSERT_OK(Put("foo", "v1")); + Compact("a", "z"); + ASSERT_EQ("v1", Get("foo")); + ASSERT_OK(Put("foo", "v2")); + ASSERT_EQ("v2", Get("foo")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("v2", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetPicksCorrectFile) { + do { + // Arrange to have multiple files in a non-level-0 level. + ASSERT_OK(Put("a", "va")); + Compact("a", "b"); + ASSERT_OK(Put("x", "vx")); + Compact("x", "y"); + ASSERT_OK(Put("f", "vf")); + Compact("f", "g"); + ASSERT_EQ("va", Get("a")); + ASSERT_EQ("vf", Get("f")); + ASSERT_EQ("vx", Get("x")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetEncountersEmptyLevel) { + do { + // Arrange for the following to happen: + // * sstable A in level 0 + // * nothing in level 1 + // * sstable B in level 2 + // Then do enough Get() calls to arrange for an automatic compaction + // of sstable A. A bug would cause the compaction to be marked as + // occurring at level 1 (instead of the correct level 0). + + // Step 1: First place sstables in levels 0 and 2 + int compaction_count = 0; + while (NumTableFilesAtLevel(0) == 0 || + NumTableFilesAtLevel(2) == 0) { + ASSERT_LE(compaction_count, 100) << "could not fill levels 0 and 2"; + compaction_count++; + Put("a", "begin"); + Put("z", "end"); + dbfull()->TEST_CompactMemTable(); + } + + // Step 2: clear level 1 if necessary. + dbfull()->TEST_CompactRange(1, NULL, NULL); + ASSERT_EQ(NumTableFilesAtLevel(0), 1); + ASSERT_EQ(NumTableFilesAtLevel(1), 0); + ASSERT_EQ(NumTableFilesAtLevel(2), 1); + + // Step 3: read a bunch of times + for (int i = 0; i < 1000; i++) { + ASSERT_EQ("NOT_FOUND", Get("missing")); + } + + // Step 4: Wait for compaction to finish + DelayMilliseconds(1000); + + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + } while (ChangeOptions()); +} + +TEST(DBTest, IterEmpty) { + Iterator* iter = db_->NewIterator(ReadOptions()); + + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek("foo"); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + delete iter; +} + +TEST(DBTest, IterSingle) { + ASSERT_OK(Put("a", "va")); + Iterator* iter = db_->NewIterator(ReadOptions()); + + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek(""); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek("a"); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek("b"); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + delete iter; +} + +TEST(DBTest, IterMulti) { + ASSERT_OK(Put("a", "va")); + ASSERT_OK(Put("b", "vb")); + ASSERT_OK(Put("c", "vc")); + Iterator* iter = db_->NewIterator(ReadOptions()); + + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek(""); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Seek("a"); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Seek("ax"); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Seek("b"); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Seek("z"); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + // Switch from reverse to forward + iter->SeekToLast(); + iter->Prev(); + iter->Prev(); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + + // Switch from forward to reverse + iter->SeekToFirst(); + iter->Next(); + iter->Next(); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + + // Make sure iter stays at snapshot + ASSERT_OK(Put("a", "va2")); + ASSERT_OK(Put("a2", "va3")); + ASSERT_OK(Put("b", "vb2")); + ASSERT_OK(Put("c", "vc2")); + ASSERT_OK(Delete("b")); + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + delete iter; +} + +TEST(DBTest, IterSmallAndLargeMix) { + ASSERT_OK(Put("a", "va")); + ASSERT_OK(Put("b", std::string(100000, 'b'))); + ASSERT_OK(Put("c", "vc")); + ASSERT_OK(Put("d", std::string(100000, 'd'))); + ASSERT_OK(Put("e", std::string(100000, 'e'))); + + Iterator* iter = db_->NewIterator(ReadOptions()); + + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "b->" + std::string(100000, 'b')); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "d->" + std::string(100000, 'd')); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "e->" + std::string(100000, 'e')); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "e->" + std::string(100000, 'e')); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "d->" + std::string(100000, 'd')); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "b->" + std::string(100000, 'b')); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + delete iter; +} + +TEST(DBTest, IterMultiWithDelete) { + do { + ASSERT_OK(Put("a", "va")); + ASSERT_OK(Put("b", "vb")); + ASSERT_OK(Put("c", "vc")); + ASSERT_OK(Delete("b")); + ASSERT_EQ("NOT_FOUND", Get("b")); + + Iterator* iter = db_->NewIterator(ReadOptions()); + iter->Seek("c"); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "a->va"); + delete iter; + } while (ChangeOptions()); +} + +TEST(DBTest, Recover) { + do { + ASSERT_OK(Put("foo", "v1")); + ASSERT_OK(Put("baz", "v5")); + + Reopen(); + ASSERT_EQ("v1", Get("foo")); + + ASSERT_EQ("v1", Get("foo")); + ASSERT_EQ("v5", Get("baz")); + ASSERT_OK(Put("bar", "v2")); + ASSERT_OK(Put("foo", "v3")); + + Reopen(); + ASSERT_EQ("v3", Get("foo")); + ASSERT_OK(Put("foo", "v4")); + ASSERT_EQ("v4", Get("foo")); + ASSERT_EQ("v2", Get("bar")); + ASSERT_EQ("v5", Get("baz")); + } while (ChangeOptions()); +} + +TEST(DBTest, RecoveryWithEmptyLog) { + do { + ASSERT_OK(Put("foo", "v1")); + ASSERT_OK(Put("foo", "v2")); + Reopen(); + Reopen(); + ASSERT_OK(Put("foo", "v3")); + Reopen(); + ASSERT_EQ("v3", Get("foo")); + } while (ChangeOptions()); +} + +// Check that writes done during a memtable compaction are recovered +// if the database is shutdown during the memtable compaction. +TEST(DBTest, RecoverDuringMemtableCompaction) { + do { + Options options = CurrentOptions(); + options.env = env_; + options.write_buffer_size = 1000000; + Reopen(&options); + + // Trigger a long memtable compaction and reopen the database during it + ASSERT_OK(Put("foo", "v1")); // Goes to 1st log file + ASSERT_OK(Put("big1", std::string(10000000, 'x'))); // Fills memtable + ASSERT_OK(Put("big2", std::string(1000, 'y'))); // Triggers compaction + ASSERT_OK(Put("bar", "v2")); // Goes to new log file + + Reopen(&options); + ASSERT_EQ("v1", Get("foo")); + ASSERT_EQ("v2", Get("bar")); + ASSERT_EQ(std::string(10000000, 'x'), Get("big1")); + ASSERT_EQ(std::string(1000, 'y'), Get("big2")); + } while (ChangeOptions()); +} + +static std::string Key(int i) { + char buf[100]; + snprintf(buf, sizeof(buf), "key%06d", i); + return std::string(buf); +} + +TEST(DBTest, MinorCompactionsHappen) { + Options options = CurrentOptions(); + options.write_buffer_size = 10000; + Reopen(&options); + + const int N = 500; + + int starting_num_tables = TotalTableFiles(); + for (int i = 0; i < N; i++) { + ASSERT_OK(Put(Key(i), Key(i) + std::string(1000, 'v'))); + } + int ending_num_tables = TotalTableFiles(); + ASSERT_GT(ending_num_tables, starting_num_tables); + + for (int i = 0; i < N; i++) { + ASSERT_EQ(Key(i) + std::string(1000, 'v'), Get(Key(i))); + } + + Reopen(); + + for (int i = 0; i < N; i++) { + ASSERT_EQ(Key(i) + std::string(1000, 'v'), Get(Key(i))); + } +} + +TEST(DBTest, RecoverWithLargeLog) { + { + Options options = CurrentOptions(); + Reopen(&options); + ASSERT_OK(Put("big1", std::string(200000, '1'))); + ASSERT_OK(Put("big2", std::string(200000, '2'))); + ASSERT_OK(Put("small3", std::string(10, '3'))); + ASSERT_OK(Put("small4", std::string(10, '4'))); + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + } + + // Make sure that if we re-open with a small write buffer size that + // we flush table files in the middle of a large log file. + Options options = CurrentOptions(); + options.write_buffer_size = 100000; + Reopen(&options); + ASSERT_EQ(NumTableFilesAtLevel(0), 3); + ASSERT_EQ(std::string(200000, '1'), Get("big1")); + ASSERT_EQ(std::string(200000, '2'), Get("big2")); + ASSERT_EQ(std::string(10, '3'), Get("small3")); + ASSERT_EQ(std::string(10, '4'), Get("small4")); + ASSERT_GT(NumTableFilesAtLevel(0), 1); +} + +TEST(DBTest, CompactionsGenerateMultipleFiles) { + Options options = CurrentOptions(); + options.write_buffer_size = 100000000; // Large write buffer + Reopen(&options); + + Random rnd(301); + + // Write 8MB (80 values, each 100K) + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + std::vector values; + for (int i = 0; i < 80; i++) { + values.push_back(RandomString(&rnd, 100000)); + ASSERT_OK(Put(Key(i), values[i])); + } + + // Reopening moves updates to level-0 + Reopen(&options); + dbfull()->TEST_CompactRange(0, NULL, NULL); + + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + ASSERT_GT(NumTableFilesAtLevel(1), 1); + for (int i = 0; i < 80; i++) { + ASSERT_EQ(Get(Key(i)), values[i]); + } +} + +TEST(DBTest, RepeatedWritesToSameKey) { + Options options = CurrentOptions(); + options.env = env_; + options.write_buffer_size = 100000; // Small write buffer + Reopen(&options); + + // We must have at most one file per level except for level-0, + // which may have up to kL0_StopWritesTrigger files. + const int kMaxFiles = config::kNumLevels + config::kL0_StopWritesTrigger; + + Random rnd(301); + std::string value = RandomString(&rnd, 2 * options.write_buffer_size); + for (int i = 0; i < 5 * kMaxFiles; i++) { + Put("key", value); + ASSERT_LE(TotalTableFiles(), kMaxFiles); + fprintf(stderr, "after %d: %d files\n", int(i+1), TotalTableFiles()); + } +} + +TEST(DBTest, SparseMerge) { + Options options = CurrentOptions(); + options.compression = kNoCompression; + Reopen(&options); + + FillLevels("A", "Z"); + + // Suppose there is: + // small amount of data with prefix A + // large amount of data with prefix B + // small amount of data with prefix C + // and that recent updates have made small changes to all three prefixes. + // Check that we do not do a compaction that merges all of B in one shot. + const std::string value(1000, 'x'); + Put("A", "va"); + // Write approximately 100MB of "B" values + for (int i = 0; i < 100000; i++) { + char key[100]; + snprintf(key, sizeof(key), "B%010d", i); + Put(key, value); + } + Put("C", "vc"); + dbfull()->TEST_CompactMemTable(); + dbfull()->TEST_CompactRange(0, NULL, NULL); + + // Make sparse update + Put("A", "va2"); + Put("B100", "bvalue2"); + Put("C", "vc2"); + dbfull()->TEST_CompactMemTable(); + + // Compactions should not cause us to create a situation where + // a file overlaps too much data at the next level. + ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576); + dbfull()->TEST_CompactRange(0, NULL, NULL); + ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576); + dbfull()->TEST_CompactRange(1, NULL, NULL); + ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576); +} + +static bool Between(uint64_t val, uint64_t low, uint64_t high) { + bool result = (val >= low) && (val <= high); + if (!result) { + fprintf(stderr, "Value %llu is not in range [%llu, %llu]\n", + (unsigned long long)(val), + (unsigned long long)(low), + (unsigned long long)(high)); + } + return result; +} + +TEST(DBTest, ApproximateSizes) { + do { + Options options = CurrentOptions(); + options.write_buffer_size = 100000000; // Large write buffer + options.compression = kNoCompression; + DestroyAndReopen(); + + ASSERT_TRUE(Between(Size("", "xyz"), 0, 0)); + Reopen(&options); + ASSERT_TRUE(Between(Size("", "xyz"), 0, 0)); + + // Write 8MB (80 values, each 100K) + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + const int N = 80; + static const int S1 = 100000; + static const int S2 = 105000; // Allow some expansion from metadata + Random rnd(301); + for (int i = 0; i < N; i++) { + ASSERT_OK(Put(Key(i), RandomString(&rnd, S1))); + } + + // 0 because GetApproximateSizes() does not account for memtable space + ASSERT_TRUE(Between(Size("", Key(50)), 0, 0)); + + // Check sizes across recovery by reopening a few times + for (int run = 0; run < 3; run++) { + Reopen(&options); + + for (int compact_start = 0; compact_start < N; compact_start += 10) { + for (int i = 0; i < N; i += 10) { + ASSERT_TRUE(Between(Size("", Key(i)), S1*i, S2*i)); + ASSERT_TRUE(Between(Size("", Key(i)+".suffix"), S1*(i+1), S2*(i+1))); + ASSERT_TRUE(Between(Size(Key(i), Key(i+10)), S1*10, S2*10)); + } + ASSERT_TRUE(Between(Size("", Key(50)), S1*50, S2*50)); + ASSERT_TRUE(Between(Size("", Key(50)+".suffix"), S1*50, S2*50)); + + std::string cstart_str = Key(compact_start); + std::string cend_str = Key(compact_start + 9); + Slice cstart = cstart_str; + Slice cend = cend_str; + dbfull()->TEST_CompactRange(0, &cstart, &cend); + } + + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + ASSERT_GT(NumTableFilesAtLevel(1), 0); + } + } while (ChangeOptions()); +} + +TEST(DBTest, ApproximateSizes_MixOfSmallAndLarge) { + do { + Options options = CurrentOptions(); + options.compression = kNoCompression; + Reopen(); + + Random rnd(301); + std::string big1 = RandomString(&rnd, 100000); + ASSERT_OK(Put(Key(0), RandomString(&rnd, 10000))); + ASSERT_OK(Put(Key(1), RandomString(&rnd, 10000))); + ASSERT_OK(Put(Key(2), big1)); + ASSERT_OK(Put(Key(3), RandomString(&rnd, 10000))); + ASSERT_OK(Put(Key(4), big1)); + ASSERT_OK(Put(Key(5), RandomString(&rnd, 10000))); + ASSERT_OK(Put(Key(6), RandomString(&rnd, 300000))); + ASSERT_OK(Put(Key(7), RandomString(&rnd, 10000))); + + // Check sizes across recovery by reopening a few times + for (int run = 0; run < 3; run++) { + Reopen(&options); + + ASSERT_TRUE(Between(Size("", Key(0)), 0, 0)); + ASSERT_TRUE(Between(Size("", Key(1)), 10000, 11000)); + ASSERT_TRUE(Between(Size("", Key(2)), 20000, 21000)); + ASSERT_TRUE(Between(Size("", Key(3)), 120000, 121000)); + ASSERT_TRUE(Between(Size("", Key(4)), 130000, 131000)); + ASSERT_TRUE(Between(Size("", Key(5)), 230000, 231000)); + ASSERT_TRUE(Between(Size("", Key(6)), 240000, 241000)); + ASSERT_TRUE(Between(Size("", Key(7)), 540000, 541000)); + ASSERT_TRUE(Between(Size("", Key(8)), 550000, 560000)); + + ASSERT_TRUE(Between(Size(Key(3), Key(5)), 110000, 111000)); + + dbfull()->TEST_CompactRange(0, NULL, NULL); + } + } while (ChangeOptions()); +} + +TEST(DBTest, IteratorPinsRef) { + Put("foo", "hello"); + + // Get iterator that will yield the current contents of the DB. + Iterator* iter = db_->NewIterator(ReadOptions()); + + // Write to force compactions + Put("foo", "newvalue1"); + for (int i = 0; i < 100; i++) { + ASSERT_OK(Put(Key(i), Key(i) + std::string(100000, 'v'))); // 100K values + } + Put("foo", "newvalue2"); + + iter->SeekToFirst(); + ASSERT_TRUE(iter->Valid()); + ASSERT_EQ("foo", iter->key().ToString()); + ASSERT_EQ("hello", iter->value().ToString()); + iter->Next(); + ASSERT_TRUE(!iter->Valid()); + delete iter; +} + +TEST(DBTest, Snapshot) { + do { + Put("foo", "v1"); + const Snapshot* s1 = db_->GetSnapshot(); + Put("foo", "v2"); + const Snapshot* s2 = db_->GetSnapshot(); + Put("foo", "v3"); + const Snapshot* s3 = db_->GetSnapshot(); + + Put("foo", "v4"); + ASSERT_EQ("v1", Get("foo", s1)); + ASSERT_EQ("v2", Get("foo", s2)); + ASSERT_EQ("v3", Get("foo", s3)); + ASSERT_EQ("v4", Get("foo")); + + db_->ReleaseSnapshot(s3); + ASSERT_EQ("v1", Get("foo", s1)); + ASSERT_EQ("v2", Get("foo", s2)); + ASSERT_EQ("v4", Get("foo")); + + db_->ReleaseSnapshot(s1); + ASSERT_EQ("v2", Get("foo", s2)); + ASSERT_EQ("v4", Get("foo")); + + db_->ReleaseSnapshot(s2); + ASSERT_EQ("v4", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, HiddenValuesAreRemoved) { + do { + Random rnd(301); + FillLevels("a", "z"); + + std::string big = RandomString(&rnd, 50000); + Put("foo", big); + Put("pastfoo", "v"); + const Snapshot* snapshot = db_->GetSnapshot(); + Put("foo", "tiny"); + Put("pastfoo2", "v2"); // Advance sequence number one more + + ASSERT_OK(dbfull()->TEST_CompactMemTable()); + ASSERT_GT(NumTableFilesAtLevel(0), 0); + + ASSERT_EQ(big, Get("foo", snapshot)); + ASSERT_TRUE(Between(Size("", "pastfoo"), 50000, 60000)); + db_->ReleaseSnapshot(snapshot); + ASSERT_EQ(AllEntriesFor("foo"), "[ tiny, " + big + " ]"); + Slice x("x"); + dbfull()->TEST_CompactRange(0, NULL, &x); + ASSERT_EQ(AllEntriesFor("foo"), "[ tiny ]"); + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + ASSERT_GE(NumTableFilesAtLevel(1), 1); + dbfull()->TEST_CompactRange(1, NULL, &x); + ASSERT_EQ(AllEntriesFor("foo"), "[ tiny ]"); + + ASSERT_TRUE(Between(Size("", "pastfoo"), 0, 1000)); + } while (ChangeOptions()); +} + +TEST(DBTest, DeletionMarkers1) { + Put("foo", "v1"); + ASSERT_OK(dbfull()->TEST_CompactMemTable()); + const int last = config::kMaxMemCompactLevel; + ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo => v1 is now in last level + + // Place a table at level last-1 to prevent merging with preceding mutation + Put("a", "begin"); + Put("z", "end"); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ(NumTableFilesAtLevel(last), 1); + ASSERT_EQ(NumTableFilesAtLevel(last-1), 1); + + Delete("foo"); + Put("foo", "v2"); + ASSERT_EQ(AllEntriesFor("foo"), "[ v2, DEL, v1 ]"); + ASSERT_OK(dbfull()->TEST_CompactMemTable()); // Moves to level last-2 + ASSERT_EQ(AllEntriesFor("foo"), "[ v2, DEL, v1 ]"); + Slice z("z"); + dbfull()->TEST_CompactRange(last-2, NULL, &z); + // DEL eliminated, but v1 remains because we aren't compacting that level + // (DEL can be eliminated because v2 hides v1). + ASSERT_EQ(AllEntriesFor("foo"), "[ v2, v1 ]"); + dbfull()->TEST_CompactRange(last-1, NULL, NULL); + // Merging last-1 w/ last, so we are the base level for "foo", so + // DEL is removed. (as is v1). + ASSERT_EQ(AllEntriesFor("foo"), "[ v2 ]"); +} + +TEST(DBTest, DeletionMarkers2) { + Put("foo", "v1"); + ASSERT_OK(dbfull()->TEST_CompactMemTable()); + const int last = config::kMaxMemCompactLevel; + ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo => v1 is now in last level + + // Place a table at level last-1 to prevent merging with preceding mutation + Put("a", "begin"); + Put("z", "end"); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ(NumTableFilesAtLevel(last), 1); + ASSERT_EQ(NumTableFilesAtLevel(last-1), 1); + + Delete("foo"); + ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]"); + ASSERT_OK(dbfull()->TEST_CompactMemTable()); // Moves to level last-2 + ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]"); + dbfull()->TEST_CompactRange(last-2, NULL, NULL); + // DEL kept: "last" file overlaps + ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]"); + dbfull()->TEST_CompactRange(last-1, NULL, NULL); + // Merging last-1 w/ last, so we are the base level for "foo", so + // DEL is removed. (as is v1). + ASSERT_EQ(AllEntriesFor("foo"), "[ ]"); +} + +TEST(DBTest, OverlapInLevel0) { + do { + ASSERT_EQ(config::kMaxMemCompactLevel, 2) << "Fix test to match config"; + + // Fill levels 1 and 2 to disable the pushing of new memtables to levels > 0. + ASSERT_OK(Put("100", "v100")); + ASSERT_OK(Put("999", "v999")); + dbfull()->TEST_CompactMemTable(); + ASSERT_OK(Delete("100")); + ASSERT_OK(Delete("999")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("0,1,1", FilesPerLevel()); + + // Make files spanning the following ranges in level-0: + // files[0] 200 .. 900 + // files[1] 300 .. 500 + // Note that files are sorted by smallest key. + ASSERT_OK(Put("300", "v300")); + ASSERT_OK(Put("500", "v500")); + dbfull()->TEST_CompactMemTable(); + ASSERT_OK(Put("200", "v200")); + ASSERT_OK(Put("600", "v600")); + ASSERT_OK(Put("900", "v900")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("2,1,1", FilesPerLevel()); + + // Compact away the placeholder files we created initially + dbfull()->TEST_CompactRange(1, NULL, NULL); + dbfull()->TEST_CompactRange(2, NULL, NULL); + ASSERT_EQ("2", FilesPerLevel()); + + // Do a memtable compaction. Before bug-fix, the compaction would + // not detect the overlap with level-0 files and would incorrectly place + // the deletion in a deeper level. + ASSERT_OK(Delete("600")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("3", FilesPerLevel()); + ASSERT_EQ("NOT_FOUND", Get("600")); + } while (ChangeOptions()); +} + +TEST(DBTest, L0_CompactionBug_Issue44_a) { + Reopen(); + ASSERT_OK(Put("b", "v")); + Reopen(); + ASSERT_OK(Delete("b")); + ASSERT_OK(Delete("a")); + Reopen(); + ASSERT_OK(Delete("a")); + Reopen(); + ASSERT_OK(Put("a", "v")); + Reopen(); + Reopen(); + ASSERT_EQ("(a->v)", Contents()); + DelayMilliseconds(1000); // Wait for compaction to finish + ASSERT_EQ("(a->v)", Contents()); +} + +TEST(DBTest, L0_CompactionBug_Issue44_b) { + Reopen(); + Put("",""); + Reopen(); + Delete("e"); + Put("",""); + Reopen(); + Put("c", "cv"); + Reopen(); + Put("",""); + Reopen(); + Put("",""); + DelayMilliseconds(1000); // Wait for compaction to finish + Reopen(); + Put("d","dv"); + Reopen(); + Put("",""); + Reopen(); + Delete("d"); + Delete("b"); + Reopen(); + ASSERT_EQ("(->)(c->cv)", Contents()); + DelayMilliseconds(1000); // Wait for compaction to finish + ASSERT_EQ("(->)(c->cv)", Contents()); +} + +TEST(DBTest, ComparatorCheck) { + class NewComparator : public Comparator { + public: + virtual const char* Name() const { return "leveldb.NewComparator"; } + virtual int Compare(const Slice& a, const Slice& b) const { + return BytewiseComparator()->Compare(a, b); + } + virtual void FindShortestSeparator(std::string* s, const Slice& l) const { + BytewiseComparator()->FindShortestSeparator(s, l); + } + virtual void FindShortSuccessor(std::string* key) const { + BytewiseComparator()->FindShortSuccessor(key); + } + }; + NewComparator cmp; + Options new_options = CurrentOptions(); + new_options.comparator = &cmp; + Status s = TryReopen(&new_options); + ASSERT_TRUE(!s.ok()); + ASSERT_TRUE(s.ToString().find("comparator") != std::string::npos) + << s.ToString(); +} + +TEST(DBTest, CustomComparator) { + class NumberComparator : public Comparator { + public: + virtual const char* Name() const { return "test.NumberComparator"; } + virtual int Compare(const Slice& a, const Slice& b) const { + return ToNumber(a) - ToNumber(b); + } + virtual void FindShortestSeparator(std::string* s, const Slice& l) const { + ToNumber(*s); // Check format + ToNumber(l); // Check format + } + virtual void FindShortSuccessor(std::string* key) const { + ToNumber(*key); // Check format + } + private: + static int ToNumber(const Slice& x) { + // Check that there are no extra characters. + ASSERT_TRUE(x.size() >= 2 && x[0] == '[' && x[x.size()-1] == ']') + << EscapeString(x); + int val; + char ignored; + ASSERT_TRUE(sscanf(x.ToString().c_str(), "[%i]%c", &val, &ignored) == 1) + << EscapeString(x); + return val; + } + }; + NumberComparator cmp; + Options new_options = CurrentOptions(); + new_options.create_if_missing = true; + new_options.comparator = &cmp; + new_options.filter_policy = NULL; // Cannot use bloom filters + new_options.write_buffer_size = 1000; // Compact more often + DestroyAndReopen(&new_options); + ASSERT_OK(Put("[10]", "ten")); + ASSERT_OK(Put("[0x14]", "twenty")); + for (int i = 0; i < 2; i++) { + ASSERT_EQ("ten", Get("[10]")); + ASSERT_EQ("ten", Get("[0xa]")); + ASSERT_EQ("twenty", Get("[20]")); + ASSERT_EQ("twenty", Get("[0x14]")); + ASSERT_EQ("NOT_FOUND", Get("[15]")); + ASSERT_EQ("NOT_FOUND", Get("[0xf]")); + Compact("[0]", "[9999]"); + } + + for (int run = 0; run < 2; run++) { + for (int i = 0; i < 1000; i++) { + char buf[100]; + snprintf(buf, sizeof(buf), "[%d]", i*10); + ASSERT_OK(Put(buf, buf)); + } + Compact("[0]", "[1000000]"); + } +} + +TEST(DBTest, ManualCompaction) { + ASSERT_EQ(config::kMaxMemCompactLevel, 2) + << "Need to update this test to match kMaxMemCompactLevel"; + + MakeTables(3, "p", "q"); + ASSERT_EQ("1,1,1", FilesPerLevel()); + + // Compaction range falls before files + Compact("", "c"); + ASSERT_EQ("1,1,1", FilesPerLevel()); + + // Compaction range falls after files + Compact("r", "z"); + ASSERT_EQ("1,1,1", FilesPerLevel()); + + // Compaction range overlaps files + Compact("p1", "p9"); + ASSERT_EQ("0,0,1", FilesPerLevel()); + + // Populate a different range + MakeTables(3, "c", "e"); + ASSERT_EQ("1,1,2", FilesPerLevel()); + + // Compact just the new range + Compact("b", "f"); + ASSERT_EQ("0,0,2", FilesPerLevel()); + + // Compact all + MakeTables(1, "a", "z"); + ASSERT_EQ("0,1,2", FilesPerLevel()); + db_->CompactRange(NULL, NULL); + ASSERT_EQ("0,0,1", FilesPerLevel()); +} + +TEST(DBTest, DBOpen_Options) { + std::string dbname = test::TmpDir() + "/db_options_test"; + DestroyDB(dbname, Options()); + + // Does not exist, and create_if_missing == false: error + DB* db = NULL; + Options opts; + opts.create_if_missing = false; + Status s = DB::Open(opts, dbname, &db); + ASSERT_TRUE(strstr(s.ToString().c_str(), "does not exist") != NULL); + ASSERT_TRUE(db == NULL); + + // Does not exist, and create_if_missing == true: OK + opts.create_if_missing = true; + s = DB::Open(opts, dbname, &db); + ASSERT_OK(s); + ASSERT_TRUE(db != NULL); + + delete db; + db = NULL; + + // Does exist, and error_if_exists == true: error + opts.create_if_missing = false; + opts.error_if_exists = true; + s = DB::Open(opts, dbname, &db); + ASSERT_TRUE(strstr(s.ToString().c_str(), "exists") != NULL); + ASSERT_TRUE(db == NULL); + + // Does exist, and error_if_exists == false: OK + opts.create_if_missing = true; + opts.error_if_exists = false; + s = DB::Open(opts, dbname, &db); + ASSERT_OK(s); + ASSERT_TRUE(db != NULL); + + delete db; + db = NULL; +} + +TEST(DBTest, Locking) { + DB* db2 = NULL; + Status s = DB::Open(CurrentOptions(), dbname_, &db2); + ASSERT_TRUE(!s.ok()) << "Locking did not prevent re-opening db"; +} + +// Check that number of files does not grow when we are out of space +TEST(DBTest, NoSpace) { + Options options = CurrentOptions(); + options.env = env_; + Reopen(&options); + + ASSERT_OK(Put("foo", "v1")); + ASSERT_EQ("v1", Get("foo")); + Compact("a", "z"); + const int num_files = CountFiles(); + env_->no_space_.Release_Store(env_); // Force out-of-space errors + for (int i = 0; i < 10; i++) { + for (int level = 0; level < config::kNumLevels-1; level++) { + dbfull()->TEST_CompactRange(level, NULL, NULL); + } + } + env_->no_space_.Release_Store(NULL); + ASSERT_LT(CountFiles(), num_files + 3); +} + +TEST(DBTest, NonWritableFileSystem) { + Options options = CurrentOptions(); + options.write_buffer_size = 1000; + options.env = env_; + Reopen(&options); + ASSERT_OK(Put("foo", "v1")); + env_->non_writable_.Release_Store(env_); // Force errors for new files + std::string big(100000, 'x'); + int errors = 0; + for (int i = 0; i < 20; i++) { + fprintf(stderr, "iter %d; errors %d\n", i, errors); + if (!Put("foo", big).ok()) { + errors++; + DelayMilliseconds(100); + } + } + ASSERT_GT(errors, 0); + env_->non_writable_.Release_Store(NULL); +} + +TEST(DBTest, WriteSyncError) { + // Check that log sync errors cause the DB to disallow future writes. + + // (a) Cause log sync calls to fail + Options options = CurrentOptions(); + options.env = env_; + Reopen(&options); + env_->data_sync_error_.Release_Store(env_); + + // (b) Normal write should succeed + WriteOptions w; + ASSERT_OK(db_->Put(w, "k1", "v1")); + ASSERT_EQ("v1", Get("k1")); + + // (c) Do a sync write; should fail + w.sync = true; + ASSERT_TRUE(!db_->Put(w, "k2", "v2").ok()); + ASSERT_EQ("v1", Get("k1")); + ASSERT_EQ("NOT_FOUND", Get("k2")); + + // (d) make sync behave normally + env_->data_sync_error_.Release_Store(NULL); + + // (e) Do a non-sync write; should fail + w.sync = false; + ASSERT_TRUE(!db_->Put(w, "k3", "v3").ok()); + ASSERT_EQ("v1", Get("k1")); + ASSERT_EQ("NOT_FOUND", Get("k2")); + ASSERT_EQ("NOT_FOUND", Get("k3")); +} + +TEST(DBTest, ManifestWriteError) { + // Test for the following problem: + // (a) Compaction produces file F + // (b) Log record containing F is written to MANIFEST file, but Sync() fails + // (c) GC deletes F + // (d) After reopening DB, reads fail since deleted F is named in log record + + // We iterate twice. In the second iteration, everything is the + // same except the log record never makes it to the MANIFEST file. + for (int iter = 0; iter < 2; iter++) { + port::AtomicPointer* error_type = (iter == 0) + ? &env_->manifest_sync_error_ + : &env_->manifest_write_error_; + + // Insert foo=>bar mapping + Options options = CurrentOptions(); + options.env = env_; + options.create_if_missing = true; + options.error_if_exists = false; + DestroyAndReopen(&options); + ASSERT_OK(Put("foo", "bar")); + ASSERT_EQ("bar", Get("foo")); + + // Memtable compaction (will succeed) + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("bar", Get("foo")); + const int last = config::kMaxMemCompactLevel; + ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo=>bar is now in last level + + // Merging compaction (will fail) + error_type->Release_Store(env_); + dbfull()->TEST_CompactRange(last, NULL, NULL); // Should fail + ASSERT_EQ("bar", Get("foo")); + + // Recovery: should not lose data + error_type->Release_Store(NULL); + Reopen(&options); + ASSERT_EQ("bar", Get("foo")); + } +} + +TEST(DBTest, MissingSSTFile) { + ASSERT_OK(Put("foo", "bar")); + ASSERT_EQ("bar", Get("foo")); + + // Dump the memtable to disk. + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("bar", Get("foo")); + + Close(); + ASSERT_TRUE(DeleteAnSSTFile()); + Options options = CurrentOptions(); + options.paranoid_checks = true; + Status s = TryReopen(&options); + ASSERT_TRUE(!s.ok()); + ASSERT_TRUE(s.ToString().find("issing") != std::string::npos) + << s.ToString(); +} + +TEST(DBTest, StillReadSST) { + ASSERT_OK(Put("foo", "bar")); + ASSERT_EQ("bar", Get("foo")); + + // Dump the memtable to disk. + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("bar", Get("foo")); + Close(); + ASSERT_GT(RenameLDBToSST(), 0); + Options options = CurrentOptions(); + options.paranoid_checks = true; + Status s = TryReopen(&options); + ASSERT_TRUE(s.ok()); + ASSERT_EQ("bar", Get("foo")); +} + +TEST(DBTest, FilesDeletedAfterCompaction) { + ASSERT_OK(Put("foo", "v2")); + Compact("a", "z"); + const int num_files = CountFiles(); + for (int i = 0; i < 10; i++) { + ASSERT_OK(Put("foo", "v2")); + Compact("a", "z"); + } + ASSERT_EQ(CountFiles(), num_files); +} + +TEST(DBTest, BloomFilter) { + env_->count_random_reads_ = true; + Options options = CurrentOptions(); + options.env = env_; + options.block_cache = NewLRUCache(0); // Prevent cache hits + options.filter_policy = NewBloomFilterPolicy(10); + Reopen(&options); + + // Populate multiple layers + const int N = 10000; + for (int i = 0; i < N; i++) { + ASSERT_OK(Put(Key(i), Key(i))); + } + Compact("a", "z"); + for (int i = 0; i < N; i += 100) { + ASSERT_OK(Put(Key(i), Key(i))); + } + dbfull()->TEST_CompactMemTable(); + + // Prevent auto compactions triggered by seeks + env_->delay_data_sync_.Release_Store(env_); + + // Lookup present keys. Should rarely read from small sstable. + env_->random_read_counter_.Reset(); + for (int i = 0; i < N; i++) { + ASSERT_EQ(Key(i), Get(Key(i))); + } + int reads = env_->random_read_counter_.Read(); + fprintf(stderr, "%d present => %d reads\n", N, reads); + ASSERT_GE(reads, N); + ASSERT_LE(reads, N + 2*N/100); + + // Lookup present keys. Should rarely read from either sstable. + env_->random_read_counter_.Reset(); + for (int i = 0; i < N; i++) { + ASSERT_EQ("NOT_FOUND", Get(Key(i) + ".missing")); + } + reads = env_->random_read_counter_.Read(); + fprintf(stderr, "%d missing => %d reads\n", N, reads); + ASSERT_LE(reads, 3*N/100); + + env_->delay_data_sync_.Release_Store(NULL); + Close(); + delete options.block_cache; + delete options.filter_policy; +} + +// Multi-threaded test: +namespace { + +static const int kNumThreads = 4; +static const int kTestSeconds = 10; +static const int kNumKeys = 1000; + +struct MTState { + DBTest* test; + port::AtomicPointer stop; + port::AtomicPointer counter[kNumThreads]; + port::AtomicPointer thread_done[kNumThreads]; +}; + +struct MTThread { + MTState* state; + int id; +}; + +static void MTThreadBody(void* arg) { + MTThread* t = reinterpret_cast(arg); + int id = t->id; + DB* db = t->state->test->db_; + uintptr_t counter = 0; + fprintf(stderr, "... starting thread %d\n", id); + Random rnd(1000 + id); + std::string value; + char valbuf[1500]; + while (t->state->stop.Acquire_Load() == NULL) { + t->state->counter[id].Release_Store(reinterpret_cast(counter)); + + int key = rnd.Uniform(kNumKeys); + char keybuf[20]; + snprintf(keybuf, sizeof(keybuf), "%016d", key); + + if (rnd.OneIn(2)) { + // Write values of the form . + // We add some padding for force compactions. + snprintf(valbuf, sizeof(valbuf), "%d.%d.%-1000d", + key, id, static_cast(counter)); + ASSERT_OK(db->Put(WriteOptions(), Slice(keybuf), Slice(valbuf))); + } else { + // Read a value and verify that it matches the pattern written above. + Status s = db->Get(ReadOptions(), Slice(keybuf), &value); + if (s.IsNotFound()) { + // Key has not yet been written + } else { + // Check that the writer thread counter is >= the counter in the value + ASSERT_OK(s); + int k, w, c; + ASSERT_EQ(3, sscanf(value.c_str(), "%d.%d.%d", &k, &w, &c)) << value; + ASSERT_EQ(k, key); + ASSERT_GE(w, 0); + ASSERT_LT(w, kNumThreads); + ASSERT_LE(static_cast(c), reinterpret_cast( + t->state->counter[w].Acquire_Load())); + } + } + counter++; + } + t->state->thread_done[id].Release_Store(t); + fprintf(stderr, "... stopping thread %d after %d ops\n", id, int(counter)); +} + +} // namespace + +TEST(DBTest, MultiThreaded) { + do { + // Initialize state + MTState mt; + mt.test = this; + mt.stop.Release_Store(0); + for (int id = 0; id < kNumThreads; id++) { + mt.counter[id].Release_Store(0); + mt.thread_done[id].Release_Store(0); + } + + // Start threads + MTThread thread[kNumThreads]; + for (int id = 0; id < kNumThreads; id++) { + thread[id].state = &mt; + thread[id].id = id; + env_->StartThread(MTThreadBody, &thread[id]); + } + + // Let them run for a while + DelayMilliseconds(kTestSeconds * 1000); + + // Stop the threads and wait for them to finish + mt.stop.Release_Store(&mt); + for (int id = 0; id < kNumThreads; id++) { + while (mt.thread_done[id].Acquire_Load() == NULL) { + DelayMilliseconds(100); + } + } + } while (ChangeOptions()); +} + +namespace { +typedef std::map KVMap; +} + +class ModelDB: public DB { + public: + class ModelSnapshot : public Snapshot { + public: + KVMap map_; + }; + + explicit ModelDB(const Options& options): options_(options) { } + ~ModelDB() { } + virtual Status Put(const WriteOptions& o, const Slice& k, const Slice& v) { + return DB::Put(o, k, v); + } + virtual Status Delete(const WriteOptions& o, const Slice& key) { + return DB::Delete(o, key); + } + virtual Status Get(const ReadOptions& options, + const Slice& key, std::string* value) { + assert(false); // Not implemented + return Status::NotFound(key); + } + virtual Iterator* NewIterator(const ReadOptions& options) { + if (options.snapshot == NULL) { + KVMap* saved = new KVMap; + *saved = map_; + return new ModelIter(saved, true); + } else { + const KVMap* snapshot_state = + &(reinterpret_cast(options.snapshot)->map_); + return new ModelIter(snapshot_state, false); + } + } + virtual const Snapshot* GetSnapshot() { + ModelSnapshot* snapshot = new ModelSnapshot; + snapshot->map_ = map_; + return snapshot; + } + + virtual void ReleaseSnapshot(const Snapshot* snapshot) { + delete reinterpret_cast(snapshot); + } + virtual Status Write(const WriteOptions& options, WriteBatch* batch) { + class Handler : public WriteBatch::Handler { + public: + KVMap* map_; + virtual void Put(const Slice& key, const Slice& value) { + (*map_)[key.ToString()] = value.ToString(); + } + virtual void Delete(const Slice& key) { + map_->erase(key.ToString()); + } + }; + Handler handler; + handler.map_ = &map_; + return batch->Iterate(&handler); + } + + virtual bool GetProperty(const Slice& property, std::string* value) { + return false; + } + virtual void GetApproximateSizes(const Range* r, int n, uint64_t* sizes) { + for (int i = 0; i < n; i++) { + sizes[i] = 0; + } + } + virtual void CompactRange(const Slice* start, const Slice* end) { + } + + private: + class ModelIter: public Iterator { + public: + ModelIter(const KVMap* map, bool owned) + : map_(map), owned_(owned), iter_(map_->end()) { + } + ~ModelIter() { + if (owned_) delete map_; + } + virtual bool Valid() const { return iter_ != map_->end(); } + virtual void SeekToFirst() { iter_ = map_->begin(); } + virtual void SeekToLast() { + if (map_->empty()) { + iter_ = map_->end(); + } else { + iter_ = map_->find(map_->rbegin()->first); + } + } + virtual void Seek(const Slice& k) { + iter_ = map_->lower_bound(k.ToString()); + } + virtual void Next() { ++iter_; } + virtual void Prev() { --iter_; } + virtual Slice key() const { return iter_->first; } + virtual Slice value() const { return iter_->second; } + virtual Status status() const { return Status::OK(); } + private: + const KVMap* const map_; + const bool owned_; // Do we own map_ + KVMap::const_iterator iter_; + }; + const Options options_; + KVMap map_; +}; + +static std::string RandomKey(Random* rnd) { + int len = (rnd->OneIn(3) + ? 1 // Short sometimes to encourage collisions + : (rnd->OneIn(100) ? rnd->Skewed(10) : rnd->Uniform(10))); + return test::RandomKey(rnd, len); +} + +static bool CompareIterators(int step, + DB* model, + DB* db, + const Snapshot* model_snap, + const Snapshot* db_snap) { + ReadOptions options; + options.snapshot = model_snap; + Iterator* miter = model->NewIterator(options); + options.snapshot = db_snap; + Iterator* dbiter = db->NewIterator(options); + bool ok = true; + int count = 0; + for (miter->SeekToFirst(), dbiter->SeekToFirst(); + ok && miter->Valid() && dbiter->Valid(); + miter->Next(), dbiter->Next()) { + count++; + if (miter->key().compare(dbiter->key()) != 0) { + fprintf(stderr, "step %d: Key mismatch: '%s' vs. '%s'\n", + step, + EscapeString(miter->key()).c_str(), + EscapeString(dbiter->key()).c_str()); + ok = false; + break; + } + + if (miter->value().compare(dbiter->value()) != 0) { + fprintf(stderr, "step %d: Value mismatch for key '%s': '%s' vs. '%s'\n", + step, + EscapeString(miter->key()).c_str(), + EscapeString(miter->value()).c_str(), + EscapeString(miter->value()).c_str()); + ok = false; + } + } + + if (ok) { + if (miter->Valid() != dbiter->Valid()) { + fprintf(stderr, "step %d: Mismatch at end of iterators: %d vs. %d\n", + step, miter->Valid(), dbiter->Valid()); + ok = false; + } + } + fprintf(stderr, "%d entries compared: ok=%d\n", count, ok); + delete miter; + delete dbiter; + return ok; +} + +TEST(DBTest, Randomized) { + Random rnd(test::RandomSeed()); + do { + ModelDB model(CurrentOptions()); + const int N = 10000; + const Snapshot* model_snap = NULL; + const Snapshot* db_snap = NULL; + std::string k, v; + for (int step = 0; step < N; step++) { + if (step % 100 == 0) { + fprintf(stderr, "Step %d of %d\n", step, N); + } + // TODO(sanjay): Test Get() works + int p = rnd.Uniform(100); + if (p < 45) { // Put + k = RandomKey(&rnd); + v = RandomString(&rnd, + rnd.OneIn(20) + ? 100 + rnd.Uniform(100) + : rnd.Uniform(8)); + ASSERT_OK(model.Put(WriteOptions(), k, v)); + ASSERT_OK(db_->Put(WriteOptions(), k, v)); + + } else if (p < 90) { // Delete + k = RandomKey(&rnd); + ASSERT_OK(model.Delete(WriteOptions(), k)); + ASSERT_OK(db_->Delete(WriteOptions(), k)); + + + } else { // Multi-element batch + WriteBatch b; + const int num = rnd.Uniform(8); + for (int i = 0; i < num; i++) { + if (i == 0 || !rnd.OneIn(10)) { + k = RandomKey(&rnd); + } else { + // Periodically re-use the same key from the previous iter, so + // we have multiple entries in the write batch for the same key + } + if (rnd.OneIn(2)) { + v = RandomString(&rnd, rnd.Uniform(10)); + b.Put(k, v); + } else { + b.Delete(k); + } + } + ASSERT_OK(model.Write(WriteOptions(), &b)); + ASSERT_OK(db_->Write(WriteOptions(), &b)); + } + + if ((step % 100) == 0) { + ASSERT_TRUE(CompareIterators(step, &model, db_, NULL, NULL)); + ASSERT_TRUE(CompareIterators(step, &model, db_, model_snap, db_snap)); + // Save a snapshot from each DB this time that we'll use next + // time we compare things, to make sure the current state is + // preserved with the snapshot + if (model_snap != NULL) model.ReleaseSnapshot(model_snap); + if (db_snap != NULL) db_->ReleaseSnapshot(db_snap); + + Reopen(); + ASSERT_TRUE(CompareIterators(step, &model, db_, NULL, NULL)); + + model_snap = model.GetSnapshot(); + db_snap = db_->GetSnapshot(); + } + } + if (model_snap != NULL) model.ReleaseSnapshot(model_snap); + if (db_snap != NULL) db_->ReleaseSnapshot(db_snap); + } while (ChangeOptions()); +} + +std::string MakeKey(unsigned int num) { + char buf[30]; + snprintf(buf, sizeof(buf), "%016u", num); + return std::string(buf); +} + +void BM_LogAndApply(int iters, int num_base_files) { + std::string dbname = test::TmpDir() + "/leveldb_test_benchmark"; + DestroyDB(dbname, Options()); + + DB* db = NULL; + Options opts; + opts.create_if_missing = true; + Status s = DB::Open(opts, dbname, &db); + ASSERT_OK(s); + ASSERT_TRUE(db != NULL); + + delete db; + db = NULL; + + Env* env = Env::Default(); + + port::Mutex mu; + MutexLock l(&mu); + + InternalKeyComparator cmp(BytewiseComparator()); + Options options; + VersionSet vset(dbname, &options, NULL, &cmp); + ASSERT_OK(vset.Recover()); + VersionEdit vbase; + uint64_t fnum = 1; + for (int i = 0; i < num_base_files; i++) { + InternalKey start(MakeKey(2*fnum), 1, kTypeValue); + InternalKey limit(MakeKey(2*fnum+1), 1, kTypeDeletion); + vbase.AddFile(2, fnum++, 1 /* file size */, start, limit); + } + ASSERT_OK(vset.LogAndApply(&vbase, &mu)); + + uint64_t start_micros = env->NowMicros(); + + for (int i = 0; i < iters; i++) { + VersionEdit vedit; + vedit.DeleteFile(2, fnum); + InternalKey start(MakeKey(2*fnum), 1, kTypeValue); + InternalKey limit(MakeKey(2*fnum+1), 1, kTypeDeletion); + vedit.AddFile(2, fnum++, 1 /* file size */, start, limit); + vset.LogAndApply(&vedit, &mu); + } + uint64_t stop_micros = env->NowMicros(); + unsigned int us = stop_micros - start_micros; + char buf[16]; + snprintf(buf, sizeof(buf), "%d", num_base_files); + fprintf(stderr, + "BM_LogAndApply/%-6s %8d iters : %9u us (%7.0f us / iter)\n", + buf, iters, us, ((float)us) / iters); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + if (argc > 1 && std::string(argv[1]) == "--benchmark") { + leveldb::BM_LogAndApply(1000, 1); + leveldb::BM_LogAndApply(1000, 100); + leveldb::BM_LogAndApply(1000, 10000); + leveldb::BM_LogAndApply(100, 100000); + return 0; + } + + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/dbformat.cc b/src/leveldb/db/dbformat.cc new file mode 100644 index 00000000..20a7ca44 --- /dev/null +++ b/src/leveldb/db/dbformat.cc @@ -0,0 +1,140 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include "db/dbformat.h" +#include "port/port.h" +#include "util/coding.h" + +namespace leveldb { + +static uint64_t PackSequenceAndType(uint64_t seq, ValueType t) { + assert(seq <= kMaxSequenceNumber); + assert(t <= kValueTypeForSeek); + return (seq << 8) | t; +} + +void AppendInternalKey(std::string* result, const ParsedInternalKey& key) { + result->append(key.user_key.data(), key.user_key.size()); + PutFixed64(result, PackSequenceAndType(key.sequence, key.type)); +} + +std::string ParsedInternalKey::DebugString() const { + char buf[50]; + snprintf(buf, sizeof(buf), "' @ %llu : %d", + (unsigned long long) sequence, + int(type)); + std::string result = "'"; + result += EscapeString(user_key.ToString()); + result += buf; + return result; +} + +std::string InternalKey::DebugString() const { + std::string result; + ParsedInternalKey parsed; + if (ParseInternalKey(rep_, &parsed)) { + result = parsed.DebugString(); + } else { + result = "(bad)"; + result.append(EscapeString(rep_)); + } + return result; +} + +const char* InternalKeyComparator::Name() const { + return "leveldb.InternalKeyComparator"; +} + +int InternalKeyComparator::Compare(const Slice& akey, const Slice& bkey) const { + // Order by: + // increasing user key (according to user-supplied comparator) + // decreasing sequence number + // decreasing type (though sequence# should be enough to disambiguate) + int r = user_comparator_->Compare(ExtractUserKey(akey), ExtractUserKey(bkey)); + if (r == 0) { + const uint64_t anum = DecodeFixed64(akey.data() + akey.size() - 8); + const uint64_t bnum = DecodeFixed64(bkey.data() + bkey.size() - 8); + if (anum > bnum) { + r = -1; + } else if (anum < bnum) { + r = +1; + } + } + return r; +} + +void InternalKeyComparator::FindShortestSeparator( + std::string* start, + const Slice& limit) const { + // Attempt to shorten the user portion of the key + Slice user_start = ExtractUserKey(*start); + Slice user_limit = ExtractUserKey(limit); + std::string tmp(user_start.data(), user_start.size()); + user_comparator_->FindShortestSeparator(&tmp, user_limit); + if (tmp.size() < user_start.size() && + user_comparator_->Compare(user_start, tmp) < 0) { + // User key has become shorter physically, but larger logically. + // Tack on the earliest possible number to the shortened user key. + PutFixed64(&tmp, PackSequenceAndType(kMaxSequenceNumber,kValueTypeForSeek)); + assert(this->Compare(*start, tmp) < 0); + assert(this->Compare(tmp, limit) < 0); + start->swap(tmp); + } +} + +void InternalKeyComparator::FindShortSuccessor(std::string* key) const { + Slice user_key = ExtractUserKey(*key); + std::string tmp(user_key.data(), user_key.size()); + user_comparator_->FindShortSuccessor(&tmp); + if (tmp.size() < user_key.size() && + user_comparator_->Compare(user_key, tmp) < 0) { + // User key has become shorter physically, but larger logically. + // Tack on the earliest possible number to the shortened user key. + PutFixed64(&tmp, PackSequenceAndType(kMaxSequenceNumber,kValueTypeForSeek)); + assert(this->Compare(*key, tmp) < 0); + key->swap(tmp); + } +} + +const char* InternalFilterPolicy::Name() const { + return user_policy_->Name(); +} + +void InternalFilterPolicy::CreateFilter(const Slice* keys, int n, + std::string* dst) const { + // We rely on the fact that the code in table.cc does not mind us + // adjusting keys[]. + Slice* mkey = const_cast(keys); + for (int i = 0; i < n; i++) { + mkey[i] = ExtractUserKey(keys[i]); + // TODO(sanjay): Suppress dups? + } + user_policy_->CreateFilter(keys, n, dst); +} + +bool InternalFilterPolicy::KeyMayMatch(const Slice& key, const Slice& f) const { + return user_policy_->KeyMayMatch(ExtractUserKey(key), f); +} + +LookupKey::LookupKey(const Slice& user_key, SequenceNumber s) { + size_t usize = user_key.size(); + size_t needed = usize + 13; // A conservative estimate + char* dst; + if (needed <= sizeof(space_)) { + dst = space_; + } else { + dst = new char[needed]; + } + start_ = dst; + dst = EncodeVarint32(dst, usize + 8); + kstart_ = dst; + memcpy(dst, user_key.data(), usize); + dst += usize; + EncodeFixed64(dst, PackSequenceAndType(s, kValueTypeForSeek)); + dst += 8; + end_ = dst; +} + +} // namespace leveldb diff --git a/src/leveldb/db/dbformat.h b/src/leveldb/db/dbformat.h new file mode 100644 index 00000000..ea897b13 --- /dev/null +++ b/src/leveldb/db/dbformat.h @@ -0,0 +1,230 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_DBFORMAT_H_ +#define STORAGE_LEVELDB_DB_DBFORMAT_H_ + +#include +#include "leveldb/comparator.h" +#include "leveldb/db.h" +#include "leveldb/filter_policy.h" +#include "leveldb/slice.h" +#include "leveldb/table_builder.h" +#include "util/coding.h" +#include "util/logging.h" + +namespace leveldb { + +// Grouping of constants. We may want to make some of these +// parameters set via options. +namespace config { +static const int kNumLevels = 7; + +// Level-0 compaction is started when we hit this many files. +static const int kL0_CompactionTrigger = 4; + +// Soft limit on number of level-0 files. We slow down writes at this point. +static const int kL0_SlowdownWritesTrigger = 8; + +// Maximum number of level-0 files. We stop writes at this point. +static const int kL0_StopWritesTrigger = 12; + +// Maximum level to which a new compacted memtable is pushed if it +// does not create overlap. We try to push to level 2 to avoid the +// relatively expensive level 0=>1 compactions and to avoid some +// expensive manifest file operations. We do not push all the way to +// the largest level since that can generate a lot of wasted disk +// space if the same key space is being repeatedly overwritten. +static const int kMaxMemCompactLevel = 2; + +// Approximate gap in bytes between samples of data read during iteration. +static const int kReadBytesPeriod = 1048576; + +} // namespace config + +class InternalKey; + +// Value types encoded as the last component of internal keys. +// DO NOT CHANGE THESE ENUM VALUES: they are embedded in the on-disk +// data structures. +enum ValueType { + kTypeDeletion = 0x0, + kTypeValue = 0x1 +}; +// kValueTypeForSeek defines the ValueType that should be passed when +// constructing a ParsedInternalKey object for seeking to a particular +// sequence number (since we sort sequence numbers in decreasing order +// and the value type is embedded as the low 8 bits in the sequence +// number in internal keys, we need to use the highest-numbered +// ValueType, not the lowest). +static const ValueType kValueTypeForSeek = kTypeValue; + +typedef uint64_t SequenceNumber; + +// We leave eight bits empty at the bottom so a type and sequence# +// can be packed together into 64-bits. +static const SequenceNumber kMaxSequenceNumber = + ((0x1ull << 56) - 1); + +struct ParsedInternalKey { + Slice user_key; + SequenceNumber sequence; + ValueType type; + + ParsedInternalKey() { } // Intentionally left uninitialized (for speed) + ParsedInternalKey(const Slice& u, const SequenceNumber& seq, ValueType t) + : user_key(u), sequence(seq), type(t) { } + std::string DebugString() const; +}; + +// Return the length of the encoding of "key". +inline size_t InternalKeyEncodingLength(const ParsedInternalKey& key) { + return key.user_key.size() + 8; +} + +// Append the serialization of "key" to *result. +extern void AppendInternalKey(std::string* result, + const ParsedInternalKey& key); + +// Attempt to parse an internal key from "internal_key". On success, +// stores the parsed data in "*result", and returns true. +// +// On error, returns false, leaves "*result" in an undefined state. +extern bool ParseInternalKey(const Slice& internal_key, + ParsedInternalKey* result); + +// Returns the user key portion of an internal key. +inline Slice ExtractUserKey(const Slice& internal_key) { + assert(internal_key.size() >= 8); + return Slice(internal_key.data(), internal_key.size() - 8); +} + +inline ValueType ExtractValueType(const Slice& internal_key) { + assert(internal_key.size() >= 8); + const size_t n = internal_key.size(); + uint64_t num = DecodeFixed64(internal_key.data() + n - 8); + unsigned char c = num & 0xff; + return static_cast(c); +} + +// A comparator for internal keys that uses a specified comparator for +// the user key portion and breaks ties by decreasing sequence number. +class InternalKeyComparator : public Comparator { + private: + const Comparator* user_comparator_; + public: + explicit InternalKeyComparator(const Comparator* c) : user_comparator_(c) { } + virtual const char* Name() const; + virtual int Compare(const Slice& a, const Slice& b) const; + virtual void FindShortestSeparator( + std::string* start, + const Slice& limit) const; + virtual void FindShortSuccessor(std::string* key) const; + + const Comparator* user_comparator() const { return user_comparator_; } + + int Compare(const InternalKey& a, const InternalKey& b) const; +}; + +// Filter policy wrapper that converts from internal keys to user keys +class InternalFilterPolicy : public FilterPolicy { + private: + const FilterPolicy* const user_policy_; + public: + explicit InternalFilterPolicy(const FilterPolicy* p) : user_policy_(p) { } + virtual const char* Name() const; + virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const; + virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const; +}; + +// Modules in this directory should keep internal keys wrapped inside +// the following class instead of plain strings so that we do not +// incorrectly use string comparisons instead of an InternalKeyComparator. +class InternalKey { + private: + std::string rep_; + public: + InternalKey() { } // Leave rep_ as empty to indicate it is invalid + InternalKey(const Slice& user_key, SequenceNumber s, ValueType t) { + AppendInternalKey(&rep_, ParsedInternalKey(user_key, s, t)); + } + + void DecodeFrom(const Slice& s) { rep_.assign(s.data(), s.size()); } + Slice Encode() const { + assert(!rep_.empty()); + return rep_; + } + + Slice user_key() const { return ExtractUserKey(rep_); } + + void SetFrom(const ParsedInternalKey& p) { + rep_.clear(); + AppendInternalKey(&rep_, p); + } + + void Clear() { rep_.clear(); } + + std::string DebugString() const; +}; + +inline int InternalKeyComparator::Compare( + const InternalKey& a, const InternalKey& b) const { + return Compare(a.Encode(), b.Encode()); +} + +inline bool ParseInternalKey(const Slice& internal_key, + ParsedInternalKey* result) { + const size_t n = internal_key.size(); + if (n < 8) return false; + uint64_t num = DecodeFixed64(internal_key.data() + n - 8); + unsigned char c = num & 0xff; + result->sequence = num >> 8; + result->type = static_cast(c); + result->user_key = Slice(internal_key.data(), n - 8); + return (c <= static_cast(kTypeValue)); +} + +// A helper class useful for DBImpl::Get() +class LookupKey { + public: + // Initialize *this for looking up user_key at a snapshot with + // the specified sequence number. + LookupKey(const Slice& user_key, SequenceNumber sequence); + + ~LookupKey(); + + // Return a key suitable for lookup in a MemTable. + Slice memtable_key() const { return Slice(start_, end_ - start_); } + + // Return an internal key (suitable for passing to an internal iterator) + Slice internal_key() const { return Slice(kstart_, end_ - kstart_); } + + // Return the user key + Slice user_key() const { return Slice(kstart_, end_ - kstart_ - 8); } + + private: + // We construct a char array of the form: + // klength varint32 <-- start_ + // userkey char[klength] <-- kstart_ + // tag uint64 + // <-- end_ + // The array is a suitable MemTable key. + // The suffix starting with "userkey" can be used as an InternalKey. + const char* start_; + const char* kstart_; + const char* end_; + char space_[200]; // Avoid allocation for short keys + + // No copying allowed + LookupKey(const LookupKey&); + void operator=(const LookupKey&); +}; + +inline LookupKey::~LookupKey() { + if (start_ != space_) delete[] start_; +} + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_DBFORMAT_H_ diff --git a/src/leveldb/db/dbformat_test.cc b/src/leveldb/db/dbformat_test.cc new file mode 100644 index 00000000..5d82f5d3 --- /dev/null +++ b/src/leveldb/db/dbformat_test.cc @@ -0,0 +1,112 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/dbformat.h" +#include "util/logging.h" +#include "util/testharness.h" + +namespace leveldb { + +static std::string IKey(const std::string& user_key, + uint64_t seq, + ValueType vt) { + std::string encoded; + AppendInternalKey(&encoded, ParsedInternalKey(user_key, seq, vt)); + return encoded; +} + +static std::string Shorten(const std::string& s, const std::string& l) { + std::string result = s; + InternalKeyComparator(BytewiseComparator()).FindShortestSeparator(&result, l); + return result; +} + +static std::string ShortSuccessor(const std::string& s) { + std::string result = s; + InternalKeyComparator(BytewiseComparator()).FindShortSuccessor(&result); + return result; +} + +static void TestKey(const std::string& key, + uint64_t seq, + ValueType vt) { + std::string encoded = IKey(key, seq, vt); + + Slice in(encoded); + ParsedInternalKey decoded("", 0, kTypeValue); + + ASSERT_TRUE(ParseInternalKey(in, &decoded)); + ASSERT_EQ(key, decoded.user_key.ToString()); + ASSERT_EQ(seq, decoded.sequence); + ASSERT_EQ(vt, decoded.type); + + ASSERT_TRUE(!ParseInternalKey(Slice("bar"), &decoded)); +} + +class FormatTest { }; + +TEST(FormatTest, InternalKey_EncodeDecode) { + const char* keys[] = { "", "k", "hello", "longggggggggggggggggggggg" }; + const uint64_t seq[] = { + 1, 2, 3, + (1ull << 8) - 1, 1ull << 8, (1ull << 8) + 1, + (1ull << 16) - 1, 1ull << 16, (1ull << 16) + 1, + (1ull << 32) - 1, 1ull << 32, (1ull << 32) + 1 + }; + for (int k = 0; k < sizeof(keys) / sizeof(keys[0]); k++) { + for (int s = 0; s < sizeof(seq) / sizeof(seq[0]); s++) { + TestKey(keys[k], seq[s], kTypeValue); + TestKey("hello", 1, kTypeDeletion); + } + } +} + +TEST(FormatTest, InternalKeyShortSeparator) { + // When user keys are same + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foo", 99, kTypeValue))); + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foo", 101, kTypeValue))); + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foo", 100, kTypeValue))); + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foo", 100, kTypeDeletion))); + + // When user keys are misordered + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("bar", 99, kTypeValue))); + + // When user keys are different, but correctly ordered + ASSERT_EQ(IKey("g", kMaxSequenceNumber, kValueTypeForSeek), + Shorten(IKey("foo", 100, kTypeValue), + IKey("hello", 200, kTypeValue))); + + // When start user key is prefix of limit user key + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foobar", 200, kTypeValue))); + + // When limit user key is prefix of start user key + ASSERT_EQ(IKey("foobar", 100, kTypeValue), + Shorten(IKey("foobar", 100, kTypeValue), + IKey("foo", 200, kTypeValue))); +} + +TEST(FormatTest, InternalKeyShortestSuccessor) { + ASSERT_EQ(IKey("g", kMaxSequenceNumber, kValueTypeForSeek), + ShortSuccessor(IKey("foo", 100, kTypeValue))); + ASSERT_EQ(IKey("\xff\xff", 100, kTypeValue), + ShortSuccessor(IKey("\xff\xff", 100, kTypeValue))); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/dumpfile.cc b/src/leveldb/db/dumpfile.cc new file mode 100644 index 00000000..61c47c2f --- /dev/null +++ b/src/leveldb/db/dumpfile.cc @@ -0,0 +1,225 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include "db/dbformat.h" +#include "db/filename.h" +#include "db/log_reader.h" +#include "db/version_edit.h" +#include "db/write_batch_internal.h" +#include "leveldb/env.h" +#include "leveldb/iterator.h" +#include "leveldb/options.h" +#include "leveldb/status.h" +#include "leveldb/table.h" +#include "leveldb/write_batch.h" +#include "util/logging.h" + +namespace leveldb { + +namespace { + +bool GuessType(const std::string& fname, FileType* type) { + size_t pos = fname.rfind('/'); + std::string basename; + if (pos == std::string::npos) { + basename = fname; + } else { + basename = std::string(fname.data() + pos + 1, fname.size() - pos - 1); + } + uint64_t ignored; + return ParseFileName(basename, &ignored, type); +} + +// Notified when log reader encounters corruption. +class CorruptionReporter : public log::Reader::Reporter { + public: + WritableFile* dst_; + virtual void Corruption(size_t bytes, const Status& status) { + std::string r = "corruption: "; + AppendNumberTo(&r, bytes); + r += " bytes; "; + r += status.ToString(); + r.push_back('\n'); + dst_->Append(r); + } +}; + +// Print contents of a log file. (*func)() is called on every record. +Status PrintLogContents(Env* env, const std::string& fname, + void (*func)(uint64_t, Slice, WritableFile*), + WritableFile* dst) { + SequentialFile* file; + Status s = env->NewSequentialFile(fname, &file); + if (!s.ok()) { + return s; + } + CorruptionReporter reporter; + reporter.dst_ = dst; + log::Reader reader(file, &reporter, true, 0); + Slice record; + std::string scratch; + while (reader.ReadRecord(&record, &scratch)) { + (*func)(reader.LastRecordOffset(), record, dst); + } + delete file; + return Status::OK(); +} + +// Called on every item found in a WriteBatch. +class WriteBatchItemPrinter : public WriteBatch::Handler { + public: + WritableFile* dst_; + virtual void Put(const Slice& key, const Slice& value) { + std::string r = " put '"; + AppendEscapedStringTo(&r, key); + r += "' '"; + AppendEscapedStringTo(&r, value); + r += "'\n"; + dst_->Append(r); + } + virtual void Delete(const Slice& key) { + std::string r = " del '"; + AppendEscapedStringTo(&r, key); + r += "'\n"; + dst_->Append(r); + } +}; + + +// Called on every log record (each one of which is a WriteBatch) +// found in a kLogFile. +static void WriteBatchPrinter(uint64_t pos, Slice record, WritableFile* dst) { + std::string r = "--- offset "; + AppendNumberTo(&r, pos); + r += "; "; + if (record.size() < 12) { + r += "log record length "; + AppendNumberTo(&r, record.size()); + r += " is too small\n"; + dst->Append(r); + return; + } + WriteBatch batch; + WriteBatchInternal::SetContents(&batch, record); + r += "sequence "; + AppendNumberTo(&r, WriteBatchInternal::Sequence(&batch)); + r.push_back('\n'); + dst->Append(r); + WriteBatchItemPrinter batch_item_printer; + batch_item_printer.dst_ = dst; + Status s = batch.Iterate(&batch_item_printer); + if (!s.ok()) { + dst->Append(" error: " + s.ToString() + "\n"); + } +} + +Status DumpLog(Env* env, const std::string& fname, WritableFile* dst) { + return PrintLogContents(env, fname, WriteBatchPrinter, dst); +} + +// Called on every log record (each one of which is a WriteBatch) +// found in a kDescriptorFile. +static void VersionEditPrinter(uint64_t pos, Slice record, WritableFile* dst) { + std::string r = "--- offset "; + AppendNumberTo(&r, pos); + r += "; "; + VersionEdit edit; + Status s = edit.DecodeFrom(record); + if (!s.ok()) { + r += s.ToString(); + r.push_back('\n'); + } else { + r += edit.DebugString(); + } + dst->Append(r); +} + +Status DumpDescriptor(Env* env, const std::string& fname, WritableFile* dst) { + return PrintLogContents(env, fname, VersionEditPrinter, dst); +} + +Status DumpTable(Env* env, const std::string& fname, WritableFile* dst) { + uint64_t file_size; + RandomAccessFile* file = NULL; + Table* table = NULL; + Status s = env->GetFileSize(fname, &file_size); + if (s.ok()) { + s = env->NewRandomAccessFile(fname, &file); + } + if (s.ok()) { + // We use the default comparator, which may or may not match the + // comparator used in this database. However this should not cause + // problems since we only use Table operations that do not require + // any comparisons. In particular, we do not call Seek or Prev. + s = Table::Open(Options(), file, file_size, &table); + } + if (!s.ok()) { + delete table; + delete file; + return s; + } + + ReadOptions ro; + ro.fill_cache = false; + Iterator* iter = table->NewIterator(ro); + std::string r; + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + r.clear(); + ParsedInternalKey key; + if (!ParseInternalKey(iter->key(), &key)) { + r = "badkey '"; + AppendEscapedStringTo(&r, iter->key()); + r += "' => '"; + AppendEscapedStringTo(&r, iter->value()); + r += "'\n"; + dst->Append(r); + } else { + r = "'"; + AppendEscapedStringTo(&r, key.user_key); + r += "' @ "; + AppendNumberTo(&r, key.sequence); + r += " : "; + if (key.type == kTypeDeletion) { + r += "del"; + } else if (key.type == kTypeValue) { + r += "val"; + } else { + AppendNumberTo(&r, key.type); + } + r += " => '"; + AppendEscapedStringTo(&r, iter->value()); + r += "'\n"; + dst->Append(r); + } + } + s = iter->status(); + if (!s.ok()) { + dst->Append("iterator error: " + s.ToString() + "\n"); + } + + delete iter; + delete table; + delete file; + return Status::OK(); +} + +} // namespace + +Status DumpFile(Env* env, const std::string& fname, WritableFile* dst) { + FileType ftype; + if (!GuessType(fname, &ftype)) { + return Status::InvalidArgument(fname + ": unknown file type"); + } + switch (ftype) { + case kLogFile: return DumpLog(env, fname, dst); + case kDescriptorFile: return DumpDescriptor(env, fname, dst); + case kTableFile: return DumpTable(env, fname, dst); + default: + break; + } + return Status::InvalidArgument(fname + ": not a dump-able file type"); +} + +} // namespace leveldb diff --git a/src/leveldb/db/filename.cc b/src/leveldb/db/filename.cc new file mode 100644 index 00000000..348a19a9 --- /dev/null +++ b/src/leveldb/db/filename.cc @@ -0,0 +1,148 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include "db/filename.h" +#include "db/dbformat.h" +#include "leveldb/env.h" +#include "util/logging.h" + +namespace leveldb { + +// A utility routine: write "data" to the named file and Sync() it. +extern Status WriteStringToFileSync(Env* env, const Slice& data, + const std::string& fname); + +static std::string MakeFileName(const std::string& name, uint64_t number, + const char* suffix) { + char buf[100]; + snprintf(buf, sizeof(buf), "/%06llu.%s", + static_cast(number), + suffix); + return name + buf; +} + +std::string LogFileName(const std::string& name, uint64_t number) { + assert(number > 0); + return MakeFileName(name, number, "log"); +} +// TableFileName returns the filenames we usually write to, while +// SSTTableFileName returns the alternative filenames we also try to read from +// for backward compatibility. For now, swap them around. +// TODO: when compatibility is no longer necessary, swap them back +// (TableFileName to use "ldb" and SSTTableFileName to use "sst"). +std::string TableFileName(const std::string& name, uint64_t number) { + assert(number > 0); + return MakeFileName(name, number, "sst"); +} + +std::string SSTTableFileName(const std::string& name, uint64_t number) { + assert(number > 0); + return MakeFileName(name, number, "ldb"); +} + +std::string DescriptorFileName(const std::string& dbname, uint64_t number) { + assert(number > 0); + char buf[100]; + snprintf(buf, sizeof(buf), "/MANIFEST-%06llu", + static_cast(number)); + return dbname + buf; +} + +std::string CurrentFileName(const std::string& dbname) { + return dbname + "/CURRENT"; +} + +std::string LockFileName(const std::string& dbname) { + return dbname + "/LOCK"; +} + +std::string TempFileName(const std::string& dbname, uint64_t number) { + assert(number > 0); + return MakeFileName(dbname, number, "dbtmp"); +} + +std::string InfoLogFileName(const std::string& dbname) { + return dbname + "/LOG"; +} + +// Return the name of the old info log file for "dbname". +std::string OldInfoLogFileName(const std::string& dbname) { + return dbname + "/LOG.old"; +} + + +// Owned filenames have the form: +// dbname/CURRENT +// dbname/LOCK +// dbname/LOG +// dbname/LOG.old +// dbname/MANIFEST-[0-9]+ +// dbname/[0-9]+.(log|sst|ldb) +bool ParseFileName(const std::string& fname, + uint64_t* number, + FileType* type) { + Slice rest(fname); + if (rest == "CURRENT") { + *number = 0; + *type = kCurrentFile; + } else if (rest == "LOCK") { + *number = 0; + *type = kDBLockFile; + } else if (rest == "LOG" || rest == "LOG.old") { + *number = 0; + *type = kInfoLogFile; + } else if (rest.starts_with("MANIFEST-")) { + rest.remove_prefix(strlen("MANIFEST-")); + uint64_t num; + if (!ConsumeDecimalNumber(&rest, &num)) { + return false; + } + if (!rest.empty()) { + return false; + } + *type = kDescriptorFile; + *number = num; + } else { + // Avoid strtoull() to keep filename format independent of the + // current locale + uint64_t num; + if (!ConsumeDecimalNumber(&rest, &num)) { + return false; + } + Slice suffix = rest; + if (suffix == Slice(".log")) { + *type = kLogFile; + } else if (suffix == Slice(".sst") || suffix == Slice(".ldb")) { + *type = kTableFile; + } else if (suffix == Slice(".dbtmp")) { + *type = kTempFile; + } else { + return false; + } + *number = num; + } + return true; +} + +Status SetCurrentFile(Env* env, const std::string& dbname, + uint64_t descriptor_number) { + // Remove leading "dbname/" and add newline to manifest file name + std::string manifest = DescriptorFileName(dbname, descriptor_number); + Slice contents = manifest; + assert(contents.starts_with(dbname + "/")); + contents.remove_prefix(dbname.size() + 1); + std::string tmp = TempFileName(dbname, descriptor_number); + Status s = WriteStringToFileSync(env, contents.ToString() + "\n", tmp); + if (s.ok()) { + s = env->RenameFile(tmp, CurrentFileName(dbname)); + } + if (!s.ok()) { + env->DeleteFile(tmp); + } + return s; +} + +} // namespace leveldb diff --git a/src/leveldb/db/filename.h b/src/leveldb/db/filename.h new file mode 100644 index 00000000..87a75260 --- /dev/null +++ b/src/leveldb/db/filename.h @@ -0,0 +1,85 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// File names used by DB code + +#ifndef STORAGE_LEVELDB_DB_FILENAME_H_ +#define STORAGE_LEVELDB_DB_FILENAME_H_ + +#include +#include +#include "leveldb/slice.h" +#include "leveldb/status.h" +#include "port/port.h" + +namespace leveldb { + +class Env; + +enum FileType { + kLogFile, + kDBLockFile, + kTableFile, + kDescriptorFile, + kCurrentFile, + kTempFile, + kInfoLogFile // Either the current one, or an old one +}; + +// Return the name of the log file with the specified number +// in the db named by "dbname". The result will be prefixed with +// "dbname". +extern std::string LogFileName(const std::string& dbname, uint64_t number); + +// Return the name of the sstable with the specified number +// in the db named by "dbname". The result will be prefixed with +// "dbname". +extern std::string TableFileName(const std::string& dbname, uint64_t number); + +// Return the legacy file name for an sstable with the specified number +// in the db named by "dbname". The result will be prefixed with +// "dbname". +extern std::string SSTTableFileName(const std::string& dbname, uint64_t number); + +// Return the name of the descriptor file for the db named by +// "dbname" and the specified incarnation number. The result will be +// prefixed with "dbname". +extern std::string DescriptorFileName(const std::string& dbname, + uint64_t number); + +// Return the name of the current file. This file contains the name +// of the current manifest file. The result will be prefixed with +// "dbname". +extern std::string CurrentFileName(const std::string& dbname); + +// Return the name of the lock file for the db named by +// "dbname". The result will be prefixed with "dbname". +extern std::string LockFileName(const std::string& dbname); + +// Return the name of a temporary file owned by the db named "dbname". +// The result will be prefixed with "dbname". +extern std::string TempFileName(const std::string& dbname, uint64_t number); + +// Return the name of the info log file for "dbname". +extern std::string InfoLogFileName(const std::string& dbname); + +// Return the name of the old info log file for "dbname". +extern std::string OldInfoLogFileName(const std::string& dbname); + +// If filename is a leveldb file, store the type of the file in *type. +// The number encoded in the filename is stored in *number. If the +// filename was successfully parsed, returns true. Else return false. +extern bool ParseFileName(const std::string& filename, + uint64_t* number, + FileType* type); + +// Make the CURRENT file point to the descriptor file with the +// specified number. +extern Status SetCurrentFile(Env* env, const std::string& dbname, + uint64_t descriptor_number); + + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_FILENAME_H_ diff --git a/src/leveldb/db/filename_test.cc b/src/leveldb/db/filename_test.cc new file mode 100644 index 00000000..a32556de --- /dev/null +++ b/src/leveldb/db/filename_test.cc @@ -0,0 +1,123 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/filename.h" + +#include "db/dbformat.h" +#include "port/port.h" +#include "util/logging.h" +#include "util/testharness.h" + +namespace leveldb { + +class FileNameTest { }; + +TEST(FileNameTest, Parse) { + Slice db; + FileType type; + uint64_t number; + + // Successful parses + static struct { + const char* fname; + uint64_t number; + FileType type; + } cases[] = { + { "100.log", 100, kLogFile }, + { "0.log", 0, kLogFile }, + { "0.sst", 0, kTableFile }, + { "0.ldb", 0, kTableFile }, + { "CURRENT", 0, kCurrentFile }, + { "LOCK", 0, kDBLockFile }, + { "MANIFEST-2", 2, kDescriptorFile }, + { "MANIFEST-7", 7, kDescriptorFile }, + { "LOG", 0, kInfoLogFile }, + { "LOG.old", 0, kInfoLogFile }, + { "18446744073709551615.log", 18446744073709551615ull, kLogFile }, + }; + for (int i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + std::string f = cases[i].fname; + ASSERT_TRUE(ParseFileName(f, &number, &type)) << f; + ASSERT_EQ(cases[i].type, type) << f; + ASSERT_EQ(cases[i].number, number) << f; + } + + // Errors + static const char* errors[] = { + "", + "foo", + "foo-dx-100.log", + ".log", + "", + "manifest", + "CURREN", + "CURRENTX", + "MANIFES", + "MANIFEST", + "MANIFEST-", + "XMANIFEST-3", + "MANIFEST-3x", + "LOC", + "LOCKx", + "LO", + "LOGx", + "18446744073709551616.log", + "184467440737095516150.log", + "100", + "100.", + "100.lop" + }; + for (int i = 0; i < sizeof(errors) / sizeof(errors[0]); i++) { + std::string f = errors[i]; + ASSERT_TRUE(!ParseFileName(f, &number, &type)) << f; + } +} + +TEST(FileNameTest, Construction) { + uint64_t number; + FileType type; + std::string fname; + + fname = CurrentFileName("foo"); + ASSERT_EQ("foo/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(0, number); + ASSERT_EQ(kCurrentFile, type); + + fname = LockFileName("foo"); + ASSERT_EQ("foo/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(0, number); + ASSERT_EQ(kDBLockFile, type); + + fname = LogFileName("foo", 192); + ASSERT_EQ("foo/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(192, number); + ASSERT_EQ(kLogFile, type); + + fname = TableFileName("bar", 200); + ASSERT_EQ("bar/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(200, number); + ASSERT_EQ(kTableFile, type); + + fname = DescriptorFileName("bar", 100); + ASSERT_EQ("bar/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(100, number); + ASSERT_EQ(kDescriptorFile, type); + + fname = TempFileName("tmp", 999); + ASSERT_EQ("tmp/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(999, number); + ASSERT_EQ(kTempFile, type); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/leveldb_main.cc b/src/leveldb/db/leveldb_main.cc new file mode 100644 index 00000000..9f4b7dd7 --- /dev/null +++ b/src/leveldb/db/leveldb_main.cc @@ -0,0 +1,64 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include "leveldb/dumpfile.h" +#include "leveldb/env.h" +#include "leveldb/status.h" + +namespace leveldb { +namespace { + +class StdoutPrinter : public WritableFile { + public: + virtual Status Append(const Slice& data) { + fwrite(data.data(), 1, data.size(), stdout); + return Status::OK(); + } + virtual Status Close() { return Status::OK(); } + virtual Status Flush() { return Status::OK(); } + virtual Status Sync() { return Status::OK(); } +}; + +bool HandleDumpCommand(Env* env, char** files, int num) { + StdoutPrinter printer; + bool ok = true; + for (int i = 0; i < num; i++) { + Status s = DumpFile(env, files[i], &printer); + if (!s.ok()) { + fprintf(stderr, "%s\n", s.ToString().c_str()); + ok = false; + } + } + return ok; +} + +} // namespace +} // namespace leveldb + +static void Usage() { + fprintf( + stderr, + "Usage: leveldbutil command...\n" + " dump files... -- dump contents of specified files\n" + ); +} + +int main(int argc, char** argv) { + leveldb::Env* env = leveldb::Env::Default(); + bool ok = true; + if (argc < 2) { + Usage(); + ok = false; + } else { + std::string command = argv[1]; + if (command == "dump") { + ok = leveldb::HandleDumpCommand(env, argv+2, argc-2); + } else { + Usage(); + ok = false; + } + } + return (ok ? 0 : 1); +} diff --git a/src/leveldb/db/log_format.h b/src/leveldb/db/log_format.h new file mode 100644 index 00000000..a8c06efe --- /dev/null +++ b/src/leveldb/db/log_format.h @@ -0,0 +1,35 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Log format information shared by reader and writer. +// See ../doc/log_format.txt for more detail. + +#ifndef STORAGE_LEVELDB_DB_LOG_FORMAT_H_ +#define STORAGE_LEVELDB_DB_LOG_FORMAT_H_ + +namespace leveldb { +namespace log { + +enum RecordType { + // Zero is reserved for preallocated files + kZeroType = 0, + + kFullType = 1, + + // For fragments + kFirstType = 2, + kMiddleType = 3, + kLastType = 4 +}; +static const int kMaxRecordType = kLastType; + +static const int kBlockSize = 32768; + +// Header is checksum (4 bytes), length (2 bytes), type (1 byte). +static const int kHeaderSize = 4 + 2 + 1; + +} // namespace log +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_LOG_FORMAT_H_ diff --git a/src/leveldb/db/log_reader.cc b/src/leveldb/db/log_reader.cc new file mode 100644 index 00000000..e44b66c8 --- /dev/null +++ b/src/leveldb/db/log_reader.cc @@ -0,0 +1,266 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/log_reader.h" + +#include +#include "leveldb/env.h" +#include "util/coding.h" +#include "util/crc32c.h" + +namespace leveldb { +namespace log { + +Reader::Reporter::~Reporter() { +} + +Reader::Reader(SequentialFile* file, Reporter* reporter, bool checksum, + uint64_t initial_offset) + : file_(file), + reporter_(reporter), + checksum_(checksum), + backing_store_(new char[kBlockSize]), + buffer_(), + eof_(false), + last_record_offset_(0), + end_of_buffer_offset_(0), + initial_offset_(initial_offset) { +} + +Reader::~Reader() { + delete[] backing_store_; +} + +bool Reader::SkipToInitialBlock() { + size_t offset_in_block = initial_offset_ % kBlockSize; + uint64_t block_start_location = initial_offset_ - offset_in_block; + + // Don't search a block if we'd be in the trailer + if (offset_in_block > kBlockSize - 6) { + offset_in_block = 0; + block_start_location += kBlockSize; + } + + end_of_buffer_offset_ = block_start_location; + + // Skip to start of first block that can contain the initial record + if (block_start_location > 0) { + Status skip_status = file_->Skip(block_start_location); + if (!skip_status.ok()) { + ReportDrop(block_start_location, skip_status); + return false; + } + } + + return true; +} + +bool Reader::ReadRecord(Slice* record, std::string* scratch) { + if (last_record_offset_ < initial_offset_) { + if (!SkipToInitialBlock()) { + return false; + } + } + + scratch->clear(); + record->clear(); + bool in_fragmented_record = false; + // Record offset of the logical record that we're reading + // 0 is a dummy value to make compilers happy + uint64_t prospective_record_offset = 0; + + Slice fragment; + while (true) { + uint64_t physical_record_offset = end_of_buffer_offset_ - buffer_.size(); + const unsigned int record_type = ReadPhysicalRecord(&fragment); + switch (record_type) { + case kFullType: + if (in_fragmented_record) { + // Handle bug in earlier versions of log::Writer where + // it could emit an empty kFirstType record at the tail end + // of a block followed by a kFullType or kFirstType record + // at the beginning of the next block. + if (scratch->empty()) { + in_fragmented_record = false; + } else { + ReportCorruption(scratch->size(), "partial record without end(1)"); + } + } + prospective_record_offset = physical_record_offset; + scratch->clear(); + *record = fragment; + last_record_offset_ = prospective_record_offset; + return true; + + case kFirstType: + if (in_fragmented_record) { + // Handle bug in earlier versions of log::Writer where + // it could emit an empty kFirstType record at the tail end + // of a block followed by a kFullType or kFirstType record + // at the beginning of the next block. + if (scratch->empty()) { + in_fragmented_record = false; + } else { + ReportCorruption(scratch->size(), "partial record without end(2)"); + } + } + prospective_record_offset = physical_record_offset; + scratch->assign(fragment.data(), fragment.size()); + in_fragmented_record = true; + break; + + case kMiddleType: + if (!in_fragmented_record) { + ReportCorruption(fragment.size(), + "missing start of fragmented record(1)"); + } else { + scratch->append(fragment.data(), fragment.size()); + } + break; + + case kLastType: + if (!in_fragmented_record) { + ReportCorruption(fragment.size(), + "missing start of fragmented record(2)"); + } else { + scratch->append(fragment.data(), fragment.size()); + *record = Slice(*scratch); + last_record_offset_ = prospective_record_offset; + return true; + } + break; + + case kEof: + if (in_fragmented_record) { + // This can be caused by the writer dying immediately after + // writing a physical record but before completing the next; don't + // treat it as a corruption, just ignore the entire logical record. + scratch->clear(); + } + return false; + + case kBadRecord: + if (in_fragmented_record) { + ReportCorruption(scratch->size(), "error in middle of record"); + in_fragmented_record = false; + scratch->clear(); + } + break; + + default: { + char buf[40]; + snprintf(buf, sizeof(buf), "unknown record type %u", record_type); + ReportCorruption( + (fragment.size() + (in_fragmented_record ? scratch->size() : 0)), + buf); + in_fragmented_record = false; + scratch->clear(); + break; + } + } + } + return false; +} + +uint64_t Reader::LastRecordOffset() { + return last_record_offset_; +} + +void Reader::ReportCorruption(uint64_t bytes, const char* reason) { + ReportDrop(bytes, Status::Corruption(reason)); +} + +void Reader::ReportDrop(uint64_t bytes, const Status& reason) { + if (reporter_ != NULL && + end_of_buffer_offset_ - buffer_.size() - bytes >= initial_offset_) { + reporter_->Corruption(static_cast(bytes), reason); + } +} + +unsigned int Reader::ReadPhysicalRecord(Slice* result) { + while (true) { + if (buffer_.size() < kHeaderSize) { + if (!eof_) { + // Last read was a full read, so this is a trailer to skip + buffer_.clear(); + Status status = file_->Read(kBlockSize, &buffer_, backing_store_); + end_of_buffer_offset_ += buffer_.size(); + if (!status.ok()) { + buffer_.clear(); + ReportDrop(kBlockSize, status); + eof_ = true; + return kEof; + } else if (buffer_.size() < kBlockSize) { + eof_ = true; + } + continue; + } else { + // Note that if buffer_ is non-empty, we have a truncated header at the + // end of the file, which can be caused by the writer crashing in the + // middle of writing the header. Instead of considering this an error, + // just report EOF. + buffer_.clear(); + return kEof; + } + } + + // Parse the header + const char* header = buffer_.data(); + const uint32_t a = static_cast(header[4]) & 0xff; + const uint32_t b = static_cast(header[5]) & 0xff; + const unsigned int type = header[6]; + const uint32_t length = a | (b << 8); + if (kHeaderSize + length > buffer_.size()) { + size_t drop_size = buffer_.size(); + buffer_.clear(); + if (!eof_) { + ReportCorruption(drop_size, "bad record length"); + return kBadRecord; + } + // If the end of the file has been reached without reading |length| bytes + // of payload, assume the writer died in the middle of writing the record. + // Don't report a corruption. + return kEof; + } + + if (type == kZeroType && length == 0) { + // Skip zero length record without reporting any drops since + // such records are produced by the mmap based writing code in + // env_posix.cc that preallocates file regions. + buffer_.clear(); + return kBadRecord; + } + + // Check crc + if (checksum_) { + uint32_t expected_crc = crc32c::Unmask(DecodeFixed32(header)); + uint32_t actual_crc = crc32c::Value(header + 6, 1 + length); + if (actual_crc != expected_crc) { + // Drop the rest of the buffer since "length" itself may have + // been corrupted and if we trust it, we could find some + // fragment of a real log record that just happens to look + // like a valid log record. + size_t drop_size = buffer_.size(); + buffer_.clear(); + ReportCorruption(drop_size, "checksum mismatch"); + return kBadRecord; + } + } + + buffer_.remove_prefix(kHeaderSize + length); + + // Skip physical record that started before initial_offset_ + if (end_of_buffer_offset_ - buffer_.size() - kHeaderSize - length < + initial_offset_) { + result->clear(); + return kBadRecord; + } + + *result = Slice(header + kHeaderSize, length); + return type; + } +} + +} // namespace log +} // namespace leveldb diff --git a/src/leveldb/db/log_reader.h b/src/leveldb/db/log_reader.h new file mode 100644 index 00000000..6aff7917 --- /dev/null +++ b/src/leveldb/db/log_reader.h @@ -0,0 +1,108 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_LOG_READER_H_ +#define STORAGE_LEVELDB_DB_LOG_READER_H_ + +#include + +#include "db/log_format.h" +#include "leveldb/slice.h" +#include "leveldb/status.h" + +namespace leveldb { + +class SequentialFile; + +namespace log { + +class Reader { + public: + // Interface for reporting errors. + class Reporter { + public: + virtual ~Reporter(); + + // Some corruption was detected. "size" is the approximate number + // of bytes dropped due to the corruption. + virtual void Corruption(size_t bytes, const Status& status) = 0; + }; + + // Create a reader that will return log records from "*file". + // "*file" must remain live while this Reader is in use. + // + // If "reporter" is non-NULL, it is notified whenever some data is + // dropped due to a detected corruption. "*reporter" must remain + // live while this Reader is in use. + // + // If "checksum" is true, verify checksums if available. + // + // The Reader will start reading at the first record located at physical + // position >= initial_offset within the file. + Reader(SequentialFile* file, Reporter* reporter, bool checksum, + uint64_t initial_offset); + + ~Reader(); + + // Read the next record into *record. Returns true if read + // successfully, false if we hit end of the input. May use + // "*scratch" as temporary storage. The contents filled in *record + // will only be valid until the next mutating operation on this + // reader or the next mutation to *scratch. + bool ReadRecord(Slice* record, std::string* scratch); + + // Returns the physical offset of the last record returned by ReadRecord. + // + // Undefined before the first call to ReadRecord. + uint64_t LastRecordOffset(); + + private: + SequentialFile* const file_; + Reporter* const reporter_; + bool const checksum_; + char* const backing_store_; + Slice buffer_; + bool eof_; // Last Read() indicated EOF by returning < kBlockSize + + // Offset of the last record returned by ReadRecord. + uint64_t last_record_offset_; + // Offset of the first location past the end of buffer_. + uint64_t end_of_buffer_offset_; + + // Offset at which to start looking for the first record to return + uint64_t const initial_offset_; + + // Extend record types with the following special values + enum { + kEof = kMaxRecordType + 1, + // Returned whenever we find an invalid physical record. + // Currently there are three situations in which this happens: + // * The record has an invalid CRC (ReadPhysicalRecord reports a drop) + // * The record is a 0-length record (No drop is reported) + // * The record is below constructor's initial_offset (No drop is reported) + kBadRecord = kMaxRecordType + 2 + }; + + // Skips all blocks that are completely before "initial_offset_". + // + // Returns true on success. Handles reporting. + bool SkipToInitialBlock(); + + // Return type, or one of the preceding special values + unsigned int ReadPhysicalRecord(Slice* result); + + // Reports dropped bytes to the reporter. + // buffer_ must be updated to remove the dropped bytes prior to invocation. + void ReportCorruption(uint64_t bytes, const char* reason); + void ReportDrop(uint64_t bytes, const Status& reason); + + // No copying allowed + Reader(const Reader&); + void operator=(const Reader&); +}; + +} // namespace log +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_LOG_READER_H_ diff --git a/src/leveldb/db/log_test.cc b/src/leveldb/db/log_test.cc new file mode 100644 index 00000000..dcf05626 --- /dev/null +++ b/src/leveldb/db/log_test.cc @@ -0,0 +1,530 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/log_reader.h" +#include "db/log_writer.h" +#include "leveldb/env.h" +#include "util/coding.h" +#include "util/crc32c.h" +#include "util/random.h" +#include "util/testharness.h" + +namespace leveldb { +namespace log { + +// Construct a string of the specified length made out of the supplied +// partial string. +static std::string BigString(const std::string& partial_string, size_t n) { + std::string result; + while (result.size() < n) { + result.append(partial_string); + } + result.resize(n); + return result; +} + +// Construct a string from a number +static std::string NumberString(int n) { + char buf[50]; + snprintf(buf, sizeof(buf), "%d.", n); + return std::string(buf); +} + +// Return a skewed potentially long string +static std::string RandomSkewedString(int i, Random* rnd) { + return BigString(NumberString(i), rnd->Skewed(17)); +} + +class LogTest { + private: + class StringDest : public WritableFile { + public: + std::string contents_; + + virtual Status Close() { return Status::OK(); } + virtual Status Flush() { return Status::OK(); } + virtual Status Sync() { return Status::OK(); } + virtual Status Append(const Slice& slice) { + contents_.append(slice.data(), slice.size()); + return Status::OK(); + } + }; + + class StringSource : public SequentialFile { + public: + Slice contents_; + bool force_error_; + bool returned_partial_; + StringSource() : force_error_(false), returned_partial_(false) { } + + virtual Status Read(size_t n, Slice* result, char* scratch) { + ASSERT_TRUE(!returned_partial_) << "must not Read() after eof/error"; + + if (force_error_) { + force_error_ = false; + returned_partial_ = true; + return Status::Corruption("read error"); + } + + if (contents_.size() < n) { + n = contents_.size(); + returned_partial_ = true; + } + *result = Slice(contents_.data(), n); + contents_.remove_prefix(n); + return Status::OK(); + } + + virtual Status Skip(uint64_t n) { + if (n > contents_.size()) { + contents_.clear(); + return Status::NotFound("in-memory file skipepd past end"); + } + + contents_.remove_prefix(n); + + return Status::OK(); + } + }; + + class ReportCollector : public Reader::Reporter { + public: + size_t dropped_bytes_; + std::string message_; + + ReportCollector() : dropped_bytes_(0) { } + virtual void Corruption(size_t bytes, const Status& status) { + dropped_bytes_ += bytes; + message_.append(status.ToString()); + } + }; + + StringDest dest_; + StringSource source_; + ReportCollector report_; + bool reading_; + Writer writer_; + Reader reader_; + + // Record metadata for testing initial offset functionality + static size_t initial_offset_record_sizes_[]; + static uint64_t initial_offset_last_record_offsets_[]; + + public: + LogTest() : reading_(false), + writer_(&dest_), + reader_(&source_, &report_, true/*checksum*/, + 0/*initial_offset*/) { + } + + void Write(const std::string& msg) { + ASSERT_TRUE(!reading_) << "Write() after starting to read"; + writer_.AddRecord(Slice(msg)); + } + + size_t WrittenBytes() const { + return dest_.contents_.size(); + } + + std::string Read() { + if (!reading_) { + reading_ = true; + source_.contents_ = Slice(dest_.contents_); + } + std::string scratch; + Slice record; + if (reader_.ReadRecord(&record, &scratch)) { + return record.ToString(); + } else { + return "EOF"; + } + } + + void IncrementByte(int offset, int delta) { + dest_.contents_[offset] += delta; + } + + void SetByte(int offset, char new_byte) { + dest_.contents_[offset] = new_byte; + } + + void ShrinkSize(int bytes) { + dest_.contents_.resize(dest_.contents_.size() - bytes); + } + + void FixChecksum(int header_offset, int len) { + // Compute crc of type/len/data + uint32_t crc = crc32c::Value(&dest_.contents_[header_offset+6], 1 + len); + crc = crc32c::Mask(crc); + EncodeFixed32(&dest_.contents_[header_offset], crc); + } + + void ForceError() { + source_.force_error_ = true; + } + + size_t DroppedBytes() const { + return report_.dropped_bytes_; + } + + std::string ReportMessage() const { + return report_.message_; + } + + // Returns OK iff recorded error message contains "msg" + std::string MatchError(const std::string& msg) const { + if (report_.message_.find(msg) == std::string::npos) { + return report_.message_; + } else { + return "OK"; + } + } + + void WriteInitialOffsetLog() { + for (int i = 0; i < 4; i++) { + std::string record(initial_offset_record_sizes_[i], + static_cast('a' + i)); + Write(record); + } + } + + void CheckOffsetPastEndReturnsNoRecords(uint64_t offset_past_end) { + WriteInitialOffsetLog(); + reading_ = true; + source_.contents_ = Slice(dest_.contents_); + Reader* offset_reader = new Reader(&source_, &report_, true/*checksum*/, + WrittenBytes() + offset_past_end); + Slice record; + std::string scratch; + ASSERT_TRUE(!offset_reader->ReadRecord(&record, &scratch)); + delete offset_reader; + } + + void CheckInitialOffsetRecord(uint64_t initial_offset, + int expected_record_offset) { + WriteInitialOffsetLog(); + reading_ = true; + source_.contents_ = Slice(dest_.contents_); + Reader* offset_reader = new Reader(&source_, &report_, true/*checksum*/, + initial_offset); + Slice record; + std::string scratch; + ASSERT_TRUE(offset_reader->ReadRecord(&record, &scratch)); + ASSERT_EQ(initial_offset_record_sizes_[expected_record_offset], + record.size()); + ASSERT_EQ(initial_offset_last_record_offsets_[expected_record_offset], + offset_reader->LastRecordOffset()); + ASSERT_EQ((char)('a' + expected_record_offset), record.data()[0]); + delete offset_reader; + } + +}; + +size_t LogTest::initial_offset_record_sizes_[] = + {10000, // Two sizable records in first block + 10000, + 2 * log::kBlockSize - 1000, // Span three blocks + 1}; + +uint64_t LogTest::initial_offset_last_record_offsets_[] = + {0, + kHeaderSize + 10000, + 2 * (kHeaderSize + 10000), + 2 * (kHeaderSize + 10000) + + (2 * log::kBlockSize - 1000) + 3 * kHeaderSize}; + + +TEST(LogTest, Empty) { + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, ReadWrite) { + Write("foo"); + Write("bar"); + Write(""); + Write("xxxx"); + ASSERT_EQ("foo", Read()); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("", Read()); + ASSERT_EQ("xxxx", Read()); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ("EOF", Read()); // Make sure reads at eof work +} + +TEST(LogTest, ManyBlocks) { + for (int i = 0; i < 100000; i++) { + Write(NumberString(i)); + } + for (int i = 0; i < 100000; i++) { + ASSERT_EQ(NumberString(i), Read()); + } + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, Fragmentation) { + Write("small"); + Write(BigString("medium", 50000)); + Write(BigString("large", 100000)); + ASSERT_EQ("small", Read()); + ASSERT_EQ(BigString("medium", 50000), Read()); + ASSERT_EQ(BigString("large", 100000), Read()); + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, MarginalTrailer) { + // Make a trailer that is exactly the same length as an empty record. + const int n = kBlockSize - 2*kHeaderSize; + Write(BigString("foo", n)); + ASSERT_EQ(kBlockSize - kHeaderSize, WrittenBytes()); + Write(""); + Write("bar"); + ASSERT_EQ(BigString("foo", n), Read()); + ASSERT_EQ("", Read()); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, MarginalTrailer2) { + // Make a trailer that is exactly the same length as an empty record. + const int n = kBlockSize - 2*kHeaderSize; + Write(BigString("foo", n)); + ASSERT_EQ(kBlockSize - kHeaderSize, WrittenBytes()); + Write("bar"); + ASSERT_EQ(BigString("foo", n), Read()); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(0, DroppedBytes()); + ASSERT_EQ("", ReportMessage()); +} + +TEST(LogTest, ShortTrailer) { + const int n = kBlockSize - 2*kHeaderSize + 4; + Write(BigString("foo", n)); + ASSERT_EQ(kBlockSize - kHeaderSize + 4, WrittenBytes()); + Write(""); + Write("bar"); + ASSERT_EQ(BigString("foo", n), Read()); + ASSERT_EQ("", Read()); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, AlignedEof) { + const int n = kBlockSize - 2*kHeaderSize + 4; + Write(BigString("foo", n)); + ASSERT_EQ(kBlockSize - kHeaderSize + 4, WrittenBytes()); + ASSERT_EQ(BigString("foo", n), Read()); + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, RandomRead) { + const int N = 500; + Random write_rnd(301); + for (int i = 0; i < N; i++) { + Write(RandomSkewedString(i, &write_rnd)); + } + Random read_rnd(301); + for (int i = 0; i < N; i++) { + ASSERT_EQ(RandomSkewedString(i, &read_rnd), Read()); + } + ASSERT_EQ("EOF", Read()); +} + +// Tests of all the error paths in log_reader.cc follow: + +TEST(LogTest, ReadError) { + Write("foo"); + ForceError(); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(kBlockSize, DroppedBytes()); + ASSERT_EQ("OK", MatchError("read error")); +} + +TEST(LogTest, BadRecordType) { + Write("foo"); + // Type is stored in header[6] + IncrementByte(6, 100); + FixChecksum(0, 3); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("unknown record type")); +} + +TEST(LogTest, TruncatedTrailingRecordIsIgnored) { + Write("foo"); + ShrinkSize(4); // Drop all payload as well as a header byte + ASSERT_EQ("EOF", Read()); + // Truncated last record is ignored, not treated as an error. + ASSERT_EQ(0, DroppedBytes()); + ASSERT_EQ("", ReportMessage()); +} + +TEST(LogTest, BadLength) { + const int kPayloadSize = kBlockSize - kHeaderSize; + Write(BigString("bar", kPayloadSize)); + Write("foo"); + // Least significant size byte is stored in header[4]. + IncrementByte(4, 1); + ASSERT_EQ("foo", Read()); + ASSERT_EQ(kBlockSize, DroppedBytes()); + ASSERT_EQ("OK", MatchError("bad record length")); +} + +TEST(LogTest, BadLengthAtEndIsIgnored) { + Write("foo"); + ShrinkSize(1); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(0, DroppedBytes()); + ASSERT_EQ("", ReportMessage()); +} + +TEST(LogTest, ChecksumMismatch) { + Write("foo"); + IncrementByte(0, 10); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(10, DroppedBytes()); + ASSERT_EQ("OK", MatchError("checksum mismatch")); +} + +TEST(LogTest, UnexpectedMiddleType) { + Write("foo"); + SetByte(6, kMiddleType); + FixChecksum(0, 3); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("missing start")); +} + +TEST(LogTest, UnexpectedLastType) { + Write("foo"); + SetByte(6, kLastType); + FixChecksum(0, 3); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("missing start")); +} + +TEST(LogTest, UnexpectedFullType) { + Write("foo"); + Write("bar"); + SetByte(6, kFirstType); + FixChecksum(0, 3); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("partial record without end")); +} + +TEST(LogTest, UnexpectedFirstType) { + Write("foo"); + Write(BigString("bar", 100000)); + SetByte(6, kFirstType); + FixChecksum(0, 3); + ASSERT_EQ(BigString("bar", 100000), Read()); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("partial record without end")); +} + +TEST(LogTest, MissingLastIsIgnored) { + Write(BigString("bar", kBlockSize)); + // Remove the LAST block, including header. + ShrinkSize(14); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ("", ReportMessage()); + ASSERT_EQ(0, DroppedBytes()); +} + +TEST(LogTest, PartialLastIsIgnored) { + Write(BigString("bar", kBlockSize)); + // Cause a bad record length in the LAST block. + ShrinkSize(1); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ("", ReportMessage()); + ASSERT_EQ(0, DroppedBytes()); +} + +TEST(LogTest, ErrorJoinsRecords) { + // Consider two fragmented records: + // first(R1) last(R1) first(R2) last(R2) + // where the middle two fragments disappear. We do not want + // first(R1),last(R2) to get joined and returned as a valid record. + + // Write records that span two blocks + Write(BigString("foo", kBlockSize)); + Write(BigString("bar", kBlockSize)); + Write("correct"); + + // Wipe the middle block + for (int offset = kBlockSize; offset < 2*kBlockSize; offset++) { + SetByte(offset, 'x'); + } + + ASSERT_EQ("correct", Read()); + ASSERT_EQ("EOF", Read()); + const size_t dropped = DroppedBytes(); + ASSERT_LE(dropped, 2*kBlockSize + 100); + ASSERT_GE(dropped, 2*kBlockSize); +} + +TEST(LogTest, ReadStart) { + CheckInitialOffsetRecord(0, 0); +} + +TEST(LogTest, ReadSecondOneOff) { + CheckInitialOffsetRecord(1, 1); +} + +TEST(LogTest, ReadSecondTenThousand) { + CheckInitialOffsetRecord(10000, 1); +} + +TEST(LogTest, ReadSecondStart) { + CheckInitialOffsetRecord(10007, 1); +} + +TEST(LogTest, ReadThirdOneOff) { + CheckInitialOffsetRecord(10008, 2); +} + +TEST(LogTest, ReadThirdStart) { + CheckInitialOffsetRecord(20014, 2); +} + +TEST(LogTest, ReadFourthOneOff) { + CheckInitialOffsetRecord(20015, 3); +} + +TEST(LogTest, ReadFourthFirstBlockTrailer) { + CheckInitialOffsetRecord(log::kBlockSize - 4, 3); +} + +TEST(LogTest, ReadFourthMiddleBlock) { + CheckInitialOffsetRecord(log::kBlockSize + 1, 3); +} + +TEST(LogTest, ReadFourthLastBlock) { + CheckInitialOffsetRecord(2 * log::kBlockSize + 1, 3); +} + +TEST(LogTest, ReadFourthStart) { + CheckInitialOffsetRecord( + 2 * (kHeaderSize + 1000) + (2 * log::kBlockSize - 1000) + 3 * kHeaderSize, + 3); +} + +TEST(LogTest, ReadEnd) { + CheckOffsetPastEndReturnsNoRecords(0); +} + +TEST(LogTest, ReadPastEnd) { + CheckOffsetPastEndReturnsNoRecords(5); +} + +} // namespace log +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/log_writer.cc b/src/leveldb/db/log_writer.cc new file mode 100644 index 00000000..2da99ac0 --- /dev/null +++ b/src/leveldb/db/log_writer.cc @@ -0,0 +1,103 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/log_writer.h" + +#include +#include "leveldb/env.h" +#include "util/coding.h" +#include "util/crc32c.h" + +namespace leveldb { +namespace log { + +Writer::Writer(WritableFile* dest) + : dest_(dest), + block_offset_(0) { + for (int i = 0; i <= kMaxRecordType; i++) { + char t = static_cast(i); + type_crc_[i] = crc32c::Value(&t, 1); + } +} + +Writer::~Writer() { +} + +Status Writer::AddRecord(const Slice& slice) { + const char* ptr = slice.data(); + size_t left = slice.size(); + + // Fragment the record if necessary and emit it. Note that if slice + // is empty, we still want to iterate once to emit a single + // zero-length record + Status s; + bool begin = true; + do { + const int leftover = kBlockSize - block_offset_; + assert(leftover >= 0); + if (leftover < kHeaderSize) { + // Switch to a new block + if (leftover > 0) { + // Fill the trailer (literal below relies on kHeaderSize being 7) + assert(kHeaderSize == 7); + dest_->Append(Slice("\x00\x00\x00\x00\x00\x00", leftover)); + } + block_offset_ = 0; + } + + // Invariant: we never leave < kHeaderSize bytes in a block. + assert(kBlockSize - block_offset_ - kHeaderSize >= 0); + + const size_t avail = kBlockSize - block_offset_ - kHeaderSize; + const size_t fragment_length = (left < avail) ? left : avail; + + RecordType type; + const bool end = (left == fragment_length); + if (begin && end) { + type = kFullType; + } else if (begin) { + type = kFirstType; + } else if (end) { + type = kLastType; + } else { + type = kMiddleType; + } + + s = EmitPhysicalRecord(type, ptr, fragment_length); + ptr += fragment_length; + left -= fragment_length; + begin = false; + } while (s.ok() && left > 0); + return s; +} + +Status Writer::EmitPhysicalRecord(RecordType t, const char* ptr, size_t n) { + assert(n <= 0xffff); // Must fit in two bytes + assert(block_offset_ + kHeaderSize + n <= kBlockSize); + + // Format the header + char buf[kHeaderSize]; + buf[4] = static_cast(n & 0xff); + buf[5] = static_cast(n >> 8); + buf[6] = static_cast(t); + + // Compute the crc of the record type and the payload. + uint32_t crc = crc32c::Extend(type_crc_[t], ptr, n); + crc = crc32c::Mask(crc); // Adjust for storage + EncodeFixed32(buf, crc); + + // Write the header and the payload + Status s = dest_->Append(Slice(buf, kHeaderSize)); + if (s.ok()) { + s = dest_->Append(Slice(ptr, n)); + if (s.ok()) { + s = dest_->Flush(); + } + } + block_offset_ += kHeaderSize + n; + return s; +} + +} // namespace log +} // namespace leveldb diff --git a/src/leveldb/db/log_writer.h b/src/leveldb/db/log_writer.h new file mode 100644 index 00000000..a3a954d9 --- /dev/null +++ b/src/leveldb/db/log_writer.h @@ -0,0 +1,48 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_LOG_WRITER_H_ +#define STORAGE_LEVELDB_DB_LOG_WRITER_H_ + +#include +#include "db/log_format.h" +#include "leveldb/slice.h" +#include "leveldb/status.h" + +namespace leveldb { + +class WritableFile; + +namespace log { + +class Writer { + public: + // Create a writer that will append data to "*dest". + // "*dest" must be initially empty. + // "*dest" must remain live while this Writer is in use. + explicit Writer(WritableFile* dest); + ~Writer(); + + Status AddRecord(const Slice& slice); + + private: + WritableFile* dest_; + int block_offset_; // Current offset in block + + // crc32c values for all supported record types. These are + // pre-computed to reduce the overhead of computing the crc of the + // record type stored in the header. + uint32_t type_crc_[kMaxRecordType + 1]; + + Status EmitPhysicalRecord(RecordType type, const char* ptr, size_t length); + + // No copying allowed + Writer(const Writer&); + void operator=(const Writer&); +}; + +} // namespace log +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_LOG_WRITER_H_ diff --git a/src/leveldb/db/memtable.cc b/src/leveldb/db/memtable.cc new file mode 100644 index 00000000..bfec0a7e --- /dev/null +++ b/src/leveldb/db/memtable.cc @@ -0,0 +1,145 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/memtable.h" +#include "db/dbformat.h" +#include "leveldb/comparator.h" +#include "leveldb/env.h" +#include "leveldb/iterator.h" +#include "util/coding.h" + +namespace leveldb { + +static Slice GetLengthPrefixedSlice(const char* data) { + uint32_t len; + const char* p = data; + p = GetVarint32Ptr(p, p + 5, &len); // +5: we assume "p" is not corrupted + return Slice(p, len); +} + +MemTable::MemTable(const InternalKeyComparator& cmp) + : comparator_(cmp), + refs_(0), + table_(comparator_, &arena_) { +} + +MemTable::~MemTable() { + assert(refs_ == 0); +} + +size_t MemTable::ApproximateMemoryUsage() { return arena_.MemoryUsage(); } + +int MemTable::KeyComparator::operator()(const char* aptr, const char* bptr) + const { + // Internal keys are encoded as length-prefixed strings. + Slice a = GetLengthPrefixedSlice(aptr); + Slice b = GetLengthPrefixedSlice(bptr); + return comparator.Compare(a, b); +} + +// Encode a suitable internal key target for "target" and return it. +// Uses *scratch as scratch space, and the returned pointer will point +// into this scratch space. +static const char* EncodeKey(std::string* scratch, const Slice& target) { + scratch->clear(); + PutVarint32(scratch, target.size()); + scratch->append(target.data(), target.size()); + return scratch->data(); +} + +class MemTableIterator: public Iterator { + public: + explicit MemTableIterator(MemTable::Table* table) : iter_(table) { } + + virtual bool Valid() const { return iter_.Valid(); } + virtual void Seek(const Slice& k) { iter_.Seek(EncodeKey(&tmp_, k)); } + virtual void SeekToFirst() { iter_.SeekToFirst(); } + virtual void SeekToLast() { iter_.SeekToLast(); } + virtual void Next() { iter_.Next(); } + virtual void Prev() { iter_.Prev(); } + virtual Slice key() const { return GetLengthPrefixedSlice(iter_.key()); } + virtual Slice value() const { + Slice key_slice = GetLengthPrefixedSlice(iter_.key()); + return GetLengthPrefixedSlice(key_slice.data() + key_slice.size()); + } + + virtual Status status() const { return Status::OK(); } + + private: + MemTable::Table::Iterator iter_; + std::string tmp_; // For passing to EncodeKey + + // No copying allowed + MemTableIterator(const MemTableIterator&); + void operator=(const MemTableIterator&); +}; + +Iterator* MemTable::NewIterator() { + return new MemTableIterator(&table_); +} + +void MemTable::Add(SequenceNumber s, ValueType type, + const Slice& key, + const Slice& value) { + // Format of an entry is concatenation of: + // key_size : varint32 of internal_key.size() + // key bytes : char[internal_key.size()] + // value_size : varint32 of value.size() + // value bytes : char[value.size()] + size_t key_size = key.size(); + size_t val_size = value.size(); + size_t internal_key_size = key_size + 8; + const size_t encoded_len = + VarintLength(internal_key_size) + internal_key_size + + VarintLength(val_size) + val_size; + char* buf = arena_.Allocate(encoded_len); + char* p = EncodeVarint32(buf, internal_key_size); + memcpy(p, key.data(), key_size); + p += key_size; + EncodeFixed64(p, (s << 8) | type); + p += 8; + p = EncodeVarint32(p, val_size); + memcpy(p, value.data(), val_size); + assert((p + val_size) - buf == encoded_len); + table_.Insert(buf); +} + +bool MemTable::Get(const LookupKey& key, std::string* value, Status* s) { + Slice memkey = key.memtable_key(); + Table::Iterator iter(&table_); + iter.Seek(memkey.data()); + if (iter.Valid()) { + // entry format is: + // klength varint32 + // userkey char[klength] + // tag uint64 + // vlength varint32 + // value char[vlength] + // Check that it belongs to same user key. We do not check the + // sequence number since the Seek() call above should have skipped + // all entries with overly large sequence numbers. + const char* entry = iter.key(); + uint32_t key_length; + const char* key_ptr = GetVarint32Ptr(entry, entry+5, &key_length); + if (comparator_.comparator.user_comparator()->Compare( + Slice(key_ptr, key_length - 8), + key.user_key()) == 0) { + // Correct user key + const uint64_t tag = DecodeFixed64(key_ptr + key_length - 8); + switch (static_cast(tag & 0xff)) { + case kTypeValue: { + Slice v = GetLengthPrefixedSlice(key_ptr + key_length); + value->assign(v.data(), v.size()); + return true; + } + case kTypeDeletion: + *s = Status::NotFound(Slice()); + return true; + } + } + } + return false; +} + +} // namespace leveldb diff --git a/src/leveldb/db/memtable.h b/src/leveldb/db/memtable.h new file mode 100644 index 00000000..92e90bb0 --- /dev/null +++ b/src/leveldb/db/memtable.h @@ -0,0 +1,91 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_MEMTABLE_H_ +#define STORAGE_LEVELDB_DB_MEMTABLE_H_ + +#include +#include "leveldb/db.h" +#include "db/dbformat.h" +#include "db/skiplist.h" +#include "util/arena.h" + +namespace leveldb { + +class InternalKeyComparator; +class Mutex; +class MemTableIterator; + +class MemTable { + public: + // MemTables are reference counted. The initial reference count + // is zero and the caller must call Ref() at least once. + explicit MemTable(const InternalKeyComparator& comparator); + + // Increase reference count. + void Ref() { ++refs_; } + + // Drop reference count. Delete if no more references exist. + void Unref() { + --refs_; + assert(refs_ >= 0); + if (refs_ <= 0) { + delete this; + } + } + + // Returns an estimate of the number of bytes of data in use by this + // data structure. + // + // REQUIRES: external synchronization to prevent simultaneous + // operations on the same MemTable. + size_t ApproximateMemoryUsage(); + + // Return an iterator that yields the contents of the memtable. + // + // The caller must ensure that the underlying MemTable remains live + // while the returned iterator is live. The keys returned by this + // iterator are internal keys encoded by AppendInternalKey in the + // db/format.{h,cc} module. + Iterator* NewIterator(); + + // Add an entry into memtable that maps key to value at the + // specified sequence number and with the specified type. + // Typically value will be empty if type==kTypeDeletion. + void Add(SequenceNumber seq, ValueType type, + const Slice& key, + const Slice& value); + + // If memtable contains a value for key, store it in *value and return true. + // If memtable contains a deletion for key, store a NotFound() error + // in *status and return true. + // Else, return false. + bool Get(const LookupKey& key, std::string* value, Status* s); + + private: + ~MemTable(); // Private since only Unref() should be used to delete it + + struct KeyComparator { + const InternalKeyComparator comparator; + explicit KeyComparator(const InternalKeyComparator& c) : comparator(c) { } + int operator()(const char* a, const char* b) const; + }; + friend class MemTableIterator; + friend class MemTableBackwardIterator; + + typedef SkipList Table; + + KeyComparator comparator_; + int refs_; + Arena arena_; + Table table_; + + // No copying allowed + MemTable(const MemTable&); + void operator=(const MemTable&); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_MEMTABLE_H_ diff --git a/src/leveldb/db/repair.cc b/src/leveldb/db/repair.cc new file mode 100644 index 00000000..4cd4bb04 --- /dev/null +++ b/src/leveldb/db/repair.cc @@ -0,0 +1,461 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// We recover the contents of the descriptor from the other files we find. +// (1) Any log files are first converted to tables +// (2) We scan every table to compute +// (a) smallest/largest for the table +// (b) largest sequence number in the table +// (3) We generate descriptor contents: +// - log number is set to zero +// - next-file-number is set to 1 + largest file number we found +// - last-sequence-number is set to largest sequence# found across +// all tables (see 2c) +// - compaction pointers are cleared +// - every table file is added at level 0 +// +// Possible optimization 1: +// (a) Compute total size and use to pick appropriate max-level M +// (b) Sort tables by largest sequence# in the table +// (c) For each table: if it overlaps earlier table, place in level-0, +// else place in level-M. +// Possible optimization 2: +// Store per-table metadata (smallest, largest, largest-seq#, ...) +// in the table's meta section to speed up ScanTable. + +#include "db/builder.h" +#include "db/db_impl.h" +#include "db/dbformat.h" +#include "db/filename.h" +#include "db/log_reader.h" +#include "db/log_writer.h" +#include "db/memtable.h" +#include "db/table_cache.h" +#include "db/version_edit.h" +#include "db/write_batch_internal.h" +#include "leveldb/comparator.h" +#include "leveldb/db.h" +#include "leveldb/env.h" + +namespace leveldb { + +namespace { + +class Repairer { + public: + Repairer(const std::string& dbname, const Options& options) + : dbname_(dbname), + env_(options.env), + icmp_(options.comparator), + ipolicy_(options.filter_policy), + options_(SanitizeOptions(dbname, &icmp_, &ipolicy_, options)), + owns_info_log_(options_.info_log != options.info_log), + owns_cache_(options_.block_cache != options.block_cache), + next_file_number_(1) { + // TableCache can be small since we expect each table to be opened once. + table_cache_ = new TableCache(dbname_, &options_, 10); + } + + ~Repairer() { + delete table_cache_; + if (owns_info_log_) { + delete options_.info_log; + } + if (owns_cache_) { + delete options_.block_cache; + } + } + + Status Run() { + Status status = FindFiles(); + if (status.ok()) { + ConvertLogFilesToTables(); + ExtractMetaData(); + status = WriteDescriptor(); + } + if (status.ok()) { + unsigned long long bytes = 0; + for (size_t i = 0; i < tables_.size(); i++) { + bytes += tables_[i].meta.file_size; + } + Log(options_.info_log, + "**** Repaired leveldb %s; " + "recovered %d files; %llu bytes. " + "Some data may have been lost. " + "****", + dbname_.c_str(), + static_cast(tables_.size()), + bytes); + } + return status; + } + + private: + struct TableInfo { + FileMetaData meta; + SequenceNumber max_sequence; + }; + + std::string const dbname_; + Env* const env_; + InternalKeyComparator const icmp_; + InternalFilterPolicy const ipolicy_; + Options const options_; + bool owns_info_log_; + bool owns_cache_; + TableCache* table_cache_; + VersionEdit edit_; + + std::vector manifests_; + std::vector table_numbers_; + std::vector logs_; + std::vector tables_; + uint64_t next_file_number_; + + Status FindFiles() { + std::vector filenames; + Status status = env_->GetChildren(dbname_, &filenames); + if (!status.ok()) { + return status; + } + if (filenames.empty()) { + return Status::IOError(dbname_, "repair found no files"); + } + + uint64_t number; + FileType type; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type)) { + if (type == kDescriptorFile) { + manifests_.push_back(filenames[i]); + } else { + if (number + 1 > next_file_number_) { + next_file_number_ = number + 1; + } + if (type == kLogFile) { + logs_.push_back(number); + } else if (type == kTableFile) { + table_numbers_.push_back(number); + } else { + // Ignore other files + } + } + } + } + return status; + } + + void ConvertLogFilesToTables() { + for (size_t i = 0; i < logs_.size(); i++) { + std::string logname = LogFileName(dbname_, logs_[i]); + Status status = ConvertLogToTable(logs_[i]); + if (!status.ok()) { + Log(options_.info_log, "Log #%llu: ignoring conversion error: %s", + (unsigned long long) logs_[i], + status.ToString().c_str()); + } + ArchiveFile(logname); + } + } + + Status ConvertLogToTable(uint64_t log) { + struct LogReporter : public log::Reader::Reporter { + Env* env; + Logger* info_log; + uint64_t lognum; + virtual void Corruption(size_t bytes, const Status& s) { + // We print error messages for corruption, but continue repairing. + Log(info_log, "Log #%llu: dropping %d bytes; %s", + (unsigned long long) lognum, + static_cast(bytes), + s.ToString().c_str()); + } + }; + + // Open the log file + std::string logname = LogFileName(dbname_, log); + SequentialFile* lfile; + Status status = env_->NewSequentialFile(logname, &lfile); + if (!status.ok()) { + return status; + } + + // Create the log reader. + LogReporter reporter; + reporter.env = env_; + reporter.info_log = options_.info_log; + reporter.lognum = log; + // We intentionally make log::Reader do checksumming so that + // corruptions cause entire commits to be skipped instead of + // propagating bad information (like overly large sequence + // numbers). + log::Reader reader(lfile, &reporter, false/*do not checksum*/, + 0/*initial_offset*/); + + // Read all the records and add to a memtable + std::string scratch; + Slice record; + WriteBatch batch; + MemTable* mem = new MemTable(icmp_); + mem->Ref(); + int counter = 0; + while (reader.ReadRecord(&record, &scratch)) { + if (record.size() < 12) { + reporter.Corruption( + record.size(), Status::Corruption("log record too small")); + continue; + } + WriteBatchInternal::SetContents(&batch, record); + status = WriteBatchInternal::InsertInto(&batch, mem); + if (status.ok()) { + counter += WriteBatchInternal::Count(&batch); + } else { + Log(options_.info_log, "Log #%llu: ignoring %s", + (unsigned long long) log, + status.ToString().c_str()); + status = Status::OK(); // Keep going with rest of file + } + } + delete lfile; + + // Do not record a version edit for this conversion to a Table + // since ExtractMetaData() will also generate edits. + FileMetaData meta; + meta.number = next_file_number_++; + Iterator* iter = mem->NewIterator(); + status = BuildTable(dbname_, env_, options_, table_cache_, iter, &meta); + delete iter; + mem->Unref(); + mem = NULL; + if (status.ok()) { + if (meta.file_size > 0) { + table_numbers_.push_back(meta.number); + } + } + Log(options_.info_log, "Log #%llu: %d ops saved to Table #%llu %s", + (unsigned long long) log, + counter, + (unsigned long long) meta.number, + status.ToString().c_str()); + return status; + } + + void ExtractMetaData() { + for (size_t i = 0; i < table_numbers_.size(); i++) { + ScanTable(table_numbers_[i]); + } + } + + Iterator* NewTableIterator(const FileMetaData& meta) { + // Same as compaction iterators: if paranoid_checks are on, turn + // on checksum verification. + ReadOptions r; + r.verify_checksums = options_.paranoid_checks; + return table_cache_->NewIterator(r, meta.number, meta.file_size); + } + + void ScanTable(uint64_t number) { + TableInfo t; + t.meta.number = number; + std::string fname = TableFileName(dbname_, number); + Status status = env_->GetFileSize(fname, &t.meta.file_size); + if (!status.ok()) { + // Try alternate file name. + fname = SSTTableFileName(dbname_, number); + Status s2 = env_->GetFileSize(fname, &t.meta.file_size); + if (s2.ok()) { + status = Status::OK(); + } + } + if (!status.ok()) { + ArchiveFile(TableFileName(dbname_, number)); + ArchiveFile(SSTTableFileName(dbname_, number)); + Log(options_.info_log, "Table #%llu: dropped: %s", + (unsigned long long) t.meta.number, + status.ToString().c_str()); + return; + } + + // Extract metadata by scanning through table. + int counter = 0; + Iterator* iter = NewTableIterator(t.meta); + bool empty = true; + ParsedInternalKey parsed; + t.max_sequence = 0; + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + Slice key = iter->key(); + if (!ParseInternalKey(key, &parsed)) { + Log(options_.info_log, "Table #%llu: unparsable key %s", + (unsigned long long) t.meta.number, + EscapeString(key).c_str()); + continue; + } + + counter++; + if (empty) { + empty = false; + t.meta.smallest.DecodeFrom(key); + } + t.meta.largest.DecodeFrom(key); + if (parsed.sequence > t.max_sequence) { + t.max_sequence = parsed.sequence; + } + } + if (!iter->status().ok()) { + status = iter->status(); + } + delete iter; + Log(options_.info_log, "Table #%llu: %d entries %s", + (unsigned long long) t.meta.number, + counter, + status.ToString().c_str()); + + if (status.ok()) { + tables_.push_back(t); + } else { + RepairTable(fname, t); // RepairTable archives input file. + } + } + + void RepairTable(const std::string& src, TableInfo t) { + // We will copy src contents to a new table and then rename the + // new table over the source. + + // Create builder. + std::string copy = TableFileName(dbname_, next_file_number_++); + WritableFile* file; + Status s = env_->NewWritableFile(copy, &file); + if (!s.ok()) { + return; + } + TableBuilder* builder = new TableBuilder(options_, file); + + // Copy data. + Iterator* iter = NewTableIterator(t.meta); + int counter = 0; + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + builder->Add(iter->key(), iter->value()); + counter++; + } + delete iter; + + ArchiveFile(src); + if (counter == 0) { + builder->Abandon(); // Nothing to save + } else { + s = builder->Finish(); + if (s.ok()) { + t.meta.file_size = builder->FileSize(); + } + } + delete builder; + builder = NULL; + + if (s.ok()) { + s = file->Close(); + } + delete file; + file = NULL; + + if (counter > 0 && s.ok()) { + std::string orig = TableFileName(dbname_, t.meta.number); + s = env_->RenameFile(copy, orig); + if (s.ok()) { + Log(options_.info_log, "Table #%llu: %d entries repaired", + (unsigned long long) t.meta.number, counter); + tables_.push_back(t); + } + } + if (!s.ok()) { + env_->DeleteFile(copy); + } + } + + Status WriteDescriptor() { + std::string tmp = TempFileName(dbname_, 1); + WritableFile* file; + Status status = env_->NewWritableFile(tmp, &file); + if (!status.ok()) { + return status; + } + + SequenceNumber max_sequence = 0; + for (size_t i = 0; i < tables_.size(); i++) { + if (max_sequence < tables_[i].max_sequence) { + max_sequence = tables_[i].max_sequence; + } + } + + edit_.SetComparatorName(icmp_.user_comparator()->Name()); + edit_.SetLogNumber(0); + edit_.SetNextFile(next_file_number_); + edit_.SetLastSequence(max_sequence); + + for (size_t i = 0; i < tables_.size(); i++) { + // TODO(opt): separate out into multiple levels + const TableInfo& t = tables_[i]; + edit_.AddFile(0, t.meta.number, t.meta.file_size, + t.meta.smallest, t.meta.largest); + } + + //fprintf(stderr, "NewDescriptor:\n%s\n", edit_.DebugString().c_str()); + { + log::Writer log(file); + std::string record; + edit_.EncodeTo(&record); + status = log.AddRecord(record); + } + if (status.ok()) { + status = file->Close(); + } + delete file; + file = NULL; + + if (!status.ok()) { + env_->DeleteFile(tmp); + } else { + // Discard older manifests + for (size_t i = 0; i < manifests_.size(); i++) { + ArchiveFile(dbname_ + "/" + manifests_[i]); + } + + // Install new manifest + status = env_->RenameFile(tmp, DescriptorFileName(dbname_, 1)); + if (status.ok()) { + status = SetCurrentFile(env_, dbname_, 1); + } else { + env_->DeleteFile(tmp); + } + } + return status; + } + + void ArchiveFile(const std::string& fname) { + // Move into another directory. E.g., for + // dir/foo + // rename to + // dir/lost/foo + const char* slash = strrchr(fname.c_str(), '/'); + std::string new_dir; + if (slash != NULL) { + new_dir.assign(fname.data(), slash - fname.data()); + } + new_dir.append("/lost"); + env_->CreateDir(new_dir); // Ignore error + std::string new_file = new_dir; + new_file.append("/"); + new_file.append((slash == NULL) ? fname.c_str() : slash + 1); + Status s = env_->RenameFile(fname, new_file); + Log(options_.info_log, "Archiving %s: %s\n", + fname.c_str(), s.ToString().c_str()); + } +}; +} // namespace + +Status RepairDB(const std::string& dbname, const Options& options) { + Repairer repairer(dbname, options); + return repairer.Run(); +} + +} // namespace leveldb diff --git a/src/leveldb/db/skiplist.h b/src/leveldb/db/skiplist.h new file mode 100644 index 00000000..ed8b0922 --- /dev/null +++ b/src/leveldb/db/skiplist.h @@ -0,0 +1,384 @@ +#ifndef STORAGE_LEVELDB_DB_SKIPLIST_H_ +#define STORAGE_LEVELDB_DB_SKIPLIST_H_ + +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Thread safety +// ------------- +// +// Writes require external synchronization, most likely a mutex. +// Reads require a guarantee that the SkipList will not be destroyed +// while the read is in progress. Apart from that, reads progress +// without any internal locking or synchronization. +// +// Invariants: +// +// (1) Allocated nodes are never deleted until the SkipList is +// destroyed. This is trivially guaranteed by the code since we +// never delete any skip list nodes. +// +// (2) The contents of a Node except for the next/prev pointers are +// immutable after the Node has been linked into the SkipList. +// Only Insert() modifies the list, and it is careful to initialize +// a node and use release-stores to publish the nodes in one or +// more lists. +// +// ... prev vs. next pointer ordering ... + +#include +#include +#include "port/port.h" +#include "util/arena.h" +#include "util/random.h" + +namespace leveldb { + +class Arena; + +template +class SkipList { + private: + struct Node; + + public: + // Create a new SkipList object that will use "cmp" for comparing keys, + // and will allocate memory using "*arena". Objects allocated in the arena + // must remain allocated for the lifetime of the skiplist object. + explicit SkipList(Comparator cmp, Arena* arena); + + // Insert key into the list. + // REQUIRES: nothing that compares equal to key is currently in the list. + void Insert(const Key& key); + + // Returns true iff an entry that compares equal to key is in the list. + bool Contains(const Key& key) const; + + // Iteration over the contents of a skip list + class Iterator { + public: + // Initialize an iterator over the specified list. + // The returned iterator is not valid. + explicit Iterator(const SkipList* list); + + // Returns true iff the iterator is positioned at a valid node. + bool Valid() const; + + // Returns the key at the current position. + // REQUIRES: Valid() + const Key& key() const; + + // Advances to the next position. + // REQUIRES: Valid() + void Next(); + + // Advances to the previous position. + // REQUIRES: Valid() + void Prev(); + + // Advance to the first entry with a key >= target + void Seek(const Key& target); + + // Position at the first entry in list. + // Final state of iterator is Valid() iff list is not empty. + void SeekToFirst(); + + // Position at the last entry in list. + // Final state of iterator is Valid() iff list is not empty. + void SeekToLast(); + + private: + const SkipList* list_; + Node* node_; + // Intentionally copyable + }; + + private: + enum { kMaxHeight = 12 }; + + // Immutable after construction + Comparator const compare_; + Arena* const arena_; // Arena used for allocations of nodes + + Node* const head_; + + // Modified only by Insert(). Read racily by readers, but stale + // values are ok. + port::AtomicPointer max_height_; // Height of the entire list + + inline int GetMaxHeight() const { + return static_cast( + reinterpret_cast(max_height_.NoBarrier_Load())); + } + + // Read/written only by Insert(). + Random rnd_; + + Node* NewNode(const Key& key, int height); + int RandomHeight(); + bool Equal(const Key& a, const Key& b) const { return (compare_(a, b) == 0); } + + // Return true if key is greater than the data stored in "n" + bool KeyIsAfterNode(const Key& key, Node* n) const; + + // Return the earliest node that comes at or after key. + // Return NULL if there is no such node. + // + // If prev is non-NULL, fills prev[level] with pointer to previous + // node at "level" for every level in [0..max_height_-1]. + Node* FindGreaterOrEqual(const Key& key, Node** prev) const; + + // Return the latest node with a key < key. + // Return head_ if there is no such node. + Node* FindLessThan(const Key& key) const; + + // Return the last node in the list. + // Return head_ if list is empty. + Node* FindLast() const; + + // No copying allowed + SkipList(const SkipList&); + void operator=(const SkipList&); +}; + +// Implementation details follow +template +struct SkipList::Node { + explicit Node(const Key& k) : key(k) { } + + Key const key; + + // Accessors/mutators for links. Wrapped in methods so we can + // add the appropriate barriers as necessary. + Node* Next(int n) { + assert(n >= 0); + // Use an 'acquire load' so that we observe a fully initialized + // version of the returned Node. + return reinterpret_cast(next_[n].Acquire_Load()); + } + void SetNext(int n, Node* x) { + assert(n >= 0); + // Use a 'release store' so that anybody who reads through this + // pointer observes a fully initialized version of the inserted node. + next_[n].Release_Store(x); + } + + // No-barrier variants that can be safely used in a few locations. + Node* NoBarrier_Next(int n) { + assert(n >= 0); + return reinterpret_cast(next_[n].NoBarrier_Load()); + } + void NoBarrier_SetNext(int n, Node* x) { + assert(n >= 0); + next_[n].NoBarrier_Store(x); + } + + private: + // Array of length equal to the node height. next_[0] is lowest level link. + port::AtomicPointer next_[1]; +}; + +template +typename SkipList::Node* +SkipList::NewNode(const Key& key, int height) { + char* mem = arena_->AllocateAligned( + sizeof(Node) + sizeof(port::AtomicPointer) * (height - 1)); + return new (mem) Node(key); +} + +template +inline SkipList::Iterator::Iterator(const SkipList* list) { + list_ = list; + node_ = NULL; +} + +template +inline bool SkipList::Iterator::Valid() const { + return node_ != NULL; +} + +template +inline const Key& SkipList::Iterator::key() const { + assert(Valid()); + return node_->key; +} + +template +inline void SkipList::Iterator::Next() { + assert(Valid()); + node_ = node_->Next(0); +} + +template +inline void SkipList::Iterator::Prev() { + // Instead of using explicit "prev" links, we just search for the + // last node that falls before key. + assert(Valid()); + node_ = list_->FindLessThan(node_->key); + if (node_ == list_->head_) { + node_ = NULL; + } +} + +template +inline void SkipList::Iterator::Seek(const Key& target) { + node_ = list_->FindGreaterOrEqual(target, NULL); +} + +template +inline void SkipList::Iterator::SeekToFirst() { + node_ = list_->head_->Next(0); +} + +template +inline void SkipList::Iterator::SeekToLast() { + node_ = list_->FindLast(); + if (node_ == list_->head_) { + node_ = NULL; + } +} + +template +int SkipList::RandomHeight() { + // Increase height with probability 1 in kBranching + static const unsigned int kBranching = 4; + int height = 1; + while (height < kMaxHeight && ((rnd_.Next() % kBranching) == 0)) { + height++; + } + assert(height > 0); + assert(height <= kMaxHeight); + return height; +} + +template +bool SkipList::KeyIsAfterNode(const Key& key, Node* n) const { + // NULL n is considered infinite + return (n != NULL) && (compare_(n->key, key) < 0); +} + +template +typename SkipList::Node* SkipList::FindGreaterOrEqual(const Key& key, Node** prev) + const { + Node* x = head_; + int level = GetMaxHeight() - 1; + while (true) { + Node* next = x->Next(level); + if (KeyIsAfterNode(key, next)) { + // Keep searching in this list + x = next; + } else { + if (prev != NULL) prev[level] = x; + if (level == 0) { + return next; + } else { + // Switch to next list + level--; + } + } + } +} + +template +typename SkipList::Node* +SkipList::FindLessThan(const Key& key) const { + Node* x = head_; + int level = GetMaxHeight() - 1; + while (true) { + assert(x == head_ || compare_(x->key, key) < 0); + Node* next = x->Next(level); + if (next == NULL || compare_(next->key, key) >= 0) { + if (level == 0) { + return x; + } else { + // Switch to next list + level--; + } + } else { + x = next; + } + } +} + +template +typename SkipList::Node* SkipList::FindLast() + const { + Node* x = head_; + int level = GetMaxHeight() - 1; + while (true) { + Node* next = x->Next(level); + if (next == NULL) { + if (level == 0) { + return x; + } else { + // Switch to next list + level--; + } + } else { + x = next; + } + } +} + +template +SkipList::SkipList(Comparator cmp, Arena* arena) + : compare_(cmp), + arena_(arena), + head_(NewNode(0 /* any key will do */, kMaxHeight)), + max_height_(reinterpret_cast(1)), + rnd_(0xdeadbeef) { + for (int i = 0; i < kMaxHeight; i++) { + head_->SetNext(i, NULL); + } +} + +template +void SkipList::Insert(const Key& key) { + // TODO(opt): We can use a barrier-free variant of FindGreaterOrEqual() + // here since Insert() is externally synchronized. + Node* prev[kMaxHeight]; + Node* x = FindGreaterOrEqual(key, prev); + + // Our data structure does not allow duplicate insertion + assert(x == NULL || !Equal(key, x->key)); + + int height = RandomHeight(); + if (height > GetMaxHeight()) { + for (int i = GetMaxHeight(); i < height; i++) { + prev[i] = head_; + } + //fprintf(stderr, "Change height from %d to %d\n", max_height_, height); + + // It is ok to mutate max_height_ without any synchronization + // with concurrent readers. A concurrent reader that observes + // the new value of max_height_ will see either the old value of + // new level pointers from head_ (NULL), or a new value set in + // the loop below. In the former case the reader will + // immediately drop to the next level since NULL sorts after all + // keys. In the latter case the reader will use the new node. + max_height_.NoBarrier_Store(reinterpret_cast(height)); + } + + x = NewNode(key, height); + for (int i = 0; i < height; i++) { + // NoBarrier_SetNext() suffices since we will add a barrier when + // we publish a pointer to "x" in prev[i]. + x->NoBarrier_SetNext(i, prev[i]->NoBarrier_Next(i)); + prev[i]->SetNext(i, x); + } +} + +template +bool SkipList::Contains(const Key& key) const { + Node* x = FindGreaterOrEqual(key, NULL); + if (x != NULL && Equal(key, x->key)) { + return true; + } else { + return false; + } +} + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_SKIPLIST_H_ diff --git a/src/leveldb/db/skiplist_test.cc b/src/leveldb/db/skiplist_test.cc new file mode 100644 index 00000000..c78f4b4f --- /dev/null +++ b/src/leveldb/db/skiplist_test.cc @@ -0,0 +1,378 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/skiplist.h" +#include +#include "leveldb/env.h" +#include "util/arena.h" +#include "util/hash.h" +#include "util/random.h" +#include "util/testharness.h" + +namespace leveldb { + +typedef uint64_t Key; + +struct Comparator { + int operator()(const Key& a, const Key& b) const { + if (a < b) { + return -1; + } else if (a > b) { + return +1; + } else { + return 0; + } + } +}; + +class SkipTest { }; + +TEST(SkipTest, Empty) { + Arena arena; + Comparator cmp; + SkipList list(cmp, &arena); + ASSERT_TRUE(!list.Contains(10)); + + SkipList::Iterator iter(&list); + ASSERT_TRUE(!iter.Valid()); + iter.SeekToFirst(); + ASSERT_TRUE(!iter.Valid()); + iter.Seek(100); + ASSERT_TRUE(!iter.Valid()); + iter.SeekToLast(); + ASSERT_TRUE(!iter.Valid()); +} + +TEST(SkipTest, InsertAndLookup) { + const int N = 2000; + const int R = 5000; + Random rnd(1000); + std::set keys; + Arena arena; + Comparator cmp; + SkipList list(cmp, &arena); + for (int i = 0; i < N; i++) { + Key key = rnd.Next() % R; + if (keys.insert(key).second) { + list.Insert(key); + } + } + + for (int i = 0; i < R; i++) { + if (list.Contains(i)) { + ASSERT_EQ(keys.count(i), 1); + } else { + ASSERT_EQ(keys.count(i), 0); + } + } + + // Simple iterator tests + { + SkipList::Iterator iter(&list); + ASSERT_TRUE(!iter.Valid()); + + iter.Seek(0); + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*(keys.begin()), iter.key()); + + iter.SeekToFirst(); + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*(keys.begin()), iter.key()); + + iter.SeekToLast(); + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*(keys.rbegin()), iter.key()); + } + + // Forward iteration test + for (int i = 0; i < R; i++) { + SkipList::Iterator iter(&list); + iter.Seek(i); + + // Compare against model iterator + std::set::iterator model_iter = keys.lower_bound(i); + for (int j = 0; j < 3; j++) { + if (model_iter == keys.end()) { + ASSERT_TRUE(!iter.Valid()); + break; + } else { + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*model_iter, iter.key()); + ++model_iter; + iter.Next(); + } + } + } + + // Backward iteration test + { + SkipList::Iterator iter(&list); + iter.SeekToLast(); + + // Compare against model iterator + for (std::set::reverse_iterator model_iter = keys.rbegin(); + model_iter != keys.rend(); + ++model_iter) { + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*model_iter, iter.key()); + iter.Prev(); + } + ASSERT_TRUE(!iter.Valid()); + } +} + +// We want to make sure that with a single writer and multiple +// concurrent readers (with no synchronization other than when a +// reader's iterator is created), the reader always observes all the +// data that was present in the skip list when the iterator was +// constructor. Because insertions are happening concurrently, we may +// also observe new values that were inserted since the iterator was +// constructed, but we should never miss any values that were present +// at iterator construction time. +// +// We generate multi-part keys: +// +// where: +// key is in range [0..K-1] +// gen is a generation number for key +// hash is hash(key,gen) +// +// The insertion code picks a random key, sets gen to be 1 + the last +// generation number inserted for that key, and sets hash to Hash(key,gen). +// +// At the beginning of a read, we snapshot the last inserted +// generation number for each key. We then iterate, including random +// calls to Next() and Seek(). For every key we encounter, we +// check that it is either expected given the initial snapshot or has +// been concurrently added since the iterator started. +class ConcurrentTest { + private: + static const uint32_t K = 4; + + static uint64_t key(Key key) { return (key >> 40); } + static uint64_t gen(Key key) { return (key >> 8) & 0xffffffffu; } + static uint64_t hash(Key key) { return key & 0xff; } + + static uint64_t HashNumbers(uint64_t k, uint64_t g) { + uint64_t data[2] = { k, g }; + return Hash(reinterpret_cast(data), sizeof(data), 0); + } + + static Key MakeKey(uint64_t k, uint64_t g) { + assert(sizeof(Key) == sizeof(uint64_t)); + assert(k <= K); // We sometimes pass K to seek to the end of the skiplist + assert(g <= 0xffffffffu); + return ((k << 40) | (g << 8) | (HashNumbers(k, g) & 0xff)); + } + + static bool IsValidKey(Key k) { + return hash(k) == (HashNumbers(key(k), gen(k)) & 0xff); + } + + static Key RandomTarget(Random* rnd) { + switch (rnd->Next() % 10) { + case 0: + // Seek to beginning + return MakeKey(0, 0); + case 1: + // Seek to end + return MakeKey(K, 0); + default: + // Seek to middle + return MakeKey(rnd->Next() % K, 0); + } + } + + // Per-key generation + struct State { + port::AtomicPointer generation[K]; + void Set(int k, intptr_t v) { + generation[k].Release_Store(reinterpret_cast(v)); + } + intptr_t Get(int k) { + return reinterpret_cast(generation[k].Acquire_Load()); + } + + State() { + for (int k = 0; k < K; k++) { + Set(k, 0); + } + } + }; + + // Current state of the test + State current_; + + Arena arena_; + + // SkipList is not protected by mu_. We just use a single writer + // thread to modify it. + SkipList list_; + + public: + ConcurrentTest() : list_(Comparator(), &arena_) { } + + // REQUIRES: External synchronization + void WriteStep(Random* rnd) { + const uint32_t k = rnd->Next() % K; + const intptr_t g = current_.Get(k) + 1; + const Key key = MakeKey(k, g); + list_.Insert(key); + current_.Set(k, g); + } + + void ReadStep(Random* rnd) { + // Remember the initial committed state of the skiplist. + State initial_state; + for (int k = 0; k < K; k++) { + initial_state.Set(k, current_.Get(k)); + } + + Key pos = RandomTarget(rnd); + SkipList::Iterator iter(&list_); + iter.Seek(pos); + while (true) { + Key current; + if (!iter.Valid()) { + current = MakeKey(K, 0); + } else { + current = iter.key(); + ASSERT_TRUE(IsValidKey(current)) << current; + } + ASSERT_LE(pos, current) << "should not go backwards"; + + // Verify that everything in [pos,current) was not present in + // initial_state. + while (pos < current) { + ASSERT_LT(key(pos), K) << pos; + + // Note that generation 0 is never inserted, so it is ok if + // <*,0,*> is missing. + ASSERT_TRUE((gen(pos) == 0) || + (gen(pos) > initial_state.Get(key(pos))) + ) << "key: " << key(pos) + << "; gen: " << gen(pos) + << "; initgen: " + << initial_state.Get(key(pos)); + + // Advance to next key in the valid key space + if (key(pos) < key(current)) { + pos = MakeKey(key(pos) + 1, 0); + } else { + pos = MakeKey(key(pos), gen(pos) + 1); + } + } + + if (!iter.Valid()) { + break; + } + + if (rnd->Next() % 2) { + iter.Next(); + pos = MakeKey(key(pos), gen(pos) + 1); + } else { + Key new_target = RandomTarget(rnd); + if (new_target > pos) { + pos = new_target; + iter.Seek(new_target); + } + } + } + } +}; +const uint32_t ConcurrentTest::K; + +// Simple test that does single-threaded testing of the ConcurrentTest +// scaffolding. +TEST(SkipTest, ConcurrentWithoutThreads) { + ConcurrentTest test; + Random rnd(test::RandomSeed()); + for (int i = 0; i < 10000; i++) { + test.ReadStep(&rnd); + test.WriteStep(&rnd); + } +} + +class TestState { + public: + ConcurrentTest t_; + int seed_; + port::AtomicPointer quit_flag_; + + enum ReaderState { + STARTING, + RUNNING, + DONE + }; + + explicit TestState(int s) + : seed_(s), + quit_flag_(NULL), + state_(STARTING), + state_cv_(&mu_) {} + + void Wait(ReaderState s) { + mu_.Lock(); + while (state_ != s) { + state_cv_.Wait(); + } + mu_.Unlock(); + } + + void Change(ReaderState s) { + mu_.Lock(); + state_ = s; + state_cv_.Signal(); + mu_.Unlock(); + } + + private: + port::Mutex mu_; + ReaderState state_; + port::CondVar state_cv_; +}; + +static void ConcurrentReader(void* arg) { + TestState* state = reinterpret_cast(arg); + Random rnd(state->seed_); + int64_t reads = 0; + state->Change(TestState::RUNNING); + while (!state->quit_flag_.Acquire_Load()) { + state->t_.ReadStep(&rnd); + ++reads; + } + state->Change(TestState::DONE); +} + +static void RunConcurrent(int run) { + const int seed = test::RandomSeed() + (run * 100); + Random rnd(seed); + const int N = 1000; + const int kSize = 1000; + for (int i = 0; i < N; i++) { + if ((i % 100) == 0) { + fprintf(stderr, "Run %d of %d\n", i, N); + } + TestState state(seed + 1); + Env::Default()->Schedule(ConcurrentReader, &state); + state.Wait(TestState::RUNNING); + for (int i = 0; i < kSize; i++) { + state.t_.WriteStep(&rnd); + } + state.quit_flag_.Release_Store(&state); // Any non-NULL arg will do + state.Wait(TestState::DONE); + } +} + +TEST(SkipTest, Concurrent1) { RunConcurrent(1); } +TEST(SkipTest, Concurrent2) { RunConcurrent(2); } +TEST(SkipTest, Concurrent3) { RunConcurrent(3); } +TEST(SkipTest, Concurrent4) { RunConcurrent(4); } +TEST(SkipTest, Concurrent5) { RunConcurrent(5); } + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/snapshot.h b/src/leveldb/db/snapshot.h new file mode 100644 index 00000000..e7f8fd2c --- /dev/null +++ b/src/leveldb/db/snapshot.h @@ -0,0 +1,66 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_SNAPSHOT_H_ +#define STORAGE_LEVELDB_DB_SNAPSHOT_H_ + +#include "leveldb/db.h" + +namespace leveldb { + +class SnapshotList; + +// Snapshots are kept in a doubly-linked list in the DB. +// Each SnapshotImpl corresponds to a particular sequence number. +class SnapshotImpl : public Snapshot { + public: + SequenceNumber number_; // const after creation + + private: + friend class SnapshotList; + + // SnapshotImpl is kept in a doubly-linked circular list + SnapshotImpl* prev_; + SnapshotImpl* next_; + + SnapshotList* list_; // just for sanity checks +}; + +class SnapshotList { + public: + SnapshotList() { + list_.prev_ = &list_; + list_.next_ = &list_; + } + + bool empty() const { return list_.next_ == &list_; } + SnapshotImpl* oldest() const { assert(!empty()); return list_.next_; } + SnapshotImpl* newest() const { assert(!empty()); return list_.prev_; } + + const SnapshotImpl* New(SequenceNumber seq) { + SnapshotImpl* s = new SnapshotImpl; + s->number_ = seq; + s->list_ = this; + s->next_ = &list_; + s->prev_ = list_.prev_; + s->prev_->next_ = s; + s->next_->prev_ = s; + return s; + } + + void Delete(const SnapshotImpl* s) { + assert(s->list_ == this); + s->prev_->next_ = s->next_; + s->next_->prev_ = s->prev_; + delete s; + } + + private: + // Dummy head of doubly-linked list of snapshots + SnapshotImpl list_; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_SNAPSHOT_H_ diff --git a/src/leveldb/db/table_cache.cc b/src/leveldb/db/table_cache.cc new file mode 100644 index 00000000..e3d82cd3 --- /dev/null +++ b/src/leveldb/db/table_cache.cc @@ -0,0 +1,127 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/table_cache.h" + +#include "db/filename.h" +#include "leveldb/env.h" +#include "leveldb/table.h" +#include "util/coding.h" + +namespace leveldb { + +struct TableAndFile { + RandomAccessFile* file; + Table* table; +}; + +static void DeleteEntry(const Slice& key, void* value) { + TableAndFile* tf = reinterpret_cast(value); + delete tf->table; + delete tf->file; + delete tf; +} + +static void UnrefEntry(void* arg1, void* arg2) { + Cache* cache = reinterpret_cast(arg1); + Cache::Handle* h = reinterpret_cast(arg2); + cache->Release(h); +} + +TableCache::TableCache(const std::string& dbname, + const Options* options, + int entries) + : env_(options->env), + dbname_(dbname), + options_(options), + cache_(NewLRUCache(entries)) { +} + +TableCache::~TableCache() { + delete cache_; +} + +Status TableCache::FindTable(uint64_t file_number, uint64_t file_size, + Cache::Handle** handle) { + Status s; + char buf[sizeof(file_number)]; + EncodeFixed64(buf, file_number); + Slice key(buf, sizeof(buf)); + *handle = cache_->Lookup(key); + if (*handle == NULL) { + std::string fname = TableFileName(dbname_, file_number); + RandomAccessFile* file = NULL; + Table* table = NULL; + s = env_->NewRandomAccessFile(fname, &file); + if (!s.ok()) { + std::string old_fname = SSTTableFileName(dbname_, file_number); + if (env_->NewRandomAccessFile(old_fname, &file).ok()) { + s = Status::OK(); + } + } + if (s.ok()) { + s = Table::Open(*options_, file, file_size, &table); + } + + if (!s.ok()) { + assert(table == NULL); + delete file; + // We do not cache error results so that if the error is transient, + // or somebody repairs the file, we recover automatically. + } else { + TableAndFile* tf = new TableAndFile; + tf->file = file; + tf->table = table; + *handle = cache_->Insert(key, tf, 1, &DeleteEntry); + } + } + return s; +} + +Iterator* TableCache::NewIterator(const ReadOptions& options, + uint64_t file_number, + uint64_t file_size, + Table** tableptr) { + if (tableptr != NULL) { + *tableptr = NULL; + } + + Cache::Handle* handle = NULL; + Status s = FindTable(file_number, file_size, &handle); + if (!s.ok()) { + return NewErrorIterator(s); + } + + Table* table = reinterpret_cast(cache_->Value(handle))->table; + Iterator* result = table->NewIterator(options); + result->RegisterCleanup(&UnrefEntry, cache_, handle); + if (tableptr != NULL) { + *tableptr = table; + } + return result; +} + +Status TableCache::Get(const ReadOptions& options, + uint64_t file_number, + uint64_t file_size, + const Slice& k, + void* arg, + void (*saver)(void*, const Slice&, const Slice&)) { + Cache::Handle* handle = NULL; + Status s = FindTable(file_number, file_size, &handle); + if (s.ok()) { + Table* t = reinterpret_cast(cache_->Value(handle))->table; + s = t->InternalGet(options, k, arg, saver); + cache_->Release(handle); + } + return s; +} + +void TableCache::Evict(uint64_t file_number) { + char buf[sizeof(file_number)]; + EncodeFixed64(buf, file_number); + cache_->Erase(Slice(buf, sizeof(buf))); +} + +} // namespace leveldb diff --git a/src/leveldb/db/table_cache.h b/src/leveldb/db/table_cache.h new file mode 100644 index 00000000..8cf4aaf1 --- /dev/null +++ b/src/leveldb/db/table_cache.h @@ -0,0 +1,61 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Thread-safe (provides internal synchronization) + +#ifndef STORAGE_LEVELDB_DB_TABLE_CACHE_H_ +#define STORAGE_LEVELDB_DB_TABLE_CACHE_H_ + +#include +#include +#include "db/dbformat.h" +#include "leveldb/cache.h" +#include "leveldb/table.h" +#include "port/port.h" + +namespace leveldb { + +class Env; + +class TableCache { + public: + TableCache(const std::string& dbname, const Options* options, int entries); + ~TableCache(); + + // Return an iterator for the specified file number (the corresponding + // file length must be exactly "file_size" bytes). If "tableptr" is + // non-NULL, also sets "*tableptr" to point to the Table object + // underlying the returned iterator, or NULL if no Table object underlies + // the returned iterator. The returned "*tableptr" object is owned by + // the cache and should not be deleted, and is valid for as long as the + // returned iterator is live. + Iterator* NewIterator(const ReadOptions& options, + uint64_t file_number, + uint64_t file_size, + Table** tableptr = NULL); + + // If a seek to internal key "k" in specified file finds an entry, + // call (*handle_result)(arg, found_key, found_value). + Status Get(const ReadOptions& options, + uint64_t file_number, + uint64_t file_size, + const Slice& k, + void* arg, + void (*handle_result)(void*, const Slice&, const Slice&)); + + // Evict any entry for the specified file number + void Evict(uint64_t file_number); + + private: + Env* const env_; + const std::string dbname_; + const Options* options_; + Cache* cache_; + + Status FindTable(uint64_t file_number, uint64_t file_size, Cache::Handle**); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_TABLE_CACHE_H_ diff --git a/src/leveldb/db/version_edit.cc b/src/leveldb/db/version_edit.cc new file mode 100644 index 00000000..f10a2d58 --- /dev/null +++ b/src/leveldb/db/version_edit.cc @@ -0,0 +1,266 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/version_edit.h" + +#include "db/version_set.h" +#include "util/coding.h" + +namespace leveldb { + +// Tag numbers for serialized VersionEdit. These numbers are written to +// disk and should not be changed. +enum Tag { + kComparator = 1, + kLogNumber = 2, + kNextFileNumber = 3, + kLastSequence = 4, + kCompactPointer = 5, + kDeletedFile = 6, + kNewFile = 7, + // 8 was used for large value refs + kPrevLogNumber = 9 +}; + +void VersionEdit::Clear() { + comparator_.clear(); + log_number_ = 0; + prev_log_number_ = 0; + last_sequence_ = 0; + next_file_number_ = 0; + has_comparator_ = false; + has_log_number_ = false; + has_prev_log_number_ = false; + has_next_file_number_ = false; + has_last_sequence_ = false; + deleted_files_.clear(); + new_files_.clear(); +} + +void VersionEdit::EncodeTo(std::string* dst) const { + if (has_comparator_) { + PutVarint32(dst, kComparator); + PutLengthPrefixedSlice(dst, comparator_); + } + if (has_log_number_) { + PutVarint32(dst, kLogNumber); + PutVarint64(dst, log_number_); + } + if (has_prev_log_number_) { + PutVarint32(dst, kPrevLogNumber); + PutVarint64(dst, prev_log_number_); + } + if (has_next_file_number_) { + PutVarint32(dst, kNextFileNumber); + PutVarint64(dst, next_file_number_); + } + if (has_last_sequence_) { + PutVarint32(dst, kLastSequence); + PutVarint64(dst, last_sequence_); + } + + for (size_t i = 0; i < compact_pointers_.size(); i++) { + PutVarint32(dst, kCompactPointer); + PutVarint32(dst, compact_pointers_[i].first); // level + PutLengthPrefixedSlice(dst, compact_pointers_[i].second.Encode()); + } + + for (DeletedFileSet::const_iterator iter = deleted_files_.begin(); + iter != deleted_files_.end(); + ++iter) { + PutVarint32(dst, kDeletedFile); + PutVarint32(dst, iter->first); // level + PutVarint64(dst, iter->second); // file number + } + + for (size_t i = 0; i < new_files_.size(); i++) { + const FileMetaData& f = new_files_[i].second; + PutVarint32(dst, kNewFile); + PutVarint32(dst, new_files_[i].first); // level + PutVarint64(dst, f.number); + PutVarint64(dst, f.file_size); + PutLengthPrefixedSlice(dst, f.smallest.Encode()); + PutLengthPrefixedSlice(dst, f.largest.Encode()); + } +} + +static bool GetInternalKey(Slice* input, InternalKey* dst) { + Slice str; + if (GetLengthPrefixedSlice(input, &str)) { + dst->DecodeFrom(str); + return true; + } else { + return false; + } +} + +static bool GetLevel(Slice* input, int* level) { + uint32_t v; + if (GetVarint32(input, &v) && + v < config::kNumLevels) { + *level = v; + return true; + } else { + return false; + } +} + +Status VersionEdit::DecodeFrom(const Slice& src) { + Clear(); + Slice input = src; + const char* msg = NULL; + uint32_t tag; + + // Temporary storage for parsing + int level; + uint64_t number; + FileMetaData f; + Slice str; + InternalKey key; + + while (msg == NULL && GetVarint32(&input, &tag)) { + switch (tag) { + case kComparator: + if (GetLengthPrefixedSlice(&input, &str)) { + comparator_ = str.ToString(); + has_comparator_ = true; + } else { + msg = "comparator name"; + } + break; + + case kLogNumber: + if (GetVarint64(&input, &log_number_)) { + has_log_number_ = true; + } else { + msg = "log number"; + } + break; + + case kPrevLogNumber: + if (GetVarint64(&input, &prev_log_number_)) { + has_prev_log_number_ = true; + } else { + msg = "previous log number"; + } + break; + + case kNextFileNumber: + if (GetVarint64(&input, &next_file_number_)) { + has_next_file_number_ = true; + } else { + msg = "next file number"; + } + break; + + case kLastSequence: + if (GetVarint64(&input, &last_sequence_)) { + has_last_sequence_ = true; + } else { + msg = "last sequence number"; + } + break; + + case kCompactPointer: + if (GetLevel(&input, &level) && + GetInternalKey(&input, &key)) { + compact_pointers_.push_back(std::make_pair(level, key)); + } else { + msg = "compaction pointer"; + } + break; + + case kDeletedFile: + if (GetLevel(&input, &level) && + GetVarint64(&input, &number)) { + deleted_files_.insert(std::make_pair(level, number)); + } else { + msg = "deleted file"; + } + break; + + case kNewFile: + if (GetLevel(&input, &level) && + GetVarint64(&input, &f.number) && + GetVarint64(&input, &f.file_size) && + GetInternalKey(&input, &f.smallest) && + GetInternalKey(&input, &f.largest)) { + new_files_.push_back(std::make_pair(level, f)); + } else { + msg = "new-file entry"; + } + break; + + default: + msg = "unknown tag"; + break; + } + } + + if (msg == NULL && !input.empty()) { + msg = "invalid tag"; + } + + Status result; + if (msg != NULL) { + result = Status::Corruption("VersionEdit", msg); + } + return result; +} + +std::string VersionEdit::DebugString() const { + std::string r; + r.append("VersionEdit {"); + if (has_comparator_) { + r.append("\n Comparator: "); + r.append(comparator_); + } + if (has_log_number_) { + r.append("\n LogNumber: "); + AppendNumberTo(&r, log_number_); + } + if (has_prev_log_number_) { + r.append("\n PrevLogNumber: "); + AppendNumberTo(&r, prev_log_number_); + } + if (has_next_file_number_) { + r.append("\n NextFile: "); + AppendNumberTo(&r, next_file_number_); + } + if (has_last_sequence_) { + r.append("\n LastSeq: "); + AppendNumberTo(&r, last_sequence_); + } + for (size_t i = 0; i < compact_pointers_.size(); i++) { + r.append("\n CompactPointer: "); + AppendNumberTo(&r, compact_pointers_[i].first); + r.append(" "); + r.append(compact_pointers_[i].second.DebugString()); + } + for (DeletedFileSet::const_iterator iter = deleted_files_.begin(); + iter != deleted_files_.end(); + ++iter) { + r.append("\n DeleteFile: "); + AppendNumberTo(&r, iter->first); + r.append(" "); + AppendNumberTo(&r, iter->second); + } + for (size_t i = 0; i < new_files_.size(); i++) { + const FileMetaData& f = new_files_[i].second; + r.append("\n AddFile: "); + AppendNumberTo(&r, new_files_[i].first); + r.append(" "); + AppendNumberTo(&r, f.number); + r.append(" "); + AppendNumberTo(&r, f.file_size); + r.append(" "); + r.append(f.smallest.DebugString()); + r.append(" .. "); + r.append(f.largest.DebugString()); + } + r.append("\n}\n"); + return r; +} + +} // namespace leveldb diff --git a/src/leveldb/db/version_edit.h b/src/leveldb/db/version_edit.h new file mode 100644 index 00000000..eaef77b3 --- /dev/null +++ b/src/leveldb/db/version_edit.h @@ -0,0 +1,107 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_VERSION_EDIT_H_ +#define STORAGE_LEVELDB_DB_VERSION_EDIT_H_ + +#include +#include +#include +#include "db/dbformat.h" + +namespace leveldb { + +class VersionSet; + +struct FileMetaData { + int refs; + int allowed_seeks; // Seeks allowed until compaction + uint64_t number; + uint64_t file_size; // File size in bytes + InternalKey smallest; // Smallest internal key served by table + InternalKey largest; // Largest internal key served by table + + FileMetaData() : refs(0), allowed_seeks(1 << 30), file_size(0) { } +}; + +class VersionEdit { + public: + VersionEdit() { Clear(); } + ~VersionEdit() { } + + void Clear(); + + void SetComparatorName(const Slice& name) { + has_comparator_ = true; + comparator_ = name.ToString(); + } + void SetLogNumber(uint64_t num) { + has_log_number_ = true; + log_number_ = num; + } + void SetPrevLogNumber(uint64_t num) { + has_prev_log_number_ = true; + prev_log_number_ = num; + } + void SetNextFile(uint64_t num) { + has_next_file_number_ = true; + next_file_number_ = num; + } + void SetLastSequence(SequenceNumber seq) { + has_last_sequence_ = true; + last_sequence_ = seq; + } + void SetCompactPointer(int level, const InternalKey& key) { + compact_pointers_.push_back(std::make_pair(level, key)); + } + + // Add the specified file at the specified number. + // REQUIRES: This version has not been saved (see VersionSet::SaveTo) + // REQUIRES: "smallest" and "largest" are smallest and largest keys in file + void AddFile(int level, uint64_t file, + uint64_t file_size, + const InternalKey& smallest, + const InternalKey& largest) { + FileMetaData f; + f.number = file; + f.file_size = file_size; + f.smallest = smallest; + f.largest = largest; + new_files_.push_back(std::make_pair(level, f)); + } + + // Delete the specified "file" from the specified "level". + void DeleteFile(int level, uint64_t file) { + deleted_files_.insert(std::make_pair(level, file)); + } + + void EncodeTo(std::string* dst) const; + Status DecodeFrom(const Slice& src); + + std::string DebugString() const; + + private: + friend class VersionSet; + + typedef std::set< std::pair > DeletedFileSet; + + std::string comparator_; + uint64_t log_number_; + uint64_t prev_log_number_; + uint64_t next_file_number_; + SequenceNumber last_sequence_; + bool has_comparator_; + bool has_log_number_; + bool has_prev_log_number_; + bool has_next_file_number_; + bool has_last_sequence_; + + std::vector< std::pair > compact_pointers_; + DeletedFileSet deleted_files_; + std::vector< std::pair > new_files_; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_VERSION_EDIT_H_ diff --git a/src/leveldb/db/version_edit_test.cc b/src/leveldb/db/version_edit_test.cc new file mode 100644 index 00000000..280310b4 --- /dev/null +++ b/src/leveldb/db/version_edit_test.cc @@ -0,0 +1,46 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/version_edit.h" +#include "util/testharness.h" + +namespace leveldb { + +static void TestEncodeDecode(const VersionEdit& edit) { + std::string encoded, encoded2; + edit.EncodeTo(&encoded); + VersionEdit parsed; + Status s = parsed.DecodeFrom(encoded); + ASSERT_TRUE(s.ok()) << s.ToString(); + parsed.EncodeTo(&encoded2); + ASSERT_EQ(encoded, encoded2); +} + +class VersionEditTest { }; + +TEST(VersionEditTest, EncodeDecode) { + static const uint64_t kBig = 1ull << 50; + + VersionEdit edit; + for (int i = 0; i < 4; i++) { + TestEncodeDecode(edit); + edit.AddFile(3, kBig + 300 + i, kBig + 400 + i, + InternalKey("foo", kBig + 500 + i, kTypeValue), + InternalKey("zoo", kBig + 600 + i, kTypeDeletion)); + edit.DeleteFile(4, kBig + 700 + i); + edit.SetCompactPointer(i, InternalKey("x", kBig + 900 + i, kTypeValue)); + } + + edit.SetComparatorName("foo"); + edit.SetLogNumber(kBig + 100); + edit.SetNextFile(kBig + 200); + edit.SetLastSequence(kBig + 1000); + TestEncodeDecode(edit); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/version_set.cc b/src/leveldb/db/version_set.cc new file mode 100644 index 00000000..aa83df55 --- /dev/null +++ b/src/leveldb/db/version_set.cc @@ -0,0 +1,1484 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/version_set.h" + +#include +#include +#include "db/filename.h" +#include "db/log_reader.h" +#include "db/log_writer.h" +#include "db/memtable.h" +#include "db/table_cache.h" +#include "leveldb/env.h" +#include "leveldb/table_builder.h" +#include "table/merger.h" +#include "table/two_level_iterator.h" +#include "util/coding.h" +#include "util/logging.h" + +namespace leveldb { + +static const int kTargetFileSize = 2 * 1048576; + +// Maximum bytes of overlaps in grandparent (i.e., level+2) before we +// stop building a single file in a level->level+1 compaction. +static const int64_t kMaxGrandParentOverlapBytes = 10 * kTargetFileSize; + +// Maximum number of bytes in all compacted files. We avoid expanding +// the lower level file set of a compaction if it would make the +// total compaction cover more than this many bytes. +static const int64_t kExpandedCompactionByteSizeLimit = 25 * kTargetFileSize; + +static double MaxBytesForLevel(int level) { + // Note: the result for level zero is not really used since we set + // the level-0 compaction threshold based on number of files. + double result = 10 * 1048576.0; // Result for both level-0 and level-1 + while (level > 1) { + result *= 10; + level--; + } + return result; +} + +static uint64_t MaxFileSizeForLevel(int level) { + return kTargetFileSize; // We could vary per level to reduce number of files? +} + +static int64_t TotalFileSize(const std::vector& files) { + int64_t sum = 0; + for (size_t i = 0; i < files.size(); i++) { + sum += files[i]->file_size; + } + return sum; +} + +Version::~Version() { + assert(refs_ == 0); + + // Remove from linked list + prev_->next_ = next_; + next_->prev_ = prev_; + + // Drop references to files + for (int level = 0; level < config::kNumLevels; level++) { + for (size_t i = 0; i < files_[level].size(); i++) { + FileMetaData* f = files_[level][i]; + assert(f->refs > 0); + f->refs--; + if (f->refs <= 0) { + delete f; + } + } + } +} + +int FindFile(const InternalKeyComparator& icmp, + const std::vector& files, + const Slice& key) { + uint32_t left = 0; + uint32_t right = files.size(); + while (left < right) { + uint32_t mid = (left + right) / 2; + const FileMetaData* f = files[mid]; + if (icmp.InternalKeyComparator::Compare(f->largest.Encode(), key) < 0) { + // Key at "mid.largest" is < "target". Therefore all + // files at or before "mid" are uninteresting. + left = mid + 1; + } else { + // Key at "mid.largest" is >= "target". Therefore all files + // after "mid" are uninteresting. + right = mid; + } + } + return right; +} + +static bool AfterFile(const Comparator* ucmp, + const Slice* user_key, const FileMetaData* f) { + // NULL user_key occurs before all keys and is therefore never after *f + return (user_key != NULL && + ucmp->Compare(*user_key, f->largest.user_key()) > 0); +} + +static bool BeforeFile(const Comparator* ucmp, + const Slice* user_key, const FileMetaData* f) { + // NULL user_key occurs after all keys and is therefore never before *f + return (user_key != NULL && + ucmp->Compare(*user_key, f->smallest.user_key()) < 0); +} + +bool SomeFileOverlapsRange( + const InternalKeyComparator& icmp, + bool disjoint_sorted_files, + const std::vector& files, + const Slice* smallest_user_key, + const Slice* largest_user_key) { + const Comparator* ucmp = icmp.user_comparator(); + if (!disjoint_sorted_files) { + // Need to check against all files + for (size_t i = 0; i < files.size(); i++) { + const FileMetaData* f = files[i]; + if (AfterFile(ucmp, smallest_user_key, f) || + BeforeFile(ucmp, largest_user_key, f)) { + // No overlap + } else { + return true; // Overlap + } + } + return false; + } + + // Binary search over file list + uint32_t index = 0; + if (smallest_user_key != NULL) { + // Find the earliest possible internal key for smallest_user_key + InternalKey small(*smallest_user_key, kMaxSequenceNumber,kValueTypeForSeek); + index = FindFile(icmp, files, small.Encode()); + } + + if (index >= files.size()) { + // beginning of range is after all files, so no overlap. + return false; + } + + return !BeforeFile(ucmp, largest_user_key, files[index]); +} + +// An internal iterator. For a given version/level pair, yields +// information about the files in the level. For a given entry, key() +// is the largest key that occurs in the file, and value() is an +// 16-byte value containing the file number and file size, both +// encoded using EncodeFixed64. +class Version::LevelFileNumIterator : public Iterator { + public: + LevelFileNumIterator(const InternalKeyComparator& icmp, + const std::vector* flist) + : icmp_(icmp), + flist_(flist), + index_(flist->size()) { // Marks as invalid + } + virtual bool Valid() const { + return index_ < flist_->size(); + } + virtual void Seek(const Slice& target) { + index_ = FindFile(icmp_, *flist_, target); + } + virtual void SeekToFirst() { index_ = 0; } + virtual void SeekToLast() { + index_ = flist_->empty() ? 0 : flist_->size() - 1; + } + virtual void Next() { + assert(Valid()); + index_++; + } + virtual void Prev() { + assert(Valid()); + if (index_ == 0) { + index_ = flist_->size(); // Marks as invalid + } else { + index_--; + } + } + Slice key() const { + assert(Valid()); + return (*flist_)[index_]->largest.Encode(); + } + Slice value() const { + assert(Valid()); + EncodeFixed64(value_buf_, (*flist_)[index_]->number); + EncodeFixed64(value_buf_+8, (*flist_)[index_]->file_size); + return Slice(value_buf_, sizeof(value_buf_)); + } + virtual Status status() const { return Status::OK(); } + private: + const InternalKeyComparator icmp_; + const std::vector* const flist_; + uint32_t index_; + + // Backing store for value(). Holds the file number and size. + mutable char value_buf_[16]; +}; + +static Iterator* GetFileIterator(void* arg, + const ReadOptions& options, + const Slice& file_value) { + TableCache* cache = reinterpret_cast(arg); + if (file_value.size() != 16) { + return NewErrorIterator( + Status::Corruption("FileReader invoked with unexpected value")); + } else { + return cache->NewIterator(options, + DecodeFixed64(file_value.data()), + DecodeFixed64(file_value.data() + 8)); + } +} + +Iterator* Version::NewConcatenatingIterator(const ReadOptions& options, + int level) const { + return NewTwoLevelIterator( + new LevelFileNumIterator(vset_->icmp_, &files_[level]), + &GetFileIterator, vset_->table_cache_, options); +} + +void Version::AddIterators(const ReadOptions& options, + std::vector* iters) { + // Merge all level zero files together since they may overlap + for (size_t i = 0; i < files_[0].size(); i++) { + iters->push_back( + vset_->table_cache_->NewIterator( + options, files_[0][i]->number, files_[0][i]->file_size)); + } + + // For levels > 0, we can use a concatenating iterator that sequentially + // walks through the non-overlapping files in the level, opening them + // lazily. + for (int level = 1; level < config::kNumLevels; level++) { + if (!files_[level].empty()) { + iters->push_back(NewConcatenatingIterator(options, level)); + } + } +} + +// Callback from TableCache::Get() +namespace { +enum SaverState { + kNotFound, + kFound, + kDeleted, + kCorrupt, +}; +struct Saver { + SaverState state; + const Comparator* ucmp; + Slice user_key; + std::string* value; +}; +} +static void SaveValue(void* arg, const Slice& ikey, const Slice& v) { + Saver* s = reinterpret_cast(arg); + ParsedInternalKey parsed_key; + if (!ParseInternalKey(ikey, &parsed_key)) { + s->state = kCorrupt; + } else { + if (s->ucmp->Compare(parsed_key.user_key, s->user_key) == 0) { + s->state = (parsed_key.type == kTypeValue) ? kFound : kDeleted; + if (s->state == kFound) { + s->value->assign(v.data(), v.size()); + } + } + } +} + +static bool NewestFirst(FileMetaData* a, FileMetaData* b) { + return a->number > b->number; +} + +void Version::ForEachOverlapping(Slice user_key, Slice internal_key, + void* arg, + bool (*func)(void*, int, FileMetaData*)) { + // TODO(sanjay): Change Version::Get() to use this function. + const Comparator* ucmp = vset_->icmp_.user_comparator(); + + // Search level-0 in order from newest to oldest. + std::vector tmp; + tmp.reserve(files_[0].size()); + for (uint32_t i = 0; i < files_[0].size(); i++) { + FileMetaData* f = files_[0][i]; + if (ucmp->Compare(user_key, f->smallest.user_key()) >= 0 && + ucmp->Compare(user_key, f->largest.user_key()) <= 0) { + tmp.push_back(f); + } + } + if (!tmp.empty()) { + std::sort(tmp.begin(), tmp.end(), NewestFirst); + for (uint32_t i = 0; i < tmp.size(); i++) { + if (!(*func)(arg, 0, tmp[i])) { + return; + } + } + } + + // Search other levels. + for (int level = 1; level < config::kNumLevels; level++) { + size_t num_files = files_[level].size(); + if (num_files == 0) continue; + + // Binary search to find earliest index whose largest key >= internal_key. + uint32_t index = FindFile(vset_->icmp_, files_[level], internal_key); + if (index < num_files) { + FileMetaData* f = files_[level][index]; + if (ucmp->Compare(user_key, f->smallest.user_key()) < 0) { + // All of "f" is past any data for user_key + } else { + if (!(*func)(arg, level, f)) { + return; + } + } + } + } +} + +Status Version::Get(const ReadOptions& options, + const LookupKey& k, + std::string* value, + GetStats* stats) { + Slice ikey = k.internal_key(); + Slice user_key = k.user_key(); + const Comparator* ucmp = vset_->icmp_.user_comparator(); + Status s; + + stats->seek_file = NULL; + stats->seek_file_level = -1; + FileMetaData* last_file_read = NULL; + int last_file_read_level = -1; + + // We can search level-by-level since entries never hop across + // levels. Therefore we are guaranteed that if we find data + // in an smaller level, later levels are irrelevant. + std::vector tmp; + FileMetaData* tmp2; + for (int level = 0; level < config::kNumLevels; level++) { + size_t num_files = files_[level].size(); + if (num_files == 0) continue; + + // Get the list of files to search in this level + FileMetaData* const* files = &files_[level][0]; + if (level == 0) { + // Level-0 files may overlap each other. Find all files that + // overlap user_key and process them in order from newest to oldest. + tmp.reserve(num_files); + for (uint32_t i = 0; i < num_files; i++) { + FileMetaData* f = files[i]; + if (ucmp->Compare(user_key, f->smallest.user_key()) >= 0 && + ucmp->Compare(user_key, f->largest.user_key()) <= 0) { + tmp.push_back(f); + } + } + if (tmp.empty()) continue; + + std::sort(tmp.begin(), tmp.end(), NewestFirst); + files = &tmp[0]; + num_files = tmp.size(); + } else { + // Binary search to find earliest index whose largest key >= ikey. + uint32_t index = FindFile(vset_->icmp_, files_[level], ikey); + if (index >= num_files) { + files = NULL; + num_files = 0; + } else { + tmp2 = files[index]; + if (ucmp->Compare(user_key, tmp2->smallest.user_key()) < 0) { + // All of "tmp2" is past any data for user_key + files = NULL; + num_files = 0; + } else { + files = &tmp2; + num_files = 1; + } + } + } + + for (uint32_t i = 0; i < num_files; ++i) { + if (last_file_read != NULL && stats->seek_file == NULL) { + // We have had more than one seek for this read. Charge the 1st file. + stats->seek_file = last_file_read; + stats->seek_file_level = last_file_read_level; + } + + FileMetaData* f = files[i]; + last_file_read = f; + last_file_read_level = level; + + Saver saver; + saver.state = kNotFound; + saver.ucmp = ucmp; + saver.user_key = user_key; + saver.value = value; + s = vset_->table_cache_->Get(options, f->number, f->file_size, + ikey, &saver, SaveValue); + if (!s.ok()) { + return s; + } + switch (saver.state) { + case kNotFound: + break; // Keep searching in other files + case kFound: + return s; + case kDeleted: + s = Status::NotFound(Slice()); // Use empty error message for speed + return s; + case kCorrupt: + s = Status::Corruption("corrupted key for ", user_key); + return s; + } + } + } + + return Status::NotFound(Slice()); // Use an empty error message for speed +} + +bool Version::UpdateStats(const GetStats& stats) { + FileMetaData* f = stats.seek_file; + if (f != NULL) { + f->allowed_seeks--; + if (f->allowed_seeks <= 0 && file_to_compact_ == NULL) { + file_to_compact_ = f; + file_to_compact_level_ = stats.seek_file_level; + return true; + } + } + return false; +} + +bool Version::RecordReadSample(Slice internal_key) { + ParsedInternalKey ikey; + if (!ParseInternalKey(internal_key, &ikey)) { + return false; + } + + struct State { + GetStats stats; // Holds first matching file + int matches; + + static bool Match(void* arg, int level, FileMetaData* f) { + State* state = reinterpret_cast(arg); + state->matches++; + if (state->matches == 1) { + // Remember first match. + state->stats.seek_file = f; + state->stats.seek_file_level = level; + } + // We can stop iterating once we have a second match. + return state->matches < 2; + } + }; + + State state; + state.matches = 0; + ForEachOverlapping(ikey.user_key, internal_key, &state, &State::Match); + + // Must have at least two matches since we want to merge across + // files. But what if we have a single file that contains many + // overwrites and deletions? Should we have another mechanism for + // finding such files? + if (state.matches >= 2) { + // 1MB cost is about 1 seek (see comment in Builder::Apply). + return UpdateStats(state.stats); + } + return false; +} + +void Version::Ref() { + ++refs_; +} + +void Version::Unref() { + assert(this != &vset_->dummy_versions_); + assert(refs_ >= 1); + --refs_; + if (refs_ == 0) { + delete this; + } +} + +bool Version::OverlapInLevel(int level, + const Slice* smallest_user_key, + const Slice* largest_user_key) { + return SomeFileOverlapsRange(vset_->icmp_, (level > 0), files_[level], + smallest_user_key, largest_user_key); +} + +int Version::PickLevelForMemTableOutput( + const Slice& smallest_user_key, + const Slice& largest_user_key) { + int level = 0; + if (!OverlapInLevel(0, &smallest_user_key, &largest_user_key)) { + // Push to next level if there is no overlap in next level, + // and the #bytes overlapping in the level after that are limited. + InternalKey start(smallest_user_key, kMaxSequenceNumber, kValueTypeForSeek); + InternalKey limit(largest_user_key, 0, static_cast(0)); + std::vector overlaps; + while (level < config::kMaxMemCompactLevel) { + if (OverlapInLevel(level + 1, &smallest_user_key, &largest_user_key)) { + break; + } + if (level + 2 < config::kNumLevels) { + // Check that file does not overlap too many grandparent bytes. + GetOverlappingInputs(level + 2, &start, &limit, &overlaps); + const int64_t sum = TotalFileSize(overlaps); + if (sum > kMaxGrandParentOverlapBytes) { + break; + } + } + level++; + } + } + return level; +} + +// Store in "*inputs" all files in "level" that overlap [begin,end] +void Version::GetOverlappingInputs( + int level, + const InternalKey* begin, + const InternalKey* end, + std::vector* inputs) { + assert(level >= 0); + assert(level < config::kNumLevels); + inputs->clear(); + Slice user_begin, user_end; + if (begin != NULL) { + user_begin = begin->user_key(); + } + if (end != NULL) { + user_end = end->user_key(); + } + const Comparator* user_cmp = vset_->icmp_.user_comparator(); + for (size_t i = 0; i < files_[level].size(); ) { + FileMetaData* f = files_[level][i++]; + const Slice file_start = f->smallest.user_key(); + const Slice file_limit = f->largest.user_key(); + if (begin != NULL && user_cmp->Compare(file_limit, user_begin) < 0) { + // "f" is completely before specified range; skip it + } else if (end != NULL && user_cmp->Compare(file_start, user_end) > 0) { + // "f" is completely after specified range; skip it + } else { + inputs->push_back(f); + if (level == 0) { + // Level-0 files may overlap each other. So check if the newly + // added file has expanded the range. If so, restart search. + if (begin != NULL && user_cmp->Compare(file_start, user_begin) < 0) { + user_begin = file_start; + inputs->clear(); + i = 0; + } else if (end != NULL && user_cmp->Compare(file_limit, user_end) > 0) { + user_end = file_limit; + inputs->clear(); + i = 0; + } + } + } + } +} + +std::string Version::DebugString() const { + std::string r; + for (int level = 0; level < config::kNumLevels; level++) { + // E.g., + // --- level 1 --- + // 17:123['a' .. 'd'] + // 20:43['e' .. 'g'] + r.append("--- level "); + AppendNumberTo(&r, level); + r.append(" ---\n"); + const std::vector& files = files_[level]; + for (size_t i = 0; i < files.size(); i++) { + r.push_back(' '); + AppendNumberTo(&r, files[i]->number); + r.push_back(':'); + AppendNumberTo(&r, files[i]->file_size); + r.append("["); + r.append(files[i]->smallest.DebugString()); + r.append(" .. "); + r.append(files[i]->largest.DebugString()); + r.append("]\n"); + } + } + return r; +} + +// A helper class so we can efficiently apply a whole sequence +// of edits to a particular state without creating intermediate +// Versions that contain full copies of the intermediate state. +class VersionSet::Builder { + private: + // Helper to sort by v->files_[file_number].smallest + struct BySmallestKey { + const InternalKeyComparator* internal_comparator; + + bool operator()(FileMetaData* f1, FileMetaData* f2) const { + int r = internal_comparator->Compare(f1->smallest, f2->smallest); + if (r != 0) { + return (r < 0); + } else { + // Break ties by file number + return (f1->number < f2->number); + } + } + }; + + typedef std::set FileSet; + struct LevelState { + std::set deleted_files; + FileSet* added_files; + }; + + VersionSet* vset_; + Version* base_; + LevelState levels_[config::kNumLevels]; + + public: + // Initialize a builder with the files from *base and other info from *vset + Builder(VersionSet* vset, Version* base) + : vset_(vset), + base_(base) { + base_->Ref(); + BySmallestKey cmp; + cmp.internal_comparator = &vset_->icmp_; + for (int level = 0; level < config::kNumLevels; level++) { + levels_[level].added_files = new FileSet(cmp); + } + } + + ~Builder() { + for (int level = 0; level < config::kNumLevels; level++) { + const FileSet* added = levels_[level].added_files; + std::vector to_unref; + to_unref.reserve(added->size()); + for (FileSet::const_iterator it = added->begin(); + it != added->end(); ++it) { + to_unref.push_back(*it); + } + delete added; + for (uint32_t i = 0; i < to_unref.size(); i++) { + FileMetaData* f = to_unref[i]; + f->refs--; + if (f->refs <= 0) { + delete f; + } + } + } + base_->Unref(); + } + + // Apply all of the edits in *edit to the current state. + void Apply(VersionEdit* edit) { + // Update compaction pointers + for (size_t i = 0; i < edit->compact_pointers_.size(); i++) { + const int level = edit->compact_pointers_[i].first; + vset_->compact_pointer_[level] = + edit->compact_pointers_[i].second.Encode().ToString(); + } + + // Delete files + const VersionEdit::DeletedFileSet& del = edit->deleted_files_; + for (VersionEdit::DeletedFileSet::const_iterator iter = del.begin(); + iter != del.end(); + ++iter) { + const int level = iter->first; + const uint64_t number = iter->second; + levels_[level].deleted_files.insert(number); + } + + // Add new files + for (size_t i = 0; i < edit->new_files_.size(); i++) { + const int level = edit->new_files_[i].first; + FileMetaData* f = new FileMetaData(edit->new_files_[i].second); + f->refs = 1; + + // We arrange to automatically compact this file after + // a certain number of seeks. Let's assume: + // (1) One seek costs 10ms + // (2) Writing or reading 1MB costs 10ms (100MB/s) + // (3) A compaction of 1MB does 25MB of IO: + // 1MB read from this level + // 10-12MB read from next level (boundaries may be misaligned) + // 10-12MB written to next level + // This implies that 25 seeks cost the same as the compaction + // of 1MB of data. I.e., one seek costs approximately the + // same as the compaction of 40KB of data. We are a little + // conservative and allow approximately one seek for every 16KB + // of data before triggering a compaction. + f->allowed_seeks = (f->file_size / 16384); + if (f->allowed_seeks < 100) f->allowed_seeks = 100; + + levels_[level].deleted_files.erase(f->number); + levels_[level].added_files->insert(f); + } + } + + // Save the current state in *v. + void SaveTo(Version* v) { + BySmallestKey cmp; + cmp.internal_comparator = &vset_->icmp_; + for (int level = 0; level < config::kNumLevels; level++) { + // Merge the set of added files with the set of pre-existing files. + // Drop any deleted files. Store the result in *v. + const std::vector& base_files = base_->files_[level]; + std::vector::const_iterator base_iter = base_files.begin(); + std::vector::const_iterator base_end = base_files.end(); + const FileSet* added = levels_[level].added_files; + v->files_[level].reserve(base_files.size() + added->size()); + for (FileSet::const_iterator added_iter = added->begin(); + added_iter != added->end(); + ++added_iter) { + // Add all smaller files listed in base_ + for (std::vector::const_iterator bpos + = std::upper_bound(base_iter, base_end, *added_iter, cmp); + base_iter != bpos; + ++base_iter) { + MaybeAddFile(v, level, *base_iter); + } + + MaybeAddFile(v, level, *added_iter); + } + + // Add remaining base files + for (; base_iter != base_end; ++base_iter) { + MaybeAddFile(v, level, *base_iter); + } + +#ifndef NDEBUG + // Make sure there is no overlap in levels > 0 + if (level > 0) { + for (uint32_t i = 1; i < v->files_[level].size(); i++) { + const InternalKey& prev_end = v->files_[level][i-1]->largest; + const InternalKey& this_begin = v->files_[level][i]->smallest; + if (vset_->icmp_.Compare(prev_end, this_begin) >= 0) { + fprintf(stderr, "overlapping ranges in same level %s vs. %s\n", + prev_end.DebugString().c_str(), + this_begin.DebugString().c_str()); + abort(); + } + } + } +#endif + } + } + + void MaybeAddFile(Version* v, int level, FileMetaData* f) { + if (levels_[level].deleted_files.count(f->number) > 0) { + // File is deleted: do nothing + } else { + std::vector* files = &v->files_[level]; + if (level > 0 && !files->empty()) { + // Must not overlap + assert(vset_->icmp_.Compare((*files)[files->size()-1]->largest, + f->smallest) < 0); + } + f->refs++; + files->push_back(f); + } + } +}; + +VersionSet::VersionSet(const std::string& dbname, + const Options* options, + TableCache* table_cache, + const InternalKeyComparator* cmp) + : env_(options->env), + dbname_(dbname), + options_(options), + table_cache_(table_cache), + icmp_(*cmp), + next_file_number_(2), + manifest_file_number_(0), // Filled by Recover() + last_sequence_(0), + log_number_(0), + prev_log_number_(0), + descriptor_file_(NULL), + descriptor_log_(NULL), + dummy_versions_(this), + current_(NULL) { + AppendVersion(new Version(this)); +} + +VersionSet::~VersionSet() { + current_->Unref(); + assert(dummy_versions_.next_ == &dummy_versions_); // List must be empty + delete descriptor_log_; + delete descriptor_file_; +} + +void VersionSet::AppendVersion(Version* v) { + // Make "v" current + assert(v->refs_ == 0); + assert(v != current_); + if (current_ != NULL) { + current_->Unref(); + } + current_ = v; + v->Ref(); + + // Append to linked list + v->prev_ = dummy_versions_.prev_; + v->next_ = &dummy_versions_; + v->prev_->next_ = v; + v->next_->prev_ = v; +} + +Status VersionSet::LogAndApply(VersionEdit* edit, port::Mutex* mu) { + if (edit->has_log_number_) { + assert(edit->log_number_ >= log_number_); + assert(edit->log_number_ < next_file_number_); + } else { + edit->SetLogNumber(log_number_); + } + + if (!edit->has_prev_log_number_) { + edit->SetPrevLogNumber(prev_log_number_); + } + + edit->SetNextFile(next_file_number_); + edit->SetLastSequence(last_sequence_); + + Version* v = new Version(this); + { + Builder builder(this, current_); + builder.Apply(edit); + builder.SaveTo(v); + } + Finalize(v); + + // Initialize new descriptor log file if necessary by creating + // a temporary file that contains a snapshot of the current version. + std::string new_manifest_file; + Status s; + if (descriptor_log_ == NULL) { + // No reason to unlock *mu here since we only hit this path in the + // first call to LogAndApply (when opening the database). + assert(descriptor_file_ == NULL); + new_manifest_file = DescriptorFileName(dbname_, manifest_file_number_); + edit->SetNextFile(next_file_number_); + s = env_->NewWritableFile(new_manifest_file, &descriptor_file_); + if (s.ok()) { + descriptor_log_ = new log::Writer(descriptor_file_); + s = WriteSnapshot(descriptor_log_); + } + } + + // Unlock during expensive MANIFEST log write + { + mu->Unlock(); + + // Write new record to MANIFEST log + if (s.ok()) { + std::string record; + edit->EncodeTo(&record); + s = descriptor_log_->AddRecord(record); + if (s.ok()) { + s = descriptor_file_->Sync(); + } + if (!s.ok()) { + Log(options_->info_log, "MANIFEST write: %s\n", s.ToString().c_str()); + } + } + + // If we just created a new descriptor file, install it by writing a + // new CURRENT file that points to it. + if (s.ok() && !new_manifest_file.empty()) { + s = SetCurrentFile(env_, dbname_, manifest_file_number_); + } + + mu->Lock(); + } + + // Install the new version + if (s.ok()) { + AppendVersion(v); + log_number_ = edit->log_number_; + prev_log_number_ = edit->prev_log_number_; + } else { + delete v; + if (!new_manifest_file.empty()) { + delete descriptor_log_; + delete descriptor_file_; + descriptor_log_ = NULL; + descriptor_file_ = NULL; + env_->DeleteFile(new_manifest_file); + } + } + + return s; +} + +Status VersionSet::Recover() { + struct LogReporter : public log::Reader::Reporter { + Status* status; + virtual void Corruption(size_t bytes, const Status& s) { + if (this->status->ok()) *this->status = s; + } + }; + + // Read "CURRENT" file, which contains a pointer to the current manifest file + std::string current; + Status s = ReadFileToString(env_, CurrentFileName(dbname_), ¤t); + if (!s.ok()) { + return s; + } + if (current.empty() || current[current.size()-1] != '\n') { + return Status::Corruption("CURRENT file does not end with newline"); + } + current.resize(current.size() - 1); + + std::string dscname = dbname_ + "/" + current; + SequentialFile* file; + s = env_->NewSequentialFile(dscname, &file); + if (!s.ok()) { + return s; + } + + bool have_log_number = false; + bool have_prev_log_number = false; + bool have_next_file = false; + bool have_last_sequence = false; + uint64_t next_file = 0; + uint64_t last_sequence = 0; + uint64_t log_number = 0; + uint64_t prev_log_number = 0; + Builder builder(this, current_); + + { + LogReporter reporter; + reporter.status = &s; + log::Reader reader(file, &reporter, true/*checksum*/, 0/*initial_offset*/); + Slice record; + std::string scratch; + while (reader.ReadRecord(&record, &scratch) && s.ok()) { + VersionEdit edit; + s = edit.DecodeFrom(record); + if (s.ok()) { + if (edit.has_comparator_ && + edit.comparator_ != icmp_.user_comparator()->Name()) { + s = Status::InvalidArgument( + edit.comparator_ + " does not match existing comparator ", + icmp_.user_comparator()->Name()); + } + } + + if (s.ok()) { + builder.Apply(&edit); + } + + if (edit.has_log_number_) { + log_number = edit.log_number_; + have_log_number = true; + } + + if (edit.has_prev_log_number_) { + prev_log_number = edit.prev_log_number_; + have_prev_log_number = true; + } + + if (edit.has_next_file_number_) { + next_file = edit.next_file_number_; + have_next_file = true; + } + + if (edit.has_last_sequence_) { + last_sequence = edit.last_sequence_; + have_last_sequence = true; + } + } + } + delete file; + file = NULL; + + if (s.ok()) { + if (!have_next_file) { + s = Status::Corruption("no meta-nextfile entry in descriptor"); + } else if (!have_log_number) { + s = Status::Corruption("no meta-lognumber entry in descriptor"); + } else if (!have_last_sequence) { + s = Status::Corruption("no last-sequence-number entry in descriptor"); + } + + if (!have_prev_log_number) { + prev_log_number = 0; + } + + MarkFileNumberUsed(prev_log_number); + MarkFileNumberUsed(log_number); + } + + if (s.ok()) { + Version* v = new Version(this); + builder.SaveTo(v); + // Install recovered version + Finalize(v); + AppendVersion(v); + manifest_file_number_ = next_file; + next_file_number_ = next_file + 1; + last_sequence_ = last_sequence; + log_number_ = log_number; + prev_log_number_ = prev_log_number; + } + + return s; +} + +void VersionSet::MarkFileNumberUsed(uint64_t number) { + if (next_file_number_ <= number) { + next_file_number_ = number + 1; + } +} + +void VersionSet::Finalize(Version* v) { + // Precomputed best level for next compaction + int best_level = -1; + double best_score = -1; + + for (int level = 0; level < config::kNumLevels-1; level++) { + double score; + if (level == 0) { + // We treat level-0 specially by bounding the number of files + // instead of number of bytes for two reasons: + // + // (1) With larger write-buffer sizes, it is nice not to do too + // many level-0 compactions. + // + // (2) The files in level-0 are merged on every read and + // therefore we wish to avoid too many files when the individual + // file size is small (perhaps because of a small write-buffer + // setting, or very high compression ratios, or lots of + // overwrites/deletions). + score = v->files_[level].size() / + static_cast(config::kL0_CompactionTrigger); + } else { + // Compute the ratio of current size to size limit. + const uint64_t level_bytes = TotalFileSize(v->files_[level]); + score = static_cast(level_bytes) / MaxBytesForLevel(level); + } + + if (score > best_score) { + best_level = level; + best_score = score; + } + } + + v->compaction_level_ = best_level; + v->compaction_score_ = best_score; +} + +Status VersionSet::WriteSnapshot(log::Writer* log) { + // TODO: Break up into multiple records to reduce memory usage on recovery? + + // Save metadata + VersionEdit edit; + edit.SetComparatorName(icmp_.user_comparator()->Name()); + + // Save compaction pointers + for (int level = 0; level < config::kNumLevels; level++) { + if (!compact_pointer_[level].empty()) { + InternalKey key; + key.DecodeFrom(compact_pointer_[level]); + edit.SetCompactPointer(level, key); + } + } + + // Save files + for (int level = 0; level < config::kNumLevels; level++) { + const std::vector& files = current_->files_[level]; + for (size_t i = 0; i < files.size(); i++) { + const FileMetaData* f = files[i]; + edit.AddFile(level, f->number, f->file_size, f->smallest, f->largest); + } + } + + std::string record; + edit.EncodeTo(&record); + return log->AddRecord(record); +} + +int VersionSet::NumLevelFiles(int level) const { + assert(level >= 0); + assert(level < config::kNumLevels); + return current_->files_[level].size(); +} + +const char* VersionSet::LevelSummary(LevelSummaryStorage* scratch) const { + // Update code if kNumLevels changes + assert(config::kNumLevels == 7); + snprintf(scratch->buffer, sizeof(scratch->buffer), + "files[ %d %d %d %d %d %d %d ]", + int(current_->files_[0].size()), + int(current_->files_[1].size()), + int(current_->files_[2].size()), + int(current_->files_[3].size()), + int(current_->files_[4].size()), + int(current_->files_[5].size()), + int(current_->files_[6].size())); + return scratch->buffer; +} + +uint64_t VersionSet::ApproximateOffsetOf(Version* v, const InternalKey& ikey) { + uint64_t result = 0; + for (int level = 0; level < config::kNumLevels; level++) { + const std::vector& files = v->files_[level]; + for (size_t i = 0; i < files.size(); i++) { + if (icmp_.Compare(files[i]->largest, ikey) <= 0) { + // Entire file is before "ikey", so just add the file size + result += files[i]->file_size; + } else if (icmp_.Compare(files[i]->smallest, ikey) > 0) { + // Entire file is after "ikey", so ignore + if (level > 0) { + // Files other than level 0 are sorted by meta->smallest, so + // no further files in this level will contain data for + // "ikey". + break; + } + } else { + // "ikey" falls in the range for this table. Add the + // approximate offset of "ikey" within the table. + Table* tableptr; + Iterator* iter = table_cache_->NewIterator( + ReadOptions(), files[i]->number, files[i]->file_size, &tableptr); + if (tableptr != NULL) { + result += tableptr->ApproximateOffsetOf(ikey.Encode()); + } + delete iter; + } + } + } + return result; +} + +void VersionSet::AddLiveFiles(std::set* live) { + for (Version* v = dummy_versions_.next_; + v != &dummy_versions_; + v = v->next_) { + for (int level = 0; level < config::kNumLevels; level++) { + const std::vector& files = v->files_[level]; + for (size_t i = 0; i < files.size(); i++) { + live->insert(files[i]->number); + } + } + } +} + +int64_t VersionSet::NumLevelBytes(int level) const { + assert(level >= 0); + assert(level < config::kNumLevels); + return TotalFileSize(current_->files_[level]); +} + +int64_t VersionSet::MaxNextLevelOverlappingBytes() { + int64_t result = 0; + std::vector overlaps; + for (int level = 1; level < config::kNumLevels - 1; level++) { + for (size_t i = 0; i < current_->files_[level].size(); i++) { + const FileMetaData* f = current_->files_[level][i]; + current_->GetOverlappingInputs(level+1, &f->smallest, &f->largest, + &overlaps); + const int64_t sum = TotalFileSize(overlaps); + if (sum > result) { + result = sum; + } + } + } + return result; +} + +// Stores the minimal range that covers all entries in inputs in +// *smallest, *largest. +// REQUIRES: inputs is not empty +void VersionSet::GetRange(const std::vector& inputs, + InternalKey* smallest, + InternalKey* largest) { + assert(!inputs.empty()); + smallest->Clear(); + largest->Clear(); + for (size_t i = 0; i < inputs.size(); i++) { + FileMetaData* f = inputs[i]; + if (i == 0) { + *smallest = f->smallest; + *largest = f->largest; + } else { + if (icmp_.Compare(f->smallest, *smallest) < 0) { + *smallest = f->smallest; + } + if (icmp_.Compare(f->largest, *largest) > 0) { + *largest = f->largest; + } + } + } +} + +// Stores the minimal range that covers all entries in inputs1 and inputs2 +// in *smallest, *largest. +// REQUIRES: inputs is not empty +void VersionSet::GetRange2(const std::vector& inputs1, + const std::vector& inputs2, + InternalKey* smallest, + InternalKey* largest) { + std::vector all = inputs1; + all.insert(all.end(), inputs2.begin(), inputs2.end()); + GetRange(all, smallest, largest); +} + +Iterator* VersionSet::MakeInputIterator(Compaction* c) { + ReadOptions options; + options.verify_checksums = options_->paranoid_checks; + options.fill_cache = false; + + // Level-0 files have to be merged together. For other levels, + // we will make a concatenating iterator per level. + // TODO(opt): use concatenating iterator for level-0 if there is no overlap + const int space = (c->level() == 0 ? c->inputs_[0].size() + 1 : 2); + Iterator** list = new Iterator*[space]; + int num = 0; + for (int which = 0; which < 2; which++) { + if (!c->inputs_[which].empty()) { + if (c->level() + which == 0) { + const std::vector& files = c->inputs_[which]; + for (size_t i = 0; i < files.size(); i++) { + list[num++] = table_cache_->NewIterator( + options, files[i]->number, files[i]->file_size); + } + } else { + // Create concatenating iterator for the files from this level + list[num++] = NewTwoLevelIterator( + new Version::LevelFileNumIterator(icmp_, &c->inputs_[which]), + &GetFileIterator, table_cache_, options); + } + } + } + assert(num <= space); + Iterator* result = NewMergingIterator(&icmp_, list, num); + delete[] list; + return result; +} + +Compaction* VersionSet::PickCompaction() { + Compaction* c; + int level; + + // We prefer compactions triggered by too much data in a level over + // the compactions triggered by seeks. + const bool size_compaction = (current_->compaction_score_ >= 1); + const bool seek_compaction = (current_->file_to_compact_ != NULL); + if (size_compaction) { + level = current_->compaction_level_; + assert(level >= 0); + assert(level+1 < config::kNumLevels); + c = new Compaction(level); + + // Pick the first file that comes after compact_pointer_[level] + for (size_t i = 0; i < current_->files_[level].size(); i++) { + FileMetaData* f = current_->files_[level][i]; + if (compact_pointer_[level].empty() || + icmp_.Compare(f->largest.Encode(), compact_pointer_[level]) > 0) { + c->inputs_[0].push_back(f); + break; + } + } + if (c->inputs_[0].empty()) { + // Wrap-around to the beginning of the key space + c->inputs_[0].push_back(current_->files_[level][0]); + } + } else if (seek_compaction) { + level = current_->file_to_compact_level_; + c = new Compaction(level); + c->inputs_[0].push_back(current_->file_to_compact_); + } else { + return NULL; + } + + c->input_version_ = current_; + c->input_version_->Ref(); + + // Files in level 0 may overlap each other, so pick up all overlapping ones + if (level == 0) { + InternalKey smallest, largest; + GetRange(c->inputs_[0], &smallest, &largest); + // Note that the next call will discard the file we placed in + // c->inputs_[0] earlier and replace it with an overlapping set + // which will include the picked file. + current_->GetOverlappingInputs(0, &smallest, &largest, &c->inputs_[0]); + assert(!c->inputs_[0].empty()); + } + + SetupOtherInputs(c); + + return c; +} + +void VersionSet::SetupOtherInputs(Compaction* c) { + const int level = c->level(); + InternalKey smallest, largest; + GetRange(c->inputs_[0], &smallest, &largest); + + current_->GetOverlappingInputs(level+1, &smallest, &largest, &c->inputs_[1]); + + // Get entire range covered by compaction + InternalKey all_start, all_limit; + GetRange2(c->inputs_[0], c->inputs_[1], &all_start, &all_limit); + + // See if we can grow the number of inputs in "level" without + // changing the number of "level+1" files we pick up. + if (!c->inputs_[1].empty()) { + std::vector expanded0; + current_->GetOverlappingInputs(level, &all_start, &all_limit, &expanded0); + const int64_t inputs0_size = TotalFileSize(c->inputs_[0]); + const int64_t inputs1_size = TotalFileSize(c->inputs_[1]); + const int64_t expanded0_size = TotalFileSize(expanded0); + if (expanded0.size() > c->inputs_[0].size() && + inputs1_size + expanded0_size < kExpandedCompactionByteSizeLimit) { + InternalKey new_start, new_limit; + GetRange(expanded0, &new_start, &new_limit); + std::vector expanded1; + current_->GetOverlappingInputs(level+1, &new_start, &new_limit, + &expanded1); + if (expanded1.size() == c->inputs_[1].size()) { + Log(options_->info_log, + "Expanding@%d %d+%d (%ld+%ld bytes) to %d+%d (%ld+%ld bytes)\n", + level, + int(c->inputs_[0].size()), + int(c->inputs_[1].size()), + long(inputs0_size), long(inputs1_size), + int(expanded0.size()), + int(expanded1.size()), + long(expanded0_size), long(inputs1_size)); + smallest = new_start; + largest = new_limit; + c->inputs_[0] = expanded0; + c->inputs_[1] = expanded1; + GetRange2(c->inputs_[0], c->inputs_[1], &all_start, &all_limit); + } + } + } + + // Compute the set of grandparent files that overlap this compaction + // (parent == level+1; grandparent == level+2) + if (level + 2 < config::kNumLevels) { + current_->GetOverlappingInputs(level + 2, &all_start, &all_limit, + &c->grandparents_); + } + + if (false) { + Log(options_->info_log, "Compacting %d '%s' .. '%s'", + level, + smallest.DebugString().c_str(), + largest.DebugString().c_str()); + } + + // Update the place where we will do the next compaction for this level. + // We update this immediately instead of waiting for the VersionEdit + // to be applied so that if the compaction fails, we will try a different + // key range next time. + compact_pointer_[level] = largest.Encode().ToString(); + c->edit_.SetCompactPointer(level, largest); +} + +Compaction* VersionSet::CompactRange( + int level, + const InternalKey* begin, + const InternalKey* end) { + std::vector inputs; + current_->GetOverlappingInputs(level, begin, end, &inputs); + if (inputs.empty()) { + return NULL; + } + + // Avoid compacting too much in one shot in case the range is large. + // But we cannot do this for level-0 since level-0 files can overlap + // and we must not pick one file and drop another older file if the + // two files overlap. + if (level > 0) { + const uint64_t limit = MaxFileSizeForLevel(level); + uint64_t total = 0; + for (size_t i = 0; i < inputs.size(); i++) { + uint64_t s = inputs[i]->file_size; + total += s; + if (total >= limit) { + inputs.resize(i + 1); + break; + } + } + } + + Compaction* c = new Compaction(level); + c->input_version_ = current_; + c->input_version_->Ref(); + c->inputs_[0] = inputs; + SetupOtherInputs(c); + return c; +} + +Compaction::Compaction(int level) + : level_(level), + max_output_file_size_(MaxFileSizeForLevel(level)), + input_version_(NULL), + grandparent_index_(0), + seen_key_(false), + overlapped_bytes_(0) { + for (int i = 0; i < config::kNumLevels; i++) { + level_ptrs_[i] = 0; + } +} + +Compaction::~Compaction() { + if (input_version_ != NULL) { + input_version_->Unref(); + } +} + +bool Compaction::IsTrivialMove() const { + // Avoid a move if there is lots of overlapping grandparent data. + // Otherwise, the move could create a parent file that will require + // a very expensive merge later on. + return (num_input_files(0) == 1 && + num_input_files(1) == 0 && + TotalFileSize(grandparents_) <= kMaxGrandParentOverlapBytes); +} + +void Compaction::AddInputDeletions(VersionEdit* edit) { + for (int which = 0; which < 2; which++) { + for (size_t i = 0; i < inputs_[which].size(); i++) { + edit->DeleteFile(level_ + which, inputs_[which][i]->number); + } + } +} + +bool Compaction::IsBaseLevelForKey(const Slice& user_key) { + // Maybe use binary search to find right entry instead of linear search? + const Comparator* user_cmp = input_version_->vset_->icmp_.user_comparator(); + for (int lvl = level_ + 2; lvl < config::kNumLevels; lvl++) { + const std::vector& files = input_version_->files_[lvl]; + for (; level_ptrs_[lvl] < files.size(); ) { + FileMetaData* f = files[level_ptrs_[lvl]]; + if (user_cmp->Compare(user_key, f->largest.user_key()) <= 0) { + // We've advanced far enough + if (user_cmp->Compare(user_key, f->smallest.user_key()) >= 0) { + // Key falls in this file's range, so definitely not base level + return false; + } + break; + } + level_ptrs_[lvl]++; + } + } + return true; +} + +bool Compaction::ShouldStopBefore(const Slice& internal_key) { + // Scan to find earliest grandparent file that contains key. + const InternalKeyComparator* icmp = &input_version_->vset_->icmp_; + while (grandparent_index_ < grandparents_.size() && + icmp->Compare(internal_key, + grandparents_[grandparent_index_]->largest.Encode()) > 0) { + if (seen_key_) { + overlapped_bytes_ += grandparents_[grandparent_index_]->file_size; + } + grandparent_index_++; + } + seen_key_ = true; + + if (overlapped_bytes_ > kMaxGrandParentOverlapBytes) { + // Too much overlap for current output; start new output + overlapped_bytes_ = 0; + return true; + } else { + return false; + } +} + +void Compaction::ReleaseInputs() { + if (input_version_ != NULL) { + input_version_->Unref(); + input_version_ = NULL; + } +} + +} // namespace leveldb diff --git a/src/leveldb/db/version_set.h b/src/leveldb/db/version_set.h new file mode 100644 index 00000000..8dc14b8e --- /dev/null +++ b/src/leveldb/db/version_set.h @@ -0,0 +1,396 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// The representation of a DBImpl consists of a set of Versions. The +// newest version is called "current". Older versions may be kept +// around to provide a consistent view to live iterators. +// +// Each Version keeps track of a set of Table files per level. The +// entire set of versions is maintained in a VersionSet. +// +// Version,VersionSet are thread-compatible, but require external +// synchronization on all accesses. + +#ifndef STORAGE_LEVELDB_DB_VERSION_SET_H_ +#define STORAGE_LEVELDB_DB_VERSION_SET_H_ + +#include +#include +#include +#include "db/dbformat.h" +#include "db/version_edit.h" +#include "port/port.h" +#include "port/thread_annotations.h" + +namespace leveldb { + +namespace log { class Writer; } + +class Compaction; +class Iterator; +class MemTable; +class TableBuilder; +class TableCache; +class Version; +class VersionSet; +class WritableFile; + +// Return the smallest index i such that files[i]->largest >= key. +// Return files.size() if there is no such file. +// REQUIRES: "files" contains a sorted list of non-overlapping files. +extern int FindFile(const InternalKeyComparator& icmp, + const std::vector& files, + const Slice& key); + +// Returns true iff some file in "files" overlaps the user key range +// [*smallest,*largest]. +// smallest==NULL represents a key smaller than all keys in the DB. +// largest==NULL represents a key largest than all keys in the DB. +// REQUIRES: If disjoint_sorted_files, files[] contains disjoint ranges +// in sorted order. +extern bool SomeFileOverlapsRange( + const InternalKeyComparator& icmp, + bool disjoint_sorted_files, + const std::vector& files, + const Slice* smallest_user_key, + const Slice* largest_user_key); + +class Version { + public: + // Append to *iters a sequence of iterators that will + // yield the contents of this Version when merged together. + // REQUIRES: This version has been saved (see VersionSet::SaveTo) + void AddIterators(const ReadOptions&, std::vector* iters); + + // Lookup the value for key. If found, store it in *val and + // return OK. Else return a non-OK status. Fills *stats. + // REQUIRES: lock is not held + struct GetStats { + FileMetaData* seek_file; + int seek_file_level; + }; + Status Get(const ReadOptions&, const LookupKey& key, std::string* val, + GetStats* stats); + + // Adds "stats" into the current state. Returns true if a new + // compaction may need to be triggered, false otherwise. + // REQUIRES: lock is held + bool UpdateStats(const GetStats& stats); + + // Record a sample of bytes read at the specified internal key. + // Samples are taken approximately once every config::kReadBytesPeriod + // bytes. Returns true if a new compaction may need to be triggered. + // REQUIRES: lock is held + bool RecordReadSample(Slice key); + + // Reference count management (so Versions do not disappear out from + // under live iterators) + void Ref(); + void Unref(); + + void GetOverlappingInputs( + int level, + const InternalKey* begin, // NULL means before all keys + const InternalKey* end, // NULL means after all keys + std::vector* inputs); + + // Returns true iff some file in the specified level overlaps + // some part of [*smallest_user_key,*largest_user_key]. + // smallest_user_key==NULL represents a key smaller than all keys in the DB. + // largest_user_key==NULL represents a key largest than all keys in the DB. + bool OverlapInLevel(int level, + const Slice* smallest_user_key, + const Slice* largest_user_key); + + // Return the level at which we should place a new memtable compaction + // result that covers the range [smallest_user_key,largest_user_key]. + int PickLevelForMemTableOutput(const Slice& smallest_user_key, + const Slice& largest_user_key); + + int NumFiles(int level) const { return files_[level].size(); } + + // Return a human readable string that describes this version's contents. + std::string DebugString() const; + + private: + friend class Compaction; + friend class VersionSet; + + class LevelFileNumIterator; + Iterator* NewConcatenatingIterator(const ReadOptions&, int level) const; + + // Call func(arg, level, f) for every file that overlaps user_key in + // order from newest to oldest. If an invocation of func returns + // false, makes no more calls. + // + // REQUIRES: user portion of internal_key == user_key. + void ForEachOverlapping(Slice user_key, Slice internal_key, + void* arg, + bool (*func)(void*, int, FileMetaData*)); + + VersionSet* vset_; // VersionSet to which this Version belongs + Version* next_; // Next version in linked list + Version* prev_; // Previous version in linked list + int refs_; // Number of live refs to this version + + // List of files per level + std::vector files_[config::kNumLevels]; + + // Next file to compact based on seek stats. + FileMetaData* file_to_compact_; + int file_to_compact_level_; + + // Level that should be compacted next and its compaction score. + // Score < 1 means compaction is not strictly needed. These fields + // are initialized by Finalize(). + double compaction_score_; + int compaction_level_; + + explicit Version(VersionSet* vset) + : vset_(vset), next_(this), prev_(this), refs_(0), + file_to_compact_(NULL), + file_to_compact_level_(-1), + compaction_score_(-1), + compaction_level_(-1) { + } + + ~Version(); + + // No copying allowed + Version(const Version&); + void operator=(const Version&); +}; + +class VersionSet { + public: + VersionSet(const std::string& dbname, + const Options* options, + TableCache* table_cache, + const InternalKeyComparator*); + ~VersionSet(); + + // Apply *edit to the current version to form a new descriptor that + // is both saved to persistent state and installed as the new + // current version. Will release *mu while actually writing to the file. + // REQUIRES: *mu is held on entry. + // REQUIRES: no other thread concurrently calls LogAndApply() + Status LogAndApply(VersionEdit* edit, port::Mutex* mu) + EXCLUSIVE_LOCKS_REQUIRED(mu); + + // Recover the last saved descriptor from persistent storage. + Status Recover(); + + // Return the current version. + Version* current() const { return current_; } + + // Return the current manifest file number + uint64_t ManifestFileNumber() const { return manifest_file_number_; } + + // Allocate and return a new file number + uint64_t NewFileNumber() { return next_file_number_++; } + + // Arrange to reuse "file_number" unless a newer file number has + // already been allocated. + // REQUIRES: "file_number" was returned by a call to NewFileNumber(). + void ReuseFileNumber(uint64_t file_number) { + if (next_file_number_ == file_number + 1) { + next_file_number_ = file_number; + } + } + + // Return the number of Table files at the specified level. + int NumLevelFiles(int level) const; + + // Return the combined file size of all files at the specified level. + int64_t NumLevelBytes(int level) const; + + // Return the last sequence number. + uint64_t LastSequence() const { return last_sequence_; } + + // Set the last sequence number to s. + void SetLastSequence(uint64_t s) { + assert(s >= last_sequence_); + last_sequence_ = s; + } + + // Mark the specified file number as used. + void MarkFileNumberUsed(uint64_t number); + + // Return the current log file number. + uint64_t LogNumber() const { return log_number_; } + + // Return the log file number for the log file that is currently + // being compacted, or zero if there is no such log file. + uint64_t PrevLogNumber() const { return prev_log_number_; } + + // Pick level and inputs for a new compaction. + // Returns NULL if there is no compaction to be done. + // Otherwise returns a pointer to a heap-allocated object that + // describes the compaction. Caller should delete the result. + Compaction* PickCompaction(); + + // Return a compaction object for compacting the range [begin,end] in + // the specified level. Returns NULL if there is nothing in that + // level that overlaps the specified range. Caller should delete + // the result. + Compaction* CompactRange( + int level, + const InternalKey* begin, + const InternalKey* end); + + // Return the maximum overlapping data (in bytes) at next level for any + // file at a level >= 1. + int64_t MaxNextLevelOverlappingBytes(); + + // Create an iterator that reads over the compaction inputs for "*c". + // The caller should delete the iterator when no longer needed. + Iterator* MakeInputIterator(Compaction* c); + + // Returns true iff some level needs a compaction. + bool NeedsCompaction() const { + Version* v = current_; + return (v->compaction_score_ >= 1) || (v->file_to_compact_ != NULL); + } + + // Add all files listed in any live version to *live. + // May also mutate some internal state. + void AddLiveFiles(std::set* live); + + // Return the approximate offset in the database of the data for + // "key" as of version "v". + uint64_t ApproximateOffsetOf(Version* v, const InternalKey& key); + + // Return a human-readable short (single-line) summary of the number + // of files per level. Uses *scratch as backing store. + struct LevelSummaryStorage { + char buffer[100]; + }; + const char* LevelSummary(LevelSummaryStorage* scratch) const; + + private: + class Builder; + + friend class Compaction; + friend class Version; + + void Finalize(Version* v); + + void GetRange(const std::vector& inputs, + InternalKey* smallest, + InternalKey* largest); + + void GetRange2(const std::vector& inputs1, + const std::vector& inputs2, + InternalKey* smallest, + InternalKey* largest); + + void SetupOtherInputs(Compaction* c); + + // Save current contents to *log + Status WriteSnapshot(log::Writer* log); + + void AppendVersion(Version* v); + + Env* const env_; + const std::string dbname_; + const Options* const options_; + TableCache* const table_cache_; + const InternalKeyComparator icmp_; + uint64_t next_file_number_; + uint64_t manifest_file_number_; + uint64_t last_sequence_; + uint64_t log_number_; + uint64_t prev_log_number_; // 0 or backing store for memtable being compacted + + // Opened lazily + WritableFile* descriptor_file_; + log::Writer* descriptor_log_; + Version dummy_versions_; // Head of circular doubly-linked list of versions. + Version* current_; // == dummy_versions_.prev_ + + // Per-level key at which the next compaction at that level should start. + // Either an empty string, or a valid InternalKey. + std::string compact_pointer_[config::kNumLevels]; + + // No copying allowed + VersionSet(const VersionSet&); + void operator=(const VersionSet&); +}; + +// A Compaction encapsulates information about a compaction. +class Compaction { + public: + ~Compaction(); + + // Return the level that is being compacted. Inputs from "level" + // and "level+1" will be merged to produce a set of "level+1" files. + int level() const { return level_; } + + // Return the object that holds the edits to the descriptor done + // by this compaction. + VersionEdit* edit() { return &edit_; } + + // "which" must be either 0 or 1 + int num_input_files(int which) const { return inputs_[which].size(); } + + // Return the ith input file at "level()+which" ("which" must be 0 or 1). + FileMetaData* input(int which, int i) const { return inputs_[which][i]; } + + // Maximum size of files to build during this compaction. + uint64_t MaxOutputFileSize() const { return max_output_file_size_; } + + // Is this a trivial compaction that can be implemented by just + // moving a single input file to the next level (no merging or splitting) + bool IsTrivialMove() const; + + // Add all inputs to this compaction as delete operations to *edit. + void AddInputDeletions(VersionEdit* edit); + + // Returns true if the information we have available guarantees that + // the compaction is producing data in "level+1" for which no data exists + // in levels greater than "level+1". + bool IsBaseLevelForKey(const Slice& user_key); + + // Returns true iff we should stop building the current output + // before processing "internal_key". + bool ShouldStopBefore(const Slice& internal_key); + + // Release the input version for the compaction, once the compaction + // is successful. + void ReleaseInputs(); + + private: + friend class Version; + friend class VersionSet; + + explicit Compaction(int level); + + int level_; + uint64_t max_output_file_size_; + Version* input_version_; + VersionEdit edit_; + + // Each compaction reads inputs from "level_" and "level_+1" + std::vector inputs_[2]; // The two sets of inputs + + // State used to check for number of of overlapping grandparent files + // (parent == level_ + 1, grandparent == level_ + 2) + std::vector grandparents_; + size_t grandparent_index_; // Index in grandparent_starts_ + bool seen_key_; // Some output key has been seen + int64_t overlapped_bytes_; // Bytes of overlap between current output + // and grandparent files + + // State for implementing IsBaseLevelForKey + + // level_ptrs_ holds indices into input_version_->levels_: our state + // is that we are positioned at one of the file ranges for each + // higher level than the ones involved in this compaction (i.e. for + // all L >= level_ + 2). + size_t level_ptrs_[config::kNumLevels]; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_VERSION_SET_H_ diff --git a/src/leveldb/db/version_set_test.cc b/src/leveldb/db/version_set_test.cc new file mode 100644 index 00000000..501e34d1 --- /dev/null +++ b/src/leveldb/db/version_set_test.cc @@ -0,0 +1,179 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/version_set.h" +#include "util/logging.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +class FindFileTest { + public: + std::vector files_; + bool disjoint_sorted_files_; + + FindFileTest() : disjoint_sorted_files_(true) { } + + ~FindFileTest() { + for (int i = 0; i < files_.size(); i++) { + delete files_[i]; + } + } + + void Add(const char* smallest, const char* largest, + SequenceNumber smallest_seq = 100, + SequenceNumber largest_seq = 100) { + FileMetaData* f = new FileMetaData; + f->number = files_.size() + 1; + f->smallest = InternalKey(smallest, smallest_seq, kTypeValue); + f->largest = InternalKey(largest, largest_seq, kTypeValue); + files_.push_back(f); + } + + int Find(const char* key) { + InternalKey target(key, 100, kTypeValue); + InternalKeyComparator cmp(BytewiseComparator()); + return FindFile(cmp, files_, target.Encode()); + } + + bool Overlaps(const char* smallest, const char* largest) { + InternalKeyComparator cmp(BytewiseComparator()); + Slice s(smallest != NULL ? smallest : ""); + Slice l(largest != NULL ? largest : ""); + return SomeFileOverlapsRange(cmp, disjoint_sorted_files_, files_, + (smallest != NULL ? &s : NULL), + (largest != NULL ? &l : NULL)); + } +}; + +TEST(FindFileTest, Empty) { + ASSERT_EQ(0, Find("foo")); + ASSERT_TRUE(! Overlaps("a", "z")); + ASSERT_TRUE(! Overlaps(NULL, "z")); + ASSERT_TRUE(! Overlaps("a", NULL)); + ASSERT_TRUE(! Overlaps(NULL, NULL)); +} + +TEST(FindFileTest, Single) { + Add("p", "q"); + ASSERT_EQ(0, Find("a")); + ASSERT_EQ(0, Find("p")); + ASSERT_EQ(0, Find("p1")); + ASSERT_EQ(0, Find("q")); + ASSERT_EQ(1, Find("q1")); + ASSERT_EQ(1, Find("z")); + + ASSERT_TRUE(! Overlaps("a", "b")); + ASSERT_TRUE(! Overlaps("z1", "z2")); + ASSERT_TRUE(Overlaps("a", "p")); + ASSERT_TRUE(Overlaps("a", "q")); + ASSERT_TRUE(Overlaps("a", "z")); + ASSERT_TRUE(Overlaps("p", "p1")); + ASSERT_TRUE(Overlaps("p", "q")); + ASSERT_TRUE(Overlaps("p", "z")); + ASSERT_TRUE(Overlaps("p1", "p2")); + ASSERT_TRUE(Overlaps("p1", "z")); + ASSERT_TRUE(Overlaps("q", "q")); + ASSERT_TRUE(Overlaps("q", "q1")); + + ASSERT_TRUE(! Overlaps(NULL, "j")); + ASSERT_TRUE(! Overlaps("r", NULL)); + ASSERT_TRUE(Overlaps(NULL, "p")); + ASSERT_TRUE(Overlaps(NULL, "p1")); + ASSERT_TRUE(Overlaps("q", NULL)); + ASSERT_TRUE(Overlaps(NULL, NULL)); +} + + +TEST(FindFileTest, Multiple) { + Add("150", "200"); + Add("200", "250"); + Add("300", "350"); + Add("400", "450"); + ASSERT_EQ(0, Find("100")); + ASSERT_EQ(0, Find("150")); + ASSERT_EQ(0, Find("151")); + ASSERT_EQ(0, Find("199")); + ASSERT_EQ(0, Find("200")); + ASSERT_EQ(1, Find("201")); + ASSERT_EQ(1, Find("249")); + ASSERT_EQ(1, Find("250")); + ASSERT_EQ(2, Find("251")); + ASSERT_EQ(2, Find("299")); + ASSERT_EQ(2, Find("300")); + ASSERT_EQ(2, Find("349")); + ASSERT_EQ(2, Find("350")); + ASSERT_EQ(3, Find("351")); + ASSERT_EQ(3, Find("400")); + ASSERT_EQ(3, Find("450")); + ASSERT_EQ(4, Find("451")); + + ASSERT_TRUE(! Overlaps("100", "149")); + ASSERT_TRUE(! Overlaps("251", "299")); + ASSERT_TRUE(! Overlaps("451", "500")); + ASSERT_TRUE(! Overlaps("351", "399")); + + ASSERT_TRUE(Overlaps("100", "150")); + ASSERT_TRUE(Overlaps("100", "200")); + ASSERT_TRUE(Overlaps("100", "300")); + ASSERT_TRUE(Overlaps("100", "400")); + ASSERT_TRUE(Overlaps("100", "500")); + ASSERT_TRUE(Overlaps("375", "400")); + ASSERT_TRUE(Overlaps("450", "450")); + ASSERT_TRUE(Overlaps("450", "500")); +} + +TEST(FindFileTest, MultipleNullBoundaries) { + Add("150", "200"); + Add("200", "250"); + Add("300", "350"); + Add("400", "450"); + ASSERT_TRUE(! Overlaps(NULL, "149")); + ASSERT_TRUE(! Overlaps("451", NULL)); + ASSERT_TRUE(Overlaps(NULL, NULL)); + ASSERT_TRUE(Overlaps(NULL, "150")); + ASSERT_TRUE(Overlaps(NULL, "199")); + ASSERT_TRUE(Overlaps(NULL, "200")); + ASSERT_TRUE(Overlaps(NULL, "201")); + ASSERT_TRUE(Overlaps(NULL, "400")); + ASSERT_TRUE(Overlaps(NULL, "800")); + ASSERT_TRUE(Overlaps("100", NULL)); + ASSERT_TRUE(Overlaps("200", NULL)); + ASSERT_TRUE(Overlaps("449", NULL)); + ASSERT_TRUE(Overlaps("450", NULL)); +} + +TEST(FindFileTest, OverlapSequenceChecks) { + Add("200", "200", 5000, 3000); + ASSERT_TRUE(! Overlaps("199", "199")); + ASSERT_TRUE(! Overlaps("201", "300")); + ASSERT_TRUE(Overlaps("200", "200")); + ASSERT_TRUE(Overlaps("190", "200")); + ASSERT_TRUE(Overlaps("200", "210")); +} + +TEST(FindFileTest, OverlappingFiles) { + Add("150", "600"); + Add("400", "500"); + disjoint_sorted_files_ = false; + ASSERT_TRUE(! Overlaps("100", "149")); + ASSERT_TRUE(! Overlaps("601", "700")); + ASSERT_TRUE(Overlaps("100", "150")); + ASSERT_TRUE(Overlaps("100", "200")); + ASSERT_TRUE(Overlaps("100", "300")); + ASSERT_TRUE(Overlaps("100", "400")); + ASSERT_TRUE(Overlaps("100", "500")); + ASSERT_TRUE(Overlaps("375", "400")); + ASSERT_TRUE(Overlaps("450", "450")); + ASSERT_TRUE(Overlaps("450", "500")); + ASSERT_TRUE(Overlaps("450", "700")); + ASSERT_TRUE(Overlaps("600", "700")); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/write_batch.cc b/src/leveldb/db/write_batch.cc new file mode 100644 index 00000000..33f4a425 --- /dev/null +++ b/src/leveldb/db/write_batch.cc @@ -0,0 +1,147 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// WriteBatch::rep_ := +// sequence: fixed64 +// count: fixed32 +// data: record[count] +// record := +// kTypeValue varstring varstring | +// kTypeDeletion varstring +// varstring := +// len: varint32 +// data: uint8[len] + +#include "leveldb/write_batch.h" + +#include "leveldb/db.h" +#include "db/dbformat.h" +#include "db/memtable.h" +#include "db/write_batch_internal.h" +#include "util/coding.h" + +namespace leveldb { + +// WriteBatch header has an 8-byte sequence number followed by a 4-byte count. +static const size_t kHeader = 12; + +WriteBatch::WriteBatch() { + Clear(); +} + +WriteBatch::~WriteBatch() { } + +WriteBatch::Handler::~Handler() { } + +void WriteBatch::Clear() { + rep_.clear(); + rep_.resize(kHeader); +} + +Status WriteBatch::Iterate(Handler* handler) const { + Slice input(rep_); + if (input.size() < kHeader) { + return Status::Corruption("malformed WriteBatch (too small)"); + } + + input.remove_prefix(kHeader); + Slice key, value; + int found = 0; + while (!input.empty()) { + found++; + char tag = input[0]; + input.remove_prefix(1); + switch (tag) { + case kTypeValue: + if (GetLengthPrefixedSlice(&input, &key) && + GetLengthPrefixedSlice(&input, &value)) { + handler->Put(key, value); + } else { + return Status::Corruption("bad WriteBatch Put"); + } + break; + case kTypeDeletion: + if (GetLengthPrefixedSlice(&input, &key)) { + handler->Delete(key); + } else { + return Status::Corruption("bad WriteBatch Delete"); + } + break; + default: + return Status::Corruption("unknown WriteBatch tag"); + } + } + if (found != WriteBatchInternal::Count(this)) { + return Status::Corruption("WriteBatch has wrong count"); + } else { + return Status::OK(); + } +} + +int WriteBatchInternal::Count(const WriteBatch* b) { + return DecodeFixed32(b->rep_.data() + 8); +} + +void WriteBatchInternal::SetCount(WriteBatch* b, int n) { + EncodeFixed32(&b->rep_[8], n); +} + +SequenceNumber WriteBatchInternal::Sequence(const WriteBatch* b) { + return SequenceNumber(DecodeFixed64(b->rep_.data())); +} + +void WriteBatchInternal::SetSequence(WriteBatch* b, SequenceNumber seq) { + EncodeFixed64(&b->rep_[0], seq); +} + +void WriteBatch::Put(const Slice& key, const Slice& value) { + WriteBatchInternal::SetCount(this, WriteBatchInternal::Count(this) + 1); + rep_.push_back(static_cast(kTypeValue)); + PutLengthPrefixedSlice(&rep_, key); + PutLengthPrefixedSlice(&rep_, value); +} + +void WriteBatch::Delete(const Slice& key) { + WriteBatchInternal::SetCount(this, WriteBatchInternal::Count(this) + 1); + rep_.push_back(static_cast(kTypeDeletion)); + PutLengthPrefixedSlice(&rep_, key); +} + +namespace { +class MemTableInserter : public WriteBatch::Handler { + public: + SequenceNumber sequence_; + MemTable* mem_; + + virtual void Put(const Slice& key, const Slice& value) { + mem_->Add(sequence_, kTypeValue, key, value); + sequence_++; + } + virtual void Delete(const Slice& key) { + mem_->Add(sequence_, kTypeDeletion, key, Slice()); + sequence_++; + } +}; +} // namespace + +Status WriteBatchInternal::InsertInto(const WriteBatch* b, + MemTable* memtable) { + MemTableInserter inserter; + inserter.sequence_ = WriteBatchInternal::Sequence(b); + inserter.mem_ = memtable; + return b->Iterate(&inserter); +} + +void WriteBatchInternal::SetContents(WriteBatch* b, const Slice& contents) { + assert(contents.size() >= kHeader); + b->rep_.assign(contents.data(), contents.size()); +} + +void WriteBatchInternal::Append(WriteBatch* dst, const WriteBatch* src) { + SetCount(dst, Count(dst) + Count(src)); + assert(src->rep_.size() >= kHeader); + dst->rep_.append(src->rep_.data() + kHeader, src->rep_.size() - kHeader); +} + +} // namespace leveldb diff --git a/src/leveldb/db/write_batch_internal.h b/src/leveldb/db/write_batch_internal.h new file mode 100644 index 00000000..310a3c89 --- /dev/null +++ b/src/leveldb/db/write_batch_internal.h @@ -0,0 +1,49 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_WRITE_BATCH_INTERNAL_H_ +#define STORAGE_LEVELDB_DB_WRITE_BATCH_INTERNAL_H_ + +#include "leveldb/write_batch.h" + +namespace leveldb { + +class MemTable; + +// WriteBatchInternal provides static methods for manipulating a +// WriteBatch that we don't want in the public WriteBatch interface. +class WriteBatchInternal { + public: + // Return the number of entries in the batch. + static int Count(const WriteBatch* batch); + + // Set the count for the number of entries in the batch. + static void SetCount(WriteBatch* batch, int n); + + // Return the sequence number for the start of this batch. + static SequenceNumber Sequence(const WriteBatch* batch); + + // Store the specified number as the sequence number for the start of + // this batch. + static void SetSequence(WriteBatch* batch, SequenceNumber seq); + + static Slice Contents(const WriteBatch* batch) { + return Slice(batch->rep_); + } + + static size_t ByteSize(const WriteBatch* batch) { + return batch->rep_.size(); + } + + static void SetContents(WriteBatch* batch, const Slice& contents); + + static Status InsertInto(const WriteBatch* batch, MemTable* memtable); + + static void Append(WriteBatch* dst, const WriteBatch* src); +}; + +} // namespace leveldb + + +#endif // STORAGE_LEVELDB_DB_WRITE_BATCH_INTERNAL_H_ diff --git a/src/leveldb/db/write_batch_test.cc b/src/leveldb/db/write_batch_test.cc new file mode 100644 index 00000000..9064e3d8 --- /dev/null +++ b/src/leveldb/db/write_batch_test.cc @@ -0,0 +1,120 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/db.h" + +#include "db/memtable.h" +#include "db/write_batch_internal.h" +#include "leveldb/env.h" +#include "util/logging.h" +#include "util/testharness.h" + +namespace leveldb { + +static std::string PrintContents(WriteBatch* b) { + InternalKeyComparator cmp(BytewiseComparator()); + MemTable* mem = new MemTable(cmp); + mem->Ref(); + std::string state; + Status s = WriteBatchInternal::InsertInto(b, mem); + int count = 0; + Iterator* iter = mem->NewIterator(); + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + ParsedInternalKey ikey; + ASSERT_TRUE(ParseInternalKey(iter->key(), &ikey)); + switch (ikey.type) { + case kTypeValue: + state.append("Put("); + state.append(ikey.user_key.ToString()); + state.append(", "); + state.append(iter->value().ToString()); + state.append(")"); + count++; + break; + case kTypeDeletion: + state.append("Delete("); + state.append(ikey.user_key.ToString()); + state.append(")"); + count++; + break; + } + state.append("@"); + state.append(NumberToString(ikey.sequence)); + } + delete iter; + if (!s.ok()) { + state.append("ParseError()"); + } else if (count != WriteBatchInternal::Count(b)) { + state.append("CountMismatch()"); + } + mem->Unref(); + return state; +} + +class WriteBatchTest { }; + +TEST(WriteBatchTest, Empty) { + WriteBatch batch; + ASSERT_EQ("", PrintContents(&batch)); + ASSERT_EQ(0, WriteBatchInternal::Count(&batch)); +} + +TEST(WriteBatchTest, Multiple) { + WriteBatch batch; + batch.Put(Slice("foo"), Slice("bar")); + batch.Delete(Slice("box")); + batch.Put(Slice("baz"), Slice("boo")); + WriteBatchInternal::SetSequence(&batch, 100); + ASSERT_EQ(100, WriteBatchInternal::Sequence(&batch)); + ASSERT_EQ(3, WriteBatchInternal::Count(&batch)); + ASSERT_EQ("Put(baz, boo)@102" + "Delete(box)@101" + "Put(foo, bar)@100", + PrintContents(&batch)); +} + +TEST(WriteBatchTest, Corruption) { + WriteBatch batch; + batch.Put(Slice("foo"), Slice("bar")); + batch.Delete(Slice("box")); + WriteBatchInternal::SetSequence(&batch, 200); + Slice contents = WriteBatchInternal::Contents(&batch); + WriteBatchInternal::SetContents(&batch, + Slice(contents.data(),contents.size()-1)); + ASSERT_EQ("Put(foo, bar)@200" + "ParseError()", + PrintContents(&batch)); +} + +TEST(WriteBatchTest, Append) { + WriteBatch b1, b2; + WriteBatchInternal::SetSequence(&b1, 200); + WriteBatchInternal::SetSequence(&b2, 300); + WriteBatchInternal::Append(&b1, &b2); + ASSERT_EQ("", + PrintContents(&b1)); + b2.Put("a", "va"); + WriteBatchInternal::Append(&b1, &b2); + ASSERT_EQ("Put(a, va)@200", + PrintContents(&b1)); + b2.Clear(); + b2.Put("b", "vb"); + WriteBatchInternal::Append(&b1, &b2); + ASSERT_EQ("Put(a, va)@200" + "Put(b, vb)@201", + PrintContents(&b1)); + b2.Delete("foo"); + WriteBatchInternal::Append(&b1, &b2); + ASSERT_EQ("Put(a, va)@200" + "Put(b, vb)@202" + "Put(b, vb)@201" + "Delete(foo)@203", + PrintContents(&b1)); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/doc/bench/db_bench_sqlite3.cc b/src/leveldb/doc/bench/db_bench_sqlite3.cc new file mode 100644 index 00000000..e63aaa8d --- /dev/null +++ b/src/leveldb/doc/bench/db_bench_sqlite3.cc @@ -0,0 +1,718 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include +#include "util/histogram.h" +#include "util/random.h" +#include "util/testutil.h" + +// Comma-separated list of operations to run in the specified order +// Actual benchmarks: +// +// fillseq -- write N values in sequential key order in async mode +// fillseqsync -- write N/100 values in sequential key order in sync mode +// fillseqbatch -- batch write N values in sequential key order in async mode +// fillrandom -- write N values in random key order in async mode +// fillrandsync -- write N/100 values in random key order in sync mode +// fillrandbatch -- batch write N values in sequential key order in async mode +// overwrite -- overwrite N values in random key order in async mode +// fillrand100K -- write N/1000 100K values in random order in async mode +// fillseq100K -- write N/1000 100K values in sequential order in async mode +// readseq -- read N times sequentially +// readrandom -- read N times in random order +// readrand100K -- read N/1000 100K values in sequential order in async mode +static const char* FLAGS_benchmarks = + "fillseq," + "fillseqsync," + "fillseqbatch," + "fillrandom," + "fillrandsync," + "fillrandbatch," + "overwrite," + "overwritebatch," + "readrandom," + "readseq," + "fillrand100K," + "fillseq100K," + "readseq," + "readrand100K," + ; + +// Number of key/values to place in database +static int FLAGS_num = 1000000; + +// Number of read operations to do. If negative, do FLAGS_num reads. +static int FLAGS_reads = -1; + +// Size of each value +static int FLAGS_value_size = 100; + +// Print histogram of operation timings +static bool FLAGS_histogram = false; + +// Arrange to generate values that shrink to this fraction of +// their original size after compression +static double FLAGS_compression_ratio = 0.5; + +// Page size. Default 1 KB. +static int FLAGS_page_size = 1024; + +// Number of pages. +// Default cache size = FLAGS_page_size * FLAGS_num_pages = 4 MB. +static int FLAGS_num_pages = 4096; + +// If true, do not destroy the existing database. If you set this +// flag and also specify a benchmark that wants a fresh database, that +// benchmark will fail. +static bool FLAGS_use_existing_db = false; + +// If true, we allow batch writes to occur +static bool FLAGS_transaction = true; + +// If true, we enable Write-Ahead Logging +static bool FLAGS_WAL_enabled = true; + +// Use the db with the following name. +static const char* FLAGS_db = NULL; + +inline +static void ExecErrorCheck(int status, char *err_msg) { + if (status != SQLITE_OK) { + fprintf(stderr, "SQL error: %s\n", err_msg); + sqlite3_free(err_msg); + exit(1); + } +} + +inline +static void StepErrorCheck(int status) { + if (status != SQLITE_DONE) { + fprintf(stderr, "SQL step error: status = %d\n", status); + exit(1); + } +} + +inline +static void ErrorCheck(int status) { + if (status != SQLITE_OK) { + fprintf(stderr, "sqlite3 error: status = %d\n", status); + exit(1); + } +} + +inline +static void WalCheckpoint(sqlite3* db_) { + // Flush all writes to disk + if (FLAGS_WAL_enabled) { + sqlite3_wal_checkpoint_v2(db_, NULL, SQLITE_CHECKPOINT_FULL, NULL, NULL); + } +} + +namespace leveldb { + +// Helper for quickly generating random data. +namespace { +class RandomGenerator { + private: + std::string data_; + int pos_; + + public: + RandomGenerator() { + // We use a limited amount of data over and over again and ensure + // that it is larger than the compression window (32KB), and also + // large enough to serve all typical value sizes we want to write. + Random rnd(301); + std::string piece; + while (data_.size() < 1048576) { + // Add a short fragment that is as compressible as specified + // by FLAGS_compression_ratio. + test::CompressibleString(&rnd, FLAGS_compression_ratio, 100, &piece); + data_.append(piece); + } + pos_ = 0; + } + + Slice Generate(int len) { + if (pos_ + len > data_.size()) { + pos_ = 0; + assert(len < data_.size()); + } + pos_ += len; + return Slice(data_.data() + pos_ - len, len); + } +}; + +static Slice TrimSpace(Slice s) { + int start = 0; + while (start < s.size() && isspace(s[start])) { + start++; + } + int limit = s.size(); + while (limit > start && isspace(s[limit-1])) { + limit--; + } + return Slice(s.data() + start, limit - start); +} + +} // namespace + +class Benchmark { + private: + sqlite3* db_; + int db_num_; + int num_; + int reads_; + double start_; + double last_op_finish_; + int64_t bytes_; + std::string message_; + Histogram hist_; + RandomGenerator gen_; + Random rand_; + + // State kept for progress messages + int done_; + int next_report_; // When to report next + + void PrintHeader() { + const int kKeySize = 16; + PrintEnvironment(); + fprintf(stdout, "Keys: %d bytes each\n", kKeySize); + fprintf(stdout, "Values: %d bytes each\n", FLAGS_value_size); + fprintf(stdout, "Entries: %d\n", num_); + fprintf(stdout, "RawSize: %.1f MB (estimated)\n", + ((static_cast(kKeySize + FLAGS_value_size) * num_) + / 1048576.0)); + PrintWarnings(); + fprintf(stdout, "------------------------------------------------\n"); + } + + void PrintWarnings() { +#if defined(__GNUC__) && !defined(__OPTIMIZE__) + fprintf(stdout, + "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n" + ); +#endif +#ifndef NDEBUG + fprintf(stdout, + "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n"); +#endif + } + + void PrintEnvironment() { + fprintf(stderr, "SQLite: version %s\n", SQLITE_VERSION); + +#if defined(__linux) + time_t now = time(NULL); + fprintf(stderr, "Date: %s", ctime(&now)); // ctime() adds newline + + FILE* cpuinfo = fopen("/proc/cpuinfo", "r"); + if (cpuinfo != NULL) { + char line[1000]; + int num_cpus = 0; + std::string cpu_type; + std::string cache_size; + while (fgets(line, sizeof(line), cpuinfo) != NULL) { + const char* sep = strchr(line, ':'); + if (sep == NULL) { + continue; + } + Slice key = TrimSpace(Slice(line, sep - 1 - line)); + Slice val = TrimSpace(Slice(sep + 1)); + if (key == "model name") { + ++num_cpus; + cpu_type = val.ToString(); + } else if (key == "cache size") { + cache_size = val.ToString(); + } + } + fclose(cpuinfo); + fprintf(stderr, "CPU: %d * %s\n", num_cpus, cpu_type.c_str()); + fprintf(stderr, "CPUCache: %s\n", cache_size.c_str()); + } +#endif + } + + void Start() { + start_ = Env::Default()->NowMicros() * 1e-6; + bytes_ = 0; + message_.clear(); + last_op_finish_ = start_; + hist_.Clear(); + done_ = 0; + next_report_ = 100; + } + + void FinishedSingleOp() { + if (FLAGS_histogram) { + double now = Env::Default()->NowMicros() * 1e-6; + double micros = (now - last_op_finish_) * 1e6; + hist_.Add(micros); + if (micros > 20000) { + fprintf(stderr, "long op: %.1f micros%30s\r", micros, ""); + fflush(stderr); + } + last_op_finish_ = now; + } + + done_++; + if (done_ >= next_report_) { + if (next_report_ < 1000) next_report_ += 100; + else if (next_report_ < 5000) next_report_ += 500; + else if (next_report_ < 10000) next_report_ += 1000; + else if (next_report_ < 50000) next_report_ += 5000; + else if (next_report_ < 100000) next_report_ += 10000; + else if (next_report_ < 500000) next_report_ += 50000; + else next_report_ += 100000; + fprintf(stderr, "... finished %d ops%30s\r", done_, ""); + fflush(stderr); + } + } + + void Stop(const Slice& name) { + double finish = Env::Default()->NowMicros() * 1e-6; + + // Pretend at least one op was done in case we are running a benchmark + // that does not call FinishedSingleOp(). + if (done_ < 1) done_ = 1; + + if (bytes_ > 0) { + char rate[100]; + snprintf(rate, sizeof(rate), "%6.1f MB/s", + (bytes_ / 1048576.0) / (finish - start_)); + if (!message_.empty()) { + message_ = std::string(rate) + " " + message_; + } else { + message_ = rate; + } + } + + fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", + name.ToString().c_str(), + (finish - start_) * 1e6 / done_, + (message_.empty() ? "" : " "), + message_.c_str()); + if (FLAGS_histogram) { + fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str()); + } + fflush(stdout); + } + + public: + enum Order { + SEQUENTIAL, + RANDOM + }; + enum DBState { + FRESH, + EXISTING + }; + + Benchmark() + : db_(NULL), + db_num_(0), + num_(FLAGS_num), + reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), + bytes_(0), + rand_(301) { + std::vector files; + std::string test_dir; + Env::Default()->GetTestDirectory(&test_dir); + Env::Default()->GetChildren(test_dir, &files); + if (!FLAGS_use_existing_db) { + for (int i = 0; i < files.size(); i++) { + if (Slice(files[i]).starts_with("dbbench_sqlite3")) { + std::string file_name(test_dir); + file_name += "/"; + file_name += files[i]; + Env::Default()->DeleteFile(file_name.c_str()); + } + } + } + } + + ~Benchmark() { + int status = sqlite3_close(db_); + ErrorCheck(status); + } + + void Run() { + PrintHeader(); + Open(); + + const char* benchmarks = FLAGS_benchmarks; + while (benchmarks != NULL) { + const char* sep = strchr(benchmarks, ','); + Slice name; + if (sep == NULL) { + name = benchmarks; + benchmarks = NULL; + } else { + name = Slice(benchmarks, sep - benchmarks); + benchmarks = sep + 1; + } + + bytes_ = 0; + Start(); + + bool known = true; + bool write_sync = false; + if (name == Slice("fillseq")) { + Write(write_sync, SEQUENTIAL, FRESH, num_, FLAGS_value_size, 1); + WalCheckpoint(db_); + } else if (name == Slice("fillseqbatch")) { + Write(write_sync, SEQUENTIAL, FRESH, num_, FLAGS_value_size, 1000); + WalCheckpoint(db_); + } else if (name == Slice("fillrandom")) { + Write(write_sync, RANDOM, FRESH, num_, FLAGS_value_size, 1); + WalCheckpoint(db_); + } else if (name == Slice("fillrandbatch")) { + Write(write_sync, RANDOM, FRESH, num_, FLAGS_value_size, 1000); + WalCheckpoint(db_); + } else if (name == Slice("overwrite")) { + Write(write_sync, RANDOM, EXISTING, num_, FLAGS_value_size, 1); + WalCheckpoint(db_); + } else if (name == Slice("overwritebatch")) { + Write(write_sync, RANDOM, EXISTING, num_, FLAGS_value_size, 1000); + WalCheckpoint(db_); + } else if (name == Slice("fillrandsync")) { + write_sync = true; + Write(write_sync, RANDOM, FRESH, num_ / 100, FLAGS_value_size, 1); + WalCheckpoint(db_); + } else if (name == Slice("fillseqsync")) { + write_sync = true; + Write(write_sync, SEQUENTIAL, FRESH, num_ / 100, FLAGS_value_size, 1); + WalCheckpoint(db_); + } else if (name == Slice("fillrand100K")) { + Write(write_sync, RANDOM, FRESH, num_ / 1000, 100 * 1000, 1); + WalCheckpoint(db_); + } else if (name == Slice("fillseq100K")) { + Write(write_sync, SEQUENTIAL, FRESH, num_ / 1000, 100 * 1000, 1); + WalCheckpoint(db_); + } else if (name == Slice("readseq")) { + ReadSequential(); + } else if (name == Slice("readrandom")) { + Read(RANDOM, 1); + } else if (name == Slice("readrand100K")) { + int n = reads_; + reads_ /= 1000; + Read(RANDOM, 1); + reads_ = n; + } else { + known = false; + if (name != Slice()) { // No error message for empty name + fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str()); + } + } + if (known) { + Stop(name); + } + } + } + + void Open() { + assert(db_ == NULL); + + int status; + char file_name[100]; + char* err_msg = NULL; + db_num_++; + + // Open database + std::string tmp_dir; + Env::Default()->GetTestDirectory(&tmp_dir); + snprintf(file_name, sizeof(file_name), + "%s/dbbench_sqlite3-%d.db", + tmp_dir.c_str(), + db_num_); + status = sqlite3_open(file_name, &db_); + if (status) { + fprintf(stderr, "open error: %s\n", sqlite3_errmsg(db_)); + exit(1); + } + + // Change SQLite cache size + char cache_size[100]; + snprintf(cache_size, sizeof(cache_size), "PRAGMA cache_size = %d", + FLAGS_num_pages); + status = sqlite3_exec(db_, cache_size, NULL, NULL, &err_msg); + ExecErrorCheck(status, err_msg); + + // FLAGS_page_size is defaulted to 1024 + if (FLAGS_page_size != 1024) { + char page_size[100]; + snprintf(page_size, sizeof(page_size), "PRAGMA page_size = %d", + FLAGS_page_size); + status = sqlite3_exec(db_, page_size, NULL, NULL, &err_msg); + ExecErrorCheck(status, err_msg); + } + + // Change journal mode to WAL if WAL enabled flag is on + if (FLAGS_WAL_enabled) { + std::string WAL_stmt = "PRAGMA journal_mode = WAL"; + + // LevelDB's default cache size is a combined 4 MB + std::string WAL_checkpoint = "PRAGMA wal_autocheckpoint = 4096"; + status = sqlite3_exec(db_, WAL_stmt.c_str(), NULL, NULL, &err_msg); + ExecErrorCheck(status, err_msg); + status = sqlite3_exec(db_, WAL_checkpoint.c_str(), NULL, NULL, &err_msg); + ExecErrorCheck(status, err_msg); + } + + // Change locking mode to exclusive and create tables/index for database + std::string locking_stmt = "PRAGMA locking_mode = EXCLUSIVE"; + std::string create_stmt = + "CREATE TABLE test (key blob, value blob, PRIMARY KEY(key))"; + std::string stmt_array[] = { locking_stmt, create_stmt }; + int stmt_array_length = sizeof(stmt_array) / sizeof(std::string); + for (int i = 0; i < stmt_array_length; i++) { + status = sqlite3_exec(db_, stmt_array[i].c_str(), NULL, NULL, &err_msg); + ExecErrorCheck(status, err_msg); + } + } + + void Write(bool write_sync, Order order, DBState state, + int num_entries, int value_size, int entries_per_batch) { + // Create new database if state == FRESH + if (state == FRESH) { + if (FLAGS_use_existing_db) { + message_ = "skipping (--use_existing_db is true)"; + return; + } + sqlite3_close(db_); + db_ = NULL; + Open(); + Start(); + } + + if (num_entries != num_) { + char msg[100]; + snprintf(msg, sizeof(msg), "(%d ops)", num_entries); + message_ = msg; + } + + char* err_msg = NULL; + int status; + + sqlite3_stmt *replace_stmt, *begin_trans_stmt, *end_trans_stmt; + std::string replace_str = "REPLACE INTO test (key, value) VALUES (?, ?)"; + std::string begin_trans_str = "BEGIN TRANSACTION;"; + std::string end_trans_str = "END TRANSACTION;"; + + // Check for synchronous flag in options + std::string sync_stmt = (write_sync) ? "PRAGMA synchronous = FULL" : + "PRAGMA synchronous = OFF"; + status = sqlite3_exec(db_, sync_stmt.c_str(), NULL, NULL, &err_msg); + ExecErrorCheck(status, err_msg); + + // Preparing sqlite3 statements + status = sqlite3_prepare_v2(db_, replace_str.c_str(), -1, + &replace_stmt, NULL); + ErrorCheck(status); + status = sqlite3_prepare_v2(db_, begin_trans_str.c_str(), -1, + &begin_trans_stmt, NULL); + ErrorCheck(status); + status = sqlite3_prepare_v2(db_, end_trans_str.c_str(), -1, + &end_trans_stmt, NULL); + ErrorCheck(status); + + bool transaction = (entries_per_batch > 1); + for (int i = 0; i < num_entries; i += entries_per_batch) { + // Begin write transaction + if (FLAGS_transaction && transaction) { + status = sqlite3_step(begin_trans_stmt); + StepErrorCheck(status); + status = sqlite3_reset(begin_trans_stmt); + ErrorCheck(status); + } + + // Create and execute SQL statements + for (int j = 0; j < entries_per_batch; j++) { + const char* value = gen_.Generate(value_size).data(); + + // Create values for key-value pair + const int k = (order == SEQUENTIAL) ? i + j : + (rand_.Next() % num_entries); + char key[100]; + snprintf(key, sizeof(key), "%016d", k); + + // Bind KV values into replace_stmt + status = sqlite3_bind_blob(replace_stmt, 1, key, 16, SQLITE_STATIC); + ErrorCheck(status); + status = sqlite3_bind_blob(replace_stmt, 2, value, + value_size, SQLITE_STATIC); + ErrorCheck(status); + + // Execute replace_stmt + bytes_ += value_size + strlen(key); + status = sqlite3_step(replace_stmt); + StepErrorCheck(status); + + // Reset SQLite statement for another use + status = sqlite3_clear_bindings(replace_stmt); + ErrorCheck(status); + status = sqlite3_reset(replace_stmt); + ErrorCheck(status); + + FinishedSingleOp(); + } + + // End write transaction + if (FLAGS_transaction && transaction) { + status = sqlite3_step(end_trans_stmt); + StepErrorCheck(status); + status = sqlite3_reset(end_trans_stmt); + ErrorCheck(status); + } + } + + status = sqlite3_finalize(replace_stmt); + ErrorCheck(status); + status = sqlite3_finalize(begin_trans_stmt); + ErrorCheck(status); + status = sqlite3_finalize(end_trans_stmt); + ErrorCheck(status); + } + + void Read(Order order, int entries_per_batch) { + int status; + sqlite3_stmt *read_stmt, *begin_trans_stmt, *end_trans_stmt; + + std::string read_str = "SELECT * FROM test WHERE key = ?"; + std::string begin_trans_str = "BEGIN TRANSACTION;"; + std::string end_trans_str = "END TRANSACTION;"; + + // Preparing sqlite3 statements + status = sqlite3_prepare_v2(db_, begin_trans_str.c_str(), -1, + &begin_trans_stmt, NULL); + ErrorCheck(status); + status = sqlite3_prepare_v2(db_, end_trans_str.c_str(), -1, + &end_trans_stmt, NULL); + ErrorCheck(status); + status = sqlite3_prepare_v2(db_, read_str.c_str(), -1, &read_stmt, NULL); + ErrorCheck(status); + + bool transaction = (entries_per_batch > 1); + for (int i = 0; i < reads_; i += entries_per_batch) { + // Begin read transaction + if (FLAGS_transaction && transaction) { + status = sqlite3_step(begin_trans_stmt); + StepErrorCheck(status); + status = sqlite3_reset(begin_trans_stmt); + ErrorCheck(status); + } + + // Create and execute SQL statements + for (int j = 0; j < entries_per_batch; j++) { + // Create key value + char key[100]; + int k = (order == SEQUENTIAL) ? i + j : (rand_.Next() % reads_); + snprintf(key, sizeof(key), "%016d", k); + + // Bind key value into read_stmt + status = sqlite3_bind_blob(read_stmt, 1, key, 16, SQLITE_STATIC); + ErrorCheck(status); + + // Execute read statement + while ((status = sqlite3_step(read_stmt)) == SQLITE_ROW) {} + StepErrorCheck(status); + + // Reset SQLite statement for another use + status = sqlite3_clear_bindings(read_stmt); + ErrorCheck(status); + status = sqlite3_reset(read_stmt); + ErrorCheck(status); + FinishedSingleOp(); + } + + // End read transaction + if (FLAGS_transaction && transaction) { + status = sqlite3_step(end_trans_stmt); + StepErrorCheck(status); + status = sqlite3_reset(end_trans_stmt); + ErrorCheck(status); + } + } + + status = sqlite3_finalize(read_stmt); + ErrorCheck(status); + status = sqlite3_finalize(begin_trans_stmt); + ErrorCheck(status); + status = sqlite3_finalize(end_trans_stmt); + ErrorCheck(status); + } + + void ReadSequential() { + int status; + sqlite3_stmt *pStmt; + std::string read_str = "SELECT * FROM test ORDER BY key"; + + status = sqlite3_prepare_v2(db_, read_str.c_str(), -1, &pStmt, NULL); + ErrorCheck(status); + for (int i = 0; i < reads_ && SQLITE_ROW == sqlite3_step(pStmt); i++) { + bytes_ += sqlite3_column_bytes(pStmt, 1) + sqlite3_column_bytes(pStmt, 2); + FinishedSingleOp(); + } + + status = sqlite3_finalize(pStmt); + ErrorCheck(status); + } + +}; + +} // namespace leveldb + +int main(int argc, char** argv) { + std::string default_db_path; + for (int i = 1; i < argc; i++) { + double d; + int n; + char junk; + if (leveldb::Slice(argv[i]).starts_with("--benchmarks=")) { + FLAGS_benchmarks = argv[i] + strlen("--benchmarks="); + } else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_histogram = n; + } else if (sscanf(argv[i], "--compression_ratio=%lf%c", &d, &junk) == 1) { + FLAGS_compression_ratio = d; + } else if (sscanf(argv[i], "--use_existing_db=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_use_existing_db = n; + } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) { + FLAGS_num = n; + } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) { + FLAGS_reads = n; + } else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) { + FLAGS_value_size = n; + } else if (leveldb::Slice(argv[i]) == leveldb::Slice("--no_transaction")) { + FLAGS_transaction = false; + } else if (sscanf(argv[i], "--page_size=%d%c", &n, &junk) == 1) { + FLAGS_page_size = n; + } else if (sscanf(argv[i], "--num_pages=%d%c", &n, &junk) == 1) { + FLAGS_num_pages = n; + } else if (sscanf(argv[i], "--WAL_enabled=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_WAL_enabled = n; + } else if (strncmp(argv[i], "--db=", 5) == 0) { + FLAGS_db = argv[i] + 5; + } else { + fprintf(stderr, "Invalid flag '%s'\n", argv[i]); + exit(1); + } + } + + // Choose a location for the test database if none given with --db= + if (FLAGS_db == NULL) { + leveldb::Env::Default()->GetTestDirectory(&default_db_path); + default_db_path += "/dbbench"; + FLAGS_db = default_db_path.c_str(); + } + + leveldb::Benchmark benchmark; + benchmark.Run(); + return 0; +} diff --git a/src/leveldb/doc/bench/db_bench_tree_db.cc b/src/leveldb/doc/bench/db_bench_tree_db.cc new file mode 100644 index 00000000..4ca381f1 --- /dev/null +++ b/src/leveldb/doc/bench/db_bench_tree_db.cc @@ -0,0 +1,528 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include +#include "util/histogram.h" +#include "util/random.h" +#include "util/testutil.h" + +// Comma-separated list of operations to run in the specified order +// Actual benchmarks: +// +// fillseq -- write N values in sequential key order in async mode +// fillrandom -- write N values in random key order in async mode +// overwrite -- overwrite N values in random key order in async mode +// fillseqsync -- write N/100 values in sequential key order in sync mode +// fillrandsync -- write N/100 values in random key order in sync mode +// fillrand100K -- write N/1000 100K values in random order in async mode +// fillseq100K -- write N/1000 100K values in seq order in async mode +// readseq -- read N times sequentially +// readseq100K -- read N/1000 100K values in sequential order in async mode +// readrand100K -- read N/1000 100K values in sequential order in async mode +// readrandom -- read N times in random order +static const char* FLAGS_benchmarks = + "fillseq," + "fillseqsync," + "fillrandsync," + "fillrandom," + "overwrite," + "readrandom," + "readseq," + "fillrand100K," + "fillseq100K," + "readseq100K," + "readrand100K," + ; + +// Number of key/values to place in database +static int FLAGS_num = 1000000; + +// Number of read operations to do. If negative, do FLAGS_num reads. +static int FLAGS_reads = -1; + +// Size of each value +static int FLAGS_value_size = 100; + +// Arrange to generate values that shrink to this fraction of +// their original size after compression +static double FLAGS_compression_ratio = 0.5; + +// Print histogram of operation timings +static bool FLAGS_histogram = false; + +// Cache size. Default 4 MB +static int FLAGS_cache_size = 4194304; + +// Page size. Default 1 KB +static int FLAGS_page_size = 1024; + +// If true, do not destroy the existing database. If you set this +// flag and also specify a benchmark that wants a fresh database, that +// benchmark will fail. +static bool FLAGS_use_existing_db = false; + +// Compression flag. If true, compression is on. If false, compression +// is off. +static bool FLAGS_compression = true; + +// Use the db with the following name. +static const char* FLAGS_db = NULL; + +inline +static void DBSynchronize(kyotocabinet::TreeDB* db_) +{ + // Synchronize will flush writes to disk + if (!db_->synchronize()) { + fprintf(stderr, "synchronize error: %s\n", db_->error().name()); + } +} + +namespace leveldb { + +// Helper for quickly generating random data. +namespace { +class RandomGenerator { + private: + std::string data_; + int pos_; + + public: + RandomGenerator() { + // We use a limited amount of data over and over again and ensure + // that it is larger than the compression window (32KB), and also + // large enough to serve all typical value sizes we want to write. + Random rnd(301); + std::string piece; + while (data_.size() < 1048576) { + // Add a short fragment that is as compressible as specified + // by FLAGS_compression_ratio. + test::CompressibleString(&rnd, FLAGS_compression_ratio, 100, &piece); + data_.append(piece); + } + pos_ = 0; + } + + Slice Generate(int len) { + if (pos_ + len > data_.size()) { + pos_ = 0; + assert(len < data_.size()); + } + pos_ += len; + return Slice(data_.data() + pos_ - len, len); + } +}; + +static Slice TrimSpace(Slice s) { + int start = 0; + while (start < s.size() && isspace(s[start])) { + start++; + } + int limit = s.size(); + while (limit > start && isspace(s[limit-1])) { + limit--; + } + return Slice(s.data() + start, limit - start); +} + +} // namespace + +class Benchmark { + private: + kyotocabinet::TreeDB* db_; + int db_num_; + int num_; + int reads_; + double start_; + double last_op_finish_; + int64_t bytes_; + std::string message_; + Histogram hist_; + RandomGenerator gen_; + Random rand_; + kyotocabinet::LZOCompressor comp_; + + // State kept for progress messages + int done_; + int next_report_; // When to report next + + void PrintHeader() { + const int kKeySize = 16; + PrintEnvironment(); + fprintf(stdout, "Keys: %d bytes each\n", kKeySize); + fprintf(stdout, "Values: %d bytes each (%d bytes after compression)\n", + FLAGS_value_size, + static_cast(FLAGS_value_size * FLAGS_compression_ratio + 0.5)); + fprintf(stdout, "Entries: %d\n", num_); + fprintf(stdout, "RawSize: %.1f MB (estimated)\n", + ((static_cast(kKeySize + FLAGS_value_size) * num_) + / 1048576.0)); + fprintf(stdout, "FileSize: %.1f MB (estimated)\n", + (((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_) + / 1048576.0)); + PrintWarnings(); + fprintf(stdout, "------------------------------------------------\n"); + } + + void PrintWarnings() { +#if defined(__GNUC__) && !defined(__OPTIMIZE__) + fprintf(stdout, + "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n" + ); +#endif +#ifndef NDEBUG + fprintf(stdout, + "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n"); +#endif + } + + void PrintEnvironment() { + fprintf(stderr, "Kyoto Cabinet: version %s, lib ver %d, lib rev %d\n", + kyotocabinet::VERSION, kyotocabinet::LIBVER, kyotocabinet::LIBREV); + +#if defined(__linux) + time_t now = time(NULL); + fprintf(stderr, "Date: %s", ctime(&now)); // ctime() adds newline + + FILE* cpuinfo = fopen("/proc/cpuinfo", "r"); + if (cpuinfo != NULL) { + char line[1000]; + int num_cpus = 0; + std::string cpu_type; + std::string cache_size; + while (fgets(line, sizeof(line), cpuinfo) != NULL) { + const char* sep = strchr(line, ':'); + if (sep == NULL) { + continue; + } + Slice key = TrimSpace(Slice(line, sep - 1 - line)); + Slice val = TrimSpace(Slice(sep + 1)); + if (key == "model name") { + ++num_cpus; + cpu_type = val.ToString(); + } else if (key == "cache size") { + cache_size = val.ToString(); + } + } + fclose(cpuinfo); + fprintf(stderr, "CPU: %d * %s\n", num_cpus, cpu_type.c_str()); + fprintf(stderr, "CPUCache: %s\n", cache_size.c_str()); + } +#endif + } + + void Start() { + start_ = Env::Default()->NowMicros() * 1e-6; + bytes_ = 0; + message_.clear(); + last_op_finish_ = start_; + hist_.Clear(); + done_ = 0; + next_report_ = 100; + } + + void FinishedSingleOp() { + if (FLAGS_histogram) { + double now = Env::Default()->NowMicros() * 1e-6; + double micros = (now - last_op_finish_) * 1e6; + hist_.Add(micros); + if (micros > 20000) { + fprintf(stderr, "long op: %.1f micros%30s\r", micros, ""); + fflush(stderr); + } + last_op_finish_ = now; + } + + done_++; + if (done_ >= next_report_) { + if (next_report_ < 1000) next_report_ += 100; + else if (next_report_ < 5000) next_report_ += 500; + else if (next_report_ < 10000) next_report_ += 1000; + else if (next_report_ < 50000) next_report_ += 5000; + else if (next_report_ < 100000) next_report_ += 10000; + else if (next_report_ < 500000) next_report_ += 50000; + else next_report_ += 100000; + fprintf(stderr, "... finished %d ops%30s\r", done_, ""); + fflush(stderr); + } + } + + void Stop(const Slice& name) { + double finish = Env::Default()->NowMicros() * 1e-6; + + // Pretend at least one op was done in case we are running a benchmark + // that does not call FinishedSingleOp(). + if (done_ < 1) done_ = 1; + + if (bytes_ > 0) { + char rate[100]; + snprintf(rate, sizeof(rate), "%6.1f MB/s", + (bytes_ / 1048576.0) / (finish - start_)); + if (!message_.empty()) { + message_ = std::string(rate) + " " + message_; + } else { + message_ = rate; + } + } + + fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", + name.ToString().c_str(), + (finish - start_) * 1e6 / done_, + (message_.empty() ? "" : " "), + message_.c_str()); + if (FLAGS_histogram) { + fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str()); + } + fflush(stdout); + } + + public: + enum Order { + SEQUENTIAL, + RANDOM + }; + enum DBState { + FRESH, + EXISTING + }; + + Benchmark() + : db_(NULL), + num_(FLAGS_num), + reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), + bytes_(0), + rand_(301) { + std::vector files; + std::string test_dir; + Env::Default()->GetTestDirectory(&test_dir); + Env::Default()->GetChildren(test_dir.c_str(), &files); + if (!FLAGS_use_existing_db) { + for (int i = 0; i < files.size(); i++) { + if (Slice(files[i]).starts_with("dbbench_polyDB")) { + std::string file_name(test_dir); + file_name += "/"; + file_name += files[i]; + Env::Default()->DeleteFile(file_name.c_str()); + } + } + } + } + + ~Benchmark() { + if (!db_->close()) { + fprintf(stderr, "close error: %s\n", db_->error().name()); + } + } + + void Run() { + PrintHeader(); + Open(false); + + const char* benchmarks = FLAGS_benchmarks; + while (benchmarks != NULL) { + const char* sep = strchr(benchmarks, ','); + Slice name; + if (sep == NULL) { + name = benchmarks; + benchmarks = NULL; + } else { + name = Slice(benchmarks, sep - benchmarks); + benchmarks = sep + 1; + } + + Start(); + + bool known = true; + bool write_sync = false; + if (name == Slice("fillseq")) { + Write(write_sync, SEQUENTIAL, FRESH, num_, FLAGS_value_size, 1); + DBSynchronize(db_); + } else if (name == Slice("fillrandom")) { + Write(write_sync, RANDOM, FRESH, num_, FLAGS_value_size, 1); + DBSynchronize(db_); + } else if (name == Slice("overwrite")) { + Write(write_sync, RANDOM, EXISTING, num_, FLAGS_value_size, 1); + DBSynchronize(db_); + } else if (name == Slice("fillrandsync")) { + write_sync = true; + Write(write_sync, RANDOM, FRESH, num_ / 100, FLAGS_value_size, 1); + DBSynchronize(db_); + } else if (name == Slice("fillseqsync")) { + write_sync = true; + Write(write_sync, SEQUENTIAL, FRESH, num_ / 100, FLAGS_value_size, 1); + DBSynchronize(db_); + } else if (name == Slice("fillrand100K")) { + Write(write_sync, RANDOM, FRESH, num_ / 1000, 100 * 1000, 1); + DBSynchronize(db_); + } else if (name == Slice("fillseq100K")) { + Write(write_sync, SEQUENTIAL, FRESH, num_ / 1000, 100 * 1000, 1); + DBSynchronize(db_); + } else if (name == Slice("readseq")) { + ReadSequential(); + } else if (name == Slice("readrandom")) { + ReadRandom(); + } else if (name == Slice("readrand100K")) { + int n = reads_; + reads_ /= 1000; + ReadRandom(); + reads_ = n; + } else if (name == Slice("readseq100K")) { + int n = reads_; + reads_ /= 1000; + ReadSequential(); + reads_ = n; + } else { + known = false; + if (name != Slice()) { // No error message for empty name + fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str()); + } + } + if (known) { + Stop(name); + } + } + } + + private: + void Open(bool sync) { + assert(db_ == NULL); + + // Initialize db_ + db_ = new kyotocabinet::TreeDB(); + char file_name[100]; + db_num_++; + std::string test_dir; + Env::Default()->GetTestDirectory(&test_dir); + snprintf(file_name, sizeof(file_name), + "%s/dbbench_polyDB-%d.kct", + test_dir.c_str(), + db_num_); + + // Create tuning options and open the database + int open_options = kyotocabinet::PolyDB::OWRITER | + kyotocabinet::PolyDB::OCREATE; + int tune_options = kyotocabinet::TreeDB::TSMALL | + kyotocabinet::TreeDB::TLINEAR; + if (FLAGS_compression) { + tune_options |= kyotocabinet::TreeDB::TCOMPRESS; + db_->tune_compressor(&comp_); + } + db_->tune_options(tune_options); + db_->tune_page_cache(FLAGS_cache_size); + db_->tune_page(FLAGS_page_size); + db_->tune_map(256LL<<20); + if (sync) { + open_options |= kyotocabinet::PolyDB::OAUTOSYNC; + } + if (!db_->open(file_name, open_options)) { + fprintf(stderr, "open error: %s\n", db_->error().name()); + } + } + + void Write(bool sync, Order order, DBState state, + int num_entries, int value_size, int entries_per_batch) { + // Create new database if state == FRESH + if (state == FRESH) { + if (FLAGS_use_existing_db) { + message_ = "skipping (--use_existing_db is true)"; + return; + } + delete db_; + db_ = NULL; + Open(sync); + Start(); // Do not count time taken to destroy/open + } + + if (num_entries != num_) { + char msg[100]; + snprintf(msg, sizeof(msg), "(%d ops)", num_entries); + message_ = msg; + } + + // Write to database + for (int i = 0; i < num_entries; i++) + { + const int k = (order == SEQUENTIAL) ? i : (rand_.Next() % num_entries); + char key[100]; + snprintf(key, sizeof(key), "%016d", k); + bytes_ += value_size + strlen(key); + std::string cpp_key = key; + if (!db_->set(cpp_key, gen_.Generate(value_size).ToString())) { + fprintf(stderr, "set error: %s\n", db_->error().name()); + } + FinishedSingleOp(); + } + } + + void ReadSequential() { + kyotocabinet::DB::Cursor* cur = db_->cursor(); + cur->jump(); + std::string ckey, cvalue; + while (cur->get(&ckey, &cvalue, true)) { + bytes_ += ckey.size() + cvalue.size(); + FinishedSingleOp(); + } + delete cur; + } + + void ReadRandom() { + std::string value; + for (int i = 0; i < reads_; i++) { + char key[100]; + const int k = rand_.Next() % reads_; + snprintf(key, sizeof(key), "%016d", k); + db_->get(key, &value); + FinishedSingleOp(); + } + } +}; + +} // namespace leveldb + +int main(int argc, char** argv) { + std::string default_db_path; + for (int i = 1; i < argc; i++) { + double d; + int n; + char junk; + if (leveldb::Slice(argv[i]).starts_with("--benchmarks=")) { + FLAGS_benchmarks = argv[i] + strlen("--benchmarks="); + } else if (sscanf(argv[i], "--compression_ratio=%lf%c", &d, &junk) == 1) { + FLAGS_compression_ratio = d; + } else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_histogram = n; + } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) { + FLAGS_num = n; + } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) { + FLAGS_reads = n; + } else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) { + FLAGS_value_size = n; + } else if (sscanf(argv[i], "--cache_size=%d%c", &n, &junk) == 1) { + FLAGS_cache_size = n; + } else if (sscanf(argv[i], "--page_size=%d%c", &n, &junk) == 1) { + FLAGS_page_size = n; + } else if (sscanf(argv[i], "--compression=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_compression = (n == 1) ? true : false; + } else if (strncmp(argv[i], "--db=", 5) == 0) { + FLAGS_db = argv[i] + 5; + } else { + fprintf(stderr, "Invalid flag '%s'\n", argv[i]); + exit(1); + } + } + + // Choose a location for the test database if none given with --db= + if (FLAGS_db == NULL) { + leveldb::Env::Default()->GetTestDirectory(&default_db_path); + default_db_path += "/dbbench"; + FLAGS_db = default_db_path.c_str(); + } + + leveldb::Benchmark benchmark; + benchmark.Run(); + return 0; +} diff --git a/src/leveldb/doc/benchmark.html b/src/leveldb/doc/benchmark.html new file mode 100644 index 00000000..c4639772 --- /dev/null +++ b/src/leveldb/doc/benchmark.html @@ -0,0 +1,459 @@ + + + +LevelDB Benchmarks + + + + +

LevelDB Benchmarks

+

Google, July 2011

+
+ +

In order to test LevelDB's performance, we benchmark it against other well-established database implementations. We compare LevelDB (revision 39) against SQLite3 (version 3.7.6.3) and Kyoto Cabinet's (version 1.2.67) TreeDB (a B+Tree based key-value store). We would like to acknowledge Scott Hess and Mikio Hirabayashi for their suggestions and contributions to the SQLite3 and Kyoto Cabinet benchmarks, respectively.

+ +

Benchmarks were all performed on a six-core Intel(R) Xeon(R) CPU X5650 @ 2.67GHz, with 12288 KB of total L3 cache and 12 GB of DDR3 RAM at 1333 MHz. (Note that LevelDB uses at most two CPUs since the benchmarks are single threaded: one to run the benchmark, and one for background compactions.) We ran the benchmarks on two machines (with identical processors), one with an Ext3 file system and one with an Ext4 file system. The machine with the Ext3 file system has a SATA Hitachi HDS721050CLA362 hard drive. The machine with the Ext4 file system has a SATA Samsung HD502HJ hard drive. Both hard drives spin at 7200 RPM and have hard drive write-caching enabled (using `hdparm -W 1 [device]`). The numbers reported below are the median of three measurements.

+ +

Benchmark Source Code

+

We wrote benchmark tools for SQLite and Kyoto TreeDB based on LevelDB's db_bench. The code for each of the benchmarks resides here:

+ + +

Custom Build Specifications

+
    +
  • LevelDB: LevelDB was compiled with the tcmalloc library and the Snappy compression library (revision 33). Assertions were disabled.
  • +
  • TreeDB: TreeDB was compiled using the LZO compression library (version 2.03). Furthermore, we enabled the TSMALL and TLINEAR options when opening the database in order to reduce the footprint of each record.
  • +
  • SQLite: We tuned SQLite's performance, by setting its locking mode to exclusive. We also enabled SQLite's write-ahead logging.
  • +
+ +

1. Baseline Performance

+

This section gives the baseline performance of all the +databases. Following sections show how performance changes as various +parameters are varied. For the baseline:

+
    +
  • Each database is allowed 4 MB of cache memory.
  • +
  • Databases are opened in asynchronous write mode. + (LevelDB's sync option, TreeDB's OAUTOSYNC option, and + SQLite3's synchronous options are all turned off). I.e., + every write is pushed to the operating system, but the + benchmark does not wait for the write to reach the disk.
  • +
  • Keys are 16 bytes each.
  • +
  • Value are 100 bytes each (with enough redundancy so that + a simple compressor shrinks them to 50% of their original + size).
  • +
  • Sequential reads/writes traverse the key space in increasing order.
  • +
  • Random reads/writes traverse the key space in random order.
  • +
+ +

A. Sequential Reads

+ + + + + + + + + + +
LevelDB4,030,000 ops/sec
 
Kyoto TreeDB1,010,000 ops/sec
 
SQLite3383,000 ops/sec
 
+

B. Random Reads

+ + + + + + + + + + +
LevelDB129,000 ops/sec
 
Kyoto TreeDB151,000 ops/sec
 
SQLite3134,000 ops/sec
 
+

C. Sequential Writes

+ + + + + + + + + + +
LevelDB779,000 ops/sec
 
Kyoto TreeDB342,000 ops/sec
 
SQLite348,600 ops/sec
 
+

D. Random Writes

+ + + + + + + + + + +
LevelDB164,000 ops/sec
 
Kyoto TreeDB88,500 ops/sec
 
SQLite39,860 ops/sec
 
+ +

LevelDB outperforms both SQLite3 and TreeDB in sequential and random write operations and sequential read operations. Kyoto Cabinet has the fastest random read operations.

+ +

2. Write Performance under Different Configurations

+

A. Large Values

+

For this benchmark, we start with an empty database, and write 100,000 byte values (~50% compressible). To keep the benchmark running time reasonable, we stop after writing 1000 values.

+

Sequential Writes

+ + + + + + + + + + +
LevelDB1,100 ops/sec
 
Kyoto TreeDB1,000 ops/sec
 
SQLite31,600 ops/sec
 
+

Random Writes

+ + + + + + + + + + +
LevelDB480 ops/sec
 
Kyoto TreeDB1,100 ops/sec
 
SQLite31,600 ops/sec
 
+

LevelDB doesn't perform as well with large values of 100,000 bytes each. This is because LevelDB writes keys and values at least twice: first time to the transaction log, and second time (during a compaction) to a sorted file. +With larger values, LevelDB's per-operation efficiency is swamped by the +cost of extra copies of large values.

+

B. Batch Writes

+

A batch write is a set of writes that are applied atomically to the underlying database. A single batch of N writes may be significantly faster than N individual writes. The following benchmark writes one thousand batches where each batch contains one thousand 100-byte values. TreeDB does not support batch writes and is omitted from this benchmark.

+

Sequential Writes

+ + + + + + + + + +
LevelDB840,000 entries/sec
 
(1.08x baseline)
SQLite3124,000 entries/sec
 
(2.55x baseline)
+

Random Writes

+ + + + + + + + + +
LevelDB221,000 entries/sec
 
(1.35x baseline)
SQLite322,000 entries/sec
 
(2.23x baseline)
+ +

Because of the way LevelDB persistent storage is organized, batches of +random writes are not much slower (only a factor of 4x) than batches +of sequential writes.

+ +

C. Synchronous Writes

+

In the following benchmark, we enable the synchronous writing modes +of all of the databases. Since this change significantly slows down the +benchmark, we stop after 10,000 writes. For synchronous write tests, we've +disabled hard drive write-caching (using `hdparm -W 0 [device]`).

+
    +
  • For LevelDB, we set WriteOptions.sync = true.
  • +
  • In TreeDB, we enabled TreeDB's OAUTOSYNC option.
  • +
  • For SQLite3, we set "PRAGMA synchronous = FULL".
  • +
+

Sequential Writes

+ + + + + + + + + + + + + +
LevelDB100 ops/sec
 
(0.003x baseline)
Kyoto TreeDB7 ops/sec
 
(0.0004x baseline)
SQLite388 ops/sec
 
(0.002x baseline)
+

Random Writes

+ + + + + + + + + + + + + +
LevelDB100 ops/sec
 
(0.015x baseline)
Kyoto TreeDB8 ops/sec
 
(0.001x baseline)
SQLite388 ops/sec
 
(0.009x baseline)
+ +

Also see the ext4 performance numbers below +since synchronous writes behave significantly differently +on ext3 and ext4.

+ +

D. Turning Compression Off

+ +

In the baseline measurements, LevelDB and TreeDB were using +light-weight compression +(Snappy for LevelDB, +and LZO for +TreeDB). SQLite3, by default does not use compression. The +experiments below show what happens when compression is disabled in +all of the databases (the SQLite3 numbers are just a copy of +its baseline measurements):

+ +

Sequential Writes

+ + + + + + + + + + + + + +
LevelDB594,000 ops/sec
 
(0.76x baseline)
Kyoto TreeDB485,000 ops/sec
 
(1.42x baseline)
SQLite348,600 ops/sec
 
(1.00x baseline)
+

Random Writes

+ + + + + + + + + + + + + +
LevelDB135,000 ops/sec
 
(0.82x baseline)
Kyoto TreeDB159,000 ops/sec
 
(1.80x baseline)
SQLite39,860 ops/sec
 
(1.00x baseline)
+ +

LevelDB's write performance is better with compression than without +since compression decreases the amount of data that has to be written +to disk. Therefore LevelDB users can leave compression enabled in +most scenarios without having worry about a tradeoff between space +usage and performance. TreeDB's performance on the other hand is +better without compression than with compression. Presumably this is +because TreeDB's compression library (LZO) is more expensive than +LevelDB's compression library (Snappy).

+ +

E. Using More Memory

+

We increased the overall cache size for each database to 128 MB. For LevelDB, we partitioned 128 MB into a 120 MB write buffer and 8 MB of cache (up from 2 MB of write buffer and 2 MB of cache). For SQLite3, we kept the page size at 1024 bytes, but increased the number of pages to 131,072 (up from 4096). For TreeDB, we also kept the page size at 1024 bytes, but increased the cache size to 128 MB (up from 4 MB).

+

Sequential Writes

+ + + + + + + + + + + + + +
LevelDB812,000 ops/sec
 
(1.04x baseline)
Kyoto TreeDB321,000 ops/sec
 
(0.94x baseline)
SQLite348,500 ops/sec
 
(1.00x baseline)
+

Random Writes

+ + + + + + + + + + + + + +
LevelDB355,000 ops/sec
 
(2.16x baseline)
Kyoto TreeDB284,000 ops/sec
 
(3.21x baseline)
SQLite39,670 ops/sec
 
(0.98x baseline)
+ +

SQLite's performance does not change substantially when compared to +the baseline, but the random write performance for both LevelDB and +TreeDB increases significantly. LevelDB's performance improves +because a larger write buffer reduces the need to merge sorted files +(since it creates a smaller number of larger sorted files). TreeDB's +performance goes up because the entire database is available in memory +for fast in-place updates.

+ +

3. Read Performance under Different Configurations

+

A. Larger Caches

+

We increased the overall memory usage to 128 MB for each database. +For LevelDB, we allocated 8 MB to LevelDB's write buffer and 120 MB +to LevelDB's cache. The other databases don't differentiate between a +write buffer and a cache, so we simply set their cache size to 128 +MB.

+

Sequential Reads

+ + + + + + + + + + + + + +
LevelDB5,210,000 ops/sec
 
(1.29x baseline)
Kyoto TreeDB1,070,000 ops/sec
 
(1.06x baseline)
SQLite3609,000 ops/sec
 
(1.59x baseline)
+ +

Random Reads

+ + + + + + + + + + + + + +
LevelDB190,000 ops/sec
 
(1.47x baseline)
Kyoto TreeDB463,000 ops/sec
 
(3.07x baseline)
SQLite3186,000 ops/sec
 
(1.39x baseline)
+ +

As expected, the read performance of all of the databases increases +when the caches are enlarged. In particular, TreeDB seems to make +very effective use of a cache that is large enough to hold the entire +database.

+ +

B. No Compression Reads

+

For this benchmark, we populated a database with 1 million entries consisting of 16 byte keys and 100 byte values. We compiled LevelDB and Kyoto Cabinet without compression support, so results that are read out from the database are already uncompressed. We've listed the SQLite3 baseline read performance as a point of comparison.

+

Sequential Reads

+ + + + + + + + + + + + + +
LevelDB4,880,000 ops/sec
 
(1.21x baseline)
Kyoto TreeDB1,230,000 ops/sec
 
(3.60x baseline)
SQLite3383,000 ops/sec
 
(1.00x baseline)
+

Random Reads

+ + + + + + + + + + + + + +
LevelDB149,000 ops/sec
 
(1.16x baseline)
Kyoto TreeDB175,000 ops/sec
 
(1.16x baseline)
SQLite3134,000 ops/sec
 
(1.00x baseline)
+ +

Performance of both LevelDB and TreeDB improves a small amount when +compression is disabled. Note however that under different workloads, +performance may very well be better with compression if it allows more +of the working set to fit in memory.

+ +

Note about Ext4 Filesystems

+

The preceding numbers are for an ext3 file system. Synchronous writes are much slower under ext4 (LevelDB drops to ~31 writes / second and TreeDB drops to ~5 writes / second; SQLite3's synchronous writes do not noticeably drop) due to ext4's different handling of fsync / msync calls. Even LevelDB's asynchronous write performance drops somewhat since it spreads its storage across multiple files and issues fsync calls when switching to a new file.

+ +

Acknowledgements

+

Jeff Dean and Sanjay Ghemawat wrote LevelDB. Kevin Tseng wrote and compiled these benchmarks. Mikio Hirabayashi, Scott Hess, and Gabor Cselle provided help and advice.

+ + diff --git a/src/leveldb/doc/doc.css b/src/leveldb/doc/doc.css new file mode 100644 index 00000000..700c564e --- /dev/null +++ b/src/leveldb/doc/doc.css @@ -0,0 +1,89 @@ +body { + margin-left: 0.5in; + margin-right: 0.5in; + background: white; + color: black; +} + +h1 { + margin-left: -0.2in; + font-size: 14pt; +} +h2 { + margin-left: -0in; + font-size: 12pt; +} +h3 { + margin-left: -0in; +} +h4 { + margin-left: -0in; +} +hr { + margin-left: -0in; +} + +/* Definition lists: definition term bold */ +dt { + font-weight: bold; +} + +address { + text-align: center; +} +code,samp,var { + color: blue; +} +kbd { + color: #600000; +} +div.note p { + float: right; + width: 3in; + margin-right: 0%; + padding: 1px; + border: 2px solid #6060a0; + background-color: #fffff0; +} + +ul { + margin-top: -0em; + margin-bottom: -0em; +} + +ol { + margin-top: -0em; + margin-bottom: -0em; +} + +UL.nobullets { + list-style-type: none; + list-style-image: none; + margin-left: -1em; +} + +p { + margin: 1em 0 1em 0; + padding: 0 0 0 0; +} + +pre { + line-height: 1.3em; + padding: 0.4em 0 0.8em 0; + margin: 0 0 0 0; + border: 0 0 0 0; + color: blue; +} + +.datatable { + margin-left: auto; + margin-right: auto; + margin-top: 2em; + margin-bottom: 2em; + border: 1px solid; +} + +.datatable td,th { + padding: 0 0.5em 0 0.5em; + text-align: right; +} diff --git a/src/leveldb/doc/impl.html b/src/leveldb/doc/impl.html new file mode 100644 index 00000000..6a468be0 --- /dev/null +++ b/src/leveldb/doc/impl.html @@ -0,0 +1,213 @@ + + + + +Leveldb file layout and compactions + + + + +

Files

+ +The implementation of leveldb is similar in spirit to the +representation of a single + +Bigtable tablet (section 5.3). +However the organization of the files that make up the representation +is somewhat different and is explained below. + +

+Each database is represented by a set of files stored in a directory. +There are several different types of files as documented below: +

+

Log files

+

+A log file (*.log) stores a sequence of recent updates. Each update +is appended to the current log file. When the log file reaches a +pre-determined size (approximately 4MB by default), it is converted +to a sorted table (see below) and a new log file is created for future +updates. +

+A copy of the current log file is kept in an in-memory structure (the +memtable). This copy is consulted on every read so that read +operations reflect all logged updates. +

+

Sorted tables

+

+A sorted table (*.sst) stores a sequence of entries sorted by key. +Each entry is either a value for the key, or a deletion marker for the +key. (Deletion markers are kept around to hide obsolete values +present in older sorted tables). +

+The set of sorted tables are organized into a sequence of levels. The +sorted table generated from a log file is placed in a special young +level (also called level-0). When the number of young files exceeds a +certain threshold (currently four), all of the young files are merged +together with all of the overlapping level-1 files to produce a +sequence of new level-1 files (we create a new level-1 file for every +2MB of data.) +

+Files in the young level may contain overlapping keys. However files +in other levels have distinct non-overlapping key ranges. Consider +level number L where L >= 1. When the combined size of files in +level-L exceeds (10^L) MB (i.e., 10MB for level-1, 100MB for level-2, +...), one file in level-L, and all of the overlapping files in +level-(L+1) are merged to form a set of new files for level-(L+1). +These merges have the effect of gradually migrating new updates from +the young level to the largest level using only bulk reads and writes +(i.e., minimizing expensive seeks). + +

Manifest

+

+A MANIFEST file lists the set of sorted tables that make up each +level, the corresponding key ranges, and other important metadata. +A new MANIFEST file (with a new number embedded in the file name) +is created whenever the database is reopened. The MANIFEST file is +formatted as a log, and changes made to the serving state (as files +are added or removed) are appended to this log. +

+

Current

+

+CURRENT is a simple text file that contains the name of the latest +MANIFEST file. +

+

Info logs

+

+Informational messages are printed to files named LOG and LOG.old. +

+

Others

+

+Other files used for miscellaneous purposes may also be present +(LOCK, *.dbtmp). + +

Level 0

+When the log file grows above a certain size (1MB by default): +
    +
  • Create a brand new memtable and log file and direct future updates here +
  • In the background: +
      +
    • Write the contents of the previous memtable to an sstable +
    • Discard the memtable +
    • Delete the old log file and the old memtable +
    • Add the new sstable to the young (level-0) level. +
    +
+ +

Compactions

+ +

+When the size of level L exceeds its limit, we compact it in a +background thread. The compaction picks a file from level L and all +overlapping files from the next level L+1. Note that if a level-L +file overlaps only part of a level-(L+1) file, the entire file at +level-(L+1) is used as an input to the compaction and will be +discarded after the compaction. Aside: because level-0 is special +(files in it may overlap each other), we treat compactions from +level-0 to level-1 specially: a level-0 compaction may pick more than +one level-0 file in case some of these files overlap each other. + +

+A compaction merges the contents of the picked files to produce a +sequence of level-(L+1) files. We switch to producing a new +level-(L+1) file after the current output file has reached the target +file size (2MB). We also switch to a new output file when the key +range of the current output file has grown enough to overlap more than +ten level-(L+2) files. This last rule ensures that a later compaction +of a level-(L+1) file will not pick up too much data from level-(L+2). + +

+The old files are discarded and the new files are added to the serving +state. + +

+Compactions for a particular level rotate through the key space. In +more detail, for each level L, we remember the ending key of the last +compaction at level L. The next compaction for level L will pick the +first file that starts after this key (wrapping around to the +beginning of the key space if there is no such file). + +

+Compactions drop overwritten values. They also drop deletion markers +if there are no higher numbered levels that contain a file whose range +overlaps the current key. + +

Timing

+ +Level-0 compactions will read up to four 1MB files from level-0, and +at worst all the level-1 files (10MB). I.e., we will read 14MB and +write 14MB. + +

+Other than the special level-0 compactions, we will pick one 2MB file +from level L. In the worst case, this will overlap ~ 12 files from +level L+1 (10 because level-(L+1) is ten times the size of level-L, +and another two at the boundaries since the file ranges at level-L +will usually not be aligned with the file ranges at level-L+1). The +compaction will therefore read 26MB and write 26MB. Assuming a disk +IO rate of 100MB/s (ballpark range for modern drives), the worst +compaction cost will be approximately 0.5 second. + +

+If we throttle the background writing to something small, say 10% of +the full 100MB/s speed, a compaction may take up to 5 seconds. If the +user is writing at 10MB/s, we might build up lots of level-0 files +(~50 to hold the 5*10MB). This may significantly increase the cost of +reads due to the overhead of merging more files together on every +read. + +

+Solution 1: To reduce this problem, we might want to increase the log +switching threshold when the number of level-0 files is large. Though +the downside is that the larger this threshold, the more memory we will +need to hold the corresponding memtable. + +

+Solution 2: We might want to decrease write rate artificially when the +number of level-0 files goes up. + +

+Solution 3: We work on reducing the cost of very wide merges. +Perhaps most of the level-0 files will have their blocks sitting +uncompressed in the cache and we will only need to worry about the +O(N) complexity in the merging iterator. + +

Number of files

+ +Instead of always making 2MB files, we could make larger files for +larger levels to reduce the total file count, though at the expense of +more bursty compactions. Alternatively, we could shard the set of +files into multiple directories. + +

+An experiment on an ext3 filesystem on Feb 04, 2011 shows +the following timings to do 100K file opens in directories with +varying number of files: + + + + + +
Files in directoryMicroseconds to open a file
10009
1000010
10000016
+So maybe even the sharding is not necessary on modern filesystems? + +

Recovery

+ +
    +
  • Read CURRENT to find name of the latest committed MANIFEST +
  • Read the named MANIFEST file +
  • Clean up stale files +
  • We could open all sstables here, but it is probably better to be lazy... +
  • Convert log chunk to a new level-0 sstable +
  • Start directing new writes to a new log file with recovered sequence# +
+ +

Garbage collection of files

+ +DeleteObsoleteFiles() is called at the end of every +compaction and at the end of recovery. It finds the names of all +files in the database. It deletes all log files that are not the +current log file. It deletes all table files that are not referenced +from some level and are not the output of an active compaction. + + + diff --git a/src/leveldb/doc/index.html b/src/leveldb/doc/index.html new file mode 100644 index 00000000..3ed0ed9d --- /dev/null +++ b/src/leveldb/doc/index.html @@ -0,0 +1,549 @@ + + + + +Leveldb + + + +

Leveldb

+
Jeff Dean, Sanjay Ghemawat
+

+The leveldb library provides a persistent key value store. Keys and +values are arbitrary byte arrays. The keys are ordered within the key +value store according to a user-specified comparator function. + +

+

Opening A Database

+

+A leveldb database has a name which corresponds to a file system +directory. All of the contents of database are stored in this +directory. The following example shows how to open a database, +creating it if necessary: +

+

+  #include <assert>
+  #include "leveldb/db.h"
+
+  leveldb::DB* db;
+  leveldb::Options options;
+  options.create_if_missing = true;
+  leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);
+  assert(status.ok());
+  ...
+
+If you want to raise an error if the database already exists, add +the following line before the leveldb::DB::Open call: +
+  options.error_if_exists = true;
+
+

Status

+

+You may have noticed the leveldb::Status type above. Values of this +type are returned by most functions in leveldb that may encounter an +error. You can check if such a result is ok, and also print an +associated error message: +

+

+   leveldb::Status s = ...;
+   if (!s.ok()) cerr << s.ToString() << endl;
+
+

Closing A Database

+

+When you are done with a database, just delete the database object. +Example: +

+

+  ... open the db as described above ...
+  ... do something with db ...
+  delete db;
+
+

Reads And Writes

+

+The database provides Put, Delete, and Get methods to +modify/query the database. For example, the following code +moves the value stored under key1 to key2. +

+  std::string value;
+  leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value);
+  if (s.ok()) s = db->Put(leveldb::WriteOptions(), key2, value);
+  if (s.ok()) s = db->Delete(leveldb::WriteOptions(), key1);
+
+ +

Atomic Updates

+

+Note that if the process dies after the Put of key2 but before the +delete of key1, the same value may be left stored under multiple keys. +Such problems can be avoided by using the WriteBatch class to +atomically apply a set of updates: +

+

+  #include "leveldb/write_batch.h"
+  ...
+  std::string value;
+  leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value);
+  if (s.ok()) {
+    leveldb::WriteBatch batch;
+    batch.Delete(key1);
+    batch.Put(key2, value);
+    s = db->Write(leveldb::WriteOptions(), &batch);
+  }
+
+The WriteBatch holds a sequence of edits to be made to the database, +and these edits within the batch are applied in order. Note that we +called Delete before Put so that if key1 is identical to key2, +we do not end up erroneously dropping the value entirely. +

+Apart from its atomicity benefits, WriteBatch may also be used to +speed up bulk updates by placing lots of individual mutations into the +same batch. + +

Synchronous Writes

+By default, each write to leveldb is asynchronous: it +returns after pushing the write from the process into the operating +system. The transfer from operating system memory to the underlying +persistent storage happens asynchronously. The sync flag +can be turned on for a particular write to make the write operation +not return until the data being written has been pushed all the way to +persistent storage. (On Posix systems, this is implemented by calling +either fsync(...) or fdatasync(...) or +msync(..., MS_SYNC) before the write operation returns.) +
+  leveldb::WriteOptions write_options;
+  write_options.sync = true;
+  db->Put(write_options, ...);
+
+Asynchronous writes are often more than a thousand times as fast as +synchronous writes. The downside of asynchronous writes is that a +crash of the machine may cause the last few updates to be lost. Note +that a crash of just the writing process (i.e., not a reboot) will not +cause any loss since even when sync is false, an update +is pushed from the process memory into the operating system before it +is considered done. + +

+Asynchronous writes can often be used safely. For example, when +loading a large amount of data into the database you can handle lost +updates by restarting the bulk load after a crash. A hybrid scheme is +also possible where every Nth write is synchronous, and in the event +of a crash, the bulk load is restarted just after the last synchronous +write finished by the previous run. (The synchronous write can update +a marker that describes where to restart on a crash.) + +

+WriteBatch provides an alternative to asynchronous writes. +Multiple updates may be placed in the same WriteBatch and +applied together using a synchronous write (i.e., +write_options.sync is set to true). The extra cost of +the synchronous write will be amortized across all of the writes in +the batch. + +

+

Concurrency

+

+A database may only be opened by one process at a time. +The leveldb implementation acquires a lock from the +operating system to prevent misuse. Within a single process, the +same leveldb::DB object may be safely shared by multiple +concurrent threads. I.e., different threads may write into or fetch +iterators or call Get on the same database without any +external synchronization (the leveldb implementation will +automatically do the required synchronization). However other objects +(like Iterator and WriteBatch) may require external synchronization. +If two threads share such an object, they must protect access to it +using their own locking protocol. More details are available in +the public header files. +

+

Iteration

+

+The following example demonstrates how to print all key,value pairs +in a database. +

+

+  leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions());
+  for (it->SeekToFirst(); it->Valid(); it->Next()) {
+    cout << it->key().ToString() << ": "  << it->value().ToString() << endl;
+  }
+  assert(it->status().ok());  // Check for any errors found during the scan
+  delete it;
+
+The following variation shows how to process just the keys in the +range [start,limit): +

+

+  for (it->Seek(start);
+       it->Valid() && it->key().ToString() < limit;
+       it->Next()) {
+    ...
+  }
+
+You can also process entries in reverse order. (Caveat: reverse +iteration may be somewhat slower than forward iteration.) +

+

+  for (it->SeekToLast(); it->Valid(); it->Prev()) {
+    ...
+  }
+
+

Snapshots

+

+Snapshots provide consistent read-only views over the entire state of +the key-value store. ReadOptions::snapshot may be non-NULL to indicate +that a read should operate on a particular version of the DB state. +If ReadOptions::snapshot is NULL, the read will operate on an +implicit snapshot of the current state. +

+Snapshots are created by the DB::GetSnapshot() method: +

+

+  leveldb::ReadOptions options;
+  options.snapshot = db->GetSnapshot();
+  ... apply some updates to db ...
+  leveldb::Iterator* iter = db->NewIterator(options);
+  ... read using iter to view the state when the snapshot was created ...
+  delete iter;
+  db->ReleaseSnapshot(options.snapshot);
+
+Note that when a snapshot is no longer needed, it should be released +using the DB::ReleaseSnapshot interface. This allows the +implementation to get rid of state that was being maintained just to +support reading as of that snapshot. +

Slice

+

+The return value of the it->key() and it->value() calls above +are instances of the leveldb::Slice type. Slice is a simple +structure that contains a length and a pointer to an external byte +array. Returning a Slice is a cheaper alternative to returning a +std::string since we do not need to copy potentially large keys and +values. In addition, leveldb methods do not return null-terminated +C-style strings since leveldb keys and values are allowed to +contain '\0' bytes. +

+C++ strings and null-terminated C-style strings can be easily converted +to a Slice: +

+

+   leveldb::Slice s1 = "hello";
+
+   std::string str("world");
+   leveldb::Slice s2 = str;
+
+A Slice can be easily converted back to a C++ string: +
+   std::string str = s1.ToString();
+   assert(str == std::string("hello"));
+
+Be careful when using Slices since it is up to the caller to ensure that +the external byte array into which the Slice points remains live while +the Slice is in use. For example, the following is buggy: +

+

+   leveldb::Slice slice;
+   if (...) {
+     std::string str = ...;
+     slice = str;
+   }
+   Use(slice);
+
+When the if statement goes out of scope, str will be destroyed and the +backing storage for slice will disappear. +

+

Comparators

+

+The preceding examples used the default ordering function for key, +which orders bytes lexicographically. You can however supply a custom +comparator when opening a database. For example, suppose each +database key consists of two numbers and we should sort by the first +number, breaking ties by the second number. First, define a proper +subclass of leveldb::Comparator that expresses these rules: +

+

+  class TwoPartComparator : public leveldb::Comparator {
+   public:
+    // Three-way comparison function:
+    //   if a < b: negative result
+    //   if a > b: positive result
+    //   else: zero result
+    int Compare(const leveldb::Slice& a, const leveldb::Slice& b) const {
+      int a1, a2, b1, b2;
+      ParseKey(a, &a1, &a2);
+      ParseKey(b, &b1, &b2);
+      if (a1 < b1) return -1;
+      if (a1 > b1) return +1;
+      if (a2 < b2) return -1;
+      if (a2 > b2) return +1;
+      return 0;
+    }
+
+    // Ignore the following methods for now:
+    const char* Name() const { return "TwoPartComparator"; }
+    void FindShortestSeparator(std::string*, const leveldb::Slice&) const { }
+    void FindShortSuccessor(std::string*) const { }
+  };
+
+Now create a database using this custom comparator: +

+

+  TwoPartComparator cmp;
+  leveldb::DB* db;
+  leveldb::Options options;
+  options.create_if_missing = true;
+  options.comparator = &cmp;
+  leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);
+  ...
+
+

Backwards compatibility

+

+The result of the comparator's Name method is attached to the +database when it is created, and is checked on every subsequent +database open. If the name changes, the leveldb::DB::Open call will +fail. Therefore, change the name if and only if the new key format +and comparison function are incompatible with existing databases, and +it is ok to discard the contents of all existing databases. +

+You can however still gradually evolve your key format over time with +a little bit of pre-planning. For example, you could store a version +number at the end of each key (one byte should suffice for most uses). +When you wish to switch to a new key format (e.g., adding an optional +third part to the keys processed by TwoPartComparator), +(a) keep the same comparator name (b) increment the version number +for new keys (c) change the comparator function so it uses the +version numbers found in the keys to decide how to interpret them. +

+

Performance

+

+Performance can be tuned by changing the default values of the +types defined in include/leveldb/options.h. + +

+

Block size

+

+leveldb groups adjacent keys together into the same block and such a +block is the unit of transfer to and from persistent storage. The +default block size is approximately 4096 uncompressed bytes. +Applications that mostly do bulk scans over the contents of the +database may wish to increase this size. Applications that do a lot +of point reads of small values may wish to switch to a smaller block +size if performance measurements indicate an improvement. There isn't +much benefit in using blocks smaller than one kilobyte, or larger than +a few megabytes. Also note that compression will be more effective +with larger block sizes. +

+

Compression

+

+Each block is individually compressed before being written to +persistent storage. Compression is on by default since the default +compression method is very fast, and is automatically disabled for +uncompressible data. In rare cases, applications may want to disable +compression entirely, but should only do so if benchmarks show a +performance improvement: +

+

+  leveldb::Options options;
+  options.compression = leveldb::kNoCompression;
+  ... leveldb::DB::Open(options, name, ...) ....
+
+

Cache

+

+The contents of the database are stored in a set of files in the +filesystem and each file stores a sequence of compressed blocks. If +options.cache is non-NULL, it is used to cache frequently used +uncompressed block contents. +

+

+  #include "leveldb/cache.h"
+
+  leveldb::Options options;
+  options.cache = leveldb::NewLRUCache(100 * 1048576);  // 100MB cache
+  leveldb::DB* db;
+  leveldb::DB::Open(options, name, &db);
+  ... use the db ...
+  delete db
+  delete options.cache;
+
+Note that the cache holds uncompressed data, and therefore it should +be sized according to application level data sizes, without any +reduction from compression. (Caching of compressed blocks is left to +the operating system buffer cache, or any custom Env +implementation provided by the client.) +

+When performing a bulk read, the application may wish to disable +caching so that the data processed by the bulk read does not end up +displacing most of the cached contents. A per-iterator option can be +used to achieve this: +

+

+  leveldb::ReadOptions options;
+  options.fill_cache = false;
+  leveldb::Iterator* it = db->NewIterator(options);
+  for (it->SeekToFirst(); it->Valid(); it->Next()) {
+    ...
+  }
+
+

Key Layout

+

+Note that the unit of disk transfer and caching is a block. Adjacent +keys (according to the database sort order) will usually be placed in +the same block. Therefore the application can improve its performance +by placing keys that are accessed together near each other and placing +infrequently used keys in a separate region of the key space. +

+For example, suppose we are implementing a simple file system on top +of leveldb. The types of entries we might wish to store are: +

+

+   filename -> permission-bits, length, list of file_block_ids
+   file_block_id -> data
+
+We might want to prefix filename keys with one letter (say '/') and the +file_block_id keys with a different letter (say '0') so that scans +over just the metadata do not force us to fetch and cache bulky file +contents. +

+

Filters

+

+Because of the way leveldb data is organized on disk, +a single Get() call may involve multiple reads from disk. +The optional FilterPolicy mechanism can be used to reduce +the number of disk reads substantially. +

+   leveldb::Options options;
+   options.filter_policy = NewBloomFilterPolicy(10);
+   leveldb::DB* db;
+   leveldb::DB::Open(options, "/tmp/testdb", &db);
+   ... use the database ...
+   delete db;
+   delete options.filter_policy;
+
+The preceding code associates a +Bloom filter +based filtering policy with the database. Bloom filter based +filtering relies on keeping some number of bits of data in memory per +key (in this case 10 bits per key since that is the argument we passed +to NewBloomFilterPolicy). This filter will reduce the number of unnecessary +disk reads needed for Get() calls by a factor of +approximately a 100. Increasing the bits per key will lead to a +larger reduction at the cost of more memory usage. We recommend that +applications whose working set does not fit in memory and that do a +lot of random reads set a filter policy. +

+If you are using a custom comparator, you should ensure that the filter +policy you are using is compatible with your comparator. For example, +consider a comparator that ignores trailing spaces when comparing keys. +NewBloomFilterPolicy must not be used with such a comparator. +Instead, the application should provide a custom filter policy that +also ignores trailing spaces. For example: +

+  class CustomFilterPolicy : public leveldb::FilterPolicy {
+   private:
+    FilterPolicy* builtin_policy_;
+   public:
+    CustomFilterPolicy() : builtin_policy_(NewBloomFilterPolicy(10)) { }
+    ~CustomFilterPolicy() { delete builtin_policy_; }
+
+    const char* Name() const { return "IgnoreTrailingSpacesFilter"; }
+
+    void CreateFilter(const Slice* keys, int n, std::string* dst) const {
+      // Use builtin bloom filter code after removing trailing spaces
+      std::vector<Slice> trimmed(n);
+      for (int i = 0; i < n; i++) {
+        trimmed[i] = RemoveTrailingSpaces(keys[i]);
+      }
+      return builtin_policy_->CreateFilter(&trimmed[i], n, dst);
+    }
+
+    bool KeyMayMatch(const Slice& key, const Slice& filter) const {
+      // Use builtin bloom filter code after removing trailing spaces
+      return builtin_policy_->KeyMayMatch(RemoveTrailingSpaces(key), filter);
+    }
+  };
+
+

+Advanced applications may provide a filter policy that does not use +a bloom filter but uses some other mechanism for summarizing a set +of keys. See leveldb/filter_policy.h for detail. +

+

Checksums

+

+leveldb associates checksums with all data it stores in the file system. +There are two separate controls provided over how aggressively these +checksums are verified: +

+

    +
  • ReadOptions::verify_checksums may be set to true to force + checksum verification of all data that is read from the file system on + behalf of a particular read. By default, no such verification is + done. +

    +

  • Options::paranoid_checks may be set to true before opening a + database to make the database implementation raise an error as soon as + it detects an internal corruption. Depending on which portion of the + database has been corrupted, the error may be raised when the database + is opened, or later by another database operation. By default, + paranoid checking is off so that the database can be used even if + parts of its persistent storage have been corrupted. +

    + If a database is corrupted (perhaps it cannot be opened when + paranoid checking is turned on), the leveldb::RepairDB function + may be used to recover as much of the data as possible +

    +

+

Approximate Sizes

+

+The GetApproximateSizes method can used to get the approximate +number of bytes of file system space used by one or more key ranges. +

+

+   leveldb::Range ranges[2];
+   ranges[0] = leveldb::Range("a", "c");
+   ranges[1] = leveldb::Range("x", "z");
+   uint64_t sizes[2];
+   leveldb::Status s = db->GetApproximateSizes(ranges, 2, sizes);
+
+The preceding call will set sizes[0] to the approximate number of +bytes of file system space used by the key range [a..c) and +sizes[1] to the approximate number of bytes used by the key range +[x..z). +

+

Environment

+

+All file operations (and other operating system calls) issued by the +leveldb implementation are routed through a leveldb::Env object. +Sophisticated clients may wish to provide their own Env +implementation to get better control. For example, an application may +introduce artificial delays in the file IO paths to limit the impact +of leveldb on other activities in the system. +

+

+  class SlowEnv : public leveldb::Env {
+    .. implementation of the Env interface ...
+  };
+
+  SlowEnv env;
+  leveldb::Options options;
+  options.env = &env;
+  Status s = leveldb::DB::Open(options, ...);
+
+

Porting

+

+leveldb may be ported to a new platform by providing platform +specific implementations of the types/methods/functions exported by +leveldb/port/port.h. See leveldb/port/port_example.h for more +details. +

+In addition, the new platform may need a new default leveldb::Env +implementation. See leveldb/util/env_posix.h for an example. + +

Other Information

+ +

+Details about the leveldb implementation may be found in +the following documents: +

+ + + diff --git a/src/leveldb/doc/log_format.txt b/src/leveldb/doc/log_format.txt new file mode 100644 index 00000000..4cca5ef6 --- /dev/null +++ b/src/leveldb/doc/log_format.txt @@ -0,0 +1,75 @@ +The log file contents are a sequence of 32KB blocks. The only +exception is that the tail of the file may contain a partial block. + +Each block consists of a sequence of records: + block := record* trailer? + record := + checksum: uint32 // crc32c of type and data[] ; little-endian + length: uint16 // little-endian + type: uint8 // One of FULL, FIRST, MIDDLE, LAST + data: uint8[length] + +A record never starts within the last six bytes of a block (since it +won't fit). Any leftover bytes here form the trailer, which must +consist entirely of zero bytes and must be skipped by readers. + +Aside: if exactly seven bytes are left in the current block, and a new +non-zero length record is added, the writer must emit a FIRST record +(which contains zero bytes of user data) to fill up the trailing seven +bytes of the block and then emit all of the user data in subsequent +blocks. + +More types may be added in the future. Some Readers may skip record +types they do not understand, others may report that some data was +skipped. + +FULL == 1 +FIRST == 2 +MIDDLE == 3 +LAST == 4 + +The FULL record contains the contents of an entire user record. + +FIRST, MIDDLE, LAST are types used for user records that have been +split into multiple fragments (typically because of block boundaries). +FIRST is the type of the first fragment of a user record, LAST is the +type of the last fragment of a user record, and MIDDLE is the type of +all interior fragments of a user record. + +Example: consider a sequence of user records: + A: length 1000 + B: length 97270 + C: length 8000 +A will be stored as a FULL record in the first block. + +B will be split into three fragments: first fragment occupies the rest +of the first block, second fragment occupies the entirety of the +second block, and the third fragment occupies a prefix of the third +block. This will leave six bytes free in the third block, which will +be left empty as the trailer. + +C will be stored as a FULL record in the fourth block. + +=================== + +Some benefits over the recordio format: + +(1) We do not need any heuristics for resyncing - just go to next +block boundary and scan. If there is a corruption, skip to the next +block. As a side-benefit, we do not get confused when part of the +contents of one log file are embedded as a record inside another log +file. + +(2) Splitting at approximate boundaries (e.g., for mapreduce) is +simple: find the next block boundary and skip records until we +hit a FULL or FIRST record. + +(3) We do not need extra buffering for large records. + +Some downsides compared to recordio format: + +(1) No packing of tiny records. This could be fixed by adding a new +record type, so it is a shortcoming of the current implementation, +not necessarily the format. + +(2) No compression. Again, this could be fixed by adding new record types. diff --git a/src/leveldb/doc/table_format.txt b/src/leveldb/doc/table_format.txt new file mode 100644 index 00000000..ca8f9b44 --- /dev/null +++ b/src/leveldb/doc/table_format.txt @@ -0,0 +1,104 @@ +File format +=========== + + + [data block 1] + [data block 2] + ... + [data block N] + [meta block 1] + ... + [meta block K] + [metaindex block] + [index block] + [Footer] (fixed size; starts at file_size - sizeof(Footer)) + + +The file contains internal pointers. Each such pointer is called +a BlockHandle and contains the following information: + offset: varint64 + size: varint64 +See https://developers.google.com/protocol-buffers/docs/encoding#varints +for an explanation of varint64 format. + +(1) The sequence of key/value pairs in the file are stored in sorted +order and partitioned into a sequence of data blocks. These blocks +come one after another at the beginning of the file. Each data block +is formatted according to the code in block_builder.cc, and then +optionally compressed. + +(2) After the data blocks we store a bunch of meta blocks. The +supported meta block types are described below. More meta block types +may be added in the future. Each meta block is again formatted using +block_builder.cc and then optionally compressed. + +(3) A "metaindex" block. It contains one entry for every other meta +block where the key is the name of the meta block and the value is a +BlockHandle pointing to that meta block. + +(4) An "index" block. This block contains one entry per data block, +where the key is a string >= last key in that data block and before +the first key in the successive data block. The value is the +BlockHandle for the data block. + +(6) At the very end of the file is a fixed length footer that contains +the BlockHandle of the metaindex and index blocks as well as a magic number. + metaindex_handle: char[p]; // Block handle for metaindex + index_handle: char[q]; // Block handle for index + padding: char[40-p-q]; // zeroed bytes to make fixed length + // (40==2*BlockHandle::kMaxEncodedLength) + magic: fixed64; // == 0xdb4775248b80fb57 (little-endian) + +"filter" Meta Block +------------------- + +If a "FilterPolicy" was specified when the database was opened, a +filter block is stored in each table. The "metaindex" block contains +an entry that maps from "filter." to the BlockHandle for the filter +block where "" is the string returned by the filter policy's +"Name()" method. + +The filter block stores a sequence of filters, where filter i contains +the output of FilterPolicy::CreateFilter() on all keys that are stored +in a block whose file offset falls within the range + + [ i*base ... (i+1)*base-1 ] + +Currently, "base" is 2KB. So for example, if blocks X and Y start in +the range [ 0KB .. 2KB-1 ], all of the keys in X and Y will be +converted to a filter by calling FilterPolicy::CreateFilter(), and the +resulting filter will be stored as the first filter in the filter +block. + +The filter block is formatted as follows: + + [filter 0] + [filter 1] + [filter 2] + ... + [filter N-1] + + [offset of filter 0] : 4 bytes + [offset of filter 1] : 4 bytes + [offset of filter 2] : 4 bytes + ... + [offset of filter N-1] : 4 bytes + + [offset of beginning of offset array] : 4 bytes + lg(base) : 1 byte + +The offset array at the end of the filter block allows efficient +mapping from a data block offset to the corresponding filter. + +"stats" Meta Block +------------------ + +This meta block contains a bunch of stats. The key is the name +of the statistic. The value contains the statistic. +TODO(postrelease): record following stats. + data size + index size + key size (uncompressed) + value size (uncompressed) + number of entries + number of data blocks diff --git a/src/leveldb/helpers/memenv/memenv.cc b/src/leveldb/helpers/memenv/memenv.cc new file mode 100644 index 00000000..43ef2e07 --- /dev/null +++ b/src/leveldb/helpers/memenv/memenv.cc @@ -0,0 +1,385 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "helpers/memenv/memenv.h" + +#include "leveldb/env.h" +#include "leveldb/status.h" +#include "port/port.h" +#include "util/mutexlock.h" +#include +#include +#include +#include + +namespace leveldb { + +namespace { + +class FileState { + public: + // FileStates are reference counted. The initial reference count is zero + // and the caller must call Ref() at least once. + FileState() : refs_(0), size_(0) {} + + // Increase the reference count. + void Ref() { + MutexLock lock(&refs_mutex_); + ++refs_; + } + + // Decrease the reference count. Delete if this is the last reference. + void Unref() { + bool do_delete = false; + + { + MutexLock lock(&refs_mutex_); + --refs_; + assert(refs_ >= 0); + if (refs_ <= 0) { + do_delete = true; + } + } + + if (do_delete) { + delete this; + } + } + + uint64_t Size() const { return size_; } + + Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) const { + if (offset > size_) { + return Status::IOError("Offset greater than file size."); + } + const uint64_t available = size_ - offset; + if (n > available) { + n = static_cast(available); + } + if (n == 0) { + *result = Slice(); + return Status::OK(); + } + + assert(offset / kBlockSize <= SIZE_MAX); + size_t block = static_cast(offset / kBlockSize); + size_t block_offset = offset % kBlockSize; + + if (n <= kBlockSize - block_offset) { + // The requested bytes are all in the first block. + *result = Slice(blocks_[block] + block_offset, n); + return Status::OK(); + } + + size_t bytes_to_copy = n; + char* dst = scratch; + + while (bytes_to_copy > 0) { + size_t avail = kBlockSize - block_offset; + if (avail > bytes_to_copy) { + avail = bytes_to_copy; + } + memcpy(dst, blocks_[block] + block_offset, avail); + + bytes_to_copy -= avail; + dst += avail; + block++; + block_offset = 0; + } + + *result = Slice(scratch, n); + return Status::OK(); + } + + Status Append(const Slice& data) { + const char* src = data.data(); + size_t src_len = data.size(); + + while (src_len > 0) { + size_t avail; + size_t offset = size_ % kBlockSize; + + if (offset != 0) { + // There is some room in the last block. + avail = kBlockSize - offset; + } else { + // No room in the last block; push new one. + blocks_.push_back(new char[kBlockSize]); + avail = kBlockSize; + } + + if (avail > src_len) { + avail = src_len; + } + memcpy(blocks_.back() + offset, src, avail); + src_len -= avail; + src += avail; + size_ += avail; + } + + return Status::OK(); + } + + private: + // Private since only Unref() should be used to delete it. + ~FileState() { + for (std::vector::iterator i = blocks_.begin(); i != blocks_.end(); + ++i) { + delete [] *i; + } + } + + // No copying allowed. + FileState(const FileState&); + void operator=(const FileState&); + + port::Mutex refs_mutex_; + int refs_; // Protected by refs_mutex_; + + // The following fields are not protected by any mutex. They are only mutable + // while the file is being written, and concurrent access is not allowed + // to writable files. + std::vector blocks_; + uint64_t size_; + + enum { kBlockSize = 8 * 1024 }; +}; + +class SequentialFileImpl : public SequentialFile { + public: + explicit SequentialFileImpl(FileState* file) : file_(file), pos_(0) { + file_->Ref(); + } + + ~SequentialFileImpl() { + file_->Unref(); + } + + virtual Status Read(size_t n, Slice* result, char* scratch) { + Status s = file_->Read(pos_, n, result, scratch); + if (s.ok()) { + pos_ += result->size(); + } + return s; + } + + virtual Status Skip(uint64_t n) { + if (pos_ > file_->Size()) { + return Status::IOError("pos_ > file_->Size()"); + } + const uint64_t available = file_->Size() - pos_; + if (n > available) { + n = available; + } + pos_ += n; + return Status::OK(); + } + + private: + FileState* file_; + uint64_t pos_; +}; + +class RandomAccessFileImpl : public RandomAccessFile { + public: + explicit RandomAccessFileImpl(FileState* file) : file_(file) { + file_->Ref(); + } + + ~RandomAccessFileImpl() { + file_->Unref(); + } + + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + return file_->Read(offset, n, result, scratch); + } + + private: + FileState* file_; +}; + +class WritableFileImpl : public WritableFile { + public: + WritableFileImpl(FileState* file) : file_(file) { + file_->Ref(); + } + + ~WritableFileImpl() { + file_->Unref(); + } + + virtual Status Append(const Slice& data) { + return file_->Append(data); + } + + virtual Status Close() { return Status::OK(); } + virtual Status Flush() { return Status::OK(); } + virtual Status Sync() { return Status::OK(); } + + private: + FileState* file_; +}; + +class NoOpLogger : public Logger { + public: + virtual void Logv(const char* format, va_list ap) { } +}; + +class InMemoryEnv : public EnvWrapper { + public: + explicit InMemoryEnv(Env* base_env) : EnvWrapper(base_env) { } + + virtual ~InMemoryEnv() { + for (FileSystem::iterator i = file_map_.begin(); i != file_map_.end(); ++i){ + i->second->Unref(); + } + } + + // Partial implementation of the Env interface. + virtual Status NewSequentialFile(const std::string& fname, + SequentialFile** result) { + MutexLock lock(&mutex_); + if (file_map_.find(fname) == file_map_.end()) { + *result = NULL; + return Status::IOError(fname, "File not found"); + } + + *result = new SequentialFileImpl(file_map_[fname]); + return Status::OK(); + } + + virtual Status NewRandomAccessFile(const std::string& fname, + RandomAccessFile** result) { + MutexLock lock(&mutex_); + if (file_map_.find(fname) == file_map_.end()) { + *result = NULL; + return Status::IOError(fname, "File not found"); + } + + *result = new RandomAccessFileImpl(file_map_[fname]); + return Status::OK(); + } + + virtual Status NewWritableFile(const std::string& fname, + WritableFile** result) { + MutexLock lock(&mutex_); + if (file_map_.find(fname) != file_map_.end()) { + DeleteFileInternal(fname); + } + + FileState* file = new FileState(); + file->Ref(); + file_map_[fname] = file; + + *result = new WritableFileImpl(file); + return Status::OK(); + } + + virtual bool FileExists(const std::string& fname) { + MutexLock lock(&mutex_); + return file_map_.find(fname) != file_map_.end(); + } + + virtual Status GetChildren(const std::string& dir, + std::vector* result) { + MutexLock lock(&mutex_); + result->clear(); + + for (FileSystem::iterator i = file_map_.begin(); i != file_map_.end(); ++i){ + const std::string& filename = i->first; + + if (filename.size() >= dir.size() + 1 && filename[dir.size()] == '/' && + Slice(filename).starts_with(Slice(dir))) { + result->push_back(filename.substr(dir.size() + 1)); + } + } + + return Status::OK(); + } + + void DeleteFileInternal(const std::string& fname) { + if (file_map_.find(fname) == file_map_.end()) { + return; + } + + file_map_[fname]->Unref(); + file_map_.erase(fname); + } + + virtual Status DeleteFile(const std::string& fname) { + MutexLock lock(&mutex_); + if (file_map_.find(fname) == file_map_.end()) { + return Status::IOError(fname, "File not found"); + } + + DeleteFileInternal(fname); + return Status::OK(); + } + + virtual Status CreateDir(const std::string& dirname) { + return Status::OK(); + } + + virtual Status DeleteDir(const std::string& dirname) { + return Status::OK(); + } + + virtual Status GetFileSize(const std::string& fname, uint64_t* file_size) { + MutexLock lock(&mutex_); + if (file_map_.find(fname) == file_map_.end()) { + return Status::IOError(fname, "File not found"); + } + + *file_size = file_map_[fname]->Size(); + return Status::OK(); + } + + virtual Status RenameFile(const std::string& src, + const std::string& target) { + MutexLock lock(&mutex_); + if (file_map_.find(src) == file_map_.end()) { + return Status::IOError(src, "File not found"); + } + + DeleteFileInternal(target); + file_map_[target] = file_map_[src]; + file_map_.erase(src); + return Status::OK(); + } + + virtual Status LockFile(const std::string& fname, FileLock** lock) { + *lock = new FileLock; + return Status::OK(); + } + + virtual Status UnlockFile(FileLock* lock) { + delete lock; + return Status::OK(); + } + + virtual Status GetTestDirectory(std::string* path) { + *path = "/test"; + return Status::OK(); + } + + virtual Status NewLogger(const std::string& fname, Logger** result) { + *result = new NoOpLogger; + return Status::OK(); + } + + private: + // Map from filenames to FileState objects, representing a simple file system. + typedef std::map FileSystem; + port::Mutex mutex_; + FileSystem file_map_; // Protected by mutex_. +}; + +} // namespace + +Env* NewMemEnv(Env* base_env) { + return new InMemoryEnv(base_env); +} + +} // namespace leveldb diff --git a/src/leveldb/helpers/memenv/memenv.h b/src/leveldb/helpers/memenv/memenv.h new file mode 100644 index 00000000..03b88de7 --- /dev/null +++ b/src/leveldb/helpers/memenv/memenv.h @@ -0,0 +1,20 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_HELPERS_MEMENV_MEMENV_H_ +#define STORAGE_LEVELDB_HELPERS_MEMENV_MEMENV_H_ + +namespace leveldb { + +class Env; + +// Returns a new environment that stores its data in memory and delegates +// all non-file-storage tasks to base_env. The caller must delete the result +// when it is no longer needed. +// *base_env must remain live while the result is in use. +Env* NewMemEnv(Env* base_env); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_HELPERS_MEMENV_MEMENV_H_ diff --git a/src/leveldb/helpers/memenv/memenv_test.cc b/src/leveldb/helpers/memenv/memenv_test.cc new file mode 100644 index 00000000..a44310fe --- /dev/null +++ b/src/leveldb/helpers/memenv/memenv_test.cc @@ -0,0 +1,232 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "helpers/memenv/memenv.h" + +#include "db/db_impl.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "util/testharness.h" +#include +#include + +namespace leveldb { + +class MemEnvTest { + public: + Env* env_; + + MemEnvTest() + : env_(NewMemEnv(Env::Default())) { + } + ~MemEnvTest() { + delete env_; + } +}; + +TEST(MemEnvTest, Basics) { + uint64_t file_size; + WritableFile* writable_file; + std::vector children; + + ASSERT_OK(env_->CreateDir("/dir")); + + // Check that the directory is empty. + ASSERT_TRUE(!env_->FileExists("/dir/non_existent")); + ASSERT_TRUE(!env_->GetFileSize("/dir/non_existent", &file_size).ok()); + ASSERT_OK(env_->GetChildren("/dir", &children)); + ASSERT_EQ(0, children.size()); + + // Create a file. + ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file)); + delete writable_file; + + // Check that the file exists. + ASSERT_TRUE(env_->FileExists("/dir/f")); + ASSERT_OK(env_->GetFileSize("/dir/f", &file_size)); + ASSERT_EQ(0, file_size); + ASSERT_OK(env_->GetChildren("/dir", &children)); + ASSERT_EQ(1, children.size()); + ASSERT_EQ("f", children[0]); + + // Write to the file. + ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file)); + ASSERT_OK(writable_file->Append("abc")); + delete writable_file; + + // Check for expected size. + ASSERT_OK(env_->GetFileSize("/dir/f", &file_size)); + ASSERT_EQ(3, file_size); + + // Check that renaming works. + ASSERT_TRUE(!env_->RenameFile("/dir/non_existent", "/dir/g").ok()); + ASSERT_OK(env_->RenameFile("/dir/f", "/dir/g")); + ASSERT_TRUE(!env_->FileExists("/dir/f")); + ASSERT_TRUE(env_->FileExists("/dir/g")); + ASSERT_OK(env_->GetFileSize("/dir/g", &file_size)); + ASSERT_EQ(3, file_size); + + // Check that opening non-existent file fails. + SequentialFile* seq_file; + RandomAccessFile* rand_file; + ASSERT_TRUE(!env_->NewSequentialFile("/dir/non_existent", &seq_file).ok()); + ASSERT_TRUE(!seq_file); + ASSERT_TRUE(!env_->NewRandomAccessFile("/dir/non_existent", &rand_file).ok()); + ASSERT_TRUE(!rand_file); + + // Check that deleting works. + ASSERT_TRUE(!env_->DeleteFile("/dir/non_existent").ok()); + ASSERT_OK(env_->DeleteFile("/dir/g")); + ASSERT_TRUE(!env_->FileExists("/dir/g")); + ASSERT_OK(env_->GetChildren("/dir", &children)); + ASSERT_EQ(0, children.size()); + ASSERT_OK(env_->DeleteDir("/dir")); +} + +TEST(MemEnvTest, ReadWrite) { + WritableFile* writable_file; + SequentialFile* seq_file; + RandomAccessFile* rand_file; + Slice result; + char scratch[100]; + + ASSERT_OK(env_->CreateDir("/dir")); + + ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file)); + ASSERT_OK(writable_file->Append("hello ")); + ASSERT_OK(writable_file->Append("world")); + delete writable_file; + + // Read sequentially. + ASSERT_OK(env_->NewSequentialFile("/dir/f", &seq_file)); + ASSERT_OK(seq_file->Read(5, &result, scratch)); // Read "hello". + ASSERT_EQ(0, result.compare("hello")); + ASSERT_OK(seq_file->Skip(1)); + ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Read "world". + ASSERT_EQ(0, result.compare("world")); + ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Try reading past EOF. + ASSERT_EQ(0, result.size()); + ASSERT_OK(seq_file->Skip(100)); // Try to skip past end of file. + ASSERT_OK(seq_file->Read(1000, &result, scratch)); + ASSERT_EQ(0, result.size()); + delete seq_file; + + // Random reads. + ASSERT_OK(env_->NewRandomAccessFile("/dir/f", &rand_file)); + ASSERT_OK(rand_file->Read(6, 5, &result, scratch)); // Read "world". + ASSERT_EQ(0, result.compare("world")); + ASSERT_OK(rand_file->Read(0, 5, &result, scratch)); // Read "hello". + ASSERT_EQ(0, result.compare("hello")); + ASSERT_OK(rand_file->Read(10, 100, &result, scratch)); // Read "d". + ASSERT_EQ(0, result.compare("d")); + + // Too high offset. + ASSERT_TRUE(!rand_file->Read(1000, 5, &result, scratch).ok()); + delete rand_file; +} + +TEST(MemEnvTest, Locks) { + FileLock* lock; + + // These are no-ops, but we test they return success. + ASSERT_OK(env_->LockFile("some file", &lock)); + ASSERT_OK(env_->UnlockFile(lock)); +} + +TEST(MemEnvTest, Misc) { + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + ASSERT_TRUE(!test_dir.empty()); + + WritableFile* writable_file; + ASSERT_OK(env_->NewWritableFile("/a/b", &writable_file)); + + // These are no-ops, but we test they return success. + ASSERT_OK(writable_file->Sync()); + ASSERT_OK(writable_file->Flush()); + ASSERT_OK(writable_file->Close()); + delete writable_file; +} + +TEST(MemEnvTest, LargeWrite) { + const size_t kWriteSize = 300 * 1024; + char* scratch = new char[kWriteSize * 2]; + + std::string write_data; + for (size_t i = 0; i < kWriteSize; ++i) { + write_data.append(1, static_cast(i)); + } + + WritableFile* writable_file; + ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file)); + ASSERT_OK(writable_file->Append("foo")); + ASSERT_OK(writable_file->Append(write_data)); + delete writable_file; + + SequentialFile* seq_file; + Slice result; + ASSERT_OK(env_->NewSequentialFile("/dir/f", &seq_file)); + ASSERT_OK(seq_file->Read(3, &result, scratch)); // Read "foo". + ASSERT_EQ(0, result.compare("foo")); + + size_t read = 0; + std::string read_data; + while (read < kWriteSize) { + ASSERT_OK(seq_file->Read(kWriteSize - read, &result, scratch)); + read_data.append(result.data(), result.size()); + read += result.size(); + } + ASSERT_TRUE(write_data == read_data); + delete seq_file; + delete [] scratch; +} + +TEST(MemEnvTest, DBTest) { + Options options; + options.create_if_missing = true; + options.env = env_; + DB* db; + + const Slice keys[] = {Slice("aaa"), Slice("bbb"), Slice("ccc")}; + const Slice vals[] = {Slice("foo"), Slice("bar"), Slice("baz")}; + + ASSERT_OK(DB::Open(options, "/dir/db", &db)); + for (size_t i = 0; i < 3; ++i) { + ASSERT_OK(db->Put(WriteOptions(), keys[i], vals[i])); + } + + for (size_t i = 0; i < 3; ++i) { + std::string res; + ASSERT_OK(db->Get(ReadOptions(), keys[i], &res)); + ASSERT_TRUE(res == vals[i]); + } + + Iterator* iterator = db->NewIterator(ReadOptions()); + iterator->SeekToFirst(); + for (size_t i = 0; i < 3; ++i) { + ASSERT_TRUE(iterator->Valid()); + ASSERT_TRUE(keys[i] == iterator->key()); + ASSERT_TRUE(vals[i] == iterator->value()); + iterator->Next(); + } + ASSERT_TRUE(!iterator->Valid()); + delete iterator; + + DBImpl* dbi = reinterpret_cast(db); + ASSERT_OK(dbi->TEST_CompactMemTable()); + + for (size_t i = 0; i < 3; ++i) { + std::string res; + ASSERT_OK(db->Get(ReadOptions(), keys[i], &res)); + ASSERT_TRUE(res == vals[i]); + } + + delete db; +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/include/leveldb/c.h b/src/leveldb/include/leveldb/c.h new file mode 100644 index 00000000..1048fe3b --- /dev/null +++ b/src/leveldb/include/leveldb/c.h @@ -0,0 +1,290 @@ +/* Copyright (c) 2011 The LevelDB Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. See the AUTHORS file for names of contributors. + + C bindings for leveldb. May be useful as a stable ABI that can be + used by programs that keep leveldb in a shared library, or for + a JNI api. + + Does not support: + . getters for the option types + . custom comparators that implement key shortening + . custom iter, db, env, cache implementations using just the C bindings + + Some conventions: + + (1) We expose just opaque struct pointers and functions to clients. + This allows us to change internal representations without having to + recompile clients. + + (2) For simplicity, there is no equivalent to the Slice type. Instead, + the caller has to pass the pointer and length as separate + arguments. + + (3) Errors are represented by a null-terminated c string. NULL + means no error. All operations that can raise an error are passed + a "char** errptr" as the last argument. One of the following must + be true on entry: + *errptr == NULL + *errptr points to a malloc()ed null-terminated error message + (On Windows, *errptr must have been malloc()-ed by this library.) + On success, a leveldb routine leaves *errptr unchanged. + On failure, leveldb frees the old value of *errptr and + set *errptr to a malloc()ed error message. + + (4) Bools have the type unsigned char (0 == false; rest == true) + + (5) All of the pointer arguments must be non-NULL. +*/ + +#ifndef STORAGE_LEVELDB_INCLUDE_C_H_ +#define STORAGE_LEVELDB_INCLUDE_C_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/* Exported types */ + +typedef struct leveldb_t leveldb_t; +typedef struct leveldb_cache_t leveldb_cache_t; +typedef struct leveldb_comparator_t leveldb_comparator_t; +typedef struct leveldb_env_t leveldb_env_t; +typedef struct leveldb_filelock_t leveldb_filelock_t; +typedef struct leveldb_filterpolicy_t leveldb_filterpolicy_t; +typedef struct leveldb_iterator_t leveldb_iterator_t; +typedef struct leveldb_logger_t leveldb_logger_t; +typedef struct leveldb_options_t leveldb_options_t; +typedef struct leveldb_randomfile_t leveldb_randomfile_t; +typedef struct leveldb_readoptions_t leveldb_readoptions_t; +typedef struct leveldb_seqfile_t leveldb_seqfile_t; +typedef struct leveldb_snapshot_t leveldb_snapshot_t; +typedef struct leveldb_writablefile_t leveldb_writablefile_t; +typedef struct leveldb_writebatch_t leveldb_writebatch_t; +typedef struct leveldb_writeoptions_t leveldb_writeoptions_t; + +/* DB operations */ + +extern leveldb_t* leveldb_open( + const leveldb_options_t* options, + const char* name, + char** errptr); + +extern void leveldb_close(leveldb_t* db); + +extern void leveldb_put( + leveldb_t* db, + const leveldb_writeoptions_t* options, + const char* key, size_t keylen, + const char* val, size_t vallen, + char** errptr); + +extern void leveldb_delete( + leveldb_t* db, + const leveldb_writeoptions_t* options, + const char* key, size_t keylen, + char** errptr); + +extern void leveldb_write( + leveldb_t* db, + const leveldb_writeoptions_t* options, + leveldb_writebatch_t* batch, + char** errptr); + +/* Returns NULL if not found. A malloc()ed array otherwise. + Stores the length of the array in *vallen. */ +extern char* leveldb_get( + leveldb_t* db, + const leveldb_readoptions_t* options, + const char* key, size_t keylen, + size_t* vallen, + char** errptr); + +extern leveldb_iterator_t* leveldb_create_iterator( + leveldb_t* db, + const leveldb_readoptions_t* options); + +extern const leveldb_snapshot_t* leveldb_create_snapshot( + leveldb_t* db); + +extern void leveldb_release_snapshot( + leveldb_t* db, + const leveldb_snapshot_t* snapshot); + +/* Returns NULL if property name is unknown. + Else returns a pointer to a malloc()-ed null-terminated value. */ +extern char* leveldb_property_value( + leveldb_t* db, + const char* propname); + +extern void leveldb_approximate_sizes( + leveldb_t* db, + int num_ranges, + const char* const* range_start_key, const size_t* range_start_key_len, + const char* const* range_limit_key, const size_t* range_limit_key_len, + uint64_t* sizes); + +extern void leveldb_compact_range( + leveldb_t* db, + const char* start_key, size_t start_key_len, + const char* limit_key, size_t limit_key_len); + +/* Management operations */ + +extern void leveldb_destroy_db( + const leveldb_options_t* options, + const char* name, + char** errptr); + +extern void leveldb_repair_db( + const leveldb_options_t* options, + const char* name, + char** errptr); + +/* Iterator */ + +extern void leveldb_iter_destroy(leveldb_iterator_t*); +extern unsigned char leveldb_iter_valid(const leveldb_iterator_t*); +extern void leveldb_iter_seek_to_first(leveldb_iterator_t*); +extern void leveldb_iter_seek_to_last(leveldb_iterator_t*); +extern void leveldb_iter_seek(leveldb_iterator_t*, const char* k, size_t klen); +extern void leveldb_iter_next(leveldb_iterator_t*); +extern void leveldb_iter_prev(leveldb_iterator_t*); +extern const char* leveldb_iter_key(const leveldb_iterator_t*, size_t* klen); +extern const char* leveldb_iter_value(const leveldb_iterator_t*, size_t* vlen); +extern void leveldb_iter_get_error(const leveldb_iterator_t*, char** errptr); + +/* Write batch */ + +extern leveldb_writebatch_t* leveldb_writebatch_create(); +extern void leveldb_writebatch_destroy(leveldb_writebatch_t*); +extern void leveldb_writebatch_clear(leveldb_writebatch_t*); +extern void leveldb_writebatch_put( + leveldb_writebatch_t*, + const char* key, size_t klen, + const char* val, size_t vlen); +extern void leveldb_writebatch_delete( + leveldb_writebatch_t*, + const char* key, size_t klen); +extern void leveldb_writebatch_iterate( + leveldb_writebatch_t*, + void* state, + void (*put)(void*, const char* k, size_t klen, const char* v, size_t vlen), + void (*deleted)(void*, const char* k, size_t klen)); + +/* Options */ + +extern leveldb_options_t* leveldb_options_create(); +extern void leveldb_options_destroy(leveldb_options_t*); +extern void leveldb_options_set_comparator( + leveldb_options_t*, + leveldb_comparator_t*); +extern void leveldb_options_set_filter_policy( + leveldb_options_t*, + leveldb_filterpolicy_t*); +extern void leveldb_options_set_create_if_missing( + leveldb_options_t*, unsigned char); +extern void leveldb_options_set_error_if_exists( + leveldb_options_t*, unsigned char); +extern void leveldb_options_set_paranoid_checks( + leveldb_options_t*, unsigned char); +extern void leveldb_options_set_env(leveldb_options_t*, leveldb_env_t*); +extern void leveldb_options_set_info_log(leveldb_options_t*, leveldb_logger_t*); +extern void leveldb_options_set_write_buffer_size(leveldb_options_t*, size_t); +extern void leveldb_options_set_max_open_files(leveldb_options_t*, int); +extern void leveldb_options_set_cache(leveldb_options_t*, leveldb_cache_t*); +extern void leveldb_options_set_block_size(leveldb_options_t*, size_t); +extern void leveldb_options_set_block_restart_interval(leveldb_options_t*, int); + +enum { + leveldb_no_compression = 0, + leveldb_snappy_compression = 1 +}; +extern void leveldb_options_set_compression(leveldb_options_t*, int); + +/* Comparator */ + +extern leveldb_comparator_t* leveldb_comparator_create( + void* state, + void (*destructor)(void*), + int (*compare)( + void*, + const char* a, size_t alen, + const char* b, size_t blen), + const char* (*name)(void*)); +extern void leveldb_comparator_destroy(leveldb_comparator_t*); + +/* Filter policy */ + +extern leveldb_filterpolicy_t* leveldb_filterpolicy_create( + void* state, + void (*destructor)(void*), + char* (*create_filter)( + void*, + const char* const* key_array, const size_t* key_length_array, + int num_keys, + size_t* filter_length), + unsigned char (*key_may_match)( + void*, + const char* key, size_t length, + const char* filter, size_t filter_length), + const char* (*name)(void*)); +extern void leveldb_filterpolicy_destroy(leveldb_filterpolicy_t*); + +extern leveldb_filterpolicy_t* leveldb_filterpolicy_create_bloom( + int bits_per_key); + +/* Read options */ + +extern leveldb_readoptions_t* leveldb_readoptions_create(); +extern void leveldb_readoptions_destroy(leveldb_readoptions_t*); +extern void leveldb_readoptions_set_verify_checksums( + leveldb_readoptions_t*, + unsigned char); +extern void leveldb_readoptions_set_fill_cache( + leveldb_readoptions_t*, unsigned char); +extern void leveldb_readoptions_set_snapshot( + leveldb_readoptions_t*, + const leveldb_snapshot_t*); + +/* Write options */ + +extern leveldb_writeoptions_t* leveldb_writeoptions_create(); +extern void leveldb_writeoptions_destroy(leveldb_writeoptions_t*); +extern void leveldb_writeoptions_set_sync( + leveldb_writeoptions_t*, unsigned char); + +/* Cache */ + +extern leveldb_cache_t* leveldb_cache_create_lru(size_t capacity); +extern void leveldb_cache_destroy(leveldb_cache_t* cache); + +/* Env */ + +extern leveldb_env_t* leveldb_create_default_env(); +extern void leveldb_env_destroy(leveldb_env_t*); + +/* Utility */ + +/* Calls free(ptr). + REQUIRES: ptr was malloc()-ed and returned by one of the routines + in this file. Note that in certain cases (typically on Windows), you + may need to call this routine instead of free(ptr) to dispose of + malloc()-ed memory returned by this library. */ +extern void leveldb_free(void* ptr); + +/* Return the major version number for this release. */ +extern int leveldb_major_version(); + +/* Return the minor version number for this release. */ +extern int leveldb_minor_version(); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* STORAGE_LEVELDB_INCLUDE_C_H_ */ diff --git a/src/leveldb/include/leveldb/cache.h b/src/leveldb/include/leveldb/cache.h new file mode 100644 index 00000000..1a201e5e --- /dev/null +++ b/src/leveldb/include/leveldb/cache.h @@ -0,0 +1,99 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// A Cache is an interface that maps keys to values. It has internal +// synchronization and may be safely accessed concurrently from +// multiple threads. It may automatically evict entries to make room +// for new entries. Values have a specified charge against the cache +// capacity. For example, a cache where the values are variable +// length strings, may use the length of the string as the charge for +// the string. +// +// A builtin cache implementation with a least-recently-used eviction +// policy is provided. Clients may use their own implementations if +// they want something more sophisticated (like scan-resistance, a +// custom eviction policy, variable cache sizing, etc.) + +#ifndef STORAGE_LEVELDB_INCLUDE_CACHE_H_ +#define STORAGE_LEVELDB_INCLUDE_CACHE_H_ + +#include +#include "leveldb/slice.h" + +namespace leveldb { + +class Cache; + +// Create a new cache with a fixed size capacity. This implementation +// of Cache uses a least-recently-used eviction policy. +extern Cache* NewLRUCache(size_t capacity); + +class Cache { + public: + Cache() { } + + // Destroys all existing entries by calling the "deleter" + // function that was passed to the constructor. + virtual ~Cache(); + + // Opaque handle to an entry stored in the cache. + struct Handle { }; + + // Insert a mapping from key->value into the cache and assign it + // the specified charge against the total cache capacity. + // + // Returns a handle that corresponds to the mapping. The caller + // must call this->Release(handle) when the returned mapping is no + // longer needed. + // + // When the inserted entry is no longer needed, the key and + // value will be passed to "deleter". + virtual Handle* Insert(const Slice& key, void* value, size_t charge, + void (*deleter)(const Slice& key, void* value)) = 0; + + // If the cache has no mapping for "key", returns NULL. + // + // Else return a handle that corresponds to the mapping. The caller + // must call this->Release(handle) when the returned mapping is no + // longer needed. + virtual Handle* Lookup(const Slice& key) = 0; + + // Release a mapping returned by a previous Lookup(). + // REQUIRES: handle must not have been released yet. + // REQUIRES: handle must have been returned by a method on *this. + virtual void Release(Handle* handle) = 0; + + // Return the value encapsulated in a handle returned by a + // successful Lookup(). + // REQUIRES: handle must not have been released yet. + // REQUIRES: handle must have been returned by a method on *this. + virtual void* Value(Handle* handle) = 0; + + // If the cache contains entry for key, erase it. Note that the + // underlying entry will be kept around until all existing handles + // to it have been released. + virtual void Erase(const Slice& key) = 0; + + // Return a new numeric id. May be used by multiple clients who are + // sharing the same cache to partition the key space. Typically the + // client will allocate a new id at startup and prepend the id to + // its cache keys. + virtual uint64_t NewId() = 0; + + private: + void LRU_Remove(Handle* e); + void LRU_Append(Handle* e); + void Unref(Handle* e); + + struct Rep; + Rep* rep_; + + // No copying allowed + Cache(const Cache&); + void operator=(const Cache&); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_CACHE_H_ diff --git a/src/leveldb/include/leveldb/comparator.h b/src/leveldb/include/leveldb/comparator.h new file mode 100644 index 00000000..556b984c --- /dev/null +++ b/src/leveldb/include/leveldb/comparator.h @@ -0,0 +1,63 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_INCLUDE_COMPARATOR_H_ +#define STORAGE_LEVELDB_INCLUDE_COMPARATOR_H_ + +#include + +namespace leveldb { + +class Slice; + +// A Comparator object provides a total order across slices that are +// used as keys in an sstable or a database. A Comparator implementation +// must be thread-safe since leveldb may invoke its methods concurrently +// from multiple threads. +class Comparator { + public: + virtual ~Comparator(); + + // Three-way comparison. Returns value: + // < 0 iff "a" < "b", + // == 0 iff "a" == "b", + // > 0 iff "a" > "b" + virtual int Compare(const Slice& a, const Slice& b) const = 0; + + // The name of the comparator. Used to check for comparator + // mismatches (i.e., a DB created with one comparator is + // accessed using a different comparator. + // + // The client of this package should switch to a new name whenever + // the comparator implementation changes in a way that will cause + // the relative ordering of any two keys to change. + // + // Names starting with "leveldb." are reserved and should not be used + // by any clients of this package. + virtual const char* Name() const = 0; + + // Advanced functions: these are used to reduce the space requirements + // for internal data structures like index blocks. + + // If *start < limit, changes *start to a short string in [start,limit). + // Simple comparator implementations may return with *start unchanged, + // i.e., an implementation of this method that does nothing is correct. + virtual void FindShortestSeparator( + std::string* start, + const Slice& limit) const = 0; + + // Changes *key to a short string >= *key. + // Simple comparator implementations may return with *key unchanged, + // i.e., an implementation of this method that does nothing is correct. + virtual void FindShortSuccessor(std::string* key) const = 0; +}; + +// Return a builtin comparator that uses lexicographic byte-wise +// ordering. The result remains the property of this module and +// must not be deleted. +extern const Comparator* BytewiseComparator(); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_COMPARATOR_H_ diff --git a/src/leveldb/include/leveldb/db.h b/src/leveldb/include/leveldb/db.h new file mode 100644 index 00000000..4c169bf2 --- /dev/null +++ b/src/leveldb/include/leveldb/db.h @@ -0,0 +1,161 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_INCLUDE_DB_H_ +#define STORAGE_LEVELDB_INCLUDE_DB_H_ + +#include +#include +#include "leveldb/iterator.h" +#include "leveldb/options.h" + +namespace leveldb { + +// Update Makefile if you change these +static const int kMajorVersion = 1; +static const int kMinorVersion = 18; + +struct Options; +struct ReadOptions; +struct WriteOptions; +class WriteBatch; + +// Abstract handle to particular state of a DB. +// A Snapshot is an immutable object and can therefore be safely +// accessed from multiple threads without any external synchronization. +class Snapshot { + protected: + virtual ~Snapshot(); +}; + +// A range of keys +struct Range { + Slice start; // Included in the range + Slice limit; // Not included in the range + + Range() { } + Range(const Slice& s, const Slice& l) : start(s), limit(l) { } +}; + +// A DB is a persistent ordered map from keys to values. +// A DB is safe for concurrent access from multiple threads without +// any external synchronization. +class DB { + public: + // Open the database with the specified "name". + // Stores a pointer to a heap-allocated database in *dbptr and returns + // OK on success. + // Stores NULL in *dbptr and returns a non-OK status on error. + // Caller should delete *dbptr when it is no longer needed. + static Status Open(const Options& options, + const std::string& name, + DB** dbptr); + + DB() { } + virtual ~DB(); + + // Set the database entry for "key" to "value". Returns OK on success, + // and a non-OK status on error. + // Note: consider setting options.sync = true. + virtual Status Put(const WriteOptions& options, + const Slice& key, + const Slice& value) = 0; + + // Remove the database entry (if any) for "key". Returns OK on + // success, and a non-OK status on error. It is not an error if "key" + // did not exist in the database. + // Note: consider setting options.sync = true. + virtual Status Delete(const WriteOptions& options, const Slice& key) = 0; + + // Apply the specified updates to the database. + // Returns OK on success, non-OK on failure. + // Note: consider setting options.sync = true. + virtual Status Write(const WriteOptions& options, WriteBatch* updates) = 0; + + // If the database contains an entry for "key" store the + // corresponding value in *value and return OK. + // + // If there is no entry for "key" leave *value unchanged and return + // a status for which Status::IsNotFound() returns true. + // + // May return some other Status on an error. + virtual Status Get(const ReadOptions& options, + const Slice& key, std::string* value) = 0; + + // Return a heap-allocated iterator over the contents of the database. + // The result of NewIterator() is initially invalid (caller must + // call one of the Seek methods on the iterator before using it). + // + // Caller should delete the iterator when it is no longer needed. + // The returned iterator should be deleted before this db is deleted. + virtual Iterator* NewIterator(const ReadOptions& options) = 0; + + // Return a handle to the current DB state. Iterators created with + // this handle will all observe a stable snapshot of the current DB + // state. The caller must call ReleaseSnapshot(result) when the + // snapshot is no longer needed. + virtual const Snapshot* GetSnapshot() = 0; + + // Release a previously acquired snapshot. The caller must not + // use "snapshot" after this call. + virtual void ReleaseSnapshot(const Snapshot* snapshot) = 0; + + // DB implementations can export properties about their state + // via this method. If "property" is a valid property understood by this + // DB implementation, fills "*value" with its current value and returns + // true. Otherwise returns false. + // + // + // Valid property names include: + // + // "leveldb.num-files-at-level" - return the number of files at level , + // where is an ASCII representation of a level number (e.g. "0"). + // "leveldb.stats" - returns a multi-line string that describes statistics + // about the internal operation of the DB. + // "leveldb.sstables" - returns a multi-line string that describes all + // of the sstables that make up the db contents. + virtual bool GetProperty(const Slice& property, std::string* value) = 0; + + // For each i in [0,n-1], store in "sizes[i]", the approximate + // file system space used by keys in "[range[i].start .. range[i].limit)". + // + // Note that the returned sizes measure file system space usage, so + // if the user data compresses by a factor of ten, the returned + // sizes will be one-tenth the size of the corresponding user data size. + // + // The results may not include the sizes of recently written data. + virtual void GetApproximateSizes(const Range* range, int n, + uint64_t* sizes) = 0; + + // Compact the underlying storage for the key range [*begin,*end]. + // In particular, deleted and overwritten versions are discarded, + // and the data is rearranged to reduce the cost of operations + // needed to access the data. This operation should typically only + // be invoked by users who understand the underlying implementation. + // + // begin==NULL is treated as a key before all keys in the database. + // end==NULL is treated as a key after all keys in the database. + // Therefore the following call will compact the entire database: + // db->CompactRange(NULL, NULL); + virtual void CompactRange(const Slice* begin, const Slice* end) = 0; + + private: + // No copying allowed + DB(const DB&); + void operator=(const DB&); +}; + +// Destroy the contents of the specified database. +// Be very careful using this method. +Status DestroyDB(const std::string& name, const Options& options); + +// If a DB cannot be opened, you may attempt to call this method to +// resurrect as much of the contents of the database as possible. +// Some data may be lost, so be careful when calling this function +// on a database that contains important information. +Status RepairDB(const std::string& dbname, const Options& options); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_DB_H_ diff --git a/src/leveldb/include/leveldb/dumpfile.h b/src/leveldb/include/leveldb/dumpfile.h new file mode 100644 index 00000000..3f97fda1 --- /dev/null +++ b/src/leveldb/include/leveldb/dumpfile.h @@ -0,0 +1,25 @@ +// Copyright (c) 2014 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_INCLUDE_DUMPFILE_H_ +#define STORAGE_LEVELDB_INCLUDE_DUMPFILE_H_ + +#include +#include "leveldb/env.h" +#include "leveldb/status.h" + +namespace leveldb { + +// Dump the contents of the file named by fname in text format to +// *dst. Makes a sequence of dst->Append() calls; each call is passed +// the newline-terminated text corresponding to a single item found +// in the file. +// +// Returns a non-OK result if fname does not name a leveldb storage +// file, or if the file cannot be read. +Status DumpFile(Env* env, const std::string& fname, WritableFile* dst); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_DUMPFILE_H_ diff --git a/src/leveldb/include/leveldb/env.h b/src/leveldb/include/leveldb/env.h new file mode 100644 index 00000000..f709514d --- /dev/null +++ b/src/leveldb/include/leveldb/env.h @@ -0,0 +1,333 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// An Env is an interface used by the leveldb implementation to access +// operating system functionality like the filesystem etc. Callers +// may wish to provide a custom Env object when opening a database to +// get fine gain control; e.g., to rate limit file system operations. +// +// All Env implementations are safe for concurrent access from +// multiple threads without any external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_ENV_H_ +#define STORAGE_LEVELDB_INCLUDE_ENV_H_ + +#include +#include +#include +#include +#include "leveldb/status.h" + +namespace leveldb { + +class FileLock; +class Logger; +class RandomAccessFile; +class SequentialFile; +class Slice; +class WritableFile; + +class Env { + public: + Env() { } + virtual ~Env(); + + // Return a default environment suitable for the current operating + // system. Sophisticated users may wish to provide their own Env + // implementation instead of relying on this default environment. + // + // The result of Default() belongs to leveldb and must never be deleted. + static Env* Default(); + + // Create a brand new sequentially-readable file with the specified name. + // On success, stores a pointer to the new file in *result and returns OK. + // On failure stores NULL in *result and returns non-OK. If the file does + // not exist, returns a non-OK status. + // + // The returned file will only be accessed by one thread at a time. + virtual Status NewSequentialFile(const std::string& fname, + SequentialFile** result) = 0; + + // Create a brand new random access read-only file with the + // specified name. On success, stores a pointer to the new file in + // *result and returns OK. On failure stores NULL in *result and + // returns non-OK. If the file does not exist, returns a non-OK + // status. + // + // The returned file may be concurrently accessed by multiple threads. + virtual Status NewRandomAccessFile(const std::string& fname, + RandomAccessFile** result) = 0; + + // Create an object that writes to a new file with the specified + // name. Deletes any existing file with the same name and creates a + // new file. On success, stores a pointer to the new file in + // *result and returns OK. On failure stores NULL in *result and + // returns non-OK. + // + // The returned file will only be accessed by one thread at a time. + virtual Status NewWritableFile(const std::string& fname, + WritableFile** result) = 0; + + // Returns true iff the named file exists. + virtual bool FileExists(const std::string& fname) = 0; + + // Store in *result the names of the children of the specified directory. + // The names are relative to "dir". + // Original contents of *results are dropped. + virtual Status GetChildren(const std::string& dir, + std::vector* result) = 0; + + // Delete the named file. + virtual Status DeleteFile(const std::string& fname) = 0; + + // Create the specified directory. + virtual Status CreateDir(const std::string& dirname) = 0; + + // Delete the specified directory. + virtual Status DeleteDir(const std::string& dirname) = 0; + + // Store the size of fname in *file_size. + virtual Status GetFileSize(const std::string& fname, uint64_t* file_size) = 0; + + // Rename file src to target. + virtual Status RenameFile(const std::string& src, + const std::string& target) = 0; + + // Lock the specified file. Used to prevent concurrent access to + // the same db by multiple processes. On failure, stores NULL in + // *lock and returns non-OK. + // + // On success, stores a pointer to the object that represents the + // acquired lock in *lock and returns OK. The caller should call + // UnlockFile(*lock) to release the lock. If the process exits, + // the lock will be automatically released. + // + // If somebody else already holds the lock, finishes immediately + // with a failure. I.e., this call does not wait for existing locks + // to go away. + // + // May create the named file if it does not already exist. + virtual Status LockFile(const std::string& fname, FileLock** lock) = 0; + + // Release the lock acquired by a previous successful call to LockFile. + // REQUIRES: lock was returned by a successful LockFile() call + // REQUIRES: lock has not already been unlocked. + virtual Status UnlockFile(FileLock* lock) = 0; + + // Arrange to run "(*function)(arg)" once in a background thread. + // + // "function" may run in an unspecified thread. Multiple functions + // added to the same Env may run concurrently in different threads. + // I.e., the caller may not assume that background work items are + // serialized. + virtual void Schedule( + void (*function)(void* arg), + void* arg) = 0; + + // Start a new thread, invoking "function(arg)" within the new thread. + // When "function(arg)" returns, the thread will be destroyed. + virtual void StartThread(void (*function)(void* arg), void* arg) = 0; + + // *path is set to a temporary directory that can be used for testing. It may + // or many not have just been created. The directory may or may not differ + // between runs of the same process, but subsequent calls will return the + // same directory. + virtual Status GetTestDirectory(std::string* path) = 0; + + // Create and return a log file for storing informational messages. + virtual Status NewLogger(const std::string& fname, Logger** result) = 0; + + // Returns the number of micro-seconds since some fixed point in time. Only + // useful for computing deltas of time. + virtual uint64_t NowMicros() = 0; + + // Sleep/delay the thread for the prescribed number of micro-seconds. + virtual void SleepForMicroseconds(int micros) = 0; + + private: + // No copying allowed + Env(const Env&); + void operator=(const Env&); +}; + +// A file abstraction for reading sequentially through a file +class SequentialFile { + public: + SequentialFile() { } + virtual ~SequentialFile(); + + // Read up to "n" bytes from the file. "scratch[0..n-1]" may be + // written by this routine. Sets "*result" to the data that was + // read (including if fewer than "n" bytes were successfully read). + // May set "*result" to point at data in "scratch[0..n-1]", so + // "scratch[0..n-1]" must be live when "*result" is used. + // If an error was encountered, returns a non-OK status. + // + // REQUIRES: External synchronization + virtual Status Read(size_t n, Slice* result, char* scratch) = 0; + + // Skip "n" bytes from the file. This is guaranteed to be no + // slower that reading the same data, but may be faster. + // + // If end of file is reached, skipping will stop at the end of the + // file, and Skip will return OK. + // + // REQUIRES: External synchronization + virtual Status Skip(uint64_t n) = 0; + + private: + // No copying allowed + SequentialFile(const SequentialFile&); + void operator=(const SequentialFile&); +}; + +// A file abstraction for randomly reading the contents of a file. +class RandomAccessFile { + public: + RandomAccessFile() { } + virtual ~RandomAccessFile(); + + // Read up to "n" bytes from the file starting at "offset". + // "scratch[0..n-1]" may be written by this routine. Sets "*result" + // to the data that was read (including if fewer than "n" bytes were + // successfully read). May set "*result" to point at data in + // "scratch[0..n-1]", so "scratch[0..n-1]" must be live when + // "*result" is used. If an error was encountered, returns a non-OK + // status. + // + // Safe for concurrent use by multiple threads. + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const = 0; + + private: + // No copying allowed + RandomAccessFile(const RandomAccessFile&); + void operator=(const RandomAccessFile&); +}; + +// A file abstraction for sequential writing. The implementation +// must provide buffering since callers may append small fragments +// at a time to the file. +class WritableFile { + public: + WritableFile() { } + virtual ~WritableFile(); + + virtual Status Append(const Slice& data) = 0; + virtual Status Close() = 0; + virtual Status Flush() = 0; + virtual Status Sync() = 0; + + private: + // No copying allowed + WritableFile(const WritableFile&); + void operator=(const WritableFile&); +}; + +// An interface for writing log messages. +class Logger { + public: + Logger() { } + virtual ~Logger(); + + // Write an entry to the log file with the specified format. + virtual void Logv(const char* format, va_list ap) = 0; + + private: + // No copying allowed + Logger(const Logger&); + void operator=(const Logger&); +}; + + +// Identifies a locked file. +class FileLock { + public: + FileLock() { } + virtual ~FileLock(); + private: + // No copying allowed + FileLock(const FileLock&); + void operator=(const FileLock&); +}; + +// Log the specified data to *info_log if info_log is non-NULL. +extern void Log(Logger* info_log, const char* format, ...) +# if defined(__GNUC__) || defined(__clang__) + __attribute__((__format__ (__printf__, 2, 3))) +# endif + ; + +// A utility routine: write "data" to the named file. +extern Status WriteStringToFile(Env* env, const Slice& data, + const std::string& fname); + +// A utility routine: read contents of named file into *data +extern Status ReadFileToString(Env* env, const std::string& fname, + std::string* data); + +// An implementation of Env that forwards all calls to another Env. +// May be useful to clients who wish to override just part of the +// functionality of another Env. +class EnvWrapper : public Env { + public: + // Initialize an EnvWrapper that delegates all calls to *t + explicit EnvWrapper(Env* t) : target_(t) { } + virtual ~EnvWrapper(); + + // Return the target to which this Env forwards all calls + Env* target() const { return target_; } + + // The following text is boilerplate that forwards all methods to target() + Status NewSequentialFile(const std::string& f, SequentialFile** r) { + return target_->NewSequentialFile(f, r); + } + Status NewRandomAccessFile(const std::string& f, RandomAccessFile** r) { + return target_->NewRandomAccessFile(f, r); + } + Status NewWritableFile(const std::string& f, WritableFile** r) { + return target_->NewWritableFile(f, r); + } + bool FileExists(const std::string& f) { return target_->FileExists(f); } + Status GetChildren(const std::string& dir, std::vector* r) { + return target_->GetChildren(dir, r); + } + Status DeleteFile(const std::string& f) { return target_->DeleteFile(f); } + Status CreateDir(const std::string& d) { return target_->CreateDir(d); } + Status DeleteDir(const std::string& d) { return target_->DeleteDir(d); } + Status GetFileSize(const std::string& f, uint64_t* s) { + return target_->GetFileSize(f, s); + } + Status RenameFile(const std::string& s, const std::string& t) { + return target_->RenameFile(s, t); + } + Status LockFile(const std::string& f, FileLock** l) { + return target_->LockFile(f, l); + } + Status UnlockFile(FileLock* l) { return target_->UnlockFile(l); } + void Schedule(void (*f)(void*), void* a) { + return target_->Schedule(f, a); + } + void StartThread(void (*f)(void*), void* a) { + return target_->StartThread(f, a); + } + virtual Status GetTestDirectory(std::string* path) { + return target_->GetTestDirectory(path); + } + virtual Status NewLogger(const std::string& fname, Logger** result) { + return target_->NewLogger(fname, result); + } + uint64_t NowMicros() { + return target_->NowMicros(); + } + void SleepForMicroseconds(int micros) { + target_->SleepForMicroseconds(micros); + } + private: + Env* target_; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_ENV_H_ diff --git a/src/leveldb/include/leveldb/filter_policy.h b/src/leveldb/include/leveldb/filter_policy.h new file mode 100644 index 00000000..1fba0800 --- /dev/null +++ b/src/leveldb/include/leveldb/filter_policy.h @@ -0,0 +1,70 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// A database can be configured with a custom FilterPolicy object. +// This object is responsible for creating a small filter from a set +// of keys. These filters are stored in leveldb and are consulted +// automatically by leveldb to decide whether or not to read some +// information from disk. In many cases, a filter can cut down the +// number of disk seeks form a handful to a single disk seek per +// DB::Get() call. +// +// Most people will want to use the builtin bloom filter support (see +// NewBloomFilterPolicy() below). + +#ifndef STORAGE_LEVELDB_INCLUDE_FILTER_POLICY_H_ +#define STORAGE_LEVELDB_INCLUDE_FILTER_POLICY_H_ + +#include + +namespace leveldb { + +class Slice; + +class FilterPolicy { + public: + virtual ~FilterPolicy(); + + // Return the name of this policy. Note that if the filter encoding + // changes in an incompatible way, the name returned by this method + // must be changed. Otherwise, old incompatible filters may be + // passed to methods of this type. + virtual const char* Name() const = 0; + + // keys[0,n-1] contains a list of keys (potentially with duplicates) + // that are ordered according to the user supplied comparator. + // Append a filter that summarizes keys[0,n-1] to *dst. + // + // Warning: do not change the initial contents of *dst. Instead, + // append the newly constructed filter to *dst. + virtual void CreateFilter(const Slice* keys, int n, std::string* dst) + const = 0; + + // "filter" contains the data appended by a preceding call to + // CreateFilter() on this class. This method must return true if + // the key was in the list of keys passed to CreateFilter(). + // This method may return true or false if the key was not on the + // list, but it should aim to return false with a high probability. + virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const = 0; +}; + +// Return a new filter policy that uses a bloom filter with approximately +// the specified number of bits per key. A good value for bits_per_key +// is 10, which yields a filter with ~ 1% false positive rate. +// +// Callers must delete the result after any database that is using the +// result has been closed. +// +// Note: if you are using a custom comparator that ignores some parts +// of the keys being compared, you must not use NewBloomFilterPolicy() +// and must provide your own FilterPolicy that also ignores the +// corresponding parts of the keys. For example, if the comparator +// ignores trailing spaces, it would be incorrect to use a +// FilterPolicy (like NewBloomFilterPolicy) that does not ignore +// trailing spaces in keys. +extern const FilterPolicy* NewBloomFilterPolicy(int bits_per_key); + +} + +#endif // STORAGE_LEVELDB_INCLUDE_FILTER_POLICY_H_ diff --git a/src/leveldb/include/leveldb/iterator.h b/src/leveldb/include/leveldb/iterator.h new file mode 100644 index 00000000..76aced04 --- /dev/null +++ b/src/leveldb/include/leveldb/iterator.h @@ -0,0 +1,100 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// An iterator yields a sequence of key/value pairs from a source. +// The following class defines the interface. Multiple implementations +// are provided by this library. In particular, iterators are provided +// to access the contents of a Table or a DB. +// +// Multiple threads can invoke const methods on an Iterator without +// external synchronization, but if any of the threads may call a +// non-const method, all threads accessing the same Iterator must use +// external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_ITERATOR_H_ +#define STORAGE_LEVELDB_INCLUDE_ITERATOR_H_ + +#include "leveldb/slice.h" +#include "leveldb/status.h" + +namespace leveldb { + +class Iterator { + public: + Iterator(); + virtual ~Iterator(); + + // An iterator is either positioned at a key/value pair, or + // not valid. This method returns true iff the iterator is valid. + virtual bool Valid() const = 0; + + // Position at the first key in the source. The iterator is Valid() + // after this call iff the source is not empty. + virtual void SeekToFirst() = 0; + + // Position at the last key in the source. The iterator is + // Valid() after this call iff the source is not empty. + virtual void SeekToLast() = 0; + + // Position at the first key in the source that at or past target + // The iterator is Valid() after this call iff the source contains + // an entry that comes at or past target. + virtual void Seek(const Slice& target) = 0; + + // Moves to the next entry in the source. After this call, Valid() is + // true iff the iterator was not positioned at the last entry in the source. + // REQUIRES: Valid() + virtual void Next() = 0; + + // Moves to the previous entry in the source. After this call, Valid() is + // true iff the iterator was not positioned at the first entry in source. + // REQUIRES: Valid() + virtual void Prev() = 0; + + // Return the key for the current entry. The underlying storage for + // the returned slice is valid only until the next modification of + // the iterator. + // REQUIRES: Valid() + virtual Slice key() const = 0; + + // Return the value for the current entry. The underlying storage for + // the returned slice is valid only until the next modification of + // the iterator. + // REQUIRES: Valid() + virtual Slice value() const = 0; + + // If an error has occurred, return it. Else return an ok status. + virtual Status status() const = 0; + + // Clients are allowed to register function/arg1/arg2 triples that + // will be invoked when this iterator is destroyed. + // + // Note that unlike all of the preceding methods, this method is + // not abstract and therefore clients should not override it. + typedef void (*CleanupFunction)(void* arg1, void* arg2); + void RegisterCleanup(CleanupFunction function, void* arg1, void* arg2); + + private: + struct Cleanup { + CleanupFunction function; + void* arg1; + void* arg2; + Cleanup* next; + }; + Cleanup cleanup_; + + // No copying allowed + Iterator(const Iterator&); + void operator=(const Iterator&); +}; + +// Return an empty iterator (yields nothing). +extern Iterator* NewEmptyIterator(); + +// Return an empty iterator with the specified status. +extern Iterator* NewErrorIterator(const Status& status); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_ITERATOR_H_ diff --git a/src/leveldb/include/leveldb/options.h b/src/leveldb/include/leveldb/options.h new file mode 100644 index 00000000..7c9b9734 --- /dev/null +++ b/src/leveldb/include/leveldb/options.h @@ -0,0 +1,195 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_INCLUDE_OPTIONS_H_ +#define STORAGE_LEVELDB_INCLUDE_OPTIONS_H_ + +#include + +namespace leveldb { + +class Cache; +class Comparator; +class Env; +class FilterPolicy; +class Logger; +class Snapshot; + +// DB contents are stored in a set of blocks, each of which holds a +// sequence of key,value pairs. Each block may be compressed before +// being stored in a file. The following enum describes which +// compression method (if any) is used to compress a block. +enum CompressionType { + // NOTE: do not change the values of existing entries, as these are + // part of the persistent format on disk. + kNoCompression = 0x0, + kSnappyCompression = 0x1 +}; + +// Options to control the behavior of a database (passed to DB::Open) +struct Options { + // ------------------- + // Parameters that affect behavior + + // Comparator used to define the order of keys in the table. + // Default: a comparator that uses lexicographic byte-wise ordering + // + // REQUIRES: The client must ensure that the comparator supplied + // here has the same name and orders keys *exactly* the same as the + // comparator provided to previous open calls on the same DB. + const Comparator* comparator; + + // If true, the database will be created if it is missing. + // Default: false + bool create_if_missing; + + // If true, an error is raised if the database already exists. + // Default: false + bool error_if_exists; + + // If true, the implementation will do aggressive checking of the + // data it is processing and will stop early if it detects any + // errors. This may have unforeseen ramifications: for example, a + // corruption of one DB entry may cause a large number of entries to + // become unreadable or for the entire DB to become unopenable. + // Default: false + bool paranoid_checks; + + // Use the specified object to interact with the environment, + // e.g. to read/write files, schedule background work, etc. + // Default: Env::Default() + Env* env; + + // Any internal progress/error information generated by the db will + // be written to info_log if it is non-NULL, or to a file stored + // in the same directory as the DB contents if info_log is NULL. + // Default: NULL + Logger* info_log; + + // ------------------- + // Parameters that affect performance + + // Amount of data to build up in memory (backed by an unsorted log + // on disk) before converting to a sorted on-disk file. + // + // Larger values increase performance, especially during bulk loads. + // Up to two write buffers may be held in memory at the same time, + // so you may wish to adjust this parameter to control memory usage. + // Also, a larger write buffer will result in a longer recovery time + // the next time the database is opened. + // + // Default: 4MB + size_t write_buffer_size; + + // Number of open files that can be used by the DB. You may need to + // increase this if your database has a large working set (budget + // one open file per 2MB of working set). + // + // Default: 1000 + int max_open_files; + + // Control over blocks (user data is stored in a set of blocks, and + // a block is the unit of reading from disk). + + // If non-NULL, use the specified cache for blocks. + // If NULL, leveldb will automatically create and use an 8MB internal cache. + // Default: NULL + Cache* block_cache; + + // Approximate size of user data packed per block. Note that the + // block size specified here corresponds to uncompressed data. The + // actual size of the unit read from disk may be smaller if + // compression is enabled. This parameter can be changed dynamically. + // + // Default: 4K + size_t block_size; + + // Number of keys between restart points for delta encoding of keys. + // This parameter can be changed dynamically. Most clients should + // leave this parameter alone. + // + // Default: 16 + int block_restart_interval; + + // Compress blocks using the specified compression algorithm. This + // parameter can be changed dynamically. + // + // Default: kSnappyCompression, which gives lightweight but fast + // compression. + // + // Typical speeds of kSnappyCompression on an Intel(R) Core(TM)2 2.4GHz: + // ~200-500MB/s compression + // ~400-800MB/s decompression + // Note that these speeds are significantly faster than most + // persistent storage speeds, and therefore it is typically never + // worth switching to kNoCompression. Even if the input data is + // incompressible, the kSnappyCompression implementation will + // efficiently detect that and will switch to uncompressed mode. + CompressionType compression; + + // If non-NULL, use the specified filter policy to reduce disk reads. + // Many applications will benefit from passing the result of + // NewBloomFilterPolicy() here. + // + // Default: NULL + const FilterPolicy* filter_policy; + + // Create an Options object with default values for all fields. + Options(); +}; + +// Options that control read operations +struct ReadOptions { + // If true, all data read from underlying storage will be + // verified against corresponding checksums. + // Default: false + bool verify_checksums; + + // Should the data read for this iteration be cached in memory? + // Callers may wish to set this field to false for bulk scans. + // Default: true + bool fill_cache; + + // If "snapshot" is non-NULL, read as of the supplied snapshot + // (which must belong to the DB that is being read and which must + // not have been released). If "snapshot" is NULL, use an implicit + // snapshot of the state at the beginning of this read operation. + // Default: NULL + const Snapshot* snapshot; + + ReadOptions() + : verify_checksums(false), + fill_cache(true), + snapshot(NULL) { + } +}; + +// Options that control write operations +struct WriteOptions { + // If true, the write will be flushed from the operating system + // buffer cache (by calling WritableFile::Sync()) before the write + // is considered complete. If this flag is true, writes will be + // slower. + // + // If this flag is false, and the machine crashes, some recent + // writes may be lost. Note that if it is just the process that + // crashes (i.e., the machine does not reboot), no writes will be + // lost even if sync==false. + // + // In other words, a DB write with sync==false has similar + // crash semantics as the "write()" system call. A DB write + // with sync==true has similar crash semantics to a "write()" + // system call followed by "fsync()". + // + // Default: false + bool sync; + + WriteOptions() + : sync(false) { + } +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_OPTIONS_H_ diff --git a/src/leveldb/include/leveldb/slice.h b/src/leveldb/include/leveldb/slice.h new file mode 100644 index 00000000..bc367986 --- /dev/null +++ b/src/leveldb/include/leveldb/slice.h @@ -0,0 +1,109 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Slice is a simple structure containing a pointer into some external +// storage and a size. The user of a Slice must ensure that the slice +// is not used after the corresponding external storage has been +// deallocated. +// +// Multiple threads can invoke const methods on a Slice without +// external synchronization, but if any of the threads may call a +// non-const method, all threads accessing the same Slice must use +// external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_SLICE_H_ +#define STORAGE_LEVELDB_INCLUDE_SLICE_H_ + +#include +#include +#include +#include + +namespace leveldb { + +class Slice { + public: + // Create an empty slice. + Slice() : data_(""), size_(0) { } + + // Create a slice that refers to d[0,n-1]. + Slice(const char* d, size_t n) : data_(d), size_(n) { } + + // Create a slice that refers to the contents of "s" + Slice(const std::string& s) : data_(s.data()), size_(s.size()) { } + + // Create a slice that refers to s[0,strlen(s)-1] + Slice(const char* s) : data_(s), size_(strlen(s)) { } + + // Return a pointer to the beginning of the referenced data + const char* data() const { return data_; } + + // Return the length (in bytes) of the referenced data + size_t size() const { return size_; } + + // Return true iff the length of the referenced data is zero + bool empty() const { return size_ == 0; } + + // Return the ith byte in the referenced data. + // REQUIRES: n < size() + char operator[](size_t n) const { + assert(n < size()); + return data_[n]; + } + + // Change this slice to refer to an empty array + void clear() { data_ = ""; size_ = 0; } + + // Drop the first "n" bytes from this slice. + void remove_prefix(size_t n) { + assert(n <= size()); + data_ += n; + size_ -= n; + } + + // Return a string that contains the copy of the referenced data. + std::string ToString() const { return std::string(data_, size_); } + + // Three-way comparison. Returns value: + // < 0 iff "*this" < "b", + // == 0 iff "*this" == "b", + // > 0 iff "*this" > "b" + int compare(const Slice& b) const; + + // Return true iff "x" is a prefix of "*this" + bool starts_with(const Slice& x) const { + return ((size_ >= x.size_) && + (memcmp(data_, x.data_, x.size_) == 0)); + } + + private: + const char* data_; + size_t size_; + + // Intentionally copyable +}; + +inline bool operator==(const Slice& x, const Slice& y) { + return ((x.size() == y.size()) && + (memcmp(x.data(), y.data(), x.size()) == 0)); +} + +inline bool operator!=(const Slice& x, const Slice& y) { + return !(x == y); +} + +inline int Slice::compare(const Slice& b) const { + const size_t min_len = (size_ < b.size_) ? size_ : b.size_; + int r = memcmp(data_, b.data_, min_len); + if (r == 0) { + if (size_ < b.size_) r = -1; + else if (size_ > b.size_) r = +1; + } + return r; +} + +} // namespace leveldb + + +#endif // STORAGE_LEVELDB_INCLUDE_SLICE_H_ diff --git a/src/leveldb/include/leveldb/status.h b/src/leveldb/include/leveldb/status.h new file mode 100644 index 00000000..11dbd4b4 --- /dev/null +++ b/src/leveldb/include/leveldb/status.h @@ -0,0 +1,106 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// A Status encapsulates the result of an operation. It may indicate success, +// or it may indicate an error with an associated error message. +// +// Multiple threads can invoke const methods on a Status without +// external synchronization, but if any of the threads may call a +// non-const method, all threads accessing the same Status must use +// external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_STATUS_H_ +#define STORAGE_LEVELDB_INCLUDE_STATUS_H_ + +#include +#include "leveldb/slice.h" + +namespace leveldb { + +class Status { + public: + // Create a success status. + Status() : state_(NULL) { } + ~Status() { delete[] state_; } + + // Copy the specified status. + Status(const Status& s); + void operator=(const Status& s); + + // Return a success status. + static Status OK() { return Status(); } + + // Return error status of an appropriate type. + static Status NotFound(const Slice& msg, const Slice& msg2 = Slice()) { + return Status(kNotFound, msg, msg2); + } + static Status Corruption(const Slice& msg, const Slice& msg2 = Slice()) { + return Status(kCorruption, msg, msg2); + } + static Status NotSupported(const Slice& msg, const Slice& msg2 = Slice()) { + return Status(kNotSupported, msg, msg2); + } + static Status InvalidArgument(const Slice& msg, const Slice& msg2 = Slice()) { + return Status(kInvalidArgument, msg, msg2); + } + static Status IOError(const Slice& msg, const Slice& msg2 = Slice()) { + return Status(kIOError, msg, msg2); + } + + // Returns true iff the status indicates success. + bool ok() const { return (state_ == NULL); } + + // Returns true iff the status indicates a NotFound error. + bool IsNotFound() const { return code() == kNotFound; } + + // Returns true iff the status indicates a Corruption error. + bool IsCorruption() const { return code() == kCorruption; } + + // Returns true iff the status indicates an IOError. + bool IsIOError() const { return code() == kIOError; } + + // Return a string representation of this status suitable for printing. + // Returns the string "OK" for success. + std::string ToString() const; + + private: + // OK status has a NULL state_. Otherwise, state_ is a new[] array + // of the following form: + // state_[0..3] == length of message + // state_[4] == code + // state_[5..] == message + const char* state_; + + enum Code { + kOk = 0, + kNotFound = 1, + kCorruption = 2, + kNotSupported = 3, + kInvalidArgument = 4, + kIOError = 5 + }; + + Code code() const { + return (state_ == NULL) ? kOk : static_cast(state_[4]); + } + + Status(Code code, const Slice& msg, const Slice& msg2); + static const char* CopyState(const char* s); +}; + +inline Status::Status(const Status& s) { + state_ = (s.state_ == NULL) ? NULL : CopyState(s.state_); +} +inline void Status::operator=(const Status& s) { + // The following condition catches both aliasing (when this == &s), + // and the common case where both s and *this are ok. + if (state_ != s.state_) { + delete[] state_; + state_ = (s.state_ == NULL) ? NULL : CopyState(s.state_); + } +} + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_STATUS_H_ diff --git a/src/leveldb/include/leveldb/table.h b/src/leveldb/include/leveldb/table.h new file mode 100644 index 00000000..a9746c3f --- /dev/null +++ b/src/leveldb/include/leveldb/table.h @@ -0,0 +1,85 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_INCLUDE_TABLE_H_ +#define STORAGE_LEVELDB_INCLUDE_TABLE_H_ + +#include +#include "leveldb/iterator.h" + +namespace leveldb { + +class Block; +class BlockHandle; +class Footer; +struct Options; +class RandomAccessFile; +struct ReadOptions; +class TableCache; + +// A Table is a sorted map from strings to strings. Tables are +// immutable and persistent. A Table may be safely accessed from +// multiple threads without external synchronization. +class Table { + public: + // Attempt to open the table that is stored in bytes [0..file_size) + // of "file", and read the metadata entries necessary to allow + // retrieving data from the table. + // + // If successful, returns ok and sets "*table" to the newly opened + // table. The client should delete "*table" when no longer needed. + // If there was an error while initializing the table, sets "*table" + // to NULL and returns a non-ok status. Does not take ownership of + // "*source", but the client must ensure that "source" remains live + // for the duration of the returned table's lifetime. + // + // *file must remain live while this Table is in use. + static Status Open(const Options& options, + RandomAccessFile* file, + uint64_t file_size, + Table** table); + + ~Table(); + + // Returns a new iterator over the table contents. + // The result of NewIterator() is initially invalid (caller must + // call one of the Seek methods on the iterator before using it). + Iterator* NewIterator(const ReadOptions&) const; + + // Given a key, return an approximate byte offset in the file where + // the data for that key begins (or would begin if the key were + // present in the file). The returned value is in terms of file + // bytes, and so includes effects like compression of the underlying data. + // E.g., the approximate offset of the last key in the table will + // be close to the file length. + uint64_t ApproximateOffsetOf(const Slice& key) const; + + private: + struct Rep; + Rep* rep_; + + explicit Table(Rep* rep) { rep_ = rep; } + static Iterator* BlockReader(void*, const ReadOptions&, const Slice&); + + // Calls (*handle_result)(arg, ...) with the entry found after a call + // to Seek(key). May not make such a call if filter policy says + // that key is not present. + friend class TableCache; + Status InternalGet( + const ReadOptions&, const Slice& key, + void* arg, + void (*handle_result)(void* arg, const Slice& k, const Slice& v)); + + + void ReadMeta(const Footer& footer); + void ReadFilter(const Slice& filter_handle_value); + + // No copying allowed + Table(const Table&); + void operator=(const Table&); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_TABLE_H_ diff --git a/src/leveldb/include/leveldb/table_builder.h b/src/leveldb/include/leveldb/table_builder.h new file mode 100644 index 00000000..5fd1dc71 --- /dev/null +++ b/src/leveldb/include/leveldb/table_builder.h @@ -0,0 +1,92 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// TableBuilder provides the interface used to build a Table +// (an immutable and sorted map from keys to values). +// +// Multiple threads can invoke const methods on a TableBuilder without +// external synchronization, but if any of the threads may call a +// non-const method, all threads accessing the same TableBuilder must use +// external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_TABLE_BUILDER_H_ +#define STORAGE_LEVELDB_INCLUDE_TABLE_BUILDER_H_ + +#include +#include "leveldb/options.h" +#include "leveldb/status.h" + +namespace leveldb { + +class BlockBuilder; +class BlockHandle; +class WritableFile; + +class TableBuilder { + public: + // Create a builder that will store the contents of the table it is + // building in *file. Does not close the file. It is up to the + // caller to close the file after calling Finish(). + TableBuilder(const Options& options, WritableFile* file); + + // REQUIRES: Either Finish() or Abandon() has been called. + ~TableBuilder(); + + // Change the options used by this builder. Note: only some of the + // option fields can be changed after construction. If a field is + // not allowed to change dynamically and its value in the structure + // passed to the constructor is different from its value in the + // structure passed to this method, this method will return an error + // without changing any fields. + Status ChangeOptions(const Options& options); + + // Add key,value to the table being constructed. + // REQUIRES: key is after any previously added key according to comparator. + // REQUIRES: Finish(), Abandon() have not been called + void Add(const Slice& key, const Slice& value); + + // Advanced operation: flush any buffered key/value pairs to file. + // Can be used to ensure that two adjacent entries never live in + // the same data block. Most clients should not need to use this method. + // REQUIRES: Finish(), Abandon() have not been called + void Flush(); + + // Return non-ok iff some error has been detected. + Status status() const; + + // Finish building the table. Stops using the file passed to the + // constructor after this function returns. + // REQUIRES: Finish(), Abandon() have not been called + Status Finish(); + + // Indicate that the contents of this builder should be abandoned. Stops + // using the file passed to the constructor after this function returns. + // If the caller is not going to call Finish(), it must call Abandon() + // before destroying this builder. + // REQUIRES: Finish(), Abandon() have not been called + void Abandon(); + + // Number of calls to Add() so far. + uint64_t NumEntries() const; + + // Size of the file generated so far. If invoked after a successful + // Finish() call, returns the size of the final generated file. + uint64_t FileSize() const; + + private: + bool ok() const { return status().ok(); } + void WriteBlock(BlockBuilder* block, BlockHandle* handle); + void WriteRawBlock(const Slice& data, CompressionType, BlockHandle* handle); + + struct Rep; + Rep* rep_; + + // No copying allowed + TableBuilder(const TableBuilder&); + void operator=(const TableBuilder&); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_TABLE_BUILDER_H_ diff --git a/src/leveldb/include/leveldb/write_batch.h b/src/leveldb/include/leveldb/write_batch.h new file mode 100644 index 00000000..ee9aab68 --- /dev/null +++ b/src/leveldb/include/leveldb/write_batch.h @@ -0,0 +1,64 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// WriteBatch holds a collection of updates to apply atomically to a DB. +// +// The updates are applied in the order in which they are added +// to the WriteBatch. For example, the value of "key" will be "v3" +// after the following batch is written: +// +// batch.Put("key", "v1"); +// batch.Delete("key"); +// batch.Put("key", "v2"); +// batch.Put("key", "v3"); +// +// Multiple threads can invoke const methods on a WriteBatch without +// external synchronization, but if any of the threads may call a +// non-const method, all threads accessing the same WriteBatch must use +// external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_WRITE_BATCH_H_ +#define STORAGE_LEVELDB_INCLUDE_WRITE_BATCH_H_ + +#include +#include "leveldb/status.h" + +namespace leveldb { + +class Slice; + +class WriteBatch { + public: + WriteBatch(); + ~WriteBatch(); + + // Store the mapping "key->value" in the database. + void Put(const Slice& key, const Slice& value); + + // If the database contains a mapping for "key", erase it. Else do nothing. + void Delete(const Slice& key); + + // Clear all updates buffered in this batch. + void Clear(); + + // Support for iterating over the contents of a batch. + class Handler { + public: + virtual ~Handler(); + virtual void Put(const Slice& key, const Slice& value) = 0; + virtual void Delete(const Slice& key) = 0; + }; + Status Iterate(Handler* handler) const; + + private: + friend class WriteBatchInternal; + + std::string rep_; // See comment in write_batch.cc for the format of rep_ + + // Intentionally copyable +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_WRITE_BATCH_H_ diff --git a/src/leveldb/issues/issue178_test.cc b/src/leveldb/issues/issue178_test.cc new file mode 100644 index 00000000..1b1cf8bb --- /dev/null +++ b/src/leveldb/issues/issue178_test.cc @@ -0,0 +1,92 @@ +// Copyright (c) 2013 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +// Test for issue 178: a manual compaction causes deleted data to reappear. +#include +#include +#include + +#include "leveldb/db.h" +#include "leveldb/write_batch.h" +#include "util/testharness.h" + +namespace { + +const int kNumKeys = 1100000; + +std::string Key1(int i) { + char buf[100]; + snprintf(buf, sizeof(buf), "my_key_%d", i); + return buf; +} + +std::string Key2(int i) { + return Key1(i) + "_xxx"; +} + +class Issue178 { }; + +TEST(Issue178, Test) { + // Get rid of any state from an old run. + std::string dbpath = leveldb::test::TmpDir() + "/leveldb_cbug_test"; + DestroyDB(dbpath, leveldb::Options()); + + // Open database. Disable compression since it affects the creation + // of layers and the code below is trying to test against a very + // specific scenario. + leveldb::DB* db; + leveldb::Options db_options; + db_options.create_if_missing = true; + db_options.compression = leveldb::kNoCompression; + ASSERT_OK(leveldb::DB::Open(db_options, dbpath, &db)); + + // create first key range + leveldb::WriteBatch batch; + for (size_t i = 0; i < kNumKeys; i++) { + batch.Put(Key1(i), "value for range 1 key"); + } + ASSERT_OK(db->Write(leveldb::WriteOptions(), &batch)); + + // create second key range + batch.Clear(); + for (size_t i = 0; i < kNumKeys; i++) { + batch.Put(Key2(i), "value for range 2 key"); + } + ASSERT_OK(db->Write(leveldb::WriteOptions(), &batch)); + + // delete second key range + batch.Clear(); + for (size_t i = 0; i < kNumKeys; i++) { + batch.Delete(Key2(i)); + } + ASSERT_OK(db->Write(leveldb::WriteOptions(), &batch)); + + // compact database + std::string start_key = Key1(0); + std::string end_key = Key1(kNumKeys - 1); + leveldb::Slice least(start_key.data(), start_key.size()); + leveldb::Slice greatest(end_key.data(), end_key.size()); + + // commenting out the line below causes the example to work correctly + db->CompactRange(&least, &greatest); + + // count the keys + leveldb::Iterator* iter = db->NewIterator(leveldb::ReadOptions()); + size_t num_keys = 0; + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + num_keys++; + } + delete iter; + ASSERT_EQ(kNumKeys, num_keys) << "Bad number of keys"; + + // close database + delete db; + DestroyDB(dbpath, leveldb::Options()); +} + +} // anonymous namespace + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/issues/issue200_test.cc b/src/leveldb/issues/issue200_test.cc new file mode 100644 index 00000000..1cec79f4 --- /dev/null +++ b/src/leveldb/issues/issue200_test.cc @@ -0,0 +1,59 @@ +// Copyright (c) 2013 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +// Test for issue 200: when iterator switches direction from backward +// to forward, the current key can be yielded unexpectedly if a new +// mutation has been added just before the current key. + +#include "leveldb/db.h" +#include "util/testharness.h" + +namespace leveldb { + +class Issue200 { }; + +TEST(Issue200, Test) { + // Get rid of any state from an old run. + std::string dbpath = test::TmpDir() + "/leveldb_issue200_test"; + DestroyDB(dbpath, Options()); + + DB *db; + Options options; + options.create_if_missing = true; + ASSERT_OK(DB::Open(options, dbpath, &db)); + + WriteOptions write_options; + ASSERT_OK(db->Put(write_options, "1", "b")); + ASSERT_OK(db->Put(write_options, "2", "c")); + ASSERT_OK(db->Put(write_options, "3", "d")); + ASSERT_OK(db->Put(write_options, "4", "e")); + ASSERT_OK(db->Put(write_options, "5", "f")); + + ReadOptions read_options; + Iterator *iter = db->NewIterator(read_options); + + // Add an element that should not be reflected in the iterator. + ASSERT_OK(db->Put(write_options, "25", "cd")); + + iter->Seek("5"); + ASSERT_EQ(iter->key().ToString(), "5"); + iter->Prev(); + ASSERT_EQ(iter->key().ToString(), "4"); + iter->Prev(); + ASSERT_EQ(iter->key().ToString(), "3"); + iter->Next(); + ASSERT_EQ(iter->key().ToString(), "4"); + iter->Next(); + ASSERT_EQ(iter->key().ToString(), "5"); + + delete iter; + delete db; + DestroyDB(dbpath, options); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/port/README b/src/leveldb/port/README new file mode 100644 index 00000000..422563e2 --- /dev/null +++ b/src/leveldb/port/README @@ -0,0 +1,10 @@ +This directory contains interfaces and implementations that isolate the +rest of the package from platform details. + +Code in the rest of the package includes "port.h" from this directory. +"port.h" in turn includes a platform specific "port_.h" file +that provides the platform specific implementation. + +See port_posix.h for an example of what must be provided in a platform +specific header file. + diff --git a/src/leveldb/port/atomic_pointer.h b/src/leveldb/port/atomic_pointer.h new file mode 100644 index 00000000..9bf091f7 --- /dev/null +++ b/src/leveldb/port/atomic_pointer.h @@ -0,0 +1,223 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +// AtomicPointer provides storage for a lock-free pointer. +// Platform-dependent implementation of AtomicPointer: +// - If the platform provides a cheap barrier, we use it with raw pointers +// - If is present (on newer versions of gcc, it is), we use +// a -based AtomicPointer. However we prefer the memory +// barrier based version, because at least on a gcc 4.4 32-bit build +// on linux, we have encountered a buggy implementation. +// Also, some implementations are much slower than a memory-barrier +// based implementation (~16ns for based acquire-load vs. ~1ns for +// a barrier based acquire-load). +// This code is based on atomicops-internals-* in Google's perftools: +// http://code.google.com/p/google-perftools/source/browse/#svn%2Ftrunk%2Fsrc%2Fbase + +#ifndef PORT_ATOMIC_POINTER_H_ +#define PORT_ATOMIC_POINTER_H_ + +#include +#ifdef LEVELDB_ATOMIC_PRESENT +#include +#endif +#ifdef OS_WIN +#include +#endif +#ifdef OS_MACOSX +#include +#endif + +#if defined(_M_X64) || defined(__x86_64__) +#define ARCH_CPU_X86_FAMILY 1 +#elif defined(_M_IX86) || defined(__i386__) || defined(__i386) +#define ARCH_CPU_X86_FAMILY 1 +#elif defined(__ARMEL__) +#define ARCH_CPU_ARM_FAMILY 1 +#elif defined(__ppc__) || defined(__powerpc__) || defined(__powerpc64__) +#define ARCH_CPU_PPC_FAMILY 1 +#endif + +namespace leveldb { +namespace port { + +// Define MemoryBarrier() if available +// Windows on x86 +#if defined(OS_WIN) && defined(COMPILER_MSVC) && defined(ARCH_CPU_X86_FAMILY) +// windows.h already provides a MemoryBarrier(void) macro +// http://msdn.microsoft.com/en-us/library/ms684208(v=vs.85).aspx +#define LEVELDB_HAVE_MEMORY_BARRIER + +// Mac OS +#elif defined(OS_MACOSX) +inline void MemoryBarrier() { + OSMemoryBarrier(); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + +// Gcc on x86 +#elif defined(ARCH_CPU_X86_FAMILY) && defined(__GNUC__) +inline void MemoryBarrier() { + // See http://gcc.gnu.org/ml/gcc/2003-04/msg01180.html for a discussion on + // this idiom. Also see http://en.wikipedia.org/wiki/Memory_ordering. + __asm__ __volatile__("" : : : "memory"); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + +// Sun Studio +#elif defined(ARCH_CPU_X86_FAMILY) && defined(__SUNPRO_CC) +inline void MemoryBarrier() { + // See http://gcc.gnu.org/ml/gcc/2003-04/msg01180.html for a discussion on + // this idiom. Also see http://en.wikipedia.org/wiki/Memory_ordering. + asm volatile("" : : : "memory"); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + +// ARM Linux +#elif defined(ARCH_CPU_ARM_FAMILY) && defined(__linux__) +typedef void (*LinuxKernelMemoryBarrierFunc)(void); +// The Linux ARM kernel provides a highly optimized device-specific memory +// barrier function at a fixed memory address that is mapped in every +// user-level process. +// +// This beats using CPU-specific instructions which are, on single-core +// devices, un-necessary and very costly (e.g. ARMv7-A "dmb" takes more +// than 180ns on a Cortex-A8 like the one on a Nexus One). Benchmarking +// shows that the extra function call cost is completely negligible on +// multi-core devices. +// +inline void MemoryBarrier() { + (*(LinuxKernelMemoryBarrierFunc)0xffff0fa0)(); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + +// PPC +#elif defined(ARCH_CPU_PPC_FAMILY) && defined(__GNUC__) +inline void MemoryBarrier() { + // TODO for some powerpc expert: is there a cheaper suitable variant? + // Perhaps by having separate barriers for acquire and release ops. + asm volatile("sync" : : : "memory"); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + +#endif + +// AtomicPointer built using platform-specific MemoryBarrier() +#if defined(LEVELDB_HAVE_MEMORY_BARRIER) +class AtomicPointer { + private: + void* rep_; + public: + AtomicPointer() { } + explicit AtomicPointer(void* p) : rep_(p) {} + inline void* NoBarrier_Load() const { return rep_; } + inline void NoBarrier_Store(void* v) { rep_ = v; } + inline void* Acquire_Load() const { + void* result = rep_; + MemoryBarrier(); + return result; + } + inline void Release_Store(void* v) { + MemoryBarrier(); + rep_ = v; + } +}; + +// AtomicPointer based on +#elif defined(LEVELDB_ATOMIC_PRESENT) +class AtomicPointer { + private: + std::atomic rep_; + public: + AtomicPointer() { } + explicit AtomicPointer(void* v) : rep_(v) { } + inline void* Acquire_Load() const { + return rep_.load(std::memory_order_acquire); + } + inline void Release_Store(void* v) { + rep_.store(v, std::memory_order_release); + } + inline void* NoBarrier_Load() const { + return rep_.load(std::memory_order_relaxed); + } + inline void NoBarrier_Store(void* v) { + rep_.store(v, std::memory_order_relaxed); + } +}; + +// Atomic pointer based on sparc memory barriers +#elif defined(__sparcv9) && defined(__GNUC__) +class AtomicPointer { + private: + void* rep_; + public: + AtomicPointer() { } + explicit AtomicPointer(void* v) : rep_(v) { } + inline void* Acquire_Load() const { + void* val; + __asm__ __volatile__ ( + "ldx [%[rep_]], %[val] \n\t" + "membar #LoadLoad|#LoadStore \n\t" + : [val] "=r" (val) + : [rep_] "r" (&rep_) + : "memory"); + return val; + } + inline void Release_Store(void* v) { + __asm__ __volatile__ ( + "membar #LoadStore|#StoreStore \n\t" + "stx %[v], [%[rep_]] \n\t" + : + : [rep_] "r" (&rep_), [v] "r" (v) + : "memory"); + } + inline void* NoBarrier_Load() const { return rep_; } + inline void NoBarrier_Store(void* v) { rep_ = v; } +}; + +// Atomic pointer based on ia64 acq/rel +#elif defined(__ia64) && defined(__GNUC__) +class AtomicPointer { + private: + void* rep_; + public: + AtomicPointer() { } + explicit AtomicPointer(void* v) : rep_(v) { } + inline void* Acquire_Load() const { + void* val ; + __asm__ __volatile__ ( + "ld8.acq %[val] = [%[rep_]] \n\t" + : [val] "=r" (val) + : [rep_] "r" (&rep_) + : "memory" + ); + return val; + } + inline void Release_Store(void* v) { + __asm__ __volatile__ ( + "st8.rel [%[rep_]] = %[v] \n\t" + : + : [rep_] "r" (&rep_), [v] "r" (v) + : "memory" + ); + } + inline void* NoBarrier_Load() const { return rep_; } + inline void NoBarrier_Store(void* v) { rep_ = v; } +}; + +// We have neither MemoryBarrier(), nor +#else +#error Please implement AtomicPointer for this platform. + +#endif + +#undef LEVELDB_HAVE_MEMORY_BARRIER +#undef ARCH_CPU_X86_FAMILY +#undef ARCH_CPU_ARM_FAMILY +#undef ARCH_CPU_PPC_FAMILY + +} // namespace port +} // namespace leveldb + +#endif // PORT_ATOMIC_POINTER_H_ diff --git a/src/leveldb/port/port.h b/src/leveldb/port/port.h new file mode 100644 index 00000000..4baafa8e --- /dev/null +++ b/src/leveldb/port/port.h @@ -0,0 +1,21 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_PORT_PORT_H_ +#define STORAGE_LEVELDB_PORT_PORT_H_ + +#include + +// Include the appropriate platform specific file below. If you are +// porting to a new platform, see "port_example.h" for documentation +// of what the new port_.h file must provide. +#if defined(LEVELDB_PLATFORM_POSIX) +# include "port/port_posix.h" +#elif defined(LEVELDB_PLATFORM_CHROMIUM) +# include "port/port_chromium.h" +#elif defined(LEVELDB_PLATFORM_WINDOWS) +# include "port/port_win.h" +#endif + +#endif // STORAGE_LEVELDB_PORT_PORT_H_ diff --git a/src/leveldb/port/port_example.h b/src/leveldb/port/port_example.h new file mode 100644 index 00000000..ab9e489b --- /dev/null +++ b/src/leveldb/port/port_example.h @@ -0,0 +1,135 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// This file contains the specification, but not the implementations, +// of the types/operations/etc. that should be defined by a platform +// specific port_.h file. Use this file as a reference for +// how to port this package to a new platform. + +#ifndef STORAGE_LEVELDB_PORT_PORT_EXAMPLE_H_ +#define STORAGE_LEVELDB_PORT_PORT_EXAMPLE_H_ + +namespace leveldb { +namespace port { + +// TODO(jorlow): Many of these belong more in the environment class rather than +// here. We should try moving them and see if it affects perf. + +// The following boolean constant must be true on a little-endian machine +// and false otherwise. +static const bool kLittleEndian = true /* or some other expression */; + +// ------------------ Threading ------------------- + +// A Mutex represents an exclusive lock. +class Mutex { + public: + Mutex(); + ~Mutex(); + + // Lock the mutex. Waits until other lockers have exited. + // Will deadlock if the mutex is already locked by this thread. + void Lock(); + + // Unlock the mutex. + // REQUIRES: This mutex was locked by this thread. + void Unlock(); + + // Optionally crash if this thread does not hold this mutex. + // The implementation must be fast, especially if NDEBUG is + // defined. The implementation is allowed to skip all checks. + void AssertHeld(); +}; + +class CondVar { + public: + explicit CondVar(Mutex* mu); + ~CondVar(); + + // Atomically release *mu and block on this condition variable until + // either a call to SignalAll(), or a call to Signal() that picks + // this thread to wakeup. + // REQUIRES: this thread holds *mu + void Wait(); + + // If there are some threads waiting, wake up at least one of them. + void Signal(); + + // Wake up all waiting threads. + void SignallAll(); +}; + +// Thread-safe initialization. +// Used as follows: +// static port::OnceType init_control = LEVELDB_ONCE_INIT; +// static void Initializer() { ... do something ...; } +// ... +// port::InitOnce(&init_control, &Initializer); +typedef intptr_t OnceType; +#define LEVELDB_ONCE_INIT 0 +extern void InitOnce(port::OnceType*, void (*initializer)()); + +// A type that holds a pointer that can be read or written atomically +// (i.e., without word-tearing.) +class AtomicPointer { + private: + intptr_t rep_; + public: + // Initialize to arbitrary value + AtomicPointer(); + + // Initialize to hold v + explicit AtomicPointer(void* v) : rep_(v) { } + + // Read and return the stored pointer with the guarantee that no + // later memory access (read or write) by this thread can be + // reordered ahead of this read. + void* Acquire_Load() const; + + // Set v as the stored pointer with the guarantee that no earlier + // memory access (read or write) by this thread can be reordered + // after this store. + void Release_Store(void* v); + + // Read the stored pointer with no ordering guarantees. + void* NoBarrier_Load() const; + + // Set va as the stored pointer with no ordering guarantees. + void NoBarrier_Store(void* v); +}; + +// ------------------ Compression ------------------- + +// Store the snappy compression of "input[0,input_length-1]" in *output. +// Returns false if snappy is not supported by this port. +extern bool Snappy_Compress(const char* input, size_t input_length, + std::string* output); + +// If input[0,input_length-1] looks like a valid snappy compressed +// buffer, store the size of the uncompressed data in *result and +// return true. Else return false. +extern bool Snappy_GetUncompressedLength(const char* input, size_t length, + size_t* result); + +// Attempt to snappy uncompress input[0,input_length-1] into *output. +// Returns true if successful, false if the input is invalid lightweight +// compressed data. +// +// REQUIRES: at least the first "n" bytes of output[] must be writable +// where "n" is the result of a successful call to +// Snappy_GetUncompressedLength. +extern bool Snappy_Uncompress(const char* input_data, size_t input_length, + char* output); + +// ------------------ Miscellaneous ------------------- + +// If heap profiling is not supported, returns false. +// Else repeatedly calls (*func)(arg, data, n) and then returns true. +// The concatenation of all "data[0,n-1]" fragments is the heap profile. +extern bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg); + +} // namespace port +} // namespace leveldb + +#endif // STORAGE_LEVELDB_PORT_PORT_EXAMPLE_H_ diff --git a/src/leveldb/port/port_posix.cc b/src/leveldb/port/port_posix.cc new file mode 100644 index 00000000..5ba127a5 --- /dev/null +++ b/src/leveldb/port/port_posix.cc @@ -0,0 +1,54 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "port/port_posix.h" + +#include +#include +#include +#include "util/logging.h" + +namespace leveldb { +namespace port { + +static void PthreadCall(const char* label, int result) { + if (result != 0) { + fprintf(stderr, "pthread %s: %s\n", label, strerror(result)); + abort(); + } +} + +Mutex::Mutex() { PthreadCall("init mutex", pthread_mutex_init(&mu_, NULL)); } + +Mutex::~Mutex() { PthreadCall("destroy mutex", pthread_mutex_destroy(&mu_)); } + +void Mutex::Lock() { PthreadCall("lock", pthread_mutex_lock(&mu_)); } + +void Mutex::Unlock() { PthreadCall("unlock", pthread_mutex_unlock(&mu_)); } + +CondVar::CondVar(Mutex* mu) + : mu_(mu) { + PthreadCall("init cv", pthread_cond_init(&cv_, NULL)); +} + +CondVar::~CondVar() { PthreadCall("destroy cv", pthread_cond_destroy(&cv_)); } + +void CondVar::Wait() { + PthreadCall("wait", pthread_cond_wait(&cv_, &mu_->mu_)); +} + +void CondVar::Signal() { + PthreadCall("signal", pthread_cond_signal(&cv_)); +} + +void CondVar::SignalAll() { + PthreadCall("broadcast", pthread_cond_broadcast(&cv_)); +} + +void InitOnce(OnceType* once, void (*initializer)()) { + PthreadCall("once", pthread_once(once, initializer)); +} + +} // namespace port +} // namespace leveldb diff --git a/src/leveldb/port/port_posix.h b/src/leveldb/port/port_posix.h new file mode 100644 index 00000000..ccca9939 --- /dev/null +++ b/src/leveldb/port/port_posix.h @@ -0,0 +1,158 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// See port_example.h for documentation for the following types/functions. + +#ifndef STORAGE_LEVELDB_PORT_PORT_POSIX_H_ +#define STORAGE_LEVELDB_PORT_PORT_POSIX_H_ + +#undef PLATFORM_IS_LITTLE_ENDIAN +#if defined(OS_MACOSX) + #include + #if defined(__DARWIN_LITTLE_ENDIAN) && defined(__DARWIN_BYTE_ORDER) + #define PLATFORM_IS_LITTLE_ENDIAN \ + (__DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN) + #endif +#elif defined(OS_SOLARIS) + #include + #ifdef _LITTLE_ENDIAN + #define PLATFORM_IS_LITTLE_ENDIAN true + #else + #define PLATFORM_IS_LITTLE_ENDIAN false + #endif +#elif defined(OS_FREEBSD) || defined(OS_OPENBSD) ||\ + defined(OS_NETBSD) || defined(OS_DRAGONFLYBSD) + #include + #include + #define PLATFORM_IS_LITTLE_ENDIAN (_BYTE_ORDER == _LITTLE_ENDIAN) +#elif defined(OS_HPUX) + #define PLATFORM_IS_LITTLE_ENDIAN false +#elif defined(OS_ANDROID) + // Due to a bug in the NDK x86 definition, + // _BYTE_ORDER must be used instead of __BYTE_ORDER on Android. + // See http://code.google.com/p/android/issues/detail?id=39824 + #include + #define PLATFORM_IS_LITTLE_ENDIAN (_BYTE_ORDER == _LITTLE_ENDIAN) +#else + #include +#endif + +#include +#ifdef SNAPPY +#include +#endif +#include +#include +#include "port/atomic_pointer.h" + +#ifndef PLATFORM_IS_LITTLE_ENDIAN +#define PLATFORM_IS_LITTLE_ENDIAN (__BYTE_ORDER == __LITTLE_ENDIAN) +#endif + +#if defined(OS_MACOSX) || defined(OS_SOLARIS) || defined(OS_FREEBSD) ||\ + defined(OS_NETBSD) || defined(OS_OPENBSD) || defined(OS_DRAGONFLYBSD) ||\ + defined(OS_ANDROID) || defined(OS_HPUX) || defined(CYGWIN) +// Use fread/fwrite/fflush on platforms without _unlocked variants +#define fread_unlocked fread +#define fwrite_unlocked fwrite +#define fflush_unlocked fflush +#endif + +#if defined(OS_FREEBSD) ||\ + defined(OS_OPENBSD) || defined(OS_DRAGONFLYBSD) +// Use fsync() on platforms without fdatasync() +#define fdatasync fsync +#endif + +#if defined(OS_MACOSX) +#define fdatasync(fd) fcntl(fd, F_FULLFSYNC, 0) +#endif + +#if defined(OS_ANDROID) && __ANDROID_API__ < 9 +// fdatasync() was only introduced in API level 9 on Android. Use fsync() +// when targetting older platforms. +#define fdatasync fsync +#endif + +namespace leveldb { +namespace port { + +static const bool kLittleEndian = PLATFORM_IS_LITTLE_ENDIAN; +#undef PLATFORM_IS_LITTLE_ENDIAN + +class CondVar; + +class Mutex { + public: + Mutex(); + ~Mutex(); + + void Lock(); + void Unlock(); + void AssertHeld() { } + + private: + friend class CondVar; + pthread_mutex_t mu_; + + // No copying + Mutex(const Mutex&); + void operator=(const Mutex&); +}; + +class CondVar { + public: + explicit CondVar(Mutex* mu); + ~CondVar(); + void Wait(); + void Signal(); + void SignalAll(); + private: + pthread_cond_t cv_; + Mutex* mu_; +}; + +typedef pthread_once_t OnceType; +#define LEVELDB_ONCE_INIT PTHREAD_ONCE_INIT +extern void InitOnce(OnceType* once, void (*initializer)()); + +inline bool Snappy_Compress(const char* input, size_t length, + ::std::string* output) { +#ifdef SNAPPY + output->resize(snappy::MaxCompressedLength(length)); + size_t outlen; + snappy::RawCompress(input, length, &(*output)[0], &outlen); + output->resize(outlen); + return true; +#endif + + return false; +} + +inline bool Snappy_GetUncompressedLength(const char* input, size_t length, + size_t* result) { +#ifdef SNAPPY + return snappy::GetUncompressedLength(input, length, result); +#else + return false; +#endif +} + +inline bool Snappy_Uncompress(const char* input, size_t length, + char* output) { +#ifdef SNAPPY + return snappy::RawUncompress(input, length, output); +#else + return false; +#endif +} + +inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) { + return false; +} + +} // namespace port +} // namespace leveldb + +#endif // STORAGE_LEVELDB_PORT_PORT_POSIX_H_ diff --git a/src/leveldb/port/port_win.cc b/src/leveldb/port/port_win.cc new file mode 100644 index 00000000..1b0f060a --- /dev/null +++ b/src/leveldb/port/port_win.cc @@ -0,0 +1,147 @@ +// LevelDB Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// See port_example.h for documentation for the following types/functions. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the University of California, Berkeley nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#include "port/port_win.h" + +#include +#include + +namespace leveldb { +namespace port { + +Mutex::Mutex() : + cs_(NULL) { + assert(!cs_); + cs_ = static_cast(new CRITICAL_SECTION()); + ::InitializeCriticalSection(static_cast(cs_)); + assert(cs_); +} + +Mutex::~Mutex() { + assert(cs_); + ::DeleteCriticalSection(static_cast(cs_)); + delete static_cast(cs_); + cs_ = NULL; + assert(!cs_); +} + +void Mutex::Lock() { + assert(cs_); + ::EnterCriticalSection(static_cast(cs_)); +} + +void Mutex::Unlock() { + assert(cs_); + ::LeaveCriticalSection(static_cast(cs_)); +} + +void Mutex::AssertHeld() { + assert(cs_); + assert(1); +} + +CondVar::CondVar(Mutex* mu) : + waiting_(0), + mu_(mu), + sem1_(::CreateSemaphore(NULL, 0, 10000, NULL)), + sem2_(::CreateSemaphore(NULL, 0, 10000, NULL)) { + assert(mu_); +} + +CondVar::~CondVar() { + ::CloseHandle(sem1_); + ::CloseHandle(sem2_); +} + +void CondVar::Wait() { + mu_->AssertHeld(); + + wait_mtx_.Lock(); + ++waiting_; + wait_mtx_.Unlock(); + + mu_->Unlock(); + + // initiate handshake + ::WaitForSingleObject(sem1_, INFINITE); + ::ReleaseSemaphore(sem2_, 1, NULL); + mu_->Lock(); +} + +void CondVar::Signal() { + wait_mtx_.Lock(); + if (waiting_ > 0) { + --waiting_; + + // finalize handshake + ::ReleaseSemaphore(sem1_, 1, NULL); + ::WaitForSingleObject(sem2_, INFINITE); + } + wait_mtx_.Unlock(); +} + +void CondVar::SignalAll() { + wait_mtx_.Lock(); + ::ReleaseSemaphore(sem1_, waiting_, NULL); + while(waiting_ > 0) { + --waiting_; + ::WaitForSingleObject(sem2_, INFINITE); + } + wait_mtx_.Unlock(); +} + +AtomicPointer::AtomicPointer(void* v) { + Release_Store(v); +} + +void InitOnce(OnceType* once, void (*initializer)()) { + once->InitOnce(initializer); +} + +void* AtomicPointer::Acquire_Load() const { + void * p = NULL; + InterlockedExchangePointer(&p, rep_); + return p; +} + +void AtomicPointer::Release_Store(void* v) { + InterlockedExchangePointer(&rep_, v); +} + +void* AtomicPointer::NoBarrier_Load() const { + return rep_; +} + +void AtomicPointer::NoBarrier_Store(void* v) { + rep_ = v; +} + +} +} diff --git a/src/leveldb/port/port_win.h b/src/leveldb/port/port_win.h new file mode 100644 index 00000000..45bf2f0e --- /dev/null +++ b/src/leveldb/port/port_win.h @@ -0,0 +1,174 @@ +// LevelDB Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// See port_example.h for documentation for the following types/functions. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the University of California, Berkeley nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef STORAGE_LEVELDB_PORT_PORT_WIN_H_ +#define STORAGE_LEVELDB_PORT_PORT_WIN_H_ + +#ifdef _MSC_VER +#define snprintf _snprintf +#define close _close +#define fread_unlocked _fread_nolock +#endif + +#include +#include +#ifdef SNAPPY +#include +#endif + +namespace leveldb { +namespace port { + +// Windows is little endian (for now :p) +static const bool kLittleEndian = true; + +class CondVar; + +class Mutex { + public: + Mutex(); + ~Mutex(); + + void Lock(); + void Unlock(); + void AssertHeld(); + + private: + friend class CondVar; + // critical sections are more efficient than mutexes + // but they are not recursive and can only be used to synchronize threads within the same process + // we use opaque void * to avoid including windows.h in port_win.h + void * cs_; + + // No copying + Mutex(const Mutex&); + void operator=(const Mutex&); +}; + +// the Win32 API offers a dependable condition variable mechanism, but only starting with +// Windows 2008 and Vista +// no matter what we will implement our own condition variable with a semaphore +// implementation as described in a paper written by Andrew D. Birrell in 2003 +class CondVar { + public: + explicit CondVar(Mutex* mu); + ~CondVar(); + void Wait(); + void Signal(); + void SignalAll(); + private: + Mutex* mu_; + + Mutex wait_mtx_; + long waiting_; + + void * sem1_; + void * sem2_; + + +}; + +class OnceType { +public: +// OnceType() : init_(false) {} + OnceType(const OnceType &once) : init_(once.init_) {} + OnceType(bool f) : init_(f) {} + void InitOnce(void (*initializer)()) { + mutex_.Lock(); + if (!init_) { + init_ = true; + initializer(); + } + mutex_.Unlock(); + } + +private: + bool init_; + Mutex mutex_; +}; + +#define LEVELDB_ONCE_INIT false +extern void InitOnce(port::OnceType*, void (*initializer)()); + +// Storage for a lock-free pointer +class AtomicPointer { + private: + void * rep_; + public: + AtomicPointer() : rep_(NULL) { } + explicit AtomicPointer(void* v); + void* Acquire_Load() const; + + void Release_Store(void* v); + + void* NoBarrier_Load() const; + + void NoBarrier_Store(void* v); +}; + +inline bool Snappy_Compress(const char* input, size_t length, + ::std::string* output) { +#ifdef SNAPPY + output->resize(snappy::MaxCompressedLength(length)); + size_t outlen; + snappy::RawCompress(input, length, &(*output)[0], &outlen); + output->resize(outlen); + return true; +#endif + + return false; +} + +inline bool Snappy_GetUncompressedLength(const char* input, size_t length, + size_t* result) { +#ifdef SNAPPY + return snappy::GetUncompressedLength(input, length, result); +#else + return false; +#endif +} + +inline bool Snappy_Uncompress(const char* input, size_t length, + char* output) { +#ifdef SNAPPY + return snappy::RawUncompress(input, length, output); +#else + return false; +#endif +} + +inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) { + return false; +} + +} +} + +#endif // STORAGE_LEVELDB_PORT_PORT_WIN_H_ diff --git a/src/leveldb/port/thread_annotations.h b/src/leveldb/port/thread_annotations.h new file mode 100644 index 00000000..9470ef58 --- /dev/null +++ b/src/leveldb/port/thread_annotations.h @@ -0,0 +1,60 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_PORT_THREAD_ANNOTATIONS_H_ +#define STORAGE_LEVELDB_PORT_THREAD_ANNOTATIONS_H_ + +// Some environments provide custom macros to aid in static thread-safety +// analysis. Provide empty definitions of such macros unless they are already +// defined. + +#ifndef EXCLUSIVE_LOCKS_REQUIRED +#define EXCLUSIVE_LOCKS_REQUIRED(...) +#endif + +#ifndef SHARED_LOCKS_REQUIRED +#define SHARED_LOCKS_REQUIRED(...) +#endif + +#ifndef LOCKS_EXCLUDED +#define LOCKS_EXCLUDED(...) +#endif + +#ifndef LOCK_RETURNED +#define LOCK_RETURNED(x) +#endif + +#ifndef LOCKABLE +#define LOCKABLE +#endif + +#ifndef SCOPED_LOCKABLE +#define SCOPED_LOCKABLE +#endif + +#ifndef EXCLUSIVE_LOCK_FUNCTION +#define EXCLUSIVE_LOCK_FUNCTION(...) +#endif + +#ifndef SHARED_LOCK_FUNCTION +#define SHARED_LOCK_FUNCTION(...) +#endif + +#ifndef EXCLUSIVE_TRYLOCK_FUNCTION +#define EXCLUSIVE_TRYLOCK_FUNCTION(...) +#endif + +#ifndef SHARED_TRYLOCK_FUNCTION +#define SHARED_TRYLOCK_FUNCTION(...) +#endif + +#ifndef UNLOCK_FUNCTION +#define UNLOCK_FUNCTION(...) +#endif + +#ifndef NO_THREAD_SAFETY_ANALYSIS +#define NO_THREAD_SAFETY_ANALYSIS +#endif + +#endif // STORAGE_LEVELDB_PORT_THREAD_ANNOTATIONS_H_ diff --git a/src/leveldb/port/win/stdint.h b/src/leveldb/port/win/stdint.h new file mode 100644 index 00000000..39edd0db --- /dev/null +++ b/src/leveldb/port/win/stdint.h @@ -0,0 +1,24 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +// MSVC didn't ship with this file until the 2010 version. + +#ifndef STORAGE_LEVELDB_PORT_WIN_STDINT_H_ +#define STORAGE_LEVELDB_PORT_WIN_STDINT_H_ + +#if !defined(_MSC_VER) +#error This file should only be included when compiling with MSVC. +#endif + +// Define C99 equivalent types. +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef signed long long int64_t; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +#endif // STORAGE_LEVELDB_PORT_WIN_STDINT_H_ diff --git a/src/leveldb/table/block.cc b/src/leveldb/table/block.cc new file mode 100644 index 00000000..43e402c9 --- /dev/null +++ b/src/leveldb/table/block.cc @@ -0,0 +1,268 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Decodes the blocks generated by block_builder.cc. + +#include "table/block.h" + +#include +#include +#include "leveldb/comparator.h" +#include "table/format.h" +#include "util/coding.h" +#include "util/logging.h" + +namespace leveldb { + +inline uint32_t Block::NumRestarts() const { + assert(size_ >= sizeof(uint32_t)); + return DecodeFixed32(data_ + size_ - sizeof(uint32_t)); +} + +Block::Block(const BlockContents& contents) + : data_(contents.data.data()), + size_(contents.data.size()), + owned_(contents.heap_allocated) { + if (size_ < sizeof(uint32_t)) { + size_ = 0; // Error marker + } else { + size_t max_restarts_allowed = (size_-sizeof(uint32_t)) / sizeof(uint32_t); + if (NumRestarts() > max_restarts_allowed) { + // The size is too small for NumRestarts() + size_ = 0; + } else { + restart_offset_ = size_ - (1 + NumRestarts()) * sizeof(uint32_t); + } + } +} + +Block::~Block() { + if (owned_) { + delete[] data_; + } +} + +// Helper routine: decode the next block entry starting at "p", +// storing the number of shared key bytes, non_shared key bytes, +// and the length of the value in "*shared", "*non_shared", and +// "*value_length", respectively. Will not dereference past "limit". +// +// If any errors are detected, returns NULL. Otherwise, returns a +// pointer to the key delta (just past the three decoded values). +static inline const char* DecodeEntry(const char* p, const char* limit, + uint32_t* shared, + uint32_t* non_shared, + uint32_t* value_length) { + if (limit - p < 3) return NULL; + *shared = reinterpret_cast(p)[0]; + *non_shared = reinterpret_cast(p)[1]; + *value_length = reinterpret_cast(p)[2]; + if ((*shared | *non_shared | *value_length) < 128) { + // Fast path: all three values are encoded in one byte each + p += 3; + } else { + if ((p = GetVarint32Ptr(p, limit, shared)) == NULL) return NULL; + if ((p = GetVarint32Ptr(p, limit, non_shared)) == NULL) return NULL; + if ((p = GetVarint32Ptr(p, limit, value_length)) == NULL) return NULL; + } + + if (static_cast(limit - p) < (*non_shared + *value_length)) { + return NULL; + } + return p; +} + +class Block::Iter : public Iterator { + private: + const Comparator* const comparator_; + const char* const data_; // underlying block contents + uint32_t const restarts_; // Offset of restart array (list of fixed32) + uint32_t const num_restarts_; // Number of uint32_t entries in restart array + + // current_ is offset in data_ of current entry. >= restarts_ if !Valid + uint32_t current_; + uint32_t restart_index_; // Index of restart block in which current_ falls + std::string key_; + Slice value_; + Status status_; + + inline int Compare(const Slice& a, const Slice& b) const { + return comparator_->Compare(a, b); + } + + // Return the offset in data_ just past the end of the current entry. + inline uint32_t NextEntryOffset() const { + return (value_.data() + value_.size()) - data_; + } + + uint32_t GetRestartPoint(uint32_t index) { + assert(index < num_restarts_); + return DecodeFixed32(data_ + restarts_ + index * sizeof(uint32_t)); + } + + void SeekToRestartPoint(uint32_t index) { + key_.clear(); + restart_index_ = index; + // current_ will be fixed by ParseNextKey(); + + // ParseNextKey() starts at the end of value_, so set value_ accordingly + uint32_t offset = GetRestartPoint(index); + value_ = Slice(data_ + offset, 0); + } + + public: + Iter(const Comparator* comparator, + const char* data, + uint32_t restarts, + uint32_t num_restarts) + : comparator_(comparator), + data_(data), + restarts_(restarts), + num_restarts_(num_restarts), + current_(restarts_), + restart_index_(num_restarts_) { + assert(num_restarts_ > 0); + } + + virtual bool Valid() const { return current_ < restarts_; } + virtual Status status() const { return status_; } + virtual Slice key() const { + assert(Valid()); + return key_; + } + virtual Slice value() const { + assert(Valid()); + return value_; + } + + virtual void Next() { + assert(Valid()); + ParseNextKey(); + } + + virtual void Prev() { + assert(Valid()); + + // Scan backwards to a restart point before current_ + const uint32_t original = current_; + while (GetRestartPoint(restart_index_) >= original) { + if (restart_index_ == 0) { + // No more entries + current_ = restarts_; + restart_index_ = num_restarts_; + return; + } + restart_index_--; + } + + SeekToRestartPoint(restart_index_); + do { + // Loop until end of current entry hits the start of original entry + } while (ParseNextKey() && NextEntryOffset() < original); + } + + virtual void Seek(const Slice& target) { + // Binary search in restart array to find the last restart point + // with a key < target + uint32_t left = 0; + uint32_t right = num_restarts_ - 1; + while (left < right) { + uint32_t mid = (left + right + 1) / 2; + uint32_t region_offset = GetRestartPoint(mid); + uint32_t shared, non_shared, value_length; + const char* key_ptr = DecodeEntry(data_ + region_offset, + data_ + restarts_, + &shared, &non_shared, &value_length); + if (key_ptr == NULL || (shared != 0)) { + CorruptionError(); + return; + } + Slice mid_key(key_ptr, non_shared); + if (Compare(mid_key, target) < 0) { + // Key at "mid" is smaller than "target". Therefore all + // blocks before "mid" are uninteresting. + left = mid; + } else { + // Key at "mid" is >= "target". Therefore all blocks at or + // after "mid" are uninteresting. + right = mid - 1; + } + } + + // Linear search (within restart block) for first key >= target + SeekToRestartPoint(left); + while (true) { + if (!ParseNextKey()) { + return; + } + if (Compare(key_, target) >= 0) { + return; + } + } + } + + virtual void SeekToFirst() { + SeekToRestartPoint(0); + ParseNextKey(); + } + + virtual void SeekToLast() { + SeekToRestartPoint(num_restarts_ - 1); + while (ParseNextKey() && NextEntryOffset() < restarts_) { + // Keep skipping + } + } + + private: + void CorruptionError() { + current_ = restarts_; + restart_index_ = num_restarts_; + status_ = Status::Corruption("bad entry in block"); + key_.clear(); + value_.clear(); + } + + bool ParseNextKey() { + current_ = NextEntryOffset(); + const char* p = data_ + current_; + const char* limit = data_ + restarts_; // Restarts come right after data + if (p >= limit) { + // No more entries to return. Mark as invalid. + current_ = restarts_; + restart_index_ = num_restarts_; + return false; + } + + // Decode next entry + uint32_t shared, non_shared, value_length; + p = DecodeEntry(p, limit, &shared, &non_shared, &value_length); + if (p == NULL || key_.size() < shared) { + CorruptionError(); + return false; + } else { + key_.resize(shared); + key_.append(p, non_shared); + value_ = Slice(p + non_shared, value_length); + while (restart_index_ + 1 < num_restarts_ && + GetRestartPoint(restart_index_ + 1) < current_) { + ++restart_index_; + } + return true; + } + } +}; + +Iterator* Block::NewIterator(const Comparator* cmp) { + if (size_ < sizeof(uint32_t)) { + return NewErrorIterator(Status::Corruption("bad block contents")); + } + const uint32_t num_restarts = NumRestarts(); + if (num_restarts == 0) { + return NewEmptyIterator(); + } else { + return new Iter(cmp, data_, restart_offset_, num_restarts); + } +} + +} // namespace leveldb diff --git a/src/leveldb/table/block.h b/src/leveldb/table/block.h new file mode 100644 index 00000000..2493eb9f --- /dev/null +++ b/src/leveldb/table/block.h @@ -0,0 +1,44 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_BLOCK_H_ +#define STORAGE_LEVELDB_TABLE_BLOCK_H_ + +#include +#include +#include "leveldb/iterator.h" + +namespace leveldb { + +struct BlockContents; +class Comparator; + +class Block { + public: + // Initialize the block with the specified contents. + explicit Block(const BlockContents& contents); + + ~Block(); + + size_t size() const { return size_; } + Iterator* NewIterator(const Comparator* comparator); + + private: + uint32_t NumRestarts() const; + + const char* data_; + size_t size_; + uint32_t restart_offset_; // Offset in data_ of restart array + bool owned_; // Block owns data_[] + + // No copying allowed + Block(const Block&); + void operator=(const Block&); + + class Iter; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_BLOCK_H_ diff --git a/src/leveldb/table/block_builder.cc b/src/leveldb/table/block_builder.cc new file mode 100644 index 00000000..db660cd0 --- /dev/null +++ b/src/leveldb/table/block_builder.cc @@ -0,0 +1,109 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// BlockBuilder generates blocks where keys are prefix-compressed: +// +// When we store a key, we drop the prefix shared with the previous +// string. This helps reduce the space requirement significantly. +// Furthermore, once every K keys, we do not apply the prefix +// compression and store the entire key. We call this a "restart +// point". The tail end of the block stores the offsets of all of the +// restart points, and can be used to do a binary search when looking +// for a particular key. Values are stored as-is (without compression) +// immediately following the corresponding key. +// +// An entry for a particular key-value pair has the form: +// shared_bytes: varint32 +// unshared_bytes: varint32 +// value_length: varint32 +// key_delta: char[unshared_bytes] +// value: char[value_length] +// shared_bytes == 0 for restart points. +// +// The trailer of the block has the form: +// restarts: uint32[num_restarts] +// num_restarts: uint32 +// restarts[i] contains the offset within the block of the ith restart point. + +#include "table/block_builder.h" + +#include +#include +#include "leveldb/comparator.h" +#include "leveldb/table_builder.h" +#include "util/coding.h" + +namespace leveldb { + +BlockBuilder::BlockBuilder(const Options* options) + : options_(options), + restarts_(), + counter_(0), + finished_(false) { + assert(options->block_restart_interval >= 1); + restarts_.push_back(0); // First restart point is at offset 0 +} + +void BlockBuilder::Reset() { + buffer_.clear(); + restarts_.clear(); + restarts_.push_back(0); // First restart point is at offset 0 + counter_ = 0; + finished_ = false; + last_key_.clear(); +} + +size_t BlockBuilder::CurrentSizeEstimate() const { + return (buffer_.size() + // Raw data buffer + restarts_.size() * sizeof(uint32_t) + // Restart array + sizeof(uint32_t)); // Restart array length +} + +Slice BlockBuilder::Finish() { + // Append restart array + for (size_t i = 0; i < restarts_.size(); i++) { + PutFixed32(&buffer_, restarts_[i]); + } + PutFixed32(&buffer_, restarts_.size()); + finished_ = true; + return Slice(buffer_); +} + +void BlockBuilder::Add(const Slice& key, const Slice& value) { + Slice last_key_piece(last_key_); + assert(!finished_); + assert(counter_ <= options_->block_restart_interval); + assert(buffer_.empty() // No values yet? + || options_->comparator->Compare(key, last_key_piece) > 0); + size_t shared = 0; + if (counter_ < options_->block_restart_interval) { + // See how much sharing to do with previous string + const size_t min_length = std::min(last_key_piece.size(), key.size()); + while ((shared < min_length) && (last_key_piece[shared] == key[shared])) { + shared++; + } + } else { + // Restart compression + restarts_.push_back(buffer_.size()); + counter_ = 0; + } + const size_t non_shared = key.size() - shared; + + // Add "" to buffer_ + PutVarint32(&buffer_, shared); + PutVarint32(&buffer_, non_shared); + PutVarint32(&buffer_, value.size()); + + // Add string delta to buffer_ followed by value + buffer_.append(key.data() + shared, non_shared); + buffer_.append(value.data(), value.size()); + + // Update state + last_key_.resize(shared); + last_key_.append(key.data() + shared, non_shared); + assert(Slice(last_key_) == key); + counter_++; +} + +} // namespace leveldb diff --git a/src/leveldb/table/block_builder.h b/src/leveldb/table/block_builder.h new file mode 100644 index 00000000..4fbcb339 --- /dev/null +++ b/src/leveldb/table/block_builder.h @@ -0,0 +1,57 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_BLOCK_BUILDER_H_ +#define STORAGE_LEVELDB_TABLE_BLOCK_BUILDER_H_ + +#include + +#include +#include "leveldb/slice.h" + +namespace leveldb { + +struct Options; + +class BlockBuilder { + public: + explicit BlockBuilder(const Options* options); + + // Reset the contents as if the BlockBuilder was just constructed. + void Reset(); + + // REQUIRES: Finish() has not been called since the last call to Reset(). + // REQUIRES: key is larger than any previously added key + void Add(const Slice& key, const Slice& value); + + // Finish building the block and return a slice that refers to the + // block contents. The returned slice will remain valid for the + // lifetime of this builder or until Reset() is called. + Slice Finish(); + + // Returns an estimate of the current (uncompressed) size of the block + // we are building. + size_t CurrentSizeEstimate() const; + + // Return true iff no entries have been added since the last Reset() + bool empty() const { + return buffer_.empty(); + } + + private: + const Options* options_; + std::string buffer_; // Destination buffer + std::vector restarts_; // Restart points + int counter_; // Number of entries emitted since restart + bool finished_; // Has Finish() been called? + std::string last_key_; + + // No copying allowed + BlockBuilder(const BlockBuilder&); + void operator=(const BlockBuilder&); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_BLOCK_BUILDER_H_ diff --git a/src/leveldb/table/filter_block.cc b/src/leveldb/table/filter_block.cc new file mode 100644 index 00000000..203e15c8 --- /dev/null +++ b/src/leveldb/table/filter_block.cc @@ -0,0 +1,111 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "table/filter_block.h" + +#include "leveldb/filter_policy.h" +#include "util/coding.h" + +namespace leveldb { + +// See doc/table_format.txt for an explanation of the filter block format. + +// Generate new filter every 2KB of data +static const size_t kFilterBaseLg = 11; +static const size_t kFilterBase = 1 << kFilterBaseLg; + +FilterBlockBuilder::FilterBlockBuilder(const FilterPolicy* policy) + : policy_(policy) { +} + +void FilterBlockBuilder::StartBlock(uint64_t block_offset) { + uint64_t filter_index = (block_offset / kFilterBase); + assert(filter_index >= filter_offsets_.size()); + while (filter_index > filter_offsets_.size()) { + GenerateFilter(); + } +} + +void FilterBlockBuilder::AddKey(const Slice& key) { + Slice k = key; + start_.push_back(keys_.size()); + keys_.append(k.data(), k.size()); +} + +Slice FilterBlockBuilder::Finish() { + if (!start_.empty()) { + GenerateFilter(); + } + + // Append array of per-filter offsets + const uint32_t array_offset = result_.size(); + for (size_t i = 0; i < filter_offsets_.size(); i++) { + PutFixed32(&result_, filter_offsets_[i]); + } + + PutFixed32(&result_, array_offset); + result_.push_back(kFilterBaseLg); // Save encoding parameter in result + return Slice(result_); +} + +void FilterBlockBuilder::GenerateFilter() { + const size_t num_keys = start_.size(); + if (num_keys == 0) { + // Fast path if there are no keys for this filter + filter_offsets_.push_back(result_.size()); + return; + } + + // Make list of keys from flattened key structure + start_.push_back(keys_.size()); // Simplify length computation + tmp_keys_.resize(num_keys); + for (size_t i = 0; i < num_keys; i++) { + const char* base = keys_.data() + start_[i]; + size_t length = start_[i+1] - start_[i]; + tmp_keys_[i] = Slice(base, length); + } + + // Generate filter for current set of keys and append to result_. + filter_offsets_.push_back(result_.size()); + policy_->CreateFilter(&tmp_keys_[0], num_keys, &result_); + + tmp_keys_.clear(); + keys_.clear(); + start_.clear(); +} + +FilterBlockReader::FilterBlockReader(const FilterPolicy* policy, + const Slice& contents) + : policy_(policy), + data_(NULL), + offset_(NULL), + num_(0), + base_lg_(0) { + size_t n = contents.size(); + if (n < 5) return; // 1 byte for base_lg_ and 4 for start of offset array + base_lg_ = contents[n-1]; + uint32_t last_word = DecodeFixed32(contents.data() + n - 5); + if (last_word > n - 5) return; + data_ = contents.data(); + offset_ = data_ + last_word; + num_ = (n - 5 - last_word) / 4; +} + +bool FilterBlockReader::KeyMayMatch(uint64_t block_offset, const Slice& key) { + uint64_t index = block_offset >> base_lg_; + if (index < num_) { + uint32_t start = DecodeFixed32(offset_ + index*4); + uint32_t limit = DecodeFixed32(offset_ + index*4 + 4); + if (start <= limit && limit <= (offset_ - data_)) { + Slice filter = Slice(data_ + start, limit - start); + return policy_->KeyMayMatch(key, filter); + } else if (start == limit) { + // Empty filters do not match any keys + return false; + } + } + return true; // Errors are treated as potential matches +} + +} diff --git a/src/leveldb/table/filter_block.h b/src/leveldb/table/filter_block.h new file mode 100644 index 00000000..c67d010b --- /dev/null +++ b/src/leveldb/table/filter_block.h @@ -0,0 +1,68 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// A filter block is stored near the end of a Table file. It contains +// filters (e.g., bloom filters) for all data blocks in the table combined +// into a single filter block. + +#ifndef STORAGE_LEVELDB_TABLE_FILTER_BLOCK_H_ +#define STORAGE_LEVELDB_TABLE_FILTER_BLOCK_H_ + +#include +#include +#include +#include +#include "leveldb/slice.h" +#include "util/hash.h" + +namespace leveldb { + +class FilterPolicy; + +// A FilterBlockBuilder is used to construct all of the filters for a +// particular Table. It generates a single string which is stored as +// a special block in the Table. +// +// The sequence of calls to FilterBlockBuilder must match the regexp: +// (StartBlock AddKey*)* Finish +class FilterBlockBuilder { + public: + explicit FilterBlockBuilder(const FilterPolicy*); + + void StartBlock(uint64_t block_offset); + void AddKey(const Slice& key); + Slice Finish(); + + private: + void GenerateFilter(); + + const FilterPolicy* policy_; + std::string keys_; // Flattened key contents + std::vector start_; // Starting index in keys_ of each key + std::string result_; // Filter data computed so far + std::vector tmp_keys_; // policy_->CreateFilter() argument + std::vector filter_offsets_; + + // No copying allowed + FilterBlockBuilder(const FilterBlockBuilder&); + void operator=(const FilterBlockBuilder&); +}; + +class FilterBlockReader { + public: + // REQUIRES: "contents" and *policy must stay live while *this is live. + FilterBlockReader(const FilterPolicy* policy, const Slice& contents); + bool KeyMayMatch(uint64_t block_offset, const Slice& key); + + private: + const FilterPolicy* policy_; + const char* data_; // Pointer to filter data (at block-start) + const char* offset_; // Pointer to beginning of offset array (at block-end) + size_t num_; // Number of entries in offset array + size_t base_lg_; // Encoding parameter (see kFilterBaseLg in .cc file) +}; + +} + +#endif // STORAGE_LEVELDB_TABLE_FILTER_BLOCK_H_ diff --git a/src/leveldb/table/filter_block_test.cc b/src/leveldb/table/filter_block_test.cc new file mode 100644 index 00000000..8c4a4741 --- /dev/null +++ b/src/leveldb/table/filter_block_test.cc @@ -0,0 +1,128 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "table/filter_block.h" + +#include "leveldb/filter_policy.h" +#include "util/coding.h" +#include "util/hash.h" +#include "util/logging.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +// For testing: emit an array with one hash value per key +class TestHashFilter : public FilterPolicy { + public: + virtual const char* Name() const { + return "TestHashFilter"; + } + + virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const { + for (int i = 0; i < n; i++) { + uint32_t h = Hash(keys[i].data(), keys[i].size(), 1); + PutFixed32(dst, h); + } + } + + virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const { + uint32_t h = Hash(key.data(), key.size(), 1); + for (size_t i = 0; i + 4 <= filter.size(); i += 4) { + if (h == DecodeFixed32(filter.data() + i)) { + return true; + } + } + return false; + } +}; + +class FilterBlockTest { + public: + TestHashFilter policy_; +}; + +TEST(FilterBlockTest, EmptyBuilder) { + FilterBlockBuilder builder(&policy_); + Slice block = builder.Finish(); + ASSERT_EQ("\\x00\\x00\\x00\\x00\\x0b", EscapeString(block)); + FilterBlockReader reader(&policy_, block); + ASSERT_TRUE(reader.KeyMayMatch(0, "foo")); + ASSERT_TRUE(reader.KeyMayMatch(100000, "foo")); +} + +TEST(FilterBlockTest, SingleChunk) { + FilterBlockBuilder builder(&policy_); + builder.StartBlock(100); + builder.AddKey("foo"); + builder.AddKey("bar"); + builder.AddKey("box"); + builder.StartBlock(200); + builder.AddKey("box"); + builder.StartBlock(300); + builder.AddKey("hello"); + Slice block = builder.Finish(); + FilterBlockReader reader(&policy_, block); + ASSERT_TRUE(reader.KeyMayMatch(100, "foo")); + ASSERT_TRUE(reader.KeyMayMatch(100, "bar")); + ASSERT_TRUE(reader.KeyMayMatch(100, "box")); + ASSERT_TRUE(reader.KeyMayMatch(100, "hello")); + ASSERT_TRUE(reader.KeyMayMatch(100, "foo")); + ASSERT_TRUE(! reader.KeyMayMatch(100, "missing")); + ASSERT_TRUE(! reader.KeyMayMatch(100, "other")); +} + +TEST(FilterBlockTest, MultiChunk) { + FilterBlockBuilder builder(&policy_); + + // First filter + builder.StartBlock(0); + builder.AddKey("foo"); + builder.StartBlock(2000); + builder.AddKey("bar"); + + // Second filter + builder.StartBlock(3100); + builder.AddKey("box"); + + // Third filter is empty + + // Last filter + builder.StartBlock(9000); + builder.AddKey("box"); + builder.AddKey("hello"); + + Slice block = builder.Finish(); + FilterBlockReader reader(&policy_, block); + + // Check first filter + ASSERT_TRUE(reader.KeyMayMatch(0, "foo")); + ASSERT_TRUE(reader.KeyMayMatch(2000, "bar")); + ASSERT_TRUE(! reader.KeyMayMatch(0, "box")); + ASSERT_TRUE(! reader.KeyMayMatch(0, "hello")); + + // Check second filter + ASSERT_TRUE(reader.KeyMayMatch(3100, "box")); + ASSERT_TRUE(! reader.KeyMayMatch(3100, "foo")); + ASSERT_TRUE(! reader.KeyMayMatch(3100, "bar")); + ASSERT_TRUE(! reader.KeyMayMatch(3100, "hello")); + + // Check third filter (empty) + ASSERT_TRUE(! reader.KeyMayMatch(4100, "foo")); + ASSERT_TRUE(! reader.KeyMayMatch(4100, "bar")); + ASSERT_TRUE(! reader.KeyMayMatch(4100, "box")); + ASSERT_TRUE(! reader.KeyMayMatch(4100, "hello")); + + // Check last filter + ASSERT_TRUE(reader.KeyMayMatch(9000, "box")); + ASSERT_TRUE(reader.KeyMayMatch(9000, "hello")); + ASSERT_TRUE(! reader.KeyMayMatch(9000, "foo")); + ASSERT_TRUE(! reader.KeyMayMatch(9000, "bar")); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/table/format.cc b/src/leveldb/table/format.cc new file mode 100644 index 00000000..aa63144c --- /dev/null +++ b/src/leveldb/table/format.cc @@ -0,0 +1,145 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "table/format.h" + +#include "leveldb/env.h" +#include "port/port.h" +#include "table/block.h" +#include "util/coding.h" +#include "util/crc32c.h" + +namespace leveldb { + +void BlockHandle::EncodeTo(std::string* dst) const { + // Sanity check that all fields have been set + assert(offset_ != ~static_cast(0)); + assert(size_ != ~static_cast(0)); + PutVarint64(dst, offset_); + PutVarint64(dst, size_); +} + +Status BlockHandle::DecodeFrom(Slice* input) { + if (GetVarint64(input, &offset_) && + GetVarint64(input, &size_)) { + return Status::OK(); + } else { + return Status::Corruption("bad block handle"); + } +} + +void Footer::EncodeTo(std::string* dst) const { +#ifndef NDEBUG + const size_t original_size = dst->size(); +#endif + metaindex_handle_.EncodeTo(dst); + index_handle_.EncodeTo(dst); + dst->resize(2 * BlockHandle::kMaxEncodedLength); // Padding + PutFixed32(dst, static_cast(kTableMagicNumber & 0xffffffffu)); + PutFixed32(dst, static_cast(kTableMagicNumber >> 32)); + assert(dst->size() == original_size + kEncodedLength); +} + +Status Footer::DecodeFrom(Slice* input) { + const char* magic_ptr = input->data() + kEncodedLength - 8; + const uint32_t magic_lo = DecodeFixed32(magic_ptr); + const uint32_t magic_hi = DecodeFixed32(magic_ptr + 4); + const uint64_t magic = ((static_cast(magic_hi) << 32) | + (static_cast(magic_lo))); + if (magic != kTableMagicNumber) { + return Status::Corruption("not an sstable (bad magic number)"); + } + + Status result = metaindex_handle_.DecodeFrom(input); + if (result.ok()) { + result = index_handle_.DecodeFrom(input); + } + if (result.ok()) { + // We skip over any leftover data (just padding for now) in "input" + const char* end = magic_ptr + 8; + *input = Slice(end, input->data() + input->size() - end); + } + return result; +} + +Status ReadBlock(RandomAccessFile* file, + const ReadOptions& options, + const BlockHandle& handle, + BlockContents* result) { + result->data = Slice(); + result->cachable = false; + result->heap_allocated = false; + + // Read the block contents as well as the type/crc footer. + // See table_builder.cc for the code that built this structure. + size_t n = static_cast(handle.size()); + char* buf = new char[n + kBlockTrailerSize]; + Slice contents; + Status s = file->Read(handle.offset(), n + kBlockTrailerSize, &contents, buf); + if (!s.ok()) { + delete[] buf; + return s; + } + if (contents.size() != n + kBlockTrailerSize) { + delete[] buf; + return Status::Corruption("truncated block read"); + } + + // Check the crc of the type and the block contents + const char* data = contents.data(); // Pointer to where Read put the data + if (options.verify_checksums) { + const uint32_t crc = crc32c::Unmask(DecodeFixed32(data + n + 1)); + const uint32_t actual = crc32c::Value(data, n + 1); + if (actual != crc) { + delete[] buf; + s = Status::Corruption("block checksum mismatch"); + return s; + } + } + + switch (data[n]) { + case kNoCompression: + if (data != buf) { + // File implementation gave us pointer to some other data. + // Use it directly under the assumption that it will be live + // while the file is open. + delete[] buf; + result->data = Slice(data, n); + result->heap_allocated = false; + result->cachable = false; // Do not double-cache + } else { + result->data = Slice(buf, n); + result->heap_allocated = true; + result->cachable = true; + } + + // Ok + break; + case kSnappyCompression: { + size_t ulength = 0; + if (!port::Snappy_GetUncompressedLength(data, n, &ulength)) { + delete[] buf; + return Status::Corruption("corrupted compressed block contents"); + } + char* ubuf = new char[ulength]; + if (!port::Snappy_Uncompress(data, n, ubuf)) { + delete[] buf; + delete[] ubuf; + return Status::Corruption("corrupted compressed block contents"); + } + delete[] buf; + result->data = Slice(ubuf, ulength); + result->heap_allocated = true; + result->cachable = true; + break; + } + default: + delete[] buf; + return Status::Corruption("bad block type"); + } + + return Status::OK(); +} + +} // namespace leveldb diff --git a/src/leveldb/table/format.h b/src/leveldb/table/format.h new file mode 100644 index 00000000..6c0b80c0 --- /dev/null +++ b/src/leveldb/table/format.h @@ -0,0 +1,108 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_FORMAT_H_ +#define STORAGE_LEVELDB_TABLE_FORMAT_H_ + +#include +#include +#include "leveldb/slice.h" +#include "leveldb/status.h" +#include "leveldb/table_builder.h" + +namespace leveldb { + +class Block; +class RandomAccessFile; +struct ReadOptions; + +// BlockHandle is a pointer to the extent of a file that stores a data +// block or a meta block. +class BlockHandle { + public: + BlockHandle(); + + // The offset of the block in the file. + uint64_t offset() const { return offset_; } + void set_offset(uint64_t offset) { offset_ = offset; } + + // The size of the stored block + uint64_t size() const { return size_; } + void set_size(uint64_t size) { size_ = size; } + + void EncodeTo(std::string* dst) const; + Status DecodeFrom(Slice* input); + + // Maximum encoding length of a BlockHandle + enum { kMaxEncodedLength = 10 + 10 }; + + private: + uint64_t offset_; + uint64_t size_; +}; + +// Footer encapsulates the fixed information stored at the tail +// end of every table file. +class Footer { + public: + Footer() { } + + // The block handle for the metaindex block of the table + const BlockHandle& metaindex_handle() const { return metaindex_handle_; } + void set_metaindex_handle(const BlockHandle& h) { metaindex_handle_ = h; } + + // The block handle for the index block of the table + const BlockHandle& index_handle() const { + return index_handle_; + } + void set_index_handle(const BlockHandle& h) { + index_handle_ = h; + } + + void EncodeTo(std::string* dst) const; + Status DecodeFrom(Slice* input); + + // Encoded length of a Footer. Note that the serialization of a + // Footer will always occupy exactly this many bytes. It consists + // of two block handles and a magic number. + enum { + kEncodedLength = 2*BlockHandle::kMaxEncodedLength + 8 + }; + + private: + BlockHandle metaindex_handle_; + BlockHandle index_handle_; +}; + +// kTableMagicNumber was picked by running +// echo http://code.google.com/p/leveldb/ | sha1sum +// and taking the leading 64 bits. +static const uint64_t kTableMagicNumber = 0xdb4775248b80fb57ull; + +// 1-byte type + 32-bit crc +static const size_t kBlockTrailerSize = 5; + +struct BlockContents { + Slice data; // Actual contents of data + bool cachable; // True iff data can be cached + bool heap_allocated; // True iff caller should delete[] data.data() +}; + +// Read the block identified by "handle" from "file". On failure +// return non-OK. On success fill *result and return OK. +extern Status ReadBlock(RandomAccessFile* file, + const ReadOptions& options, + const BlockHandle& handle, + BlockContents* result); + +// Implementation details follow. Clients should ignore, + +inline BlockHandle::BlockHandle() + : offset_(~static_cast(0)), + size_(~static_cast(0)) { +} + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_FORMAT_H_ diff --git a/src/leveldb/table/iterator.cc b/src/leveldb/table/iterator.cc new file mode 100644 index 00000000..3d1c87fd --- /dev/null +++ b/src/leveldb/table/iterator.cc @@ -0,0 +1,67 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/iterator.h" + +namespace leveldb { + +Iterator::Iterator() { + cleanup_.function = NULL; + cleanup_.next = NULL; +} + +Iterator::~Iterator() { + if (cleanup_.function != NULL) { + (*cleanup_.function)(cleanup_.arg1, cleanup_.arg2); + for (Cleanup* c = cleanup_.next; c != NULL; ) { + (*c->function)(c->arg1, c->arg2); + Cleanup* next = c->next; + delete c; + c = next; + } + } +} + +void Iterator::RegisterCleanup(CleanupFunction func, void* arg1, void* arg2) { + assert(func != NULL); + Cleanup* c; + if (cleanup_.function == NULL) { + c = &cleanup_; + } else { + c = new Cleanup; + c->next = cleanup_.next; + cleanup_.next = c; + } + c->function = func; + c->arg1 = arg1; + c->arg2 = arg2; +} + +namespace { +class EmptyIterator : public Iterator { + public: + EmptyIterator(const Status& s) : status_(s) { } + virtual bool Valid() const { return false; } + virtual void Seek(const Slice& target) { } + virtual void SeekToFirst() { } + virtual void SeekToLast() { } + virtual void Next() { assert(false); } + virtual void Prev() { assert(false); } + Slice key() const { assert(false); return Slice(); } + Slice value() const { assert(false); return Slice(); } + virtual Status status() const { return status_; } + private: + Status status_; +}; +} // namespace + +Iterator* NewEmptyIterator() { + return new EmptyIterator(Status::OK()); +} + +Iterator* NewErrorIterator(const Status& status) { + return new EmptyIterator(status); +} + +} // namespace leveldb diff --git a/src/leveldb/table/iterator_wrapper.h b/src/leveldb/table/iterator_wrapper.h new file mode 100644 index 00000000..9e16b3db --- /dev/null +++ b/src/leveldb/table/iterator_wrapper.h @@ -0,0 +1,63 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_ITERATOR_WRAPPER_H_ +#define STORAGE_LEVELDB_TABLE_ITERATOR_WRAPPER_H_ + +namespace leveldb { + +// A internal wrapper class with an interface similar to Iterator that +// caches the valid() and key() results for an underlying iterator. +// This can help avoid virtual function calls and also gives better +// cache locality. +class IteratorWrapper { + public: + IteratorWrapper(): iter_(NULL), valid_(false) { } + explicit IteratorWrapper(Iterator* iter): iter_(NULL) { + Set(iter); + } + ~IteratorWrapper() { delete iter_; } + Iterator* iter() const { return iter_; } + + // Takes ownership of "iter" and will delete it when destroyed, or + // when Set() is invoked again. + void Set(Iterator* iter) { + delete iter_; + iter_ = iter; + if (iter_ == NULL) { + valid_ = false; + } else { + Update(); + } + } + + + // Iterator interface methods + bool Valid() const { return valid_; } + Slice key() const { assert(Valid()); return key_; } + Slice value() const { assert(Valid()); return iter_->value(); } + // Methods below require iter() != NULL + Status status() const { assert(iter_); return iter_->status(); } + void Next() { assert(iter_); iter_->Next(); Update(); } + void Prev() { assert(iter_); iter_->Prev(); Update(); } + void Seek(const Slice& k) { assert(iter_); iter_->Seek(k); Update(); } + void SeekToFirst() { assert(iter_); iter_->SeekToFirst(); Update(); } + void SeekToLast() { assert(iter_); iter_->SeekToLast(); Update(); } + + private: + void Update() { + valid_ = iter_->Valid(); + if (valid_) { + key_ = iter_->key(); + } + } + + Iterator* iter_; + bool valid_; + Slice key_; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_ITERATOR_WRAPPER_H_ diff --git a/src/leveldb/table/merger.cc b/src/leveldb/table/merger.cc new file mode 100644 index 00000000..2dde4dc2 --- /dev/null +++ b/src/leveldb/table/merger.cc @@ -0,0 +1,197 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "table/merger.h" + +#include "leveldb/comparator.h" +#include "leveldb/iterator.h" +#include "table/iterator_wrapper.h" + +namespace leveldb { + +namespace { +class MergingIterator : public Iterator { + public: + MergingIterator(const Comparator* comparator, Iterator** children, int n) + : comparator_(comparator), + children_(new IteratorWrapper[n]), + n_(n), + current_(NULL), + direction_(kForward) { + for (int i = 0; i < n; i++) { + children_[i].Set(children[i]); + } + } + + virtual ~MergingIterator() { + delete[] children_; + } + + virtual bool Valid() const { + return (current_ != NULL); + } + + virtual void SeekToFirst() { + for (int i = 0; i < n_; i++) { + children_[i].SeekToFirst(); + } + FindSmallest(); + direction_ = kForward; + } + + virtual void SeekToLast() { + for (int i = 0; i < n_; i++) { + children_[i].SeekToLast(); + } + FindLargest(); + direction_ = kReverse; + } + + virtual void Seek(const Slice& target) { + for (int i = 0; i < n_; i++) { + children_[i].Seek(target); + } + FindSmallest(); + direction_ = kForward; + } + + virtual void Next() { + assert(Valid()); + + // Ensure that all children are positioned after key(). + // If we are moving in the forward direction, it is already + // true for all of the non-current_ children since current_ is + // the smallest child and key() == current_->key(). Otherwise, + // we explicitly position the non-current_ children. + if (direction_ != kForward) { + for (int i = 0; i < n_; i++) { + IteratorWrapper* child = &children_[i]; + if (child != current_) { + child->Seek(key()); + if (child->Valid() && + comparator_->Compare(key(), child->key()) == 0) { + child->Next(); + } + } + } + direction_ = kForward; + } + + current_->Next(); + FindSmallest(); + } + + virtual void Prev() { + assert(Valid()); + + // Ensure that all children are positioned before key(). + // If we are moving in the reverse direction, it is already + // true for all of the non-current_ children since current_ is + // the largest child and key() == current_->key(). Otherwise, + // we explicitly position the non-current_ children. + if (direction_ != kReverse) { + for (int i = 0; i < n_; i++) { + IteratorWrapper* child = &children_[i]; + if (child != current_) { + child->Seek(key()); + if (child->Valid()) { + // Child is at first entry >= key(). Step back one to be < key() + child->Prev(); + } else { + // Child has no entries >= key(). Position at last entry. + child->SeekToLast(); + } + } + } + direction_ = kReverse; + } + + current_->Prev(); + FindLargest(); + } + + virtual Slice key() const { + assert(Valid()); + return current_->key(); + } + + virtual Slice value() const { + assert(Valid()); + return current_->value(); + } + + virtual Status status() const { + Status status; + for (int i = 0; i < n_; i++) { + status = children_[i].status(); + if (!status.ok()) { + break; + } + } + return status; + } + + private: + void FindSmallest(); + void FindLargest(); + + // We might want to use a heap in case there are lots of children. + // For now we use a simple array since we expect a very small number + // of children in leveldb. + const Comparator* comparator_; + IteratorWrapper* children_; + int n_; + IteratorWrapper* current_; + + // Which direction is the iterator moving? + enum Direction { + kForward, + kReverse + }; + Direction direction_; +}; + +void MergingIterator::FindSmallest() { + IteratorWrapper* smallest = NULL; + for (int i = 0; i < n_; i++) { + IteratorWrapper* child = &children_[i]; + if (child->Valid()) { + if (smallest == NULL) { + smallest = child; + } else if (comparator_->Compare(child->key(), smallest->key()) < 0) { + smallest = child; + } + } + } + current_ = smallest; +} + +void MergingIterator::FindLargest() { + IteratorWrapper* largest = NULL; + for (int i = n_-1; i >= 0; i--) { + IteratorWrapper* child = &children_[i]; + if (child->Valid()) { + if (largest == NULL) { + largest = child; + } else if (comparator_->Compare(child->key(), largest->key()) > 0) { + largest = child; + } + } + } + current_ = largest; +} +} // namespace + +Iterator* NewMergingIterator(const Comparator* cmp, Iterator** list, int n) { + assert(n >= 0); + if (n == 0) { + return NewEmptyIterator(); + } else if (n == 1) { + return list[0]; + } else { + return new MergingIterator(cmp, list, n); + } +} + +} // namespace leveldb diff --git a/src/leveldb/table/merger.h b/src/leveldb/table/merger.h new file mode 100644 index 00000000..91ddd80f --- /dev/null +++ b/src/leveldb/table/merger.h @@ -0,0 +1,26 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_MERGER_H_ +#define STORAGE_LEVELDB_TABLE_MERGER_H_ + +namespace leveldb { + +class Comparator; +class Iterator; + +// Return an iterator that provided the union of the data in +// children[0,n-1]. Takes ownership of the child iterators and +// will delete them when the result iterator is deleted. +// +// The result does no duplicate suppression. I.e., if a particular +// key is present in K child iterators, it will be yielded K times. +// +// REQUIRES: n >= 0 +extern Iterator* NewMergingIterator( + const Comparator* comparator, Iterator** children, int n); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_MERGER_H_ diff --git a/src/leveldb/table/table.cc b/src/leveldb/table/table.cc new file mode 100644 index 00000000..dff8a825 --- /dev/null +++ b/src/leveldb/table/table.cc @@ -0,0 +1,285 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/table.h" + +#include "leveldb/cache.h" +#include "leveldb/comparator.h" +#include "leveldb/env.h" +#include "leveldb/filter_policy.h" +#include "leveldb/options.h" +#include "table/block.h" +#include "table/filter_block.h" +#include "table/format.h" +#include "table/two_level_iterator.h" +#include "util/coding.h" + +namespace leveldb { + +struct Table::Rep { + ~Rep() { + delete filter; + delete [] filter_data; + delete index_block; + } + + Options options; + Status status; + RandomAccessFile* file; + uint64_t cache_id; + FilterBlockReader* filter; + const char* filter_data; + + BlockHandle metaindex_handle; // Handle to metaindex_block: saved from footer + Block* index_block; +}; + +Status Table::Open(const Options& options, + RandomAccessFile* file, + uint64_t size, + Table** table) { + *table = NULL; + if (size < Footer::kEncodedLength) { + return Status::Corruption("file is too short to be an sstable"); + } + + char footer_space[Footer::kEncodedLength]; + Slice footer_input; + Status s = file->Read(size - Footer::kEncodedLength, Footer::kEncodedLength, + &footer_input, footer_space); + if (!s.ok()) return s; + + Footer footer; + s = footer.DecodeFrom(&footer_input); + if (!s.ok()) return s; + + // Read the index block + BlockContents contents; + Block* index_block = NULL; + if (s.ok()) { + ReadOptions opt; + if (options.paranoid_checks) { + opt.verify_checksums = true; + } + s = ReadBlock(file, opt, footer.index_handle(), &contents); + if (s.ok()) { + index_block = new Block(contents); + } + } + + if (s.ok()) { + // We've successfully read the footer and the index block: we're + // ready to serve requests. + Rep* rep = new Table::Rep; + rep->options = options; + rep->file = file; + rep->metaindex_handle = footer.metaindex_handle(); + rep->index_block = index_block; + rep->cache_id = (options.block_cache ? options.block_cache->NewId() : 0); + rep->filter_data = NULL; + rep->filter = NULL; + *table = new Table(rep); + (*table)->ReadMeta(footer); + } else { + if (index_block) delete index_block; + } + + return s; +} + +void Table::ReadMeta(const Footer& footer) { + if (rep_->options.filter_policy == NULL) { + return; // Do not need any metadata + } + + // TODO(sanjay): Skip this if footer.metaindex_handle() size indicates + // it is an empty block. + ReadOptions opt; + if (rep_->options.paranoid_checks) { + opt.verify_checksums = true; + } + BlockContents contents; + if (!ReadBlock(rep_->file, opt, footer.metaindex_handle(), &contents).ok()) { + // Do not propagate errors since meta info is not needed for operation + return; + } + Block* meta = new Block(contents); + + Iterator* iter = meta->NewIterator(BytewiseComparator()); + std::string key = "filter."; + key.append(rep_->options.filter_policy->Name()); + iter->Seek(key); + if (iter->Valid() && iter->key() == Slice(key)) { + ReadFilter(iter->value()); + } + delete iter; + delete meta; +} + +void Table::ReadFilter(const Slice& filter_handle_value) { + Slice v = filter_handle_value; + BlockHandle filter_handle; + if (!filter_handle.DecodeFrom(&v).ok()) { + return; + } + + // We might want to unify with ReadBlock() if we start + // requiring checksum verification in Table::Open. + ReadOptions opt; + if (rep_->options.paranoid_checks) { + opt.verify_checksums = true; + } + BlockContents block; + if (!ReadBlock(rep_->file, opt, filter_handle, &block).ok()) { + return; + } + if (block.heap_allocated) { + rep_->filter_data = block.data.data(); // Will need to delete later + } + rep_->filter = new FilterBlockReader(rep_->options.filter_policy, block.data); +} + +Table::~Table() { + delete rep_; +} + +static void DeleteBlock(void* arg, void* ignored) { + delete reinterpret_cast(arg); +} + +static void DeleteCachedBlock(const Slice& key, void* value) { + Block* block = reinterpret_cast(value); + delete block; +} + +static void ReleaseBlock(void* arg, void* h) { + Cache* cache = reinterpret_cast(arg); + Cache::Handle* handle = reinterpret_cast(h); + cache->Release(handle); +} + +// Convert an index iterator value (i.e., an encoded BlockHandle) +// into an iterator over the contents of the corresponding block. +Iterator* Table::BlockReader(void* arg, + const ReadOptions& options, + const Slice& index_value) { + Table* table = reinterpret_cast(arg); + Cache* block_cache = table->rep_->options.block_cache; + Block* block = NULL; + Cache::Handle* cache_handle = NULL; + + BlockHandle handle; + Slice input = index_value; + Status s = handle.DecodeFrom(&input); + // We intentionally allow extra stuff in index_value so that we + // can add more features in the future. + + if (s.ok()) { + BlockContents contents; + if (block_cache != NULL) { + char cache_key_buffer[16]; + EncodeFixed64(cache_key_buffer, table->rep_->cache_id); + EncodeFixed64(cache_key_buffer+8, handle.offset()); + Slice key(cache_key_buffer, sizeof(cache_key_buffer)); + cache_handle = block_cache->Lookup(key); + if (cache_handle != NULL) { + block = reinterpret_cast(block_cache->Value(cache_handle)); + } else { + s = ReadBlock(table->rep_->file, options, handle, &contents); + if (s.ok()) { + block = new Block(contents); + if (contents.cachable && options.fill_cache) { + cache_handle = block_cache->Insert( + key, block, block->size(), &DeleteCachedBlock); + } + } + } + } else { + s = ReadBlock(table->rep_->file, options, handle, &contents); + if (s.ok()) { + block = new Block(contents); + } + } + } + + Iterator* iter; + if (block != NULL) { + iter = block->NewIterator(table->rep_->options.comparator); + if (cache_handle == NULL) { + iter->RegisterCleanup(&DeleteBlock, block, NULL); + } else { + iter->RegisterCleanup(&ReleaseBlock, block_cache, cache_handle); + } + } else { + iter = NewErrorIterator(s); + } + return iter; +} + +Iterator* Table::NewIterator(const ReadOptions& options) const { + return NewTwoLevelIterator( + rep_->index_block->NewIterator(rep_->options.comparator), + &Table::BlockReader, const_cast(this), options); +} + +Status Table::InternalGet(const ReadOptions& options, const Slice& k, + void* arg, + void (*saver)(void*, const Slice&, const Slice&)) { + Status s; + Iterator* iiter = rep_->index_block->NewIterator(rep_->options.comparator); + iiter->Seek(k); + if (iiter->Valid()) { + Slice handle_value = iiter->value(); + FilterBlockReader* filter = rep_->filter; + BlockHandle handle; + if (filter != NULL && + handle.DecodeFrom(&handle_value).ok() && + !filter->KeyMayMatch(handle.offset(), k)) { + // Not found + } else { + Iterator* block_iter = BlockReader(this, options, iiter->value()); + block_iter->Seek(k); + if (block_iter->Valid()) { + (*saver)(arg, block_iter->key(), block_iter->value()); + } + s = block_iter->status(); + delete block_iter; + } + } + if (s.ok()) { + s = iiter->status(); + } + delete iiter; + return s; +} + + +uint64_t Table::ApproximateOffsetOf(const Slice& key) const { + Iterator* index_iter = + rep_->index_block->NewIterator(rep_->options.comparator); + index_iter->Seek(key); + uint64_t result; + if (index_iter->Valid()) { + BlockHandle handle; + Slice input = index_iter->value(); + Status s = handle.DecodeFrom(&input); + if (s.ok()) { + result = handle.offset(); + } else { + // Strange: we can't decode the block handle in the index block. + // We'll just return the offset of the metaindex block, which is + // close to the whole file size for this case. + result = rep_->metaindex_handle.offset(); + } + } else { + // key is past the last key in the file. Approximate the offset + // by returning the offset of the metaindex block (which is + // right near the end of the file). + result = rep_->metaindex_handle.offset(); + } + delete index_iter; + return result; +} + +} // namespace leveldb diff --git a/src/leveldb/table/table_builder.cc b/src/leveldb/table/table_builder.cc new file mode 100644 index 00000000..62002c84 --- /dev/null +++ b/src/leveldb/table/table_builder.cc @@ -0,0 +1,270 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/table_builder.h" + +#include +#include "leveldb/comparator.h" +#include "leveldb/env.h" +#include "leveldb/filter_policy.h" +#include "leveldb/options.h" +#include "table/block_builder.h" +#include "table/filter_block.h" +#include "table/format.h" +#include "util/coding.h" +#include "util/crc32c.h" + +namespace leveldb { + +struct TableBuilder::Rep { + Options options; + Options index_block_options; + WritableFile* file; + uint64_t offset; + Status status; + BlockBuilder data_block; + BlockBuilder index_block; + std::string last_key; + int64_t num_entries; + bool closed; // Either Finish() or Abandon() has been called. + FilterBlockBuilder* filter_block; + + // We do not emit the index entry for a block until we have seen the + // first key for the next data block. This allows us to use shorter + // keys in the index block. For example, consider a block boundary + // between the keys "the quick brown fox" and "the who". We can use + // "the r" as the key for the index block entry since it is >= all + // entries in the first block and < all entries in subsequent + // blocks. + // + // Invariant: r->pending_index_entry is true only if data_block is empty. + bool pending_index_entry; + BlockHandle pending_handle; // Handle to add to index block + + std::string compressed_output; + + Rep(const Options& opt, WritableFile* f) + : options(opt), + index_block_options(opt), + file(f), + offset(0), + data_block(&options), + index_block(&index_block_options), + num_entries(0), + closed(false), + filter_block(opt.filter_policy == NULL ? NULL + : new FilterBlockBuilder(opt.filter_policy)), + pending_index_entry(false) { + index_block_options.block_restart_interval = 1; + } +}; + +TableBuilder::TableBuilder(const Options& options, WritableFile* file) + : rep_(new Rep(options, file)) { + if (rep_->filter_block != NULL) { + rep_->filter_block->StartBlock(0); + } +} + +TableBuilder::~TableBuilder() { + assert(rep_->closed); // Catch errors where caller forgot to call Finish() + delete rep_->filter_block; + delete rep_; +} + +Status TableBuilder::ChangeOptions(const Options& options) { + // Note: if more fields are added to Options, update + // this function to catch changes that should not be allowed to + // change in the middle of building a Table. + if (options.comparator != rep_->options.comparator) { + return Status::InvalidArgument("changing comparator while building table"); + } + + // Note that any live BlockBuilders point to rep_->options and therefore + // will automatically pick up the updated options. + rep_->options = options; + rep_->index_block_options = options; + rep_->index_block_options.block_restart_interval = 1; + return Status::OK(); +} + +void TableBuilder::Add(const Slice& key, const Slice& value) { + Rep* r = rep_; + assert(!r->closed); + if (!ok()) return; + if (r->num_entries > 0) { + assert(r->options.comparator->Compare(key, Slice(r->last_key)) > 0); + } + + if (r->pending_index_entry) { + assert(r->data_block.empty()); + r->options.comparator->FindShortestSeparator(&r->last_key, key); + std::string handle_encoding; + r->pending_handle.EncodeTo(&handle_encoding); + r->index_block.Add(r->last_key, Slice(handle_encoding)); + r->pending_index_entry = false; + } + + if (r->filter_block != NULL) { + r->filter_block->AddKey(key); + } + + r->last_key.assign(key.data(), key.size()); + r->num_entries++; + r->data_block.Add(key, value); + + const size_t estimated_block_size = r->data_block.CurrentSizeEstimate(); + if (estimated_block_size >= r->options.block_size) { + Flush(); + } +} + +void TableBuilder::Flush() { + Rep* r = rep_; + assert(!r->closed); + if (!ok()) return; + if (r->data_block.empty()) return; + assert(!r->pending_index_entry); + WriteBlock(&r->data_block, &r->pending_handle); + if (ok()) { + r->pending_index_entry = true; + r->status = r->file->Flush(); + } + if (r->filter_block != NULL) { + r->filter_block->StartBlock(r->offset); + } +} + +void TableBuilder::WriteBlock(BlockBuilder* block, BlockHandle* handle) { + // File format contains a sequence of blocks where each block has: + // block_data: uint8[n] + // type: uint8 + // crc: uint32 + assert(ok()); + Rep* r = rep_; + Slice raw = block->Finish(); + + Slice block_contents; + CompressionType type = r->options.compression; + // TODO(postrelease): Support more compression options: zlib? + switch (type) { + case kNoCompression: + block_contents = raw; + break; + + case kSnappyCompression: { + std::string* compressed = &r->compressed_output; + if (port::Snappy_Compress(raw.data(), raw.size(), compressed) && + compressed->size() < raw.size() - (raw.size() / 8u)) { + block_contents = *compressed; + } else { + // Snappy not supported, or compressed less than 12.5%, so just + // store uncompressed form + block_contents = raw; + type = kNoCompression; + } + break; + } + } + WriteRawBlock(block_contents, type, handle); + r->compressed_output.clear(); + block->Reset(); +} + +void TableBuilder::WriteRawBlock(const Slice& block_contents, + CompressionType type, + BlockHandle* handle) { + Rep* r = rep_; + handle->set_offset(r->offset); + handle->set_size(block_contents.size()); + r->status = r->file->Append(block_contents); + if (r->status.ok()) { + char trailer[kBlockTrailerSize]; + trailer[0] = type; + uint32_t crc = crc32c::Value(block_contents.data(), block_contents.size()); + crc = crc32c::Extend(crc, trailer, 1); // Extend crc to cover block type + EncodeFixed32(trailer+1, crc32c::Mask(crc)); + r->status = r->file->Append(Slice(trailer, kBlockTrailerSize)); + if (r->status.ok()) { + r->offset += block_contents.size() + kBlockTrailerSize; + } + } +} + +Status TableBuilder::status() const { + return rep_->status; +} + +Status TableBuilder::Finish() { + Rep* r = rep_; + Flush(); + assert(!r->closed); + r->closed = true; + + BlockHandle filter_block_handle, metaindex_block_handle, index_block_handle; + + // Write filter block + if (ok() && r->filter_block != NULL) { + WriteRawBlock(r->filter_block->Finish(), kNoCompression, + &filter_block_handle); + } + + // Write metaindex block + if (ok()) { + BlockBuilder meta_index_block(&r->options); + if (r->filter_block != NULL) { + // Add mapping from "filter.Name" to location of filter data + std::string key = "filter."; + key.append(r->options.filter_policy->Name()); + std::string handle_encoding; + filter_block_handle.EncodeTo(&handle_encoding); + meta_index_block.Add(key, handle_encoding); + } + + // TODO(postrelease): Add stats and other meta blocks + WriteBlock(&meta_index_block, &metaindex_block_handle); + } + + // Write index block + if (ok()) { + if (r->pending_index_entry) { + r->options.comparator->FindShortSuccessor(&r->last_key); + std::string handle_encoding; + r->pending_handle.EncodeTo(&handle_encoding); + r->index_block.Add(r->last_key, Slice(handle_encoding)); + r->pending_index_entry = false; + } + WriteBlock(&r->index_block, &index_block_handle); + } + + // Write footer + if (ok()) { + Footer footer; + footer.set_metaindex_handle(metaindex_block_handle); + footer.set_index_handle(index_block_handle); + std::string footer_encoding; + footer.EncodeTo(&footer_encoding); + r->status = r->file->Append(footer_encoding); + if (r->status.ok()) { + r->offset += footer_encoding.size(); + } + } + return r->status; +} + +void TableBuilder::Abandon() { + Rep* r = rep_; + assert(!r->closed); + r->closed = true; +} + +uint64_t TableBuilder::NumEntries() const { + return rep_->num_entries; +} + +uint64_t TableBuilder::FileSize() const { + return rep_->offset; +} + +} // namespace leveldb diff --git a/src/leveldb/table/table_test.cc b/src/leveldb/table/table_test.cc new file mode 100644 index 00000000..c723bf84 --- /dev/null +++ b/src/leveldb/table/table_test.cc @@ -0,0 +1,868 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/table.h" + +#include +#include +#include "db/dbformat.h" +#include "db/memtable.h" +#include "db/write_batch_internal.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/iterator.h" +#include "leveldb/table_builder.h" +#include "table/block.h" +#include "table/block_builder.h" +#include "table/format.h" +#include "util/random.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +// Return reverse of "key". +// Used to test non-lexicographic comparators. +static std::string Reverse(const Slice& key) { + std::string str(key.ToString()); + std::string rev(""); + for (std::string::reverse_iterator rit = str.rbegin(); + rit != str.rend(); ++rit) { + rev.push_back(*rit); + } + return rev; +} + +namespace { +class ReverseKeyComparator : public Comparator { + public: + virtual const char* Name() const { + return "leveldb.ReverseBytewiseComparator"; + } + + virtual int Compare(const Slice& a, const Slice& b) const { + return BytewiseComparator()->Compare(Reverse(a), Reverse(b)); + } + + virtual void FindShortestSeparator( + std::string* start, + const Slice& limit) const { + std::string s = Reverse(*start); + std::string l = Reverse(limit); + BytewiseComparator()->FindShortestSeparator(&s, l); + *start = Reverse(s); + } + + virtual void FindShortSuccessor(std::string* key) const { + std::string s = Reverse(*key); + BytewiseComparator()->FindShortSuccessor(&s); + *key = Reverse(s); + } +}; +} // namespace +static ReverseKeyComparator reverse_key_comparator; + +static void Increment(const Comparator* cmp, std::string* key) { + if (cmp == BytewiseComparator()) { + key->push_back('\0'); + } else { + assert(cmp == &reverse_key_comparator); + std::string rev = Reverse(*key); + rev.push_back('\0'); + *key = Reverse(rev); + } +} + +// An STL comparator that uses a Comparator +namespace { +struct STLLessThan { + const Comparator* cmp; + + STLLessThan() : cmp(BytewiseComparator()) { } + STLLessThan(const Comparator* c) : cmp(c) { } + bool operator()(const std::string& a, const std::string& b) const { + return cmp->Compare(Slice(a), Slice(b)) < 0; + } +}; +} // namespace + +class StringSink: public WritableFile { + public: + ~StringSink() { } + + const std::string& contents() const { return contents_; } + + virtual Status Close() { return Status::OK(); } + virtual Status Flush() { return Status::OK(); } + virtual Status Sync() { return Status::OK(); } + + virtual Status Append(const Slice& data) { + contents_.append(data.data(), data.size()); + return Status::OK(); + } + + private: + std::string contents_; +}; + + +class StringSource: public RandomAccessFile { + public: + StringSource(const Slice& contents) + : contents_(contents.data(), contents.size()) { + } + + virtual ~StringSource() { } + + uint64_t Size() const { return contents_.size(); } + + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + if (offset > contents_.size()) { + return Status::InvalidArgument("invalid Read offset"); + } + if (offset + n > contents_.size()) { + n = contents_.size() - offset; + } + memcpy(scratch, &contents_[offset], n); + *result = Slice(scratch, n); + return Status::OK(); + } + + private: + std::string contents_; +}; + +typedef std::map KVMap; + +// Helper class for tests to unify the interface between +// BlockBuilder/TableBuilder and Block/Table. +class Constructor { + public: + explicit Constructor(const Comparator* cmp) : data_(STLLessThan(cmp)) { } + virtual ~Constructor() { } + + void Add(const std::string& key, const Slice& value) { + data_[key] = value.ToString(); + } + + // Finish constructing the data structure with all the keys that have + // been added so far. Returns the keys in sorted order in "*keys" + // and stores the key/value pairs in "*kvmap" + void Finish(const Options& options, + std::vector* keys, + KVMap* kvmap) { + *kvmap = data_; + keys->clear(); + for (KVMap::const_iterator it = data_.begin(); + it != data_.end(); + ++it) { + keys->push_back(it->first); + } + data_.clear(); + Status s = FinishImpl(options, *kvmap); + ASSERT_TRUE(s.ok()) << s.ToString(); + } + + // Construct the data structure from the data in "data" + virtual Status FinishImpl(const Options& options, const KVMap& data) = 0; + + virtual Iterator* NewIterator() const = 0; + + virtual const KVMap& data() { return data_; } + + virtual DB* db() const { return NULL; } // Overridden in DBConstructor + + private: + KVMap data_; +}; + +class BlockConstructor: public Constructor { + public: + explicit BlockConstructor(const Comparator* cmp) + : Constructor(cmp), + comparator_(cmp), + block_(NULL) { } + ~BlockConstructor() { + delete block_; + } + virtual Status FinishImpl(const Options& options, const KVMap& data) { + delete block_; + block_ = NULL; + BlockBuilder builder(&options); + + for (KVMap::const_iterator it = data.begin(); + it != data.end(); + ++it) { + builder.Add(it->first, it->second); + } + // Open the block + data_ = builder.Finish().ToString(); + BlockContents contents; + contents.data = data_; + contents.cachable = false; + contents.heap_allocated = false; + block_ = new Block(contents); + return Status::OK(); + } + virtual Iterator* NewIterator() const { + return block_->NewIterator(comparator_); + } + + private: + const Comparator* comparator_; + std::string data_; + Block* block_; + + BlockConstructor(); +}; + +class TableConstructor: public Constructor { + public: + TableConstructor(const Comparator* cmp) + : Constructor(cmp), + source_(NULL), table_(NULL) { + } + ~TableConstructor() { + Reset(); + } + virtual Status FinishImpl(const Options& options, const KVMap& data) { + Reset(); + StringSink sink; + TableBuilder builder(options, &sink); + + for (KVMap::const_iterator it = data.begin(); + it != data.end(); + ++it) { + builder.Add(it->first, it->second); + ASSERT_TRUE(builder.status().ok()); + } + Status s = builder.Finish(); + ASSERT_TRUE(s.ok()) << s.ToString(); + + ASSERT_EQ(sink.contents().size(), builder.FileSize()); + + // Open the table + source_ = new StringSource(sink.contents()); + Options table_options; + table_options.comparator = options.comparator; + return Table::Open(table_options, source_, sink.contents().size(), &table_); + } + + virtual Iterator* NewIterator() const { + return table_->NewIterator(ReadOptions()); + } + + uint64_t ApproximateOffsetOf(const Slice& key) const { + return table_->ApproximateOffsetOf(key); + } + + private: + void Reset() { + delete table_; + delete source_; + table_ = NULL; + source_ = NULL; + } + + StringSource* source_; + Table* table_; + + TableConstructor(); +}; + +// A helper class that converts internal format keys into user keys +class KeyConvertingIterator: public Iterator { + public: + explicit KeyConvertingIterator(Iterator* iter) : iter_(iter) { } + virtual ~KeyConvertingIterator() { delete iter_; } + virtual bool Valid() const { return iter_->Valid(); } + virtual void Seek(const Slice& target) { + ParsedInternalKey ikey(target, kMaxSequenceNumber, kTypeValue); + std::string encoded; + AppendInternalKey(&encoded, ikey); + iter_->Seek(encoded); + } + virtual void SeekToFirst() { iter_->SeekToFirst(); } + virtual void SeekToLast() { iter_->SeekToLast(); } + virtual void Next() { iter_->Next(); } + virtual void Prev() { iter_->Prev(); } + + virtual Slice key() const { + assert(Valid()); + ParsedInternalKey key; + if (!ParseInternalKey(iter_->key(), &key)) { + status_ = Status::Corruption("malformed internal key"); + return Slice("corrupted key"); + } + return key.user_key; + } + + virtual Slice value() const { return iter_->value(); } + virtual Status status() const { + return status_.ok() ? iter_->status() : status_; + } + + private: + mutable Status status_; + Iterator* iter_; + + // No copying allowed + KeyConvertingIterator(const KeyConvertingIterator&); + void operator=(const KeyConvertingIterator&); +}; + +class MemTableConstructor: public Constructor { + public: + explicit MemTableConstructor(const Comparator* cmp) + : Constructor(cmp), + internal_comparator_(cmp) { + memtable_ = new MemTable(internal_comparator_); + memtable_->Ref(); + } + ~MemTableConstructor() { + memtable_->Unref(); + } + virtual Status FinishImpl(const Options& options, const KVMap& data) { + memtable_->Unref(); + memtable_ = new MemTable(internal_comparator_); + memtable_->Ref(); + int seq = 1; + for (KVMap::const_iterator it = data.begin(); + it != data.end(); + ++it) { + memtable_->Add(seq, kTypeValue, it->first, it->second); + seq++; + } + return Status::OK(); + } + virtual Iterator* NewIterator() const { + return new KeyConvertingIterator(memtable_->NewIterator()); + } + + private: + InternalKeyComparator internal_comparator_; + MemTable* memtable_; +}; + +class DBConstructor: public Constructor { + public: + explicit DBConstructor(const Comparator* cmp) + : Constructor(cmp), + comparator_(cmp) { + db_ = NULL; + NewDB(); + } + ~DBConstructor() { + delete db_; + } + virtual Status FinishImpl(const Options& options, const KVMap& data) { + delete db_; + db_ = NULL; + NewDB(); + for (KVMap::const_iterator it = data.begin(); + it != data.end(); + ++it) { + WriteBatch batch; + batch.Put(it->first, it->second); + ASSERT_TRUE(db_->Write(WriteOptions(), &batch).ok()); + } + return Status::OK(); + } + virtual Iterator* NewIterator() const { + return db_->NewIterator(ReadOptions()); + } + + virtual DB* db() const { return db_; } + + private: + void NewDB() { + std::string name = test::TmpDir() + "/table_testdb"; + + Options options; + options.comparator = comparator_; + Status status = DestroyDB(name, options); + ASSERT_TRUE(status.ok()) << status.ToString(); + + options.create_if_missing = true; + options.error_if_exists = true; + options.write_buffer_size = 10000; // Something small to force merging + status = DB::Open(options, name, &db_); + ASSERT_TRUE(status.ok()) << status.ToString(); + } + + const Comparator* comparator_; + DB* db_; +}; + +enum TestType { + TABLE_TEST, + BLOCK_TEST, + MEMTABLE_TEST, + DB_TEST +}; + +struct TestArgs { + TestType type; + bool reverse_compare; + int restart_interval; +}; + +static const TestArgs kTestArgList[] = { + { TABLE_TEST, false, 16 }, + { TABLE_TEST, false, 1 }, + { TABLE_TEST, false, 1024 }, + { TABLE_TEST, true, 16 }, + { TABLE_TEST, true, 1 }, + { TABLE_TEST, true, 1024 }, + + { BLOCK_TEST, false, 16 }, + { BLOCK_TEST, false, 1 }, + { BLOCK_TEST, false, 1024 }, + { BLOCK_TEST, true, 16 }, + { BLOCK_TEST, true, 1 }, + { BLOCK_TEST, true, 1024 }, + + // Restart interval does not matter for memtables + { MEMTABLE_TEST, false, 16 }, + { MEMTABLE_TEST, true, 16 }, + + // Do not bother with restart interval variations for DB + { DB_TEST, false, 16 }, + { DB_TEST, true, 16 }, +}; +static const int kNumTestArgs = sizeof(kTestArgList) / sizeof(kTestArgList[0]); + +class Harness { + public: + Harness() : constructor_(NULL) { } + + void Init(const TestArgs& args) { + delete constructor_; + constructor_ = NULL; + options_ = Options(); + + options_.block_restart_interval = args.restart_interval; + // Use shorter block size for tests to exercise block boundary + // conditions more. + options_.block_size = 256; + if (args.reverse_compare) { + options_.comparator = &reverse_key_comparator; + } + switch (args.type) { + case TABLE_TEST: + constructor_ = new TableConstructor(options_.comparator); + break; + case BLOCK_TEST: + constructor_ = new BlockConstructor(options_.comparator); + break; + case MEMTABLE_TEST: + constructor_ = new MemTableConstructor(options_.comparator); + break; + case DB_TEST: + constructor_ = new DBConstructor(options_.comparator); + break; + } + } + + ~Harness() { + delete constructor_; + } + + void Add(const std::string& key, const std::string& value) { + constructor_->Add(key, value); + } + + void Test(Random* rnd) { + std::vector keys; + KVMap data; + constructor_->Finish(options_, &keys, &data); + + TestForwardScan(keys, data); + TestBackwardScan(keys, data); + TestRandomAccess(rnd, keys, data); + } + + void TestForwardScan(const std::vector& keys, + const KVMap& data) { + Iterator* iter = constructor_->NewIterator(); + ASSERT_TRUE(!iter->Valid()); + iter->SeekToFirst(); + for (KVMap::const_iterator model_iter = data.begin(); + model_iter != data.end(); + ++model_iter) { + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + iter->Next(); + } + ASSERT_TRUE(!iter->Valid()); + delete iter; + } + + void TestBackwardScan(const std::vector& keys, + const KVMap& data) { + Iterator* iter = constructor_->NewIterator(); + ASSERT_TRUE(!iter->Valid()); + iter->SeekToLast(); + for (KVMap::const_reverse_iterator model_iter = data.rbegin(); + model_iter != data.rend(); + ++model_iter) { + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + iter->Prev(); + } + ASSERT_TRUE(!iter->Valid()); + delete iter; + } + + void TestRandomAccess(Random* rnd, + const std::vector& keys, + const KVMap& data) { + static const bool kVerbose = false; + Iterator* iter = constructor_->NewIterator(); + ASSERT_TRUE(!iter->Valid()); + KVMap::const_iterator model_iter = data.begin(); + if (kVerbose) fprintf(stderr, "---\n"); + for (int i = 0; i < 200; i++) { + const int toss = rnd->Uniform(5); + switch (toss) { + case 0: { + if (iter->Valid()) { + if (kVerbose) fprintf(stderr, "Next\n"); + iter->Next(); + ++model_iter; + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + } + break; + } + + case 1: { + if (kVerbose) fprintf(stderr, "SeekToFirst\n"); + iter->SeekToFirst(); + model_iter = data.begin(); + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + break; + } + + case 2: { + std::string key = PickRandomKey(rnd, keys); + model_iter = data.lower_bound(key); + if (kVerbose) fprintf(stderr, "Seek '%s'\n", + EscapeString(key).c_str()); + iter->Seek(Slice(key)); + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + break; + } + + case 3: { + if (iter->Valid()) { + if (kVerbose) fprintf(stderr, "Prev\n"); + iter->Prev(); + if (model_iter == data.begin()) { + model_iter = data.end(); // Wrap around to invalid value + } else { + --model_iter; + } + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + } + break; + } + + case 4: { + if (kVerbose) fprintf(stderr, "SeekToLast\n"); + iter->SeekToLast(); + if (keys.empty()) { + model_iter = data.end(); + } else { + std::string last = data.rbegin()->first; + model_iter = data.lower_bound(last); + } + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + break; + } + } + } + delete iter; + } + + std::string ToString(const KVMap& data, const KVMap::const_iterator& it) { + if (it == data.end()) { + return "END"; + } else { + return "'" + it->first + "->" + it->second + "'"; + } + } + + std::string ToString(const KVMap& data, + const KVMap::const_reverse_iterator& it) { + if (it == data.rend()) { + return "END"; + } else { + return "'" + it->first + "->" + it->second + "'"; + } + } + + std::string ToString(const Iterator* it) { + if (!it->Valid()) { + return "END"; + } else { + return "'" + it->key().ToString() + "->" + it->value().ToString() + "'"; + } + } + + std::string PickRandomKey(Random* rnd, const std::vector& keys) { + if (keys.empty()) { + return "foo"; + } else { + const int index = rnd->Uniform(keys.size()); + std::string result = keys[index]; + switch (rnd->Uniform(3)) { + case 0: + // Return an existing key + break; + case 1: { + // Attempt to return something smaller than an existing key + if (result.size() > 0 && result[result.size()-1] > '\0') { + result[result.size()-1]--; + } + break; + } + case 2: { + // Return something larger than an existing key + Increment(options_.comparator, &result); + break; + } + } + return result; + } + } + + // Returns NULL if not running against a DB + DB* db() const { return constructor_->db(); } + + private: + Options options_; + Constructor* constructor_; +}; + +// Test empty table/block. +TEST(Harness, Empty) { + for (int i = 0; i < kNumTestArgs; i++) { + Init(kTestArgList[i]); + Random rnd(test::RandomSeed() + 1); + Test(&rnd); + } +} + +// Special test for a block with no restart entries. The C++ leveldb +// code never generates such blocks, but the Java version of leveldb +// seems to. +TEST(Harness, ZeroRestartPointsInBlock) { + char data[sizeof(uint32_t)]; + memset(data, 0, sizeof(data)); + BlockContents contents; + contents.data = Slice(data, sizeof(data)); + contents.cachable = false; + contents.heap_allocated = false; + Block block(contents); + Iterator* iter = block.NewIterator(BytewiseComparator()); + iter->SeekToFirst(); + ASSERT_TRUE(!iter->Valid()); + iter->SeekToLast(); + ASSERT_TRUE(!iter->Valid()); + iter->Seek("foo"); + ASSERT_TRUE(!iter->Valid()); + delete iter; +} + +// Test the empty key +TEST(Harness, SimpleEmptyKey) { + for (int i = 0; i < kNumTestArgs; i++) { + Init(kTestArgList[i]); + Random rnd(test::RandomSeed() + 1); + Add("", "v"); + Test(&rnd); + } +} + +TEST(Harness, SimpleSingle) { + for (int i = 0; i < kNumTestArgs; i++) { + Init(kTestArgList[i]); + Random rnd(test::RandomSeed() + 2); + Add("abc", "v"); + Test(&rnd); + } +} + +TEST(Harness, SimpleMulti) { + for (int i = 0; i < kNumTestArgs; i++) { + Init(kTestArgList[i]); + Random rnd(test::RandomSeed() + 3); + Add("abc", "v"); + Add("abcd", "v"); + Add("ac", "v2"); + Test(&rnd); + } +} + +TEST(Harness, SimpleSpecialKey) { + for (int i = 0; i < kNumTestArgs; i++) { + Init(kTestArgList[i]); + Random rnd(test::RandomSeed() + 4); + Add("\xff\xff", "v3"); + Test(&rnd); + } +} + +TEST(Harness, Randomized) { + for (int i = 0; i < kNumTestArgs; i++) { + Init(kTestArgList[i]); + Random rnd(test::RandomSeed() + 5); + for (int num_entries = 0; num_entries < 2000; + num_entries += (num_entries < 50 ? 1 : 200)) { + if ((num_entries % 10) == 0) { + fprintf(stderr, "case %d of %d: num_entries = %d\n", + (i + 1), int(kNumTestArgs), num_entries); + } + for (int e = 0; e < num_entries; e++) { + std::string v; + Add(test::RandomKey(&rnd, rnd.Skewed(4)), + test::RandomString(&rnd, rnd.Skewed(5), &v).ToString()); + } + Test(&rnd); + } + } +} + +TEST(Harness, RandomizedLongDB) { + Random rnd(test::RandomSeed()); + TestArgs args = { DB_TEST, false, 16 }; + Init(args); + int num_entries = 100000; + for (int e = 0; e < num_entries; e++) { + std::string v; + Add(test::RandomKey(&rnd, rnd.Skewed(4)), + test::RandomString(&rnd, rnd.Skewed(5), &v).ToString()); + } + Test(&rnd); + + // We must have created enough data to force merging + int files = 0; + for (int level = 0; level < config::kNumLevels; level++) { + std::string value; + char name[100]; + snprintf(name, sizeof(name), "leveldb.num-files-at-level%d", level); + ASSERT_TRUE(db()->GetProperty(name, &value)); + files += atoi(value.c_str()); + } + ASSERT_GT(files, 0); +} + +class MemTableTest { }; + +TEST(MemTableTest, Simple) { + InternalKeyComparator cmp(BytewiseComparator()); + MemTable* memtable = new MemTable(cmp); + memtable->Ref(); + WriteBatch batch; + WriteBatchInternal::SetSequence(&batch, 100); + batch.Put(std::string("k1"), std::string("v1")); + batch.Put(std::string("k2"), std::string("v2")); + batch.Put(std::string("k3"), std::string("v3")); + batch.Put(std::string("largekey"), std::string("vlarge")); + ASSERT_TRUE(WriteBatchInternal::InsertInto(&batch, memtable).ok()); + + Iterator* iter = memtable->NewIterator(); + iter->SeekToFirst(); + while (iter->Valid()) { + fprintf(stderr, "key: '%s' -> '%s'\n", + iter->key().ToString().c_str(), + iter->value().ToString().c_str()); + iter->Next(); + } + + delete iter; + memtable->Unref(); +} + +static bool Between(uint64_t val, uint64_t low, uint64_t high) { + bool result = (val >= low) && (val <= high); + if (!result) { + fprintf(stderr, "Value %llu is not in range [%llu, %llu]\n", + (unsigned long long)(val), + (unsigned long long)(low), + (unsigned long long)(high)); + } + return result; +} + +class TableTest { }; + +TEST(TableTest, ApproximateOffsetOfPlain) { + TableConstructor c(BytewiseComparator()); + c.Add("k01", "hello"); + c.Add("k02", "hello2"); + c.Add("k03", std::string(10000, 'x')); + c.Add("k04", std::string(200000, 'x')); + c.Add("k05", std::string(300000, 'x')); + c.Add("k06", "hello3"); + c.Add("k07", std::string(100000, 'x')); + std::vector keys; + KVMap kvmap; + Options options; + options.block_size = 1024; + options.compression = kNoCompression; + c.Finish(options, &keys, &kvmap); + + ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01a"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), 10000, 11000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04a"), 210000, 211000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k05"), 210000, 211000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k06"), 510000, 511000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k07"), 510000, 511000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 610000, 612000)); + +} + +static bool SnappyCompressionSupported() { + std::string out; + Slice in = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + return port::Snappy_Compress(in.data(), in.size(), &out); +} + +TEST(TableTest, ApproximateOffsetOfCompressed) { + if (!SnappyCompressionSupported()) { + fprintf(stderr, "skipping compression tests\n"); + return; + } + + Random rnd(301); + TableConstructor c(BytewiseComparator()); + std::string tmp; + c.Add("k01", "hello"); + c.Add("k02", test::CompressibleString(&rnd, 0.25, 10000, &tmp)); + c.Add("k03", "hello3"); + c.Add("k04", test::CompressibleString(&rnd, 0.25, 10000, &tmp)); + std::vector keys; + KVMap kvmap; + Options options; + options.block_size = 1024; + options.compression = kSnappyCompression; + c.Finish(options, &keys, &kvmap); + + ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), 2000, 3000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), 2000, 3000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 4000, 6000)); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/table/two_level_iterator.cc b/src/leveldb/table/two_level_iterator.cc new file mode 100644 index 00000000..7822ebab --- /dev/null +++ b/src/leveldb/table/two_level_iterator.cc @@ -0,0 +1,182 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "table/two_level_iterator.h" + +#include "leveldb/table.h" +#include "table/block.h" +#include "table/format.h" +#include "table/iterator_wrapper.h" + +namespace leveldb { + +namespace { + +typedef Iterator* (*BlockFunction)(void*, const ReadOptions&, const Slice&); + +class TwoLevelIterator: public Iterator { + public: + TwoLevelIterator( + Iterator* index_iter, + BlockFunction block_function, + void* arg, + const ReadOptions& options); + + virtual ~TwoLevelIterator(); + + virtual void Seek(const Slice& target); + virtual void SeekToFirst(); + virtual void SeekToLast(); + virtual void Next(); + virtual void Prev(); + + virtual bool Valid() const { + return data_iter_.Valid(); + } + virtual Slice key() const { + assert(Valid()); + return data_iter_.key(); + } + virtual Slice value() const { + assert(Valid()); + return data_iter_.value(); + } + virtual Status status() const { + // It'd be nice if status() returned a const Status& instead of a Status + if (!index_iter_.status().ok()) { + return index_iter_.status(); + } else if (data_iter_.iter() != NULL && !data_iter_.status().ok()) { + return data_iter_.status(); + } else { + return status_; + } + } + + private: + void SaveError(const Status& s) { + if (status_.ok() && !s.ok()) status_ = s; + } + void SkipEmptyDataBlocksForward(); + void SkipEmptyDataBlocksBackward(); + void SetDataIterator(Iterator* data_iter); + void InitDataBlock(); + + BlockFunction block_function_; + void* arg_; + const ReadOptions options_; + Status status_; + IteratorWrapper index_iter_; + IteratorWrapper data_iter_; // May be NULL + // If data_iter_ is non-NULL, then "data_block_handle_" holds the + // "index_value" passed to block_function_ to create the data_iter_. + std::string data_block_handle_; +}; + +TwoLevelIterator::TwoLevelIterator( + Iterator* index_iter, + BlockFunction block_function, + void* arg, + const ReadOptions& options) + : block_function_(block_function), + arg_(arg), + options_(options), + index_iter_(index_iter), + data_iter_(NULL) { +} + +TwoLevelIterator::~TwoLevelIterator() { +} + +void TwoLevelIterator::Seek(const Slice& target) { + index_iter_.Seek(target); + InitDataBlock(); + if (data_iter_.iter() != NULL) data_iter_.Seek(target); + SkipEmptyDataBlocksForward(); +} + +void TwoLevelIterator::SeekToFirst() { + index_iter_.SeekToFirst(); + InitDataBlock(); + if (data_iter_.iter() != NULL) data_iter_.SeekToFirst(); + SkipEmptyDataBlocksForward(); +} + +void TwoLevelIterator::SeekToLast() { + index_iter_.SeekToLast(); + InitDataBlock(); + if (data_iter_.iter() != NULL) data_iter_.SeekToLast(); + SkipEmptyDataBlocksBackward(); +} + +void TwoLevelIterator::Next() { + assert(Valid()); + data_iter_.Next(); + SkipEmptyDataBlocksForward(); +} + +void TwoLevelIterator::Prev() { + assert(Valid()); + data_iter_.Prev(); + SkipEmptyDataBlocksBackward(); +} + + +void TwoLevelIterator::SkipEmptyDataBlocksForward() { + while (data_iter_.iter() == NULL || !data_iter_.Valid()) { + // Move to next block + if (!index_iter_.Valid()) { + SetDataIterator(NULL); + return; + } + index_iter_.Next(); + InitDataBlock(); + if (data_iter_.iter() != NULL) data_iter_.SeekToFirst(); + } +} + +void TwoLevelIterator::SkipEmptyDataBlocksBackward() { + while (data_iter_.iter() == NULL || !data_iter_.Valid()) { + // Move to next block + if (!index_iter_.Valid()) { + SetDataIterator(NULL); + return; + } + index_iter_.Prev(); + InitDataBlock(); + if (data_iter_.iter() != NULL) data_iter_.SeekToLast(); + } +} + +void TwoLevelIterator::SetDataIterator(Iterator* data_iter) { + if (data_iter_.iter() != NULL) SaveError(data_iter_.status()); + data_iter_.Set(data_iter); +} + +void TwoLevelIterator::InitDataBlock() { + if (!index_iter_.Valid()) { + SetDataIterator(NULL); + } else { + Slice handle = index_iter_.value(); + if (data_iter_.iter() != NULL && handle.compare(data_block_handle_) == 0) { + // data_iter_ is already constructed with this iterator, so + // no need to change anything + } else { + Iterator* iter = (*block_function_)(arg_, options_, handle); + data_block_handle_.assign(handle.data(), handle.size()); + SetDataIterator(iter); + } + } +} + +} // namespace + +Iterator* NewTwoLevelIterator( + Iterator* index_iter, + BlockFunction block_function, + void* arg, + const ReadOptions& options) { + return new TwoLevelIterator(index_iter, block_function, arg, options); +} + +} // namespace leveldb diff --git a/src/leveldb/table/two_level_iterator.h b/src/leveldb/table/two_level_iterator.h new file mode 100644 index 00000000..629ca345 --- /dev/null +++ b/src/leveldb/table/two_level_iterator.h @@ -0,0 +1,34 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_TWO_LEVEL_ITERATOR_H_ +#define STORAGE_LEVELDB_TABLE_TWO_LEVEL_ITERATOR_H_ + +#include "leveldb/iterator.h" + +namespace leveldb { + +struct ReadOptions; + +// Return a new two level iterator. A two-level iterator contains an +// index iterator whose values point to a sequence of blocks where +// each block is itself a sequence of key,value pairs. The returned +// two-level iterator yields the concatenation of all key/value pairs +// in the sequence of blocks. Takes ownership of "index_iter" and +// will delete it when no longer needed. +// +// Uses a supplied function to convert an index_iter value into +// an iterator over the contents of the corresponding block. +extern Iterator* NewTwoLevelIterator( + Iterator* index_iter, + Iterator* (*block_function)( + void* arg, + const ReadOptions& options, + const Slice& index_value), + void* arg, + const ReadOptions& options); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_TWO_LEVEL_ITERATOR_H_ diff --git a/src/leveldb/util/arena.cc b/src/leveldb/util/arena.cc new file mode 100644 index 00000000..9367f714 --- /dev/null +++ b/src/leveldb/util/arena.cc @@ -0,0 +1,68 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/arena.h" +#include + +namespace leveldb { + +static const int kBlockSize = 4096; + +Arena::Arena() { + blocks_memory_ = 0; + alloc_ptr_ = NULL; // First allocation will allocate a block + alloc_bytes_remaining_ = 0; +} + +Arena::~Arena() { + for (size_t i = 0; i < blocks_.size(); i++) { + delete[] blocks_[i]; + } +} + +char* Arena::AllocateFallback(size_t bytes) { + if (bytes > kBlockSize / 4) { + // Object is more than a quarter of our block size. Allocate it separately + // to avoid wasting too much space in leftover bytes. + char* result = AllocateNewBlock(bytes); + return result; + } + + // We waste the remaining space in the current block. + alloc_ptr_ = AllocateNewBlock(kBlockSize); + alloc_bytes_remaining_ = kBlockSize; + + char* result = alloc_ptr_; + alloc_ptr_ += bytes; + alloc_bytes_remaining_ -= bytes; + return result; +} + +char* Arena::AllocateAligned(size_t bytes) { + const int align = (sizeof(void*) > 8) ? sizeof(void*) : 8; + assert((align & (align-1)) == 0); // Pointer size should be a power of 2 + size_t current_mod = reinterpret_cast(alloc_ptr_) & (align-1); + size_t slop = (current_mod == 0 ? 0 : align - current_mod); + size_t needed = bytes + slop; + char* result; + if (needed <= alloc_bytes_remaining_) { + result = alloc_ptr_ + slop; + alloc_ptr_ += needed; + alloc_bytes_remaining_ -= needed; + } else { + // AllocateFallback always returned aligned memory + result = AllocateFallback(bytes); + } + assert((reinterpret_cast(result) & (align-1)) == 0); + return result; +} + +char* Arena::AllocateNewBlock(size_t block_bytes) { + char* result = new char[block_bytes]; + blocks_memory_ += block_bytes; + blocks_.push_back(result); + return result; +} + +} // namespace leveldb diff --git a/src/leveldb/util/arena.h b/src/leveldb/util/arena.h new file mode 100644 index 00000000..73bbf1cb --- /dev/null +++ b/src/leveldb/util/arena.h @@ -0,0 +1,68 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_ARENA_H_ +#define STORAGE_LEVELDB_UTIL_ARENA_H_ + +#include +#include +#include +#include + +namespace leveldb { + +class Arena { + public: + Arena(); + ~Arena(); + + // Return a pointer to a newly allocated memory block of "bytes" bytes. + char* Allocate(size_t bytes); + + // Allocate memory with the normal alignment guarantees provided by malloc + char* AllocateAligned(size_t bytes); + + // Returns an estimate of the total memory usage of data allocated + // by the arena (including space allocated but not yet used for user + // allocations). + size_t MemoryUsage() const { + return blocks_memory_ + blocks_.capacity() * sizeof(char*); + } + + private: + char* AllocateFallback(size_t bytes); + char* AllocateNewBlock(size_t block_bytes); + + // Allocation state + char* alloc_ptr_; + size_t alloc_bytes_remaining_; + + // Array of new[] allocated memory blocks + std::vector blocks_; + + // Bytes of memory in blocks allocated so far + size_t blocks_memory_; + + // No copying allowed + Arena(const Arena&); + void operator=(const Arena&); +}; + +inline char* Arena::Allocate(size_t bytes) { + // The semantics of what to return are a bit messy if we allow + // 0-byte allocations, so we disallow them here (we don't need + // them for our internal use). + assert(bytes > 0); + if (bytes <= alloc_bytes_remaining_) { + char* result = alloc_ptr_; + alloc_ptr_ += bytes; + alloc_bytes_remaining_ -= bytes; + return result; + } + return AllocateFallback(bytes); +} + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_ARENA_H_ diff --git a/src/leveldb/util/arena_test.cc b/src/leveldb/util/arena_test.cc new file mode 100644 index 00000000..58e870ec --- /dev/null +++ b/src/leveldb/util/arena_test.cc @@ -0,0 +1,68 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/arena.h" + +#include "util/random.h" +#include "util/testharness.h" + +namespace leveldb { + +class ArenaTest { }; + +TEST(ArenaTest, Empty) { + Arena arena; +} + +TEST(ArenaTest, Simple) { + std::vector > allocated; + Arena arena; + const int N = 100000; + size_t bytes = 0; + Random rnd(301); + for (int i = 0; i < N; i++) { + size_t s; + if (i % (N / 10) == 0) { + s = i; + } else { + s = rnd.OneIn(4000) ? rnd.Uniform(6000) : + (rnd.OneIn(10) ? rnd.Uniform(100) : rnd.Uniform(20)); + } + if (s == 0) { + // Our arena disallows size 0 allocations. + s = 1; + } + char* r; + if (rnd.OneIn(10)) { + r = arena.AllocateAligned(s); + } else { + r = arena.Allocate(s); + } + + for (size_t b = 0; b < s; b++) { + // Fill the "i"th allocation with a known bit pattern + r[b] = i % 256; + } + bytes += s; + allocated.push_back(std::make_pair(s, r)); + ASSERT_GE(arena.MemoryUsage(), bytes); + if (i > N/10) { + ASSERT_LE(arena.MemoryUsage(), bytes * 1.10); + } + } + for (size_t i = 0; i < allocated.size(); i++) { + size_t num_bytes = allocated[i].first; + const char* p = allocated[i].second; + for (size_t b = 0; b < num_bytes; b++) { + // Check the "i"th allocation for the known bit pattern + ASSERT_EQ(int(p[b]) & 0xff, i % 256); + } + } +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/util/bloom.cc b/src/leveldb/util/bloom.cc new file mode 100644 index 00000000..a27a2ace --- /dev/null +++ b/src/leveldb/util/bloom.cc @@ -0,0 +1,95 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/filter_policy.h" + +#include "leveldb/slice.h" +#include "util/hash.h" + +namespace leveldb { + +namespace { +static uint32_t BloomHash(const Slice& key) { + return Hash(key.data(), key.size(), 0xbc9f1d34); +} + +class BloomFilterPolicy : public FilterPolicy { + private: + size_t bits_per_key_; + size_t k_; + + public: + explicit BloomFilterPolicy(int bits_per_key) + : bits_per_key_(bits_per_key) { + // We intentionally round down to reduce probing cost a little bit + k_ = static_cast(bits_per_key * 0.69); // 0.69 =~ ln(2) + if (k_ < 1) k_ = 1; + if (k_ > 30) k_ = 30; + } + + virtual const char* Name() const { + return "leveldb.BuiltinBloomFilter2"; + } + + virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const { + // Compute bloom filter size (in both bits and bytes) + size_t bits = n * bits_per_key_; + + // For small n, we can see a very high false positive rate. Fix it + // by enforcing a minimum bloom filter length. + if (bits < 64) bits = 64; + + size_t bytes = (bits + 7) / 8; + bits = bytes * 8; + + const size_t init_size = dst->size(); + dst->resize(init_size + bytes, 0); + dst->push_back(static_cast(k_)); // Remember # of probes in filter + char* array = &(*dst)[init_size]; + for (size_t i = 0; i < n; i++) { + // Use double-hashing to generate a sequence of hash values. + // See analysis in [Kirsch,Mitzenmacher 2006]. + uint32_t h = BloomHash(keys[i]); + const uint32_t delta = (h >> 17) | (h << 15); // Rotate right 17 bits + for (size_t j = 0; j < k_; j++) { + const uint32_t bitpos = h % bits; + array[bitpos/8] |= (1 << (bitpos % 8)); + h += delta; + } + } + } + + virtual bool KeyMayMatch(const Slice& key, const Slice& bloom_filter) const { + const size_t len = bloom_filter.size(); + if (len < 2) return false; + + const char* array = bloom_filter.data(); + const size_t bits = (len - 1) * 8; + + // Use the encoded k so that we can read filters generated by + // bloom filters created using different parameters. + const size_t k = array[len-1]; + if (k > 30) { + // Reserved for potentially new encodings for short bloom filters. + // Consider it a match. + return true; + } + + uint32_t h = BloomHash(key); + const uint32_t delta = (h >> 17) | (h << 15); // Rotate right 17 bits + for (size_t j = 0; j < k; j++) { + const uint32_t bitpos = h % bits; + if ((array[bitpos/8] & (1 << (bitpos % 8))) == 0) return false; + h += delta; + } + return true; + } +}; +} + +const FilterPolicy* NewBloomFilterPolicy(int bits_per_key) { + return new BloomFilterPolicy(bits_per_key); +} + +} // namespace leveldb diff --git a/src/leveldb/util/bloom_test.cc b/src/leveldb/util/bloom_test.cc new file mode 100644 index 00000000..77fb1b31 --- /dev/null +++ b/src/leveldb/util/bloom_test.cc @@ -0,0 +1,161 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/filter_policy.h" + +#include "util/coding.h" +#include "util/logging.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +static const int kVerbose = 1; + +static Slice Key(int i, char* buffer) { + EncodeFixed32(buffer, i); + return Slice(buffer, sizeof(uint32_t)); +} + +class BloomTest { + private: + const FilterPolicy* policy_; + std::string filter_; + std::vector keys_; + + public: + BloomTest() : policy_(NewBloomFilterPolicy(10)) { } + + ~BloomTest() { + delete policy_; + } + + void Reset() { + keys_.clear(); + filter_.clear(); + } + + void Add(const Slice& s) { + keys_.push_back(s.ToString()); + } + + void Build() { + std::vector key_slices; + for (size_t i = 0; i < keys_.size(); i++) { + key_slices.push_back(Slice(keys_[i])); + } + filter_.clear(); + policy_->CreateFilter(&key_slices[0], key_slices.size(), &filter_); + keys_.clear(); + if (kVerbose >= 2) DumpFilter(); + } + + size_t FilterSize() const { + return filter_.size(); + } + + void DumpFilter() { + fprintf(stderr, "F("); + for (size_t i = 0; i+1 < filter_.size(); i++) { + const unsigned int c = static_cast(filter_[i]); + for (int j = 0; j < 8; j++) { + fprintf(stderr, "%c", (c & (1 <KeyMayMatch(s, filter_); + } + + double FalsePositiveRate() { + char buffer[sizeof(int)]; + int result = 0; + for (int i = 0; i < 10000; i++) { + if (Matches(Key(i + 1000000000, buffer))) { + result++; + } + } + return result / 10000.0; + } +}; + +TEST(BloomTest, EmptyFilter) { + ASSERT_TRUE(! Matches("hello")); + ASSERT_TRUE(! Matches("world")); +} + +TEST(BloomTest, Small) { + Add("hello"); + Add("world"); + ASSERT_TRUE(Matches("hello")); + ASSERT_TRUE(Matches("world")); + ASSERT_TRUE(! Matches("x")); + ASSERT_TRUE(! Matches("foo")); +} + +static int NextLength(int length) { + if (length < 10) { + length += 1; + } else if (length < 100) { + length += 10; + } else if (length < 1000) { + length += 100; + } else { + length += 1000; + } + return length; +} + +TEST(BloomTest, VaryingLengths) { + char buffer[sizeof(int)]; + + // Count number of filters that significantly exceed the false positive rate + int mediocre_filters = 0; + int good_filters = 0; + + for (int length = 1; length <= 10000; length = NextLength(length)) { + Reset(); + for (int i = 0; i < length; i++) { + Add(Key(i, buffer)); + } + Build(); + + ASSERT_LE(FilterSize(), static_cast((length * 10 / 8) + 40)) + << length; + + // All added keys must match + for (int i = 0; i < length; i++) { + ASSERT_TRUE(Matches(Key(i, buffer))) + << "Length " << length << "; key " << i; + } + + // Check false positive rate + double rate = FalsePositiveRate(); + if (kVerbose >= 1) { + fprintf(stderr, "False positives: %5.2f%% @ length = %6d ; bytes = %6d\n", + rate*100.0, length, static_cast(FilterSize())); + } + ASSERT_LE(rate, 0.02); // Must not be over 2% + if (rate > 0.0125) mediocre_filters++; // Allowed, but not too often + else good_filters++; + } + if (kVerbose >= 1) { + fprintf(stderr, "Filters: %d good, %d mediocre\n", + good_filters, mediocre_filters); + } + ASSERT_LE(mediocre_filters, good_filters/5); +} + +// Different bits-per-byte + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/util/cache.cc b/src/leveldb/util/cache.cc new file mode 100644 index 00000000..8b197bc0 --- /dev/null +++ b/src/leveldb/util/cache.cc @@ -0,0 +1,325 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include + +#include "leveldb/cache.h" +#include "port/port.h" +#include "util/hash.h" +#include "util/mutexlock.h" + +namespace leveldb { + +Cache::~Cache() { +} + +namespace { + +// LRU cache implementation + +// An entry is a variable length heap-allocated structure. Entries +// are kept in a circular doubly linked list ordered by access time. +struct LRUHandle { + void* value; + void (*deleter)(const Slice&, void* value); + LRUHandle* next_hash; + LRUHandle* next; + LRUHandle* prev; + size_t charge; // TODO(opt): Only allow uint32_t? + size_t key_length; + uint32_t refs; + uint32_t hash; // Hash of key(); used for fast sharding and comparisons + char key_data[1]; // Beginning of key + + Slice key() const { + // For cheaper lookups, we allow a temporary Handle object + // to store a pointer to a key in "value". + if (next == this) { + return *(reinterpret_cast(value)); + } else { + return Slice(key_data, key_length); + } + } +}; + +// We provide our own simple hash table since it removes a whole bunch +// of porting hacks and is also faster than some of the built-in hash +// table implementations in some of the compiler/runtime combinations +// we have tested. E.g., readrandom speeds up by ~5% over the g++ +// 4.4.3's builtin hashtable. +class HandleTable { + public: + HandleTable() : length_(0), elems_(0), list_(NULL) { Resize(); } + ~HandleTable() { delete[] list_; } + + LRUHandle* Lookup(const Slice& key, uint32_t hash) { + return *FindPointer(key, hash); + } + + LRUHandle* Insert(LRUHandle* h) { + LRUHandle** ptr = FindPointer(h->key(), h->hash); + LRUHandle* old = *ptr; + h->next_hash = (old == NULL ? NULL : old->next_hash); + *ptr = h; + if (old == NULL) { + ++elems_; + if (elems_ > length_) { + // Since each cache entry is fairly large, we aim for a small + // average linked list length (<= 1). + Resize(); + } + } + return old; + } + + LRUHandle* Remove(const Slice& key, uint32_t hash) { + LRUHandle** ptr = FindPointer(key, hash); + LRUHandle* result = *ptr; + if (result != NULL) { + *ptr = result->next_hash; + --elems_; + } + return result; + } + + private: + // The table consists of an array of buckets where each bucket is + // a linked list of cache entries that hash into the bucket. + uint32_t length_; + uint32_t elems_; + LRUHandle** list_; + + // Return a pointer to slot that points to a cache entry that + // matches key/hash. If there is no such cache entry, return a + // pointer to the trailing slot in the corresponding linked list. + LRUHandle** FindPointer(const Slice& key, uint32_t hash) { + LRUHandle** ptr = &list_[hash & (length_ - 1)]; + while (*ptr != NULL && + ((*ptr)->hash != hash || key != (*ptr)->key())) { + ptr = &(*ptr)->next_hash; + } + return ptr; + } + + void Resize() { + uint32_t new_length = 4; + while (new_length < elems_) { + new_length *= 2; + } + LRUHandle** new_list = new LRUHandle*[new_length]; + memset(new_list, 0, sizeof(new_list[0]) * new_length); + uint32_t count = 0; + for (uint32_t i = 0; i < length_; i++) { + LRUHandle* h = list_[i]; + while (h != NULL) { + LRUHandle* next = h->next_hash; + uint32_t hash = h->hash; + LRUHandle** ptr = &new_list[hash & (new_length - 1)]; + h->next_hash = *ptr; + *ptr = h; + h = next; + count++; + } + } + assert(elems_ == count); + delete[] list_; + list_ = new_list; + length_ = new_length; + } +}; + +// A single shard of sharded cache. +class LRUCache { + public: + LRUCache(); + ~LRUCache(); + + // Separate from constructor so caller can easily make an array of LRUCache + void SetCapacity(size_t capacity) { capacity_ = capacity; } + + // Like Cache methods, but with an extra "hash" parameter. + Cache::Handle* Insert(const Slice& key, uint32_t hash, + void* value, size_t charge, + void (*deleter)(const Slice& key, void* value)); + Cache::Handle* Lookup(const Slice& key, uint32_t hash); + void Release(Cache::Handle* handle); + void Erase(const Slice& key, uint32_t hash); + + private: + void LRU_Remove(LRUHandle* e); + void LRU_Append(LRUHandle* e); + void Unref(LRUHandle* e); + + // Initialized before use. + size_t capacity_; + + // mutex_ protects the following state. + port::Mutex mutex_; + size_t usage_; + + // Dummy head of LRU list. + // lru.prev is newest entry, lru.next is oldest entry. + LRUHandle lru_; + + HandleTable table_; +}; + +LRUCache::LRUCache() + : usage_(0) { + // Make empty circular linked list + lru_.next = &lru_; + lru_.prev = &lru_; +} + +LRUCache::~LRUCache() { + for (LRUHandle* e = lru_.next; e != &lru_; ) { + LRUHandle* next = e->next; + assert(e->refs == 1); // Error if caller has an unreleased handle + Unref(e); + e = next; + } +} + +void LRUCache::Unref(LRUHandle* e) { + assert(e->refs > 0); + e->refs--; + if (e->refs <= 0) { + usage_ -= e->charge; + (*e->deleter)(e->key(), e->value); + free(e); + } +} + +void LRUCache::LRU_Remove(LRUHandle* e) { + e->next->prev = e->prev; + e->prev->next = e->next; +} + +void LRUCache::LRU_Append(LRUHandle* e) { + // Make "e" newest entry by inserting just before lru_ + e->next = &lru_; + e->prev = lru_.prev; + e->prev->next = e; + e->next->prev = e; +} + +Cache::Handle* LRUCache::Lookup(const Slice& key, uint32_t hash) { + MutexLock l(&mutex_); + LRUHandle* e = table_.Lookup(key, hash); + if (e != NULL) { + e->refs++; + LRU_Remove(e); + LRU_Append(e); + } + return reinterpret_cast(e); +} + +void LRUCache::Release(Cache::Handle* handle) { + MutexLock l(&mutex_); + Unref(reinterpret_cast(handle)); +} + +Cache::Handle* LRUCache::Insert( + const Slice& key, uint32_t hash, void* value, size_t charge, + void (*deleter)(const Slice& key, void* value)) { + MutexLock l(&mutex_); + + LRUHandle* e = reinterpret_cast( + malloc(sizeof(LRUHandle)-1 + key.size())); + e->value = value; + e->deleter = deleter; + e->charge = charge; + e->key_length = key.size(); + e->hash = hash; + e->refs = 2; // One from LRUCache, one for the returned handle + memcpy(e->key_data, key.data(), key.size()); + LRU_Append(e); + usage_ += charge; + + LRUHandle* old = table_.Insert(e); + if (old != NULL) { + LRU_Remove(old); + Unref(old); + } + + while (usage_ > capacity_ && lru_.next != &lru_) { + LRUHandle* old = lru_.next; + LRU_Remove(old); + table_.Remove(old->key(), old->hash); + Unref(old); + } + + return reinterpret_cast(e); +} + +void LRUCache::Erase(const Slice& key, uint32_t hash) { + MutexLock l(&mutex_); + LRUHandle* e = table_.Remove(key, hash); + if (e != NULL) { + LRU_Remove(e); + Unref(e); + } +} + +static const int kNumShardBits = 4; +static const int kNumShards = 1 << kNumShardBits; + +class ShardedLRUCache : public Cache { + private: + LRUCache shard_[kNumShards]; + port::Mutex id_mutex_; + uint64_t last_id_; + + static inline uint32_t HashSlice(const Slice& s) { + return Hash(s.data(), s.size(), 0); + } + + static uint32_t Shard(uint32_t hash) { + return hash >> (32 - kNumShardBits); + } + + public: + explicit ShardedLRUCache(size_t capacity) + : last_id_(0) { + const size_t per_shard = (capacity + (kNumShards - 1)) / kNumShards; + for (int s = 0; s < kNumShards; s++) { + shard_[s].SetCapacity(per_shard); + } + } + virtual ~ShardedLRUCache() { } + virtual Handle* Insert(const Slice& key, void* value, size_t charge, + void (*deleter)(const Slice& key, void* value)) { + const uint32_t hash = HashSlice(key); + return shard_[Shard(hash)].Insert(key, hash, value, charge, deleter); + } + virtual Handle* Lookup(const Slice& key) { + const uint32_t hash = HashSlice(key); + return shard_[Shard(hash)].Lookup(key, hash); + } + virtual void Release(Handle* handle) { + LRUHandle* h = reinterpret_cast(handle); + shard_[Shard(h->hash)].Release(handle); + } + virtual void Erase(const Slice& key) { + const uint32_t hash = HashSlice(key); + shard_[Shard(hash)].Erase(key, hash); + } + virtual void* Value(Handle* handle) { + return reinterpret_cast(handle)->value; + } + virtual uint64_t NewId() { + MutexLock l(&id_mutex_); + return ++(last_id_); + } +}; + +} // end anonymous namespace + +Cache* NewLRUCache(size_t capacity) { + return new ShardedLRUCache(capacity); +} + +} // namespace leveldb diff --git a/src/leveldb/util/cache_test.cc b/src/leveldb/util/cache_test.cc new file mode 100644 index 00000000..43716715 --- /dev/null +++ b/src/leveldb/util/cache_test.cc @@ -0,0 +1,186 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/cache.h" + +#include +#include "util/coding.h" +#include "util/testharness.h" + +namespace leveldb { + +// Conversions between numeric keys/values and the types expected by Cache. +static std::string EncodeKey(int k) { + std::string result; + PutFixed32(&result, k); + return result; +} +static int DecodeKey(const Slice& k) { + assert(k.size() == 4); + return DecodeFixed32(k.data()); +} +static void* EncodeValue(uintptr_t v) { return reinterpret_cast(v); } +static int DecodeValue(void* v) { return reinterpret_cast(v); } + +class CacheTest { + public: + static CacheTest* current_; + + static void Deleter(const Slice& key, void* v) { + current_->deleted_keys_.push_back(DecodeKey(key)); + current_->deleted_values_.push_back(DecodeValue(v)); + } + + static const int kCacheSize = 1000; + std::vector deleted_keys_; + std::vector deleted_values_; + Cache* cache_; + + CacheTest() : cache_(NewLRUCache(kCacheSize)) { + current_ = this; + } + + ~CacheTest() { + delete cache_; + } + + int Lookup(int key) { + Cache::Handle* handle = cache_->Lookup(EncodeKey(key)); + const int r = (handle == NULL) ? -1 : DecodeValue(cache_->Value(handle)); + if (handle != NULL) { + cache_->Release(handle); + } + return r; + } + + void Insert(int key, int value, int charge = 1) { + cache_->Release(cache_->Insert(EncodeKey(key), EncodeValue(value), charge, + &CacheTest::Deleter)); + } + + void Erase(int key) { + cache_->Erase(EncodeKey(key)); + } +}; +CacheTest* CacheTest::current_; + +TEST(CacheTest, HitAndMiss) { + ASSERT_EQ(-1, Lookup(100)); + + Insert(100, 101); + ASSERT_EQ(101, Lookup(100)); + ASSERT_EQ(-1, Lookup(200)); + ASSERT_EQ(-1, Lookup(300)); + + Insert(200, 201); + ASSERT_EQ(101, Lookup(100)); + ASSERT_EQ(201, Lookup(200)); + ASSERT_EQ(-1, Lookup(300)); + + Insert(100, 102); + ASSERT_EQ(102, Lookup(100)); + ASSERT_EQ(201, Lookup(200)); + ASSERT_EQ(-1, Lookup(300)); + + ASSERT_EQ(1, deleted_keys_.size()); + ASSERT_EQ(100, deleted_keys_[0]); + ASSERT_EQ(101, deleted_values_[0]); +} + +TEST(CacheTest, Erase) { + Erase(200); + ASSERT_EQ(0, deleted_keys_.size()); + + Insert(100, 101); + Insert(200, 201); + Erase(100); + ASSERT_EQ(-1, Lookup(100)); + ASSERT_EQ(201, Lookup(200)); + ASSERT_EQ(1, deleted_keys_.size()); + ASSERT_EQ(100, deleted_keys_[0]); + ASSERT_EQ(101, deleted_values_[0]); + + Erase(100); + ASSERT_EQ(-1, Lookup(100)); + ASSERT_EQ(201, Lookup(200)); + ASSERT_EQ(1, deleted_keys_.size()); +} + +TEST(CacheTest, EntriesArePinned) { + Insert(100, 101); + Cache::Handle* h1 = cache_->Lookup(EncodeKey(100)); + ASSERT_EQ(101, DecodeValue(cache_->Value(h1))); + + Insert(100, 102); + Cache::Handle* h2 = cache_->Lookup(EncodeKey(100)); + ASSERT_EQ(102, DecodeValue(cache_->Value(h2))); + ASSERT_EQ(0, deleted_keys_.size()); + + cache_->Release(h1); + ASSERT_EQ(1, deleted_keys_.size()); + ASSERT_EQ(100, deleted_keys_[0]); + ASSERT_EQ(101, deleted_values_[0]); + + Erase(100); + ASSERT_EQ(-1, Lookup(100)); + ASSERT_EQ(1, deleted_keys_.size()); + + cache_->Release(h2); + ASSERT_EQ(2, deleted_keys_.size()); + ASSERT_EQ(100, deleted_keys_[1]); + ASSERT_EQ(102, deleted_values_[1]); +} + +TEST(CacheTest, EvictionPolicy) { + Insert(100, 101); + Insert(200, 201); + + // Frequently used entry must be kept around + for (int i = 0; i < kCacheSize + 100; i++) { + Insert(1000+i, 2000+i); + ASSERT_EQ(2000+i, Lookup(1000+i)); + ASSERT_EQ(101, Lookup(100)); + } + ASSERT_EQ(101, Lookup(100)); + ASSERT_EQ(-1, Lookup(200)); +} + +TEST(CacheTest, HeavyEntries) { + // Add a bunch of light and heavy entries and then count the combined + // size of items still in the cache, which must be approximately the + // same as the total capacity. + const int kLight = 1; + const int kHeavy = 10; + int added = 0; + int index = 0; + while (added < 2*kCacheSize) { + const int weight = (index & 1) ? kLight : kHeavy; + Insert(index, 1000+index, weight); + added += weight; + index++; + } + + int cached_weight = 0; + for (int i = 0; i < index; i++) { + const int weight = (i & 1 ? kLight : kHeavy); + int r = Lookup(i); + if (r >= 0) { + cached_weight += weight; + ASSERT_EQ(1000+i, r); + } + } + ASSERT_LE(cached_weight, kCacheSize + kCacheSize/10); +} + +TEST(CacheTest, NewId) { + uint64_t a = cache_->NewId(); + uint64_t b = cache_->NewId(); + ASSERT_NE(a, b); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/util/coding.cc b/src/leveldb/util/coding.cc new file mode 100644 index 00000000..21e3186d --- /dev/null +++ b/src/leveldb/util/coding.cc @@ -0,0 +1,194 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/coding.h" + +namespace leveldb { + +void EncodeFixed32(char* buf, uint32_t value) { + if (port::kLittleEndian) { + memcpy(buf, &value, sizeof(value)); + } else { + buf[0] = value & 0xff; + buf[1] = (value >> 8) & 0xff; + buf[2] = (value >> 16) & 0xff; + buf[3] = (value >> 24) & 0xff; + } +} + +void EncodeFixed64(char* buf, uint64_t value) { + if (port::kLittleEndian) { + memcpy(buf, &value, sizeof(value)); + } else { + buf[0] = value & 0xff; + buf[1] = (value >> 8) & 0xff; + buf[2] = (value >> 16) & 0xff; + buf[3] = (value >> 24) & 0xff; + buf[4] = (value >> 32) & 0xff; + buf[5] = (value >> 40) & 0xff; + buf[6] = (value >> 48) & 0xff; + buf[7] = (value >> 56) & 0xff; + } +} + +void PutFixed32(std::string* dst, uint32_t value) { + char buf[sizeof(value)]; + EncodeFixed32(buf, value); + dst->append(buf, sizeof(buf)); +} + +void PutFixed64(std::string* dst, uint64_t value) { + char buf[sizeof(value)]; + EncodeFixed64(buf, value); + dst->append(buf, sizeof(buf)); +} + +char* EncodeVarint32(char* dst, uint32_t v) { + // Operate on characters as unsigneds + unsigned char* ptr = reinterpret_cast(dst); + static const int B = 128; + if (v < (1<<7)) { + *(ptr++) = v; + } else if (v < (1<<14)) { + *(ptr++) = v | B; + *(ptr++) = v>>7; + } else if (v < (1<<21)) { + *(ptr++) = v | B; + *(ptr++) = (v>>7) | B; + *(ptr++) = v>>14; + } else if (v < (1<<28)) { + *(ptr++) = v | B; + *(ptr++) = (v>>7) | B; + *(ptr++) = (v>>14) | B; + *(ptr++) = v>>21; + } else { + *(ptr++) = v | B; + *(ptr++) = (v>>7) | B; + *(ptr++) = (v>>14) | B; + *(ptr++) = (v>>21) | B; + *(ptr++) = v>>28; + } + return reinterpret_cast(ptr); +} + +void PutVarint32(std::string* dst, uint32_t v) { + char buf[5]; + char* ptr = EncodeVarint32(buf, v); + dst->append(buf, ptr - buf); +} + +char* EncodeVarint64(char* dst, uint64_t v) { + static const int B = 128; + unsigned char* ptr = reinterpret_cast(dst); + while (v >= B) { + *(ptr++) = (v & (B-1)) | B; + v >>= 7; + } + *(ptr++) = static_cast(v); + return reinterpret_cast(ptr); +} + +void PutVarint64(std::string* dst, uint64_t v) { + char buf[10]; + char* ptr = EncodeVarint64(buf, v); + dst->append(buf, ptr - buf); +} + +void PutLengthPrefixedSlice(std::string* dst, const Slice& value) { + PutVarint32(dst, value.size()); + dst->append(value.data(), value.size()); +} + +int VarintLength(uint64_t v) { + int len = 1; + while (v >= 128) { + v >>= 7; + len++; + } + return len; +} + +const char* GetVarint32PtrFallback(const char* p, + const char* limit, + uint32_t* value) { + uint32_t result = 0; + for (uint32_t shift = 0; shift <= 28 && p < limit; shift += 7) { + uint32_t byte = *(reinterpret_cast(p)); + p++; + if (byte & 128) { + // More bytes are present + result |= ((byte & 127) << shift); + } else { + result |= (byte << shift); + *value = result; + return reinterpret_cast(p); + } + } + return NULL; +} + +bool GetVarint32(Slice* input, uint32_t* value) { + const char* p = input->data(); + const char* limit = p + input->size(); + const char* q = GetVarint32Ptr(p, limit, value); + if (q == NULL) { + return false; + } else { + *input = Slice(q, limit - q); + return true; + } +} + +const char* GetVarint64Ptr(const char* p, const char* limit, uint64_t* value) { + uint64_t result = 0; + for (uint32_t shift = 0; shift <= 63 && p < limit; shift += 7) { + uint64_t byte = *(reinterpret_cast(p)); + p++; + if (byte & 128) { + // More bytes are present + result |= ((byte & 127) << shift); + } else { + result |= (byte << shift); + *value = result; + return reinterpret_cast(p); + } + } + return NULL; +} + +bool GetVarint64(Slice* input, uint64_t* value) { + const char* p = input->data(); + const char* limit = p + input->size(); + const char* q = GetVarint64Ptr(p, limit, value); + if (q == NULL) { + return false; + } else { + *input = Slice(q, limit - q); + return true; + } +} + +const char* GetLengthPrefixedSlice(const char* p, const char* limit, + Slice* result) { + uint32_t len; + p = GetVarint32Ptr(p, limit, &len); + if (p == NULL) return NULL; + if (p + len > limit) return NULL; + *result = Slice(p, len); + return p + len; +} + +bool GetLengthPrefixedSlice(Slice* input, Slice* result) { + uint32_t len; + if (GetVarint32(input, &len) && + input->size() >= len) { + *result = Slice(input->data(), len); + input->remove_prefix(len); + return true; + } else { + return false; + } +} + +} // namespace leveldb diff --git a/src/leveldb/util/coding.h b/src/leveldb/util/coding.h new file mode 100644 index 00000000..3993c4a7 --- /dev/null +++ b/src/leveldb/util/coding.h @@ -0,0 +1,104 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Endian-neutral encoding: +// * Fixed-length numbers are encoded with least-significant byte first +// * In addition we support variable length "varint" encoding +// * Strings are encoded prefixed by their length in varint format + +#ifndef STORAGE_LEVELDB_UTIL_CODING_H_ +#define STORAGE_LEVELDB_UTIL_CODING_H_ + +#include +#include +#include +#include "leveldb/slice.h" +#include "port/port.h" + +namespace leveldb { + +// Standard Put... routines append to a string +extern void PutFixed32(std::string* dst, uint32_t value); +extern void PutFixed64(std::string* dst, uint64_t value); +extern void PutVarint32(std::string* dst, uint32_t value); +extern void PutVarint64(std::string* dst, uint64_t value); +extern void PutLengthPrefixedSlice(std::string* dst, const Slice& value); + +// Standard Get... routines parse a value from the beginning of a Slice +// and advance the slice past the parsed value. +extern bool GetVarint32(Slice* input, uint32_t* value); +extern bool GetVarint64(Slice* input, uint64_t* value); +extern bool GetLengthPrefixedSlice(Slice* input, Slice* result); + +// Pointer-based variants of GetVarint... These either store a value +// in *v and return a pointer just past the parsed value, or return +// NULL on error. These routines only look at bytes in the range +// [p..limit-1] +extern const char* GetVarint32Ptr(const char* p,const char* limit, uint32_t* v); +extern const char* GetVarint64Ptr(const char* p,const char* limit, uint64_t* v); + +// Returns the length of the varint32 or varint64 encoding of "v" +extern int VarintLength(uint64_t v); + +// Lower-level versions of Put... that write directly into a character buffer +// REQUIRES: dst has enough space for the value being written +extern void EncodeFixed32(char* dst, uint32_t value); +extern void EncodeFixed64(char* dst, uint64_t value); + +// Lower-level versions of Put... that write directly into a character buffer +// and return a pointer just past the last byte written. +// REQUIRES: dst has enough space for the value being written +extern char* EncodeVarint32(char* dst, uint32_t value); +extern char* EncodeVarint64(char* dst, uint64_t value); + +// Lower-level versions of Get... that read directly from a character buffer +// without any bounds checking. + +inline uint32_t DecodeFixed32(const char* ptr) { + if (port::kLittleEndian) { + // Load the raw bytes + uint32_t result; + memcpy(&result, ptr, sizeof(result)); // gcc optimizes this to a plain load + return result; + } else { + return ((static_cast(static_cast(ptr[0]))) + | (static_cast(static_cast(ptr[1])) << 8) + | (static_cast(static_cast(ptr[2])) << 16) + | (static_cast(static_cast(ptr[3])) << 24)); + } +} + +inline uint64_t DecodeFixed64(const char* ptr) { + if (port::kLittleEndian) { + // Load the raw bytes + uint64_t result; + memcpy(&result, ptr, sizeof(result)); // gcc optimizes this to a plain load + return result; + } else { + uint64_t lo = DecodeFixed32(ptr); + uint64_t hi = DecodeFixed32(ptr + 4); + return (hi << 32) | lo; + } +} + +// Internal routine for use by fallback path of GetVarint32Ptr +extern const char* GetVarint32PtrFallback(const char* p, + const char* limit, + uint32_t* value); +inline const char* GetVarint32Ptr(const char* p, + const char* limit, + uint32_t* value) { + if (p < limit) { + uint32_t result = *(reinterpret_cast(p)); + if ((result & 128) == 0) { + *value = result; + return p + 1; + } + } + return GetVarint32PtrFallback(p, limit, value); +} + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_CODING_H_ diff --git a/src/leveldb/util/coding_test.cc b/src/leveldb/util/coding_test.cc new file mode 100644 index 00000000..521541ea --- /dev/null +++ b/src/leveldb/util/coding_test.cc @@ -0,0 +1,196 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/coding.h" + +#include "util/testharness.h" + +namespace leveldb { + +class Coding { }; + +TEST(Coding, Fixed32) { + std::string s; + for (uint32_t v = 0; v < 100000; v++) { + PutFixed32(&s, v); + } + + const char* p = s.data(); + for (uint32_t v = 0; v < 100000; v++) { + uint32_t actual = DecodeFixed32(p); + ASSERT_EQ(v, actual); + p += sizeof(uint32_t); + } +} + +TEST(Coding, Fixed64) { + std::string s; + for (int power = 0; power <= 63; power++) { + uint64_t v = static_cast(1) << power; + PutFixed64(&s, v - 1); + PutFixed64(&s, v + 0); + PutFixed64(&s, v + 1); + } + + const char* p = s.data(); + for (int power = 0; power <= 63; power++) { + uint64_t v = static_cast(1) << power; + uint64_t actual; + actual = DecodeFixed64(p); + ASSERT_EQ(v-1, actual); + p += sizeof(uint64_t); + + actual = DecodeFixed64(p); + ASSERT_EQ(v+0, actual); + p += sizeof(uint64_t); + + actual = DecodeFixed64(p); + ASSERT_EQ(v+1, actual); + p += sizeof(uint64_t); + } +} + +// Test that encoding routines generate little-endian encodings +TEST(Coding, EncodingOutput) { + std::string dst; + PutFixed32(&dst, 0x04030201); + ASSERT_EQ(4, dst.size()); + ASSERT_EQ(0x01, static_cast(dst[0])); + ASSERT_EQ(0x02, static_cast(dst[1])); + ASSERT_EQ(0x03, static_cast(dst[2])); + ASSERT_EQ(0x04, static_cast(dst[3])); + + dst.clear(); + PutFixed64(&dst, 0x0807060504030201ull); + ASSERT_EQ(8, dst.size()); + ASSERT_EQ(0x01, static_cast(dst[0])); + ASSERT_EQ(0x02, static_cast(dst[1])); + ASSERT_EQ(0x03, static_cast(dst[2])); + ASSERT_EQ(0x04, static_cast(dst[3])); + ASSERT_EQ(0x05, static_cast(dst[4])); + ASSERT_EQ(0x06, static_cast(dst[5])); + ASSERT_EQ(0x07, static_cast(dst[6])); + ASSERT_EQ(0x08, static_cast(dst[7])); +} + +TEST(Coding, Varint32) { + std::string s; + for (uint32_t i = 0; i < (32 * 32); i++) { + uint32_t v = (i / 32) << (i % 32); + PutVarint32(&s, v); + } + + const char* p = s.data(); + const char* limit = p + s.size(); + for (uint32_t i = 0; i < (32 * 32); i++) { + uint32_t expected = (i / 32) << (i % 32); + uint32_t actual; + const char* start = p; + p = GetVarint32Ptr(p, limit, &actual); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(expected, actual); + ASSERT_EQ(VarintLength(actual), p - start); + } + ASSERT_EQ(p, s.data() + s.size()); +} + +TEST(Coding, Varint64) { + // Construct the list of values to check + std::vector values; + // Some special values + values.push_back(0); + values.push_back(100); + values.push_back(~static_cast(0)); + values.push_back(~static_cast(0) - 1); + for (uint32_t k = 0; k < 64; k++) { + // Test values near powers of two + const uint64_t power = 1ull << k; + values.push_back(power); + values.push_back(power-1); + values.push_back(power+1); + } + + std::string s; + for (size_t i = 0; i < values.size(); i++) { + PutVarint64(&s, values[i]); + } + + const char* p = s.data(); + const char* limit = p + s.size(); + for (size_t i = 0; i < values.size(); i++) { + ASSERT_TRUE(p < limit); + uint64_t actual; + const char* start = p; + p = GetVarint64Ptr(p, limit, &actual); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(values[i], actual); + ASSERT_EQ(VarintLength(actual), p - start); + } + ASSERT_EQ(p, limit); + +} + +TEST(Coding, Varint32Overflow) { + uint32_t result; + std::string input("\x81\x82\x83\x84\x85\x11"); + ASSERT_TRUE(GetVarint32Ptr(input.data(), input.data() + input.size(), &result) + == NULL); +} + +TEST(Coding, Varint32Truncation) { + uint32_t large_value = (1u << 31) + 100; + std::string s; + PutVarint32(&s, large_value); + uint32_t result; + for (size_t len = 0; len < s.size() - 1; len++) { + ASSERT_TRUE(GetVarint32Ptr(s.data(), s.data() + len, &result) == NULL); + } + ASSERT_TRUE(GetVarint32Ptr(s.data(), s.data() + s.size(), &result) != NULL); + ASSERT_EQ(large_value, result); +} + +TEST(Coding, Varint64Overflow) { + uint64_t result; + std::string input("\x81\x82\x83\x84\x85\x81\x82\x83\x84\x85\x11"); + ASSERT_TRUE(GetVarint64Ptr(input.data(), input.data() + input.size(), &result) + == NULL); +} + +TEST(Coding, Varint64Truncation) { + uint64_t large_value = (1ull << 63) + 100ull; + std::string s; + PutVarint64(&s, large_value); + uint64_t result; + for (size_t len = 0; len < s.size() - 1; len++) { + ASSERT_TRUE(GetVarint64Ptr(s.data(), s.data() + len, &result) == NULL); + } + ASSERT_TRUE(GetVarint64Ptr(s.data(), s.data() + s.size(), &result) != NULL); + ASSERT_EQ(large_value, result); +} + +TEST(Coding, Strings) { + std::string s; + PutLengthPrefixedSlice(&s, Slice("")); + PutLengthPrefixedSlice(&s, Slice("foo")); + PutLengthPrefixedSlice(&s, Slice("bar")); + PutLengthPrefixedSlice(&s, Slice(std::string(200, 'x'))); + + Slice input(s); + Slice v; + ASSERT_TRUE(GetLengthPrefixedSlice(&input, &v)); + ASSERT_EQ("", v.ToString()); + ASSERT_TRUE(GetLengthPrefixedSlice(&input, &v)); + ASSERT_EQ("foo", v.ToString()); + ASSERT_TRUE(GetLengthPrefixedSlice(&input, &v)); + ASSERT_EQ("bar", v.ToString()); + ASSERT_TRUE(GetLengthPrefixedSlice(&input, &v)); + ASSERT_EQ(std::string(200, 'x'), v.ToString()); + ASSERT_EQ("", input.ToString()); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/util/comparator.cc b/src/leveldb/util/comparator.cc new file mode 100644 index 00000000..4b7b5724 --- /dev/null +++ b/src/leveldb/util/comparator.cc @@ -0,0 +1,81 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include "leveldb/comparator.h" +#include "leveldb/slice.h" +#include "port/port.h" +#include "util/logging.h" + +namespace leveldb { + +Comparator::~Comparator() { } + +namespace { +class BytewiseComparatorImpl : public Comparator { + public: + BytewiseComparatorImpl() { } + + virtual const char* Name() const { + return "leveldb.BytewiseComparator"; + } + + virtual int Compare(const Slice& a, const Slice& b) const { + return a.compare(b); + } + + virtual void FindShortestSeparator( + std::string* start, + const Slice& limit) const { + // Find length of common prefix + size_t min_length = std::min(start->size(), limit.size()); + size_t diff_index = 0; + while ((diff_index < min_length) && + ((*start)[diff_index] == limit[diff_index])) { + diff_index++; + } + + if (diff_index >= min_length) { + // Do not shorten if one string is a prefix of the other + } else { + uint8_t diff_byte = static_cast((*start)[diff_index]); + if (diff_byte < static_cast(0xff) && + diff_byte + 1 < static_cast(limit[diff_index])) { + (*start)[diff_index]++; + start->resize(diff_index + 1); + assert(Compare(*start, limit) < 0); + } + } + } + + virtual void FindShortSuccessor(std::string* key) const { + // Find first character that can be incremented + size_t n = key->size(); + for (size_t i = 0; i < n; i++) { + const uint8_t byte = (*key)[i]; + if (byte != static_cast(0xff)) { + (*key)[i] = byte + 1; + key->resize(i+1); + return; + } + } + // *key is a run of 0xffs. Leave it alone. + } +}; +} // namespace + +static port::OnceType once = LEVELDB_ONCE_INIT; +static const Comparator* bytewise; + +static void InitModule() { + bytewise = new BytewiseComparatorImpl; +} + +const Comparator* BytewiseComparator() { + port::InitOnce(&once, InitModule); + return bytewise; +} + +} // namespace leveldb diff --git a/src/leveldb/util/crc32c.cc b/src/leveldb/util/crc32c.cc new file mode 100644 index 00000000..6db9e770 --- /dev/null +++ b/src/leveldb/util/crc32c.cc @@ -0,0 +1,332 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// A portable implementation of crc32c, optimized to handle +// four bytes at a time. + +#include "util/crc32c.h" + +#include +#include "util/coding.h" + +namespace leveldb { +namespace crc32c { + +static const uint32_t table0_[256] = { + 0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, + 0xc79a971f, 0x35f1141c, 0x26a1e7e8, 0xd4ca64eb, + 0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b, + 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24, + 0x105ec76f, 0xe235446c, 0xf165b798, 0x030e349b, + 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384, + 0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, + 0x5d1d08bf, 0xaf768bbc, 0xbc267848, 0x4e4dfb4b, + 0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a, + 0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35, + 0xaa64d611, 0x580f5512, 0x4b5fa6e6, 0xb93425e5, + 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa, + 0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, + 0xf779deae, 0x05125dad, 0x1642ae59, 0xe4292d5a, + 0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a, + 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595, + 0x417b1dbc, 0xb3109ebf, 0xa0406d4b, 0x522bee48, + 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957, + 0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, + 0x0c38d26c, 0xfe53516f, 0xed03a29b, 0x1f682198, + 0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927, + 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38, + 0xdbfc821c, 0x2997011f, 0x3ac7f2eb, 0xc8ac71e8, + 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7, + 0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, + 0xa65c047d, 0x5437877e, 0x4767748a, 0xb50cf789, + 0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859, + 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46, + 0x7198540d, 0x83f3d70e, 0x90a324fa, 0x62c8a7f9, + 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6, + 0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, + 0x3cdb9bdd, 0xceb018de, 0xdde0eb2a, 0x2f8b6829, + 0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c, + 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93, + 0x082f63b7, 0xfa44e0b4, 0xe9141340, 0x1b7f9043, + 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c, + 0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, + 0x55326b08, 0xa759e80b, 0xb4091bff, 0x466298fc, + 0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c, + 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033, + 0xa24bb5a6, 0x502036a5, 0x4370c551, 0xb11b4652, + 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d, + 0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, + 0xef087a76, 0x1d63f975, 0x0e330a81, 0xfc588982, + 0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d, + 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622, + 0x38cc2a06, 0xcaa7a905, 0xd9f75af1, 0x2b9cd9f2, + 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed, + 0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, + 0x0417b1db, 0xf67c32d8, 0xe52cc12c, 0x1747422f, + 0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff, + 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0, + 0xd3d3e1ab, 0x21b862a8, 0x32e8915c, 0xc083125f, + 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540, + 0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, + 0x9e902e7b, 0x6cfbad78, 0x7fab5e8c, 0x8dc0dd8f, + 0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee, + 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1, + 0x69e9f0d5, 0x9b8273d6, 0x88d28022, 0x7ab90321, + 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e, + 0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, + 0x34f4f86a, 0xc69f7b69, 0xd5cf889d, 0x27a40b9e, + 0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e, + 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351 +}; +static const uint32_t table1_[256] = { + 0x00000000, 0x13a29877, 0x274530ee, 0x34e7a899, + 0x4e8a61dc, 0x5d28f9ab, 0x69cf5132, 0x7a6dc945, + 0x9d14c3b8, 0x8eb65bcf, 0xba51f356, 0xa9f36b21, + 0xd39ea264, 0xc03c3a13, 0xf4db928a, 0xe7790afd, + 0x3fc5f181, 0x2c6769f6, 0x1880c16f, 0x0b225918, + 0x714f905d, 0x62ed082a, 0x560aa0b3, 0x45a838c4, + 0xa2d13239, 0xb173aa4e, 0x859402d7, 0x96369aa0, + 0xec5b53e5, 0xfff9cb92, 0xcb1e630b, 0xd8bcfb7c, + 0x7f8be302, 0x6c297b75, 0x58ced3ec, 0x4b6c4b9b, + 0x310182de, 0x22a31aa9, 0x1644b230, 0x05e62a47, + 0xe29f20ba, 0xf13db8cd, 0xc5da1054, 0xd6788823, + 0xac154166, 0xbfb7d911, 0x8b507188, 0x98f2e9ff, + 0x404e1283, 0x53ec8af4, 0x670b226d, 0x74a9ba1a, + 0x0ec4735f, 0x1d66eb28, 0x298143b1, 0x3a23dbc6, + 0xdd5ad13b, 0xcef8494c, 0xfa1fe1d5, 0xe9bd79a2, + 0x93d0b0e7, 0x80722890, 0xb4958009, 0xa737187e, + 0xff17c604, 0xecb55e73, 0xd852f6ea, 0xcbf06e9d, + 0xb19da7d8, 0xa23f3faf, 0x96d89736, 0x857a0f41, + 0x620305bc, 0x71a19dcb, 0x45463552, 0x56e4ad25, + 0x2c896460, 0x3f2bfc17, 0x0bcc548e, 0x186eccf9, + 0xc0d23785, 0xd370aff2, 0xe797076b, 0xf4359f1c, + 0x8e585659, 0x9dface2e, 0xa91d66b7, 0xbabffec0, + 0x5dc6f43d, 0x4e646c4a, 0x7a83c4d3, 0x69215ca4, + 0x134c95e1, 0x00ee0d96, 0x3409a50f, 0x27ab3d78, + 0x809c2506, 0x933ebd71, 0xa7d915e8, 0xb47b8d9f, + 0xce1644da, 0xddb4dcad, 0xe9537434, 0xfaf1ec43, + 0x1d88e6be, 0x0e2a7ec9, 0x3acdd650, 0x296f4e27, + 0x53028762, 0x40a01f15, 0x7447b78c, 0x67e52ffb, + 0xbf59d487, 0xacfb4cf0, 0x981ce469, 0x8bbe7c1e, + 0xf1d3b55b, 0xe2712d2c, 0xd69685b5, 0xc5341dc2, + 0x224d173f, 0x31ef8f48, 0x050827d1, 0x16aabfa6, + 0x6cc776e3, 0x7f65ee94, 0x4b82460d, 0x5820de7a, + 0xfbc3faf9, 0xe861628e, 0xdc86ca17, 0xcf245260, + 0xb5499b25, 0xa6eb0352, 0x920cabcb, 0x81ae33bc, + 0x66d73941, 0x7575a136, 0x419209af, 0x523091d8, + 0x285d589d, 0x3bffc0ea, 0x0f186873, 0x1cbaf004, + 0xc4060b78, 0xd7a4930f, 0xe3433b96, 0xf0e1a3e1, + 0x8a8c6aa4, 0x992ef2d3, 0xadc95a4a, 0xbe6bc23d, + 0x5912c8c0, 0x4ab050b7, 0x7e57f82e, 0x6df56059, + 0x1798a91c, 0x043a316b, 0x30dd99f2, 0x237f0185, + 0x844819fb, 0x97ea818c, 0xa30d2915, 0xb0afb162, + 0xcac27827, 0xd960e050, 0xed8748c9, 0xfe25d0be, + 0x195cda43, 0x0afe4234, 0x3e19eaad, 0x2dbb72da, + 0x57d6bb9f, 0x447423e8, 0x70938b71, 0x63311306, + 0xbb8de87a, 0xa82f700d, 0x9cc8d894, 0x8f6a40e3, + 0xf50789a6, 0xe6a511d1, 0xd242b948, 0xc1e0213f, + 0x26992bc2, 0x353bb3b5, 0x01dc1b2c, 0x127e835b, + 0x68134a1e, 0x7bb1d269, 0x4f567af0, 0x5cf4e287, + 0x04d43cfd, 0x1776a48a, 0x23910c13, 0x30339464, + 0x4a5e5d21, 0x59fcc556, 0x6d1b6dcf, 0x7eb9f5b8, + 0x99c0ff45, 0x8a626732, 0xbe85cfab, 0xad2757dc, + 0xd74a9e99, 0xc4e806ee, 0xf00fae77, 0xe3ad3600, + 0x3b11cd7c, 0x28b3550b, 0x1c54fd92, 0x0ff665e5, + 0x759baca0, 0x663934d7, 0x52de9c4e, 0x417c0439, + 0xa6050ec4, 0xb5a796b3, 0x81403e2a, 0x92e2a65d, + 0xe88f6f18, 0xfb2df76f, 0xcfca5ff6, 0xdc68c781, + 0x7b5fdfff, 0x68fd4788, 0x5c1aef11, 0x4fb87766, + 0x35d5be23, 0x26772654, 0x12908ecd, 0x013216ba, + 0xe64b1c47, 0xf5e98430, 0xc10e2ca9, 0xd2acb4de, + 0xa8c17d9b, 0xbb63e5ec, 0x8f844d75, 0x9c26d502, + 0x449a2e7e, 0x5738b609, 0x63df1e90, 0x707d86e7, + 0x0a104fa2, 0x19b2d7d5, 0x2d557f4c, 0x3ef7e73b, + 0xd98eedc6, 0xca2c75b1, 0xfecbdd28, 0xed69455f, + 0x97048c1a, 0x84a6146d, 0xb041bcf4, 0xa3e32483 +}; +static const uint32_t table2_[256] = { + 0x00000000, 0xa541927e, 0x4f6f520d, 0xea2ec073, + 0x9edea41a, 0x3b9f3664, 0xd1b1f617, 0x74f06469, + 0x38513ec5, 0x9d10acbb, 0x773e6cc8, 0xd27ffeb6, + 0xa68f9adf, 0x03ce08a1, 0xe9e0c8d2, 0x4ca15aac, + 0x70a27d8a, 0xd5e3eff4, 0x3fcd2f87, 0x9a8cbdf9, + 0xee7cd990, 0x4b3d4bee, 0xa1138b9d, 0x045219e3, + 0x48f3434f, 0xedb2d131, 0x079c1142, 0xa2dd833c, + 0xd62de755, 0x736c752b, 0x9942b558, 0x3c032726, + 0xe144fb14, 0x4405696a, 0xae2ba919, 0x0b6a3b67, + 0x7f9a5f0e, 0xdadbcd70, 0x30f50d03, 0x95b49f7d, + 0xd915c5d1, 0x7c5457af, 0x967a97dc, 0x333b05a2, + 0x47cb61cb, 0xe28af3b5, 0x08a433c6, 0xade5a1b8, + 0x91e6869e, 0x34a714e0, 0xde89d493, 0x7bc846ed, + 0x0f382284, 0xaa79b0fa, 0x40577089, 0xe516e2f7, + 0xa9b7b85b, 0x0cf62a25, 0xe6d8ea56, 0x43997828, + 0x37691c41, 0x92288e3f, 0x78064e4c, 0xdd47dc32, + 0xc76580d9, 0x622412a7, 0x880ad2d4, 0x2d4b40aa, + 0x59bb24c3, 0xfcfab6bd, 0x16d476ce, 0xb395e4b0, + 0xff34be1c, 0x5a752c62, 0xb05bec11, 0x151a7e6f, + 0x61ea1a06, 0xc4ab8878, 0x2e85480b, 0x8bc4da75, + 0xb7c7fd53, 0x12866f2d, 0xf8a8af5e, 0x5de93d20, + 0x29195949, 0x8c58cb37, 0x66760b44, 0xc337993a, + 0x8f96c396, 0x2ad751e8, 0xc0f9919b, 0x65b803e5, + 0x1148678c, 0xb409f5f2, 0x5e273581, 0xfb66a7ff, + 0x26217bcd, 0x8360e9b3, 0x694e29c0, 0xcc0fbbbe, + 0xb8ffdfd7, 0x1dbe4da9, 0xf7908dda, 0x52d11fa4, + 0x1e704508, 0xbb31d776, 0x511f1705, 0xf45e857b, + 0x80aee112, 0x25ef736c, 0xcfc1b31f, 0x6a802161, + 0x56830647, 0xf3c29439, 0x19ec544a, 0xbcadc634, + 0xc85da25d, 0x6d1c3023, 0x8732f050, 0x2273622e, + 0x6ed23882, 0xcb93aafc, 0x21bd6a8f, 0x84fcf8f1, + 0xf00c9c98, 0x554d0ee6, 0xbf63ce95, 0x1a225ceb, + 0x8b277743, 0x2e66e53d, 0xc448254e, 0x6109b730, + 0x15f9d359, 0xb0b84127, 0x5a968154, 0xffd7132a, + 0xb3764986, 0x1637dbf8, 0xfc191b8b, 0x595889f5, + 0x2da8ed9c, 0x88e97fe2, 0x62c7bf91, 0xc7862def, + 0xfb850ac9, 0x5ec498b7, 0xb4ea58c4, 0x11abcaba, + 0x655baed3, 0xc01a3cad, 0x2a34fcde, 0x8f756ea0, + 0xc3d4340c, 0x6695a672, 0x8cbb6601, 0x29faf47f, + 0x5d0a9016, 0xf84b0268, 0x1265c21b, 0xb7245065, + 0x6a638c57, 0xcf221e29, 0x250cde5a, 0x804d4c24, + 0xf4bd284d, 0x51fcba33, 0xbbd27a40, 0x1e93e83e, + 0x5232b292, 0xf77320ec, 0x1d5de09f, 0xb81c72e1, + 0xccec1688, 0x69ad84f6, 0x83834485, 0x26c2d6fb, + 0x1ac1f1dd, 0xbf8063a3, 0x55aea3d0, 0xf0ef31ae, + 0x841f55c7, 0x215ec7b9, 0xcb7007ca, 0x6e3195b4, + 0x2290cf18, 0x87d15d66, 0x6dff9d15, 0xc8be0f6b, + 0xbc4e6b02, 0x190ff97c, 0xf321390f, 0x5660ab71, + 0x4c42f79a, 0xe90365e4, 0x032da597, 0xa66c37e9, + 0xd29c5380, 0x77ddc1fe, 0x9df3018d, 0x38b293f3, + 0x7413c95f, 0xd1525b21, 0x3b7c9b52, 0x9e3d092c, + 0xeacd6d45, 0x4f8cff3b, 0xa5a23f48, 0x00e3ad36, + 0x3ce08a10, 0x99a1186e, 0x738fd81d, 0xd6ce4a63, + 0xa23e2e0a, 0x077fbc74, 0xed517c07, 0x4810ee79, + 0x04b1b4d5, 0xa1f026ab, 0x4bdee6d8, 0xee9f74a6, + 0x9a6f10cf, 0x3f2e82b1, 0xd50042c2, 0x7041d0bc, + 0xad060c8e, 0x08479ef0, 0xe2695e83, 0x4728ccfd, + 0x33d8a894, 0x96993aea, 0x7cb7fa99, 0xd9f668e7, + 0x9557324b, 0x3016a035, 0xda386046, 0x7f79f238, + 0x0b899651, 0xaec8042f, 0x44e6c45c, 0xe1a75622, + 0xdda47104, 0x78e5e37a, 0x92cb2309, 0x378ab177, + 0x437ad51e, 0xe63b4760, 0x0c158713, 0xa954156d, + 0xe5f54fc1, 0x40b4ddbf, 0xaa9a1dcc, 0x0fdb8fb2, + 0x7b2bebdb, 0xde6a79a5, 0x3444b9d6, 0x91052ba8 +}; +static const uint32_t table3_[256] = { + 0x00000000, 0xdd45aab8, 0xbf672381, 0x62228939, + 0x7b2231f3, 0xa6679b4b, 0xc4451272, 0x1900b8ca, + 0xf64463e6, 0x2b01c95e, 0x49234067, 0x9466eadf, + 0x8d665215, 0x5023f8ad, 0x32017194, 0xef44db2c, + 0xe964b13d, 0x34211b85, 0x560392bc, 0x8b463804, + 0x924680ce, 0x4f032a76, 0x2d21a34f, 0xf06409f7, + 0x1f20d2db, 0xc2657863, 0xa047f15a, 0x7d025be2, + 0x6402e328, 0xb9474990, 0xdb65c0a9, 0x06206a11, + 0xd725148b, 0x0a60be33, 0x6842370a, 0xb5079db2, + 0xac072578, 0x71428fc0, 0x136006f9, 0xce25ac41, + 0x2161776d, 0xfc24ddd5, 0x9e0654ec, 0x4343fe54, + 0x5a43469e, 0x8706ec26, 0xe524651f, 0x3861cfa7, + 0x3e41a5b6, 0xe3040f0e, 0x81268637, 0x5c632c8f, + 0x45639445, 0x98263efd, 0xfa04b7c4, 0x27411d7c, + 0xc805c650, 0x15406ce8, 0x7762e5d1, 0xaa274f69, + 0xb327f7a3, 0x6e625d1b, 0x0c40d422, 0xd1057e9a, + 0xaba65fe7, 0x76e3f55f, 0x14c17c66, 0xc984d6de, + 0xd0846e14, 0x0dc1c4ac, 0x6fe34d95, 0xb2a6e72d, + 0x5de23c01, 0x80a796b9, 0xe2851f80, 0x3fc0b538, + 0x26c00df2, 0xfb85a74a, 0x99a72e73, 0x44e284cb, + 0x42c2eeda, 0x9f874462, 0xfda5cd5b, 0x20e067e3, + 0x39e0df29, 0xe4a57591, 0x8687fca8, 0x5bc25610, + 0xb4868d3c, 0x69c32784, 0x0be1aebd, 0xd6a40405, + 0xcfa4bccf, 0x12e11677, 0x70c39f4e, 0xad8635f6, + 0x7c834b6c, 0xa1c6e1d4, 0xc3e468ed, 0x1ea1c255, + 0x07a17a9f, 0xdae4d027, 0xb8c6591e, 0x6583f3a6, + 0x8ac7288a, 0x57828232, 0x35a00b0b, 0xe8e5a1b3, + 0xf1e51979, 0x2ca0b3c1, 0x4e823af8, 0x93c79040, + 0x95e7fa51, 0x48a250e9, 0x2a80d9d0, 0xf7c57368, + 0xeec5cba2, 0x3380611a, 0x51a2e823, 0x8ce7429b, + 0x63a399b7, 0xbee6330f, 0xdcc4ba36, 0x0181108e, + 0x1881a844, 0xc5c402fc, 0xa7e68bc5, 0x7aa3217d, + 0x52a0c93f, 0x8fe56387, 0xedc7eabe, 0x30824006, + 0x2982f8cc, 0xf4c75274, 0x96e5db4d, 0x4ba071f5, + 0xa4e4aad9, 0x79a10061, 0x1b838958, 0xc6c623e0, + 0xdfc69b2a, 0x02833192, 0x60a1b8ab, 0xbde41213, + 0xbbc47802, 0x6681d2ba, 0x04a35b83, 0xd9e6f13b, + 0xc0e649f1, 0x1da3e349, 0x7f816a70, 0xa2c4c0c8, + 0x4d801be4, 0x90c5b15c, 0xf2e73865, 0x2fa292dd, + 0x36a22a17, 0xebe780af, 0x89c50996, 0x5480a32e, + 0x8585ddb4, 0x58c0770c, 0x3ae2fe35, 0xe7a7548d, + 0xfea7ec47, 0x23e246ff, 0x41c0cfc6, 0x9c85657e, + 0x73c1be52, 0xae8414ea, 0xcca69dd3, 0x11e3376b, + 0x08e38fa1, 0xd5a62519, 0xb784ac20, 0x6ac10698, + 0x6ce16c89, 0xb1a4c631, 0xd3864f08, 0x0ec3e5b0, + 0x17c35d7a, 0xca86f7c2, 0xa8a47efb, 0x75e1d443, + 0x9aa50f6f, 0x47e0a5d7, 0x25c22cee, 0xf8878656, + 0xe1873e9c, 0x3cc29424, 0x5ee01d1d, 0x83a5b7a5, + 0xf90696d8, 0x24433c60, 0x4661b559, 0x9b241fe1, + 0x8224a72b, 0x5f610d93, 0x3d4384aa, 0xe0062e12, + 0x0f42f53e, 0xd2075f86, 0xb025d6bf, 0x6d607c07, + 0x7460c4cd, 0xa9256e75, 0xcb07e74c, 0x16424df4, + 0x106227e5, 0xcd278d5d, 0xaf050464, 0x7240aedc, + 0x6b401616, 0xb605bcae, 0xd4273597, 0x09629f2f, + 0xe6264403, 0x3b63eebb, 0x59416782, 0x8404cd3a, + 0x9d0475f0, 0x4041df48, 0x22635671, 0xff26fcc9, + 0x2e238253, 0xf36628eb, 0x9144a1d2, 0x4c010b6a, + 0x5501b3a0, 0x88441918, 0xea669021, 0x37233a99, + 0xd867e1b5, 0x05224b0d, 0x6700c234, 0xba45688c, + 0xa345d046, 0x7e007afe, 0x1c22f3c7, 0xc167597f, + 0xc747336e, 0x1a0299d6, 0x782010ef, 0xa565ba57, + 0xbc65029d, 0x6120a825, 0x0302211c, 0xde478ba4, + 0x31035088, 0xec46fa30, 0x8e647309, 0x5321d9b1, + 0x4a21617b, 0x9764cbc3, 0xf54642fa, 0x2803e842 +}; + +// Used to fetch a naturally-aligned 32-bit word in little endian byte-order +static inline uint32_t LE_LOAD32(const uint8_t *p) { + return DecodeFixed32(reinterpret_cast(p)); +} + +uint32_t Extend(uint32_t crc, const char* buf, size_t size) { + const uint8_t *p = reinterpret_cast(buf); + const uint8_t *e = p + size; + uint32_t l = crc ^ 0xffffffffu; + +#define STEP1 do { \ + int c = (l & 0xff) ^ *p++; \ + l = table0_[c] ^ (l >> 8); \ +} while (0) +#define STEP4 do { \ + uint32_t c = l ^ LE_LOAD32(p); \ + p += 4; \ + l = table3_[c & 0xff] ^ \ + table2_[(c >> 8) & 0xff] ^ \ + table1_[(c >> 16) & 0xff] ^ \ + table0_[c >> 24]; \ +} while (0) + + // Point x at first 4-byte aligned byte in string. This might be + // just past the end of the string. + const uintptr_t pval = reinterpret_cast(p); + const uint8_t* x = reinterpret_cast(((pval + 3) >> 2) << 2); + if (x <= e) { + // Process bytes until finished or p is 4-byte aligned + while (p != x) { + STEP1; + } + } + // Process bytes 16 at a time + while ((e-p) >= 16) { + STEP4; STEP4; STEP4; STEP4; + } + // Process bytes 4 at a time + while ((e-p) >= 4) { + STEP4; + } + // Process the last few bytes + while (p != e) { + STEP1; + } +#undef STEP4 +#undef STEP1 + return l ^ 0xffffffffu; +} + +} // namespace crc32c +} // namespace leveldb diff --git a/src/leveldb/util/crc32c.h b/src/leveldb/util/crc32c.h new file mode 100644 index 00000000..1d7e5c07 --- /dev/null +++ b/src/leveldb/util/crc32c.h @@ -0,0 +1,45 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_CRC32C_H_ +#define STORAGE_LEVELDB_UTIL_CRC32C_H_ + +#include +#include + +namespace leveldb { +namespace crc32c { + +// Return the crc32c of concat(A, data[0,n-1]) where init_crc is the +// crc32c of some string A. Extend() is often used to maintain the +// crc32c of a stream of data. +extern uint32_t Extend(uint32_t init_crc, const char* data, size_t n); + +// Return the crc32c of data[0,n-1] +inline uint32_t Value(const char* data, size_t n) { + return Extend(0, data, n); +} + +static const uint32_t kMaskDelta = 0xa282ead8ul; + +// Return a masked representation of crc. +// +// Motivation: it is problematic to compute the CRC of a string that +// contains embedded CRCs. Therefore we recommend that CRCs stored +// somewhere (e.g., in files) should be masked before being stored. +inline uint32_t Mask(uint32_t crc) { + // Rotate right by 15 bits and add a constant. + return ((crc >> 15) | (crc << 17)) + kMaskDelta; +} + +// Return the crc whose masked representation is masked_crc. +inline uint32_t Unmask(uint32_t masked_crc) { + uint32_t rot = masked_crc - kMaskDelta; + return ((rot >> 17) | (rot << 15)); +} + +} // namespace crc32c +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_CRC32C_H_ diff --git a/src/leveldb/util/crc32c_test.cc b/src/leveldb/util/crc32c_test.cc new file mode 100644 index 00000000..4b957ee1 --- /dev/null +++ b/src/leveldb/util/crc32c_test.cc @@ -0,0 +1,72 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/crc32c.h" +#include "util/testharness.h" + +namespace leveldb { +namespace crc32c { + +class CRC { }; + +TEST(CRC, StandardResults) { + // From rfc3720 section B.4. + char buf[32]; + + memset(buf, 0, sizeof(buf)); + ASSERT_EQ(0x8a9136aa, Value(buf, sizeof(buf))); + + memset(buf, 0xff, sizeof(buf)); + ASSERT_EQ(0x62a8ab43, Value(buf, sizeof(buf))); + + for (int i = 0; i < 32; i++) { + buf[i] = i; + } + ASSERT_EQ(0x46dd794e, Value(buf, sizeof(buf))); + + for (int i = 0; i < 32; i++) { + buf[i] = 31 - i; + } + ASSERT_EQ(0x113fdb5c, Value(buf, sizeof(buf))); + + unsigned char data[48] = { + 0x01, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x18, + 0x28, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }; + ASSERT_EQ(0xd9963a56, Value(reinterpret_cast(data), sizeof(data))); +} + +TEST(CRC, Values) { + ASSERT_NE(Value("a", 1), Value("foo", 3)); +} + +TEST(CRC, Extend) { + ASSERT_EQ(Value("hello world", 11), + Extend(Value("hello ", 6), "world", 5)); +} + +TEST(CRC, Mask) { + uint32_t crc = Value("foo", 3); + ASSERT_NE(crc, Mask(crc)); + ASSERT_NE(crc, Mask(Mask(crc))); + ASSERT_EQ(crc, Unmask(Mask(crc))); + ASSERT_EQ(crc, Unmask(Unmask(Mask(Mask(crc))))); +} + +} // namespace crc32c +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/util/env.cc b/src/leveldb/util/env.cc new file mode 100644 index 00000000..c2600e96 --- /dev/null +++ b/src/leveldb/util/env.cc @@ -0,0 +1,96 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/env.h" + +namespace leveldb { + +Env::~Env() { +} + +SequentialFile::~SequentialFile() { +} + +RandomAccessFile::~RandomAccessFile() { +} + +WritableFile::~WritableFile() { +} + +Logger::~Logger() { +} + +FileLock::~FileLock() { +} + +void Log(Logger* info_log, const char* format, ...) { + if (info_log != NULL) { + va_list ap; + va_start(ap, format); + info_log->Logv(format, ap); + va_end(ap); + } +} + +static Status DoWriteStringToFile(Env* env, const Slice& data, + const std::string& fname, + bool should_sync) { + WritableFile* file; + Status s = env->NewWritableFile(fname, &file); + if (!s.ok()) { + return s; + } + s = file->Append(data); + if (s.ok() && should_sync) { + s = file->Sync(); + } + if (s.ok()) { + s = file->Close(); + } + delete file; // Will auto-close if we did not close above + if (!s.ok()) { + env->DeleteFile(fname); + } + return s; +} + +Status WriteStringToFile(Env* env, const Slice& data, + const std::string& fname) { + return DoWriteStringToFile(env, data, fname, false); +} + +Status WriteStringToFileSync(Env* env, const Slice& data, + const std::string& fname) { + return DoWriteStringToFile(env, data, fname, true); +} + +Status ReadFileToString(Env* env, const std::string& fname, std::string* data) { + data->clear(); + SequentialFile* file; + Status s = env->NewSequentialFile(fname, &file); + if (!s.ok()) { + return s; + } + static const int kBufferSize = 8192; + char* space = new char[kBufferSize]; + while (true) { + Slice fragment; + s = file->Read(kBufferSize, &fragment, space); + if (!s.ok()) { + break; + } + data->append(fragment.data(), fragment.size()); + if (fragment.empty()) { + break; + } + } + delete[] space; + delete file; + return s; +} + +EnvWrapper::~EnvWrapper() { +} + +} // namespace leveldb diff --git a/src/leveldb/util/env_posix.cc b/src/leveldb/util/env_posix.cc new file mode 100644 index 00000000..ba266786 --- /dev/null +++ b/src/leveldb/util/env_posix.cc @@ -0,0 +1,608 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +#if !defined(LEVELDB_PLATFORM_WINDOWS) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "leveldb/env.h" +#include "leveldb/slice.h" +#include "port/port.h" +#include "util/logging.h" +#include "util/mutexlock.h" +#include "util/posix_logger.h" + +namespace leveldb { + +namespace { + +static Status IOError(const std::string& context, int err_number) { + return Status::IOError(context, strerror(err_number)); +} + +class PosixSequentialFile: public SequentialFile { + private: + std::string filename_; + FILE* file_; + + public: + PosixSequentialFile(const std::string& fname, FILE* f) + : filename_(fname), file_(f) { } + virtual ~PosixSequentialFile() { fclose(file_); } + + virtual Status Read(size_t n, Slice* result, char* scratch) { + Status s; + size_t r = fread_unlocked(scratch, 1, n, file_); + *result = Slice(scratch, r); + if (r < n) { + if (feof(file_)) { + // We leave status as ok if we hit the end of the file + } else { + // A partial read with an error: return a non-ok status + s = IOError(filename_, errno); + } + } + return s; + } + + virtual Status Skip(uint64_t n) { + if (fseek(file_, n, SEEK_CUR)) { + return IOError(filename_, errno); + } + return Status::OK(); + } +}; + +// pread() based random-access +class PosixRandomAccessFile: public RandomAccessFile { + private: + std::string filename_; + int fd_; + + public: + PosixRandomAccessFile(const std::string& fname, int fd) + : filename_(fname), fd_(fd) { } + virtual ~PosixRandomAccessFile() { close(fd_); } + + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + Status s; + ssize_t r = pread(fd_, scratch, n, static_cast(offset)); + *result = Slice(scratch, (r < 0) ? 0 : r); + if (r < 0) { + // An error: return a non-ok status + s = IOError(filename_, errno); + } + return s; + } +}; + +// Helper class to limit mmap file usage so that we do not end up +// running out virtual memory or running into kernel performance +// problems for very large databases. +class MmapLimiter { + public: + // Up to 1000 mmaps for 64-bit binaries; none for smaller pointer sizes. + MmapLimiter() { + SetAllowed(sizeof(void*) >= 8 ? 1000 : 0); + } + + // If another mmap slot is available, acquire it and return true. + // Else return false. + bool Acquire() { + if (GetAllowed() <= 0) { + return false; + } + MutexLock l(&mu_); + intptr_t x = GetAllowed(); + if (x <= 0) { + return false; + } else { + SetAllowed(x - 1); + return true; + } + } + + // Release a slot acquired by a previous call to Acquire() that returned true. + void Release() { + MutexLock l(&mu_); + SetAllowed(GetAllowed() + 1); + } + + private: + port::Mutex mu_; + port::AtomicPointer allowed_; + + intptr_t GetAllowed() const { + return reinterpret_cast(allowed_.Acquire_Load()); + } + + // REQUIRES: mu_ must be held + void SetAllowed(intptr_t v) { + allowed_.Release_Store(reinterpret_cast(v)); + } + + MmapLimiter(const MmapLimiter&); + void operator=(const MmapLimiter&); +}; + +// mmap() based random-access +class PosixMmapReadableFile: public RandomAccessFile { + private: + std::string filename_; + void* mmapped_region_; + size_t length_; + MmapLimiter* limiter_; + + public: + // base[0,length-1] contains the mmapped contents of the file. + PosixMmapReadableFile(const std::string& fname, void* base, size_t length, + MmapLimiter* limiter) + : filename_(fname), mmapped_region_(base), length_(length), + limiter_(limiter) { + } + + virtual ~PosixMmapReadableFile() { + munmap(mmapped_region_, length_); + limiter_->Release(); + } + + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + Status s; + if (offset + n > length_) { + *result = Slice(); + s = IOError(filename_, EINVAL); + } else { + *result = Slice(reinterpret_cast(mmapped_region_) + offset, n); + } + return s; + } +}; + +class PosixWritableFile : public WritableFile { + private: + std::string filename_; + FILE* file_; + + public: + PosixWritableFile(const std::string& fname, FILE* f) + : filename_(fname), file_(f) { } + + ~PosixWritableFile() { + if (file_ != NULL) { + // Ignoring any potential errors + fclose(file_); + } + } + + virtual Status Append(const Slice& data) { + size_t r = fwrite_unlocked(data.data(), 1, data.size(), file_); + if (r != data.size()) { + return IOError(filename_, errno); + } + return Status::OK(); + } + + virtual Status Close() { + Status result; + if (fclose(file_) != 0) { + result = IOError(filename_, errno); + } + file_ = NULL; + return result; + } + + virtual Status Flush() { + if (fflush_unlocked(file_) != 0) { + return IOError(filename_, errno); + } + return Status::OK(); + } + + Status SyncDirIfManifest() { + const char* f = filename_.c_str(); + const char* sep = strrchr(f, '/'); + Slice basename; + std::string dir; + if (sep == NULL) { + dir = "."; + basename = f; + } else { + dir = std::string(f, sep - f); + basename = sep + 1; + } + Status s; + if (basename.starts_with("MANIFEST")) { + int fd = open(dir.c_str(), O_RDONLY); + if (fd < 0) { + s = IOError(dir, errno); + } else { + if (fsync(fd) < 0) { + s = IOError(dir, errno); + } + close(fd); + } + } + return s; + } + + virtual Status Sync() { + // Ensure new files referred to by the manifest are in the filesystem. + Status s = SyncDirIfManifest(); + if (!s.ok()) { + return s; + } + if (fflush_unlocked(file_) != 0 || + fdatasync(fileno(file_)) != 0) { + s = Status::IOError(filename_, strerror(errno)); + } + return s; + } +}; + +static int LockOrUnlock(int fd, bool lock) { + errno = 0; + struct flock f; + memset(&f, 0, sizeof(f)); + f.l_type = (lock ? F_WRLCK : F_UNLCK); + f.l_whence = SEEK_SET; + f.l_start = 0; + f.l_len = 0; // Lock/unlock entire file + return fcntl(fd, F_SETLK, &f); +} + +class PosixFileLock : public FileLock { + public: + int fd_; + std::string name_; +}; + +// Set of locked files. We keep a separate set instead of just +// relying on fcntrl(F_SETLK) since fcntl(F_SETLK) does not provide +// any protection against multiple uses from the same process. +class PosixLockTable { + private: + port::Mutex mu_; + std::set locked_files_; + public: + bool Insert(const std::string& fname) { + MutexLock l(&mu_); + return locked_files_.insert(fname).second; + } + void Remove(const std::string& fname) { + MutexLock l(&mu_); + locked_files_.erase(fname); + } +}; + +class PosixEnv : public Env { + public: + PosixEnv(); + virtual ~PosixEnv() { + char msg[] = "Destroying Env::Default()\n"; + fwrite(msg, 1, sizeof(msg), stderr); + abort(); + } + + virtual Status NewSequentialFile(const std::string& fname, + SequentialFile** result) { + FILE* f = fopen(fname.c_str(), "r"); + if (f == NULL) { + *result = NULL; + return IOError(fname, errno); + } else { + *result = new PosixSequentialFile(fname, f); + return Status::OK(); + } + } + + virtual Status NewRandomAccessFile(const std::string& fname, + RandomAccessFile** result) { + *result = NULL; + Status s; + int fd = open(fname.c_str(), O_RDONLY); + if (fd < 0) { + s = IOError(fname, errno); + } else if (mmap_limit_.Acquire()) { + uint64_t size; + s = GetFileSize(fname, &size); + if (s.ok()) { + void* base = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (base != MAP_FAILED) { + *result = new PosixMmapReadableFile(fname, base, size, &mmap_limit_); + } else { + s = IOError(fname, errno); + } + } + close(fd); + if (!s.ok()) { + mmap_limit_.Release(); + } + } else { + *result = new PosixRandomAccessFile(fname, fd); + } + return s; + } + + virtual Status NewWritableFile(const std::string& fname, + WritableFile** result) { + Status s; + FILE* f = fopen(fname.c_str(), "w"); + if (f == NULL) { + *result = NULL; + s = IOError(fname, errno); + } else { + *result = new PosixWritableFile(fname, f); + } + return s; + } + + virtual bool FileExists(const std::string& fname) { + return access(fname.c_str(), F_OK) == 0; + } + + virtual Status GetChildren(const std::string& dir, + std::vector* result) { + result->clear(); + DIR* d = opendir(dir.c_str()); + if (d == NULL) { + return IOError(dir, errno); + } + struct dirent* entry; + while ((entry = readdir(d)) != NULL) { + result->push_back(entry->d_name); + } + closedir(d); + return Status::OK(); + } + + virtual Status DeleteFile(const std::string& fname) { + Status result; + if (unlink(fname.c_str()) != 0) { + result = IOError(fname, errno); + } + return result; + } + + virtual Status CreateDir(const std::string& name) { + Status result; + if (mkdir(name.c_str(), 0755) != 0) { + result = IOError(name, errno); + } + return result; + } + + virtual Status DeleteDir(const std::string& name) { + Status result; + if (rmdir(name.c_str()) != 0) { + result = IOError(name, errno); + } + return result; + } + + virtual Status GetFileSize(const std::string& fname, uint64_t* size) { + Status s; + struct stat sbuf; + if (stat(fname.c_str(), &sbuf) != 0) { + *size = 0; + s = IOError(fname, errno); + } else { + *size = sbuf.st_size; + } + return s; + } + + virtual Status RenameFile(const std::string& src, const std::string& target) { + Status result; + if (rename(src.c_str(), target.c_str()) != 0) { + result = IOError(src, errno); + } + return result; + } + + virtual Status LockFile(const std::string& fname, FileLock** lock) { + *lock = NULL; + Status result; + int fd = open(fname.c_str(), O_RDWR | O_CREAT, 0644); + if (fd < 0) { + result = IOError(fname, errno); + } else if (!locks_.Insert(fname)) { + close(fd); + result = Status::IOError("lock " + fname, "already held by process"); + } else if (LockOrUnlock(fd, true) == -1) { + result = IOError("lock " + fname, errno); + close(fd); + locks_.Remove(fname); + } else { + PosixFileLock* my_lock = new PosixFileLock; + my_lock->fd_ = fd; + my_lock->name_ = fname; + *lock = my_lock; + } + return result; + } + + virtual Status UnlockFile(FileLock* lock) { + PosixFileLock* my_lock = reinterpret_cast(lock); + Status result; + if (LockOrUnlock(my_lock->fd_, false) == -1) { + result = IOError("unlock", errno); + } + locks_.Remove(my_lock->name_); + close(my_lock->fd_); + delete my_lock; + return result; + } + + virtual void Schedule(void (*function)(void*), void* arg); + + virtual void StartThread(void (*function)(void* arg), void* arg); + + virtual Status GetTestDirectory(std::string* result) { + const char* env = getenv("TEST_TMPDIR"); + if (env && env[0] != '\0') { + *result = env; + } else { + char buf[100]; + snprintf(buf, sizeof(buf), "/tmp/leveldbtest-%d", int(geteuid())); + *result = buf; + } + // Directory may already exist + CreateDir(*result); + return Status::OK(); + } + + static uint64_t gettid() { + pthread_t tid = pthread_self(); + uint64_t thread_id = 0; + memcpy(&thread_id, &tid, std::min(sizeof(thread_id), sizeof(tid))); + return thread_id; + } + + virtual Status NewLogger(const std::string& fname, Logger** result) { + FILE* f = fopen(fname.c_str(), "w"); + if (f == NULL) { + *result = NULL; + return IOError(fname, errno); + } else { + *result = new PosixLogger(f, &PosixEnv::gettid); + return Status::OK(); + } + } + + virtual uint64_t NowMicros() { + struct timeval tv; + gettimeofday(&tv, NULL); + return static_cast(tv.tv_sec) * 1000000 + tv.tv_usec; + } + + virtual void SleepForMicroseconds(int micros) { + usleep(micros); + } + + private: + void PthreadCall(const char* label, int result) { + if (result != 0) { + fprintf(stderr, "pthread %s: %s\n", label, strerror(result)); + abort(); + } + } + + // BGThread() is the body of the background thread + void BGThread(); + static void* BGThreadWrapper(void* arg) { + reinterpret_cast(arg)->BGThread(); + return NULL; + } + + pthread_mutex_t mu_; + pthread_cond_t bgsignal_; + pthread_t bgthread_; + bool started_bgthread_; + + // Entry per Schedule() call + struct BGItem { void* arg; void (*function)(void*); }; + typedef std::deque BGQueue; + BGQueue queue_; + + PosixLockTable locks_; + MmapLimiter mmap_limit_; +}; + +PosixEnv::PosixEnv() : started_bgthread_(false) { + PthreadCall("mutex_init", pthread_mutex_init(&mu_, NULL)); + PthreadCall("cvar_init", pthread_cond_init(&bgsignal_, NULL)); +} + +void PosixEnv::Schedule(void (*function)(void*), void* arg) { + PthreadCall("lock", pthread_mutex_lock(&mu_)); + + // Start background thread if necessary + if (!started_bgthread_) { + started_bgthread_ = true; + PthreadCall( + "create thread", + pthread_create(&bgthread_, NULL, &PosixEnv::BGThreadWrapper, this)); + } + + // If the queue is currently empty, the background thread may currently be + // waiting. + if (queue_.empty()) { + PthreadCall("signal", pthread_cond_signal(&bgsignal_)); + } + + // Add to priority queue + queue_.push_back(BGItem()); + queue_.back().function = function; + queue_.back().arg = arg; + + PthreadCall("unlock", pthread_mutex_unlock(&mu_)); +} + +void PosixEnv::BGThread() { + while (true) { + // Wait until there is an item that is ready to run + PthreadCall("lock", pthread_mutex_lock(&mu_)); + while (queue_.empty()) { + PthreadCall("wait", pthread_cond_wait(&bgsignal_, &mu_)); + } + + void (*function)(void*) = queue_.front().function; + void* arg = queue_.front().arg; + queue_.pop_front(); + + PthreadCall("unlock", pthread_mutex_unlock(&mu_)); + (*function)(arg); + } +} + +namespace { +struct StartThreadState { + void (*user_function)(void*); + void* arg; +}; +} +static void* StartThreadWrapper(void* arg) { + StartThreadState* state = reinterpret_cast(arg); + state->user_function(state->arg); + delete state; + return NULL; +} + +void PosixEnv::StartThread(void (*function)(void* arg), void* arg) { + pthread_t t; + StartThreadState* state = new StartThreadState; + state->user_function = function; + state->arg = arg; + PthreadCall("start thread", + pthread_create(&t, NULL, &StartThreadWrapper, state)); +} + +} // namespace + +static pthread_once_t once = PTHREAD_ONCE_INIT; +static Env* default_env; +static void InitDefaultEnv() { default_env = new PosixEnv; } + +Env* Env::Default() { + pthread_once(&once, InitDefaultEnv); + return default_env; +} + +} // namespace leveldb + +#endif diff --git a/src/leveldb/util/env_test.cc b/src/leveldb/util/env_test.cc new file mode 100644 index 00000000..b72cb443 --- /dev/null +++ b/src/leveldb/util/env_test.cc @@ -0,0 +1,104 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/env.h" + +#include "port/port.h" +#include "util/testharness.h" + +namespace leveldb { + +static const int kDelayMicros = 100000; + +class EnvPosixTest { + private: + port::Mutex mu_; + std::string events_; + + public: + Env* env_; + EnvPosixTest() : env_(Env::Default()) { } +}; + +static void SetBool(void* ptr) { + reinterpret_cast(ptr)->NoBarrier_Store(ptr); +} + +TEST(EnvPosixTest, RunImmediately) { + port::AtomicPointer called (NULL); + env_->Schedule(&SetBool, &called); + Env::Default()->SleepForMicroseconds(kDelayMicros); + ASSERT_TRUE(called.NoBarrier_Load() != NULL); +} + +TEST(EnvPosixTest, RunMany) { + port::AtomicPointer last_id (NULL); + + struct CB { + port::AtomicPointer* last_id_ptr; // Pointer to shared slot + uintptr_t id; // Order# for the execution of this callback + + CB(port::AtomicPointer* p, int i) : last_id_ptr(p), id(i) { } + + static void Run(void* v) { + CB* cb = reinterpret_cast(v); + void* cur = cb->last_id_ptr->NoBarrier_Load(); + ASSERT_EQ(cb->id-1, reinterpret_cast(cur)); + cb->last_id_ptr->Release_Store(reinterpret_cast(cb->id)); + } + }; + + // Schedule in different order than start time + CB cb1(&last_id, 1); + CB cb2(&last_id, 2); + CB cb3(&last_id, 3); + CB cb4(&last_id, 4); + env_->Schedule(&CB::Run, &cb1); + env_->Schedule(&CB::Run, &cb2); + env_->Schedule(&CB::Run, &cb3); + env_->Schedule(&CB::Run, &cb4); + + Env::Default()->SleepForMicroseconds(kDelayMicros); + void* cur = last_id.Acquire_Load(); + ASSERT_EQ(4, reinterpret_cast(cur)); +} + +struct State { + port::Mutex mu; + int val; + int num_running; +}; + +static void ThreadBody(void* arg) { + State* s = reinterpret_cast(arg); + s->mu.Lock(); + s->val += 1; + s->num_running -= 1; + s->mu.Unlock(); +} + +TEST(EnvPosixTest, StartThread) { + State state; + state.val = 0; + state.num_running = 3; + for (int i = 0; i < 3; i++) { + env_->StartThread(&ThreadBody, &state); + } + while (true) { + state.mu.Lock(); + int num = state.num_running; + state.mu.Unlock(); + if (num == 0) { + break; + } + Env::Default()->SleepForMicroseconds(kDelayMicros); + } + ASSERT_EQ(state.val, 3); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/util/env_win.cc b/src/leveldb/util/env_win.cc new file mode 100644 index 00000000..2c8c9b4b --- /dev/null +++ b/src/leveldb/util/env_win.cc @@ -0,0 +1,1040 @@ +// This file contains source that originates from: +// http://code.google.com/p/leveldbwin/source/browse/trunk/win32_impl_src/env_win32.h +// http://code.google.com/p/leveldbwin/source/browse/trunk/win32_impl_src/port_win32.cc +// Those files dont' have any explict license headers but the +// project (http://code.google.com/p/leveldbwin/) lists the 'New BSD License' +// as the license. +#if defined(LEVELDB_PLATFORM_WINDOWS) +#include + + +#include "leveldb/env.h" + +#include "port/port.h" +#include "leveldb/slice.h" +#include "util/logging.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef max +#undef max +#endif + +#ifndef va_copy +#define va_copy(d,s) ((d) = (s)) +#endif + +#if defined DeleteFile +#undef DeleteFile +#endif + +//Declarations +namespace leveldb +{ + +namespace Win32 +{ + +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +std::string GetCurrentDir(); +std::wstring GetCurrentDirW(); + +static const std::string CurrentDir = GetCurrentDir(); +static const std::wstring CurrentDirW = GetCurrentDirW(); + +std::string& ModifyPath(std::string& path); +std::wstring& ModifyPath(std::wstring& path); + +std::string GetLastErrSz(); +std::wstring GetLastErrSzW(); + +size_t GetPageSize(); + +typedef void (*ScheduleProc)(void*) ; + +struct WorkItemWrapper +{ + WorkItemWrapper(ScheduleProc proc_,void* content_); + ScheduleProc proc; + void* pContent; +}; + +DWORD WINAPI WorkItemWrapperProc(LPVOID pContent); + +class Win32SequentialFile : public SequentialFile +{ +public: + friend class Win32Env; + virtual ~Win32SequentialFile(); + virtual Status Read(size_t n, Slice* result, char* scratch); + virtual Status Skip(uint64_t n); + BOOL isEnable(); +private: + BOOL _Init(); + void _CleanUp(); + Win32SequentialFile(const std::string& fname); + std::string _filename; + ::HANDLE _hFile; + DISALLOW_COPY_AND_ASSIGN(Win32SequentialFile); +}; + +class Win32RandomAccessFile : public RandomAccessFile +{ +public: + friend class Win32Env; + virtual ~Win32RandomAccessFile(); + virtual Status Read(uint64_t offset, size_t n, Slice* result,char* scratch) const; + BOOL isEnable(); +private: + BOOL _Init(LPCWSTR path); + void _CleanUp(); + Win32RandomAccessFile(const std::string& fname); + HANDLE _hFile; + const std::string _filename; + DISALLOW_COPY_AND_ASSIGN(Win32RandomAccessFile); +}; + +class Win32MapFile : public WritableFile +{ +public: + Win32MapFile(const std::string& fname); + + ~Win32MapFile(); + virtual Status Append(const Slice& data); + virtual Status Close(); + virtual Status Flush(); + virtual Status Sync(); + BOOL isEnable(); +private: + std::string _filename; + HANDLE _hFile; + size_t _page_size; + size_t _map_size; // How much extra memory to map at a time + char* _base; // The mapped region + HANDLE _base_handle; + char* _limit; // Limit of the mapped region + char* _dst; // Where to write next (in range [base_,limit_]) + char* _last_sync; // Where have we synced up to + uint64_t _file_offset; // Offset of base_ in file + //LARGE_INTEGER file_offset_; + // Have we done an munmap of unsynced data? + bool _pending_sync; + + // Roundup x to a multiple of y + static size_t _Roundup(size_t x, size_t y); + size_t _TruncateToPageBoundary(size_t s); + bool _UnmapCurrentRegion(); + bool _MapNewRegion(); + DISALLOW_COPY_AND_ASSIGN(Win32MapFile); + BOOL _Init(LPCWSTR Path); +}; + +class Win32FileLock : public FileLock +{ +public: + friend class Win32Env; + virtual ~Win32FileLock(); + BOOL isEnable(); +private: + BOOL _Init(LPCWSTR path); + void _CleanUp(); + Win32FileLock(const std::string& fname); + HANDLE _hFile; + std::string _filename; + DISALLOW_COPY_AND_ASSIGN(Win32FileLock); +}; + +class Win32Logger : public Logger +{ +public: + friend class Win32Env; + virtual ~Win32Logger(); + virtual void Logv(const char* format, va_list ap); +private: + explicit Win32Logger(WritableFile* pFile); + WritableFile* _pFileProxy; + DISALLOW_COPY_AND_ASSIGN(Win32Logger); +}; + +class Win32Env : public Env +{ +public: + Win32Env(); + virtual ~Win32Env(); + virtual Status NewSequentialFile(const std::string& fname, + SequentialFile** result); + + virtual Status NewRandomAccessFile(const std::string& fname, + RandomAccessFile** result); + virtual Status NewWritableFile(const std::string& fname, + WritableFile** result); + + virtual bool FileExists(const std::string& fname); + + virtual Status GetChildren(const std::string& dir, + std::vector* result); + + virtual Status DeleteFile(const std::string& fname); + + virtual Status CreateDir(const std::string& dirname); + + virtual Status DeleteDir(const std::string& dirname); + + virtual Status GetFileSize(const std::string& fname, uint64_t* file_size); + + virtual Status RenameFile(const std::string& src, + const std::string& target); + + virtual Status LockFile(const std::string& fname, FileLock** lock); + + virtual Status UnlockFile(FileLock* lock); + + virtual void Schedule( + void (*function)(void* arg), + void* arg); + + virtual void StartThread(void (*function)(void* arg), void* arg); + + virtual Status GetTestDirectory(std::string* path); + + //virtual void Logv(WritableFile* log, const char* format, va_list ap); + + virtual Status NewLogger(const std::string& fname, Logger** result); + + virtual uint64_t NowMicros(); + + virtual void SleepForMicroseconds(int micros); +}; + +void ToWidePath(const std::string& value, std::wstring& target) { + wchar_t buffer[MAX_PATH]; + MultiByteToWideChar(CP_ACP, 0, value.c_str(), -1, buffer, MAX_PATH); + target = buffer; +} + +void ToNarrowPath(const std::wstring& value, std::string& target) { + char buffer[MAX_PATH]; + WideCharToMultiByte(CP_ACP, 0, value.c_str(), -1, buffer, MAX_PATH, NULL, NULL); + target = buffer; +} + +std::string GetCurrentDir() +{ + CHAR path[MAX_PATH]; + ::GetModuleFileNameA(::GetModuleHandleA(NULL),path,MAX_PATH); + *strrchr(path,'\\') = 0; + return std::string(path); +} + +std::wstring GetCurrentDirW() +{ + WCHAR path[MAX_PATH]; + ::GetModuleFileNameW(::GetModuleHandleW(NULL),path,MAX_PATH); + *wcsrchr(path,L'\\') = 0; + return std::wstring(path); +} + +std::string& ModifyPath(std::string& path) +{ + if(path[0] == '/' || path[0] == '\\'){ + path = CurrentDir + path; + } + std::replace(path.begin(),path.end(),'/','\\'); + + return path; +} + +std::wstring& ModifyPath(std::wstring& path) +{ + if(path[0] == L'/' || path[0] == L'\\'){ + path = CurrentDirW + path; + } + std::replace(path.begin(),path.end(),L'/',L'\\'); + return path; +} + +std::string GetLastErrSz() +{ + LPWSTR lpMsgBuf; + FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + 0, // Default language + (LPWSTR) &lpMsgBuf, + 0, + NULL + ); + std::string Err; + ToNarrowPath(lpMsgBuf, Err); + LocalFree( lpMsgBuf ); + return Err; +} + +std::wstring GetLastErrSzW() +{ + LPVOID lpMsgBuf; + FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + 0, // Default language + (LPWSTR) &lpMsgBuf, + 0, + NULL + ); + std::wstring Err = (LPCWSTR)lpMsgBuf; + LocalFree(lpMsgBuf); + return Err; +} + +WorkItemWrapper::WorkItemWrapper( ScheduleProc proc_,void* content_ ) : + proc(proc_),pContent(content_) +{ + +} + +DWORD WINAPI WorkItemWrapperProc(LPVOID pContent) +{ + WorkItemWrapper* item = static_cast(pContent); + ScheduleProc TempProc = item->proc; + void* arg = item->pContent; + delete item; + TempProc(arg); + return 0; +} + +size_t GetPageSize() +{ + SYSTEM_INFO si; + GetSystemInfo(&si); + return std::max(si.dwPageSize,si.dwAllocationGranularity); +} + +const size_t g_PageSize = GetPageSize(); + + +Win32SequentialFile::Win32SequentialFile( const std::string& fname ) : + _filename(fname),_hFile(NULL) +{ + _Init(); +} + +Win32SequentialFile::~Win32SequentialFile() +{ + _CleanUp(); +} + +Status Win32SequentialFile::Read( size_t n, Slice* result, char* scratch ) +{ + Status sRet; + DWORD hasRead = 0; + if(_hFile && ReadFile(_hFile,scratch,n,&hasRead,NULL) ){ + *result = Slice(scratch,hasRead); + } else { + sRet = Status::IOError(_filename, Win32::GetLastErrSz() ); + } + return sRet; +} + +Status Win32SequentialFile::Skip( uint64_t n ) +{ + Status sRet; + LARGE_INTEGER Move,NowPointer; + Move.QuadPart = n; + if(!SetFilePointerEx(_hFile,Move,&NowPointer,FILE_CURRENT)){ + sRet = Status::IOError(_filename,Win32::GetLastErrSz()); + } + return sRet; +} + +BOOL Win32SequentialFile::isEnable() +{ + return _hFile ? TRUE : FALSE; +} + +BOOL Win32SequentialFile::_Init() +{ + std::wstring path; + ToWidePath(_filename, path); + _hFile = CreateFileW(path.c_str(), + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + return _hFile ? TRUE : FALSE; +} + +void Win32SequentialFile::_CleanUp() +{ + if(_hFile){ + CloseHandle(_hFile); + _hFile = NULL; + } +} + +Win32RandomAccessFile::Win32RandomAccessFile( const std::string& fname ) : + _filename(fname),_hFile(NULL) +{ + std::wstring path; + ToWidePath(fname, path); + _Init( path.c_str() ); +} + +Win32RandomAccessFile::~Win32RandomAccessFile() +{ + _CleanUp(); +} + +Status Win32RandomAccessFile::Read(uint64_t offset,size_t n,Slice* result,char* scratch) const +{ + Status sRet; + OVERLAPPED ol = {0}; + ZeroMemory(&ol,sizeof(ol)); + ol.Offset = (DWORD)offset; + ol.OffsetHigh = (DWORD)(offset >> 32); + DWORD hasRead = 0; + if(!ReadFile(_hFile,scratch,n,&hasRead,&ol)) + sRet = Status::IOError(_filename,Win32::GetLastErrSz()); + else + *result = Slice(scratch,hasRead); + return sRet; +} + +BOOL Win32RandomAccessFile::_Init( LPCWSTR path ) +{ + BOOL bRet = FALSE; + if(!_hFile) + _hFile = ::CreateFileW(path,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,NULL); + if(!_hFile || _hFile == INVALID_HANDLE_VALUE ) + _hFile = NULL; + else + bRet = TRUE; + return bRet; +} + +BOOL Win32RandomAccessFile::isEnable() +{ + return _hFile ? TRUE : FALSE; +} + +void Win32RandomAccessFile::_CleanUp() +{ + if(_hFile){ + ::CloseHandle(_hFile); + _hFile = NULL; + } +} + +size_t Win32MapFile::_Roundup( size_t x, size_t y ) +{ + return ((x + y - 1) / y) * y; +} + +size_t Win32MapFile::_TruncateToPageBoundary( size_t s ) +{ + s -= (s & (_page_size - 1)); + assert((s % _page_size) == 0); + return s; +} + +bool Win32MapFile::_UnmapCurrentRegion() +{ + bool result = true; + if (_base != NULL) { + if (_last_sync < _limit) { + // Defer syncing this data until next Sync() call, if any + _pending_sync = true; + } + if (!UnmapViewOfFile(_base) || !CloseHandle(_base_handle)) + result = false; + _file_offset += _limit - _base; + _base = NULL; + _base_handle = NULL; + _limit = NULL; + _last_sync = NULL; + _dst = NULL; + // Increase the amount we map the next time, but capped at 1MB + if (_map_size < (1<<20)) { + _map_size *= 2; + } + } + return result; +} + +bool Win32MapFile::_MapNewRegion() +{ + assert(_base == NULL); + //LONG newSizeHigh = (LONG)((file_offset_ + map_size_) >> 32); + //LONG newSizeLow = (LONG)((file_offset_ + map_size_) & 0xFFFFFFFF); + DWORD off_hi = (DWORD)(_file_offset >> 32); + DWORD off_lo = (DWORD)(_file_offset & 0xFFFFFFFF); + LARGE_INTEGER newSize; + newSize.QuadPart = _file_offset + _map_size; + SetFilePointerEx(_hFile, newSize, NULL, FILE_BEGIN); + SetEndOfFile(_hFile); + + _base_handle = CreateFileMappingA( + _hFile, + NULL, + PAGE_READWRITE, + 0, + 0, + 0); + if (_base_handle != NULL) { + _base = (char*) MapViewOfFile(_base_handle, + FILE_MAP_ALL_ACCESS, + off_hi, + off_lo, + _map_size); + if (_base != NULL) { + _limit = _base + _map_size; + _dst = _base; + _last_sync = _base; + return true; + } + } + return false; +} + +Win32MapFile::Win32MapFile( const std::string& fname) : + _filename(fname), + _hFile(NULL), + _page_size(Win32::g_PageSize), + _map_size(_Roundup(65536, Win32::g_PageSize)), + _base(NULL), + _base_handle(NULL), + _limit(NULL), + _dst(NULL), + _last_sync(NULL), + _file_offset(0), + _pending_sync(false) +{ + std::wstring path; + ToWidePath(fname, path); + _Init(path.c_str()); + assert((Win32::g_PageSize & (Win32::g_PageSize - 1)) == 0); +} + +Status Win32MapFile::Append( const Slice& data ) +{ + const char* src = data.data(); + size_t left = data.size(); + Status s; + while (left > 0) { + assert(_base <= _dst); + assert(_dst <= _limit); + size_t avail = _limit - _dst; + if (avail == 0) { + if (!_UnmapCurrentRegion() || + !_MapNewRegion()) { + return Status::IOError("WinMmapFile.Append::UnmapCurrentRegion or MapNewRegion: ", Win32::GetLastErrSz()); + } + } + size_t n = (left <= avail) ? left : avail; + memcpy(_dst, src, n); + _dst += n; + src += n; + left -= n; + } + return s; +} + +Status Win32MapFile::Close() +{ + Status s; + size_t unused = _limit - _dst; + if (!_UnmapCurrentRegion()) { + s = Status::IOError("WinMmapFile.Close::UnmapCurrentRegion: ",Win32::GetLastErrSz()); + } else if (unused > 0) { + // Trim the extra space at the end of the file + LARGE_INTEGER newSize; + newSize.QuadPart = _file_offset - unused; + if (!SetFilePointerEx(_hFile, newSize, NULL, FILE_BEGIN)) { + s = Status::IOError("WinMmapFile.Close::SetFilePointer: ",Win32::GetLastErrSz()); + } else + SetEndOfFile(_hFile); + } + if (!CloseHandle(_hFile)) { + if (s.ok()) { + s = Status::IOError("WinMmapFile.Close::CloseHandle: ", Win32::GetLastErrSz()); + } + } + _hFile = INVALID_HANDLE_VALUE; + _base = NULL; + _base_handle = NULL; + _limit = NULL; + + return s; +} + +Status Win32MapFile::Sync() +{ + Status s; + if (_pending_sync) { + // Some unmapped data was not synced + _pending_sync = false; + if (!FlushFileBuffers(_hFile)) { + s = Status::IOError("WinMmapFile.Sync::FlushFileBuffers: ",Win32::GetLastErrSz()); + } + } + if (_dst > _last_sync) { + // Find the beginnings of the pages that contain the first and last + // bytes to be synced. + size_t p1 = _TruncateToPageBoundary(_last_sync - _base); + size_t p2 = _TruncateToPageBoundary(_dst - _base - 1); + _last_sync = _dst; + if (!FlushViewOfFile(_base + p1, p2 - p1 + _page_size)) { + s = Status::IOError("WinMmapFile.Sync::FlushViewOfFile: ",Win32::GetLastErrSz()); + } + } + return s; +} + +Status Win32MapFile::Flush() +{ + return Status::OK(); +} + +Win32MapFile::~Win32MapFile() +{ + if (_hFile != INVALID_HANDLE_VALUE) { + Win32MapFile::Close(); + } +} + +BOOL Win32MapFile::_Init( LPCWSTR Path ) +{ + DWORD Flag = PathFileExistsW(Path) ? OPEN_EXISTING : CREATE_ALWAYS; + _hFile = CreateFileW(Path, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE, + NULL, + Flag, + FILE_ATTRIBUTE_NORMAL, + NULL); + if(!_hFile || _hFile == INVALID_HANDLE_VALUE) + return FALSE; + else + return TRUE; +} + +BOOL Win32MapFile::isEnable() +{ + return _hFile ? TRUE : FALSE; +} + +Win32FileLock::Win32FileLock( const std::string& fname ) : + _hFile(NULL),_filename(fname) +{ + std::wstring path; + ToWidePath(fname, path); + _Init(path.c_str()); +} + +Win32FileLock::~Win32FileLock() +{ + _CleanUp(); +} + +BOOL Win32FileLock::_Init( LPCWSTR path ) +{ + BOOL bRet = FALSE; + if(!_hFile) + _hFile = ::CreateFileW(path,0,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); + if(!_hFile || _hFile == INVALID_HANDLE_VALUE ){ + _hFile = NULL; + } + else + bRet = TRUE; + return bRet; +} + +void Win32FileLock::_CleanUp() +{ + ::CloseHandle(_hFile); + _hFile = NULL; +} + +BOOL Win32FileLock::isEnable() +{ + return _hFile ? TRUE : FALSE; +} + +Win32Logger::Win32Logger(WritableFile* pFile) : _pFileProxy(pFile) +{ + assert(_pFileProxy); +} + +Win32Logger::~Win32Logger() +{ + if(_pFileProxy) + delete _pFileProxy; +} + +void Win32Logger::Logv( const char* format, va_list ap ) +{ + uint64_t thread_id = ::GetCurrentThreadId(); + + // We try twice: the first time with a fixed-size stack allocated buffer, + // and the second time with a much larger dynamically allocated buffer. + char buffer[500]; + for (int iter = 0; iter < 2; iter++) { + char* base; + int bufsize; + if (iter == 0) { + bufsize = sizeof(buffer); + base = buffer; + } else { + bufsize = 30000; + base = new char[bufsize]; + } + char* p = base; + char* limit = base + bufsize; + + SYSTEMTIME st; + GetLocalTime(&st); + p += snprintf(p, limit - p, + "%04d/%02d/%02d-%02d:%02d:%02d.%06d %llx ", + int(st.wYear), + int(st.wMonth), + int(st.wDay), + int(st.wHour), + int(st.wMinute), + int(st.wMinute), + int(st.wMilliseconds), + static_cast(thread_id)); + + // Print the message + if (p < limit) { + va_list backup_ap; + va_copy(backup_ap, ap); + p += vsnprintf(p, limit - p, format, backup_ap); + va_end(backup_ap); + } + + // Truncate to available space if necessary + if (p >= limit) { + if (iter == 0) { + continue; // Try again with larger buffer + } else { + p = limit - 1; + } + } + + // Add newline if necessary + if (p == base || p[-1] != '\n') { + *p++ = '\n'; + } + + assert(p <= limit); + DWORD hasWritten = 0; + if(_pFileProxy){ + _pFileProxy->Append(Slice(base, p - base)); + _pFileProxy->Flush(); + } + if (base != buffer) { + delete[] base; + } + break; + } +} + +bool Win32Env::FileExists(const std::string& fname) +{ + std::string path = fname; + std::wstring wpath; + ToWidePath(ModifyPath(path), wpath); + return ::PathFileExistsW(wpath.c_str()) ? true : false; +} + +Status Win32Env::GetChildren(const std::string& dir, std::vector* result) +{ + Status sRet; + ::WIN32_FIND_DATAW wfd; + std::string path = dir; + ModifyPath(path); + path += "\\*.*"; + std::wstring wpath; + ToWidePath(path, wpath); + + ::HANDLE hFind = ::FindFirstFileW(wpath.c_str() ,&wfd); + if(hFind && hFind != INVALID_HANDLE_VALUE){ + BOOL hasNext = TRUE; + std::string child; + while(hasNext){ + ToNarrowPath(wfd.cFileName, child); + if(child != ".." && child != ".") { + result->push_back(child); + } + hasNext = ::FindNextFileW(hFind,&wfd); + } + ::FindClose(hFind); + } + else + sRet = Status::IOError(dir,"Could not get children."); + return sRet; +} + +void Win32Env::SleepForMicroseconds( int micros ) +{ + ::Sleep((micros + 999) /1000); +} + + +Status Win32Env::DeleteFile( const std::string& fname ) +{ + Status sRet; + std::string path = fname; + std::wstring wpath; + ToWidePath(ModifyPath(path), wpath); + + if(!::DeleteFileW(wpath.c_str())) { + sRet = Status::IOError(path, "Could not delete file."); + } + return sRet; +} + +Status Win32Env::GetFileSize( const std::string& fname, uint64_t* file_size ) +{ + Status sRet; + std::string path = fname; + std::wstring wpath; + ToWidePath(ModifyPath(path), wpath); + + HANDLE file = ::CreateFileW(wpath.c_str(), + GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); + LARGE_INTEGER li; + if(::GetFileSizeEx(file,&li)){ + *file_size = (uint64_t)li.QuadPart; + }else + sRet = Status::IOError(path,"Could not get the file size."); + CloseHandle(file); + return sRet; +} + +Status Win32Env::RenameFile( const std::string& src, const std::string& target ) +{ + Status sRet; + std::string src_path = src; + std::wstring wsrc_path; + ToWidePath(ModifyPath(src_path), wsrc_path); + std::string target_path = target; + std::wstring wtarget_path; + ToWidePath(ModifyPath(target_path), wtarget_path); + + if(!MoveFileW(wsrc_path.c_str(), wtarget_path.c_str() ) ){ + DWORD err = GetLastError(); + if(err == 0x000000b7){ + if(!::DeleteFileW(wtarget_path.c_str() ) ) + sRet = Status::IOError(src, "Could not rename file."); + else if(!::MoveFileW(wsrc_path.c_str(), + wtarget_path.c_str() ) ) + sRet = Status::IOError(src, "Could not rename file."); + } + } + return sRet; +} + +Status Win32Env::LockFile( const std::string& fname, FileLock** lock ) +{ + Status sRet; + std::string path = fname; + ModifyPath(path); + Win32FileLock* _lock = new Win32FileLock(path); + if(!_lock->isEnable()){ + delete _lock; + *lock = NULL; + sRet = Status::IOError(path, "Could not lock file."); + } + else + *lock = _lock; + return sRet; +} + +Status Win32Env::UnlockFile( FileLock* lock ) +{ + Status sRet; + delete lock; + return sRet; +} + +void Win32Env::Schedule( void (*function)(void* arg), void* arg ) +{ + QueueUserWorkItem(Win32::WorkItemWrapperProc, + new Win32::WorkItemWrapper(function,arg), + WT_EXECUTEDEFAULT); +} + +void Win32Env::StartThread( void (*function)(void* arg), void* arg ) +{ + ::_beginthread(function,0,arg); +} + +Status Win32Env::GetTestDirectory( std::string* path ) +{ + Status sRet; + WCHAR TempPath[MAX_PATH]; + ::GetTempPathW(MAX_PATH,TempPath); + ToNarrowPath(TempPath, *path); + path->append("leveldb\\test\\"); + ModifyPath(*path); + return sRet; +} + +uint64_t Win32Env::NowMicros() +{ +#ifndef USE_VISTA_API +#define GetTickCount64 GetTickCount +#endif + return (uint64_t)(GetTickCount64()*1000); +} + +static Status CreateDirInner( const std::string& dirname ) +{ + Status sRet; +#ifdef _MSC_VER + DWORD attr = ::GetFileAttributesA(dirname.c_str()); +#else + DWORD attr = ::GetFileAttributes(dirname.c_str()); +#endif + + if (attr == INVALID_FILE_ATTRIBUTES) { // doesn't exist: + std::size_t slash = dirname.find_last_of("\\"); + if (slash != std::string::npos){ + sRet = CreateDirInner(dirname.substr(0, slash)); + if (!sRet.ok()) return sRet; + } +#ifdef _MSC_VER + BOOL result = ::CreateDirectoryA(dirname.c_str(), NULL); +#else + BOOL result = ::CreateDirectory(dirname.c_str(), NULL); +#endif + if (result == FALSE) { + sRet = Status::IOError(dirname, "Could not create directory."); + return sRet; + } + } + return sRet; +} + +Status Win32Env::CreateDir( const std::string& dirname ) +{ + std::string path = dirname; + if(path[path.length() - 1] != '\\'){ + path += '\\'; + } + ModifyPath(path); + + return CreateDirInner(path); +} + +Status Win32Env::DeleteDir( const std::string& dirname ) +{ + Status sRet; + std::wstring path; + ToWidePath(dirname, path); + ModifyPath(path); + if(!::RemoveDirectoryW( path.c_str() ) ){ + sRet = Status::IOError(dirname, "Could not delete directory."); + } + return sRet; +} + +Status Win32Env::NewSequentialFile( const std::string& fname, SequentialFile** result ) +{ + Status sRet; + std::string path = fname; + ModifyPath(path); + Win32SequentialFile* pFile = new Win32SequentialFile(path); + if(pFile->isEnable()){ + *result = pFile; + }else { + delete pFile; + sRet = Status::IOError(path, Win32::GetLastErrSz()); + } + return sRet; +} + +Status Win32Env::NewRandomAccessFile( const std::string& fname, RandomAccessFile** result ) +{ + Status sRet; + std::string path = fname; + Win32RandomAccessFile* pFile = new Win32RandomAccessFile(ModifyPath(path)); + if(!pFile->isEnable()){ + delete pFile; + *result = NULL; + sRet = Status::IOError(path, Win32::GetLastErrSz()); + }else + *result = pFile; + return sRet; +} + +Status Win32Env::NewLogger( const std::string& fname, Logger** result ) +{ + Status sRet; + std::string path = fname; + Win32MapFile* pMapFile = new Win32MapFile(ModifyPath(path)); + if(!pMapFile->isEnable()){ + delete pMapFile; + *result = NULL; + sRet = Status::IOError(path,"could not create a logger."); + }else + *result = new Win32Logger(pMapFile); + return sRet; +} + +Status Win32Env::NewWritableFile( const std::string& fname, WritableFile** result ) +{ + Status sRet; + std::string path = fname; + Win32MapFile* pFile = new Win32MapFile(ModifyPath(path)); + if(!pFile->isEnable()){ + *result = NULL; + sRet = Status::IOError(fname,Win32::GetLastErrSz()); + }else + *result = pFile; + return sRet; +} + +Win32Env::Win32Env() +{ + +} + +Win32Env::~Win32Env() +{ + +} + + +} // Win32 namespace + +static port::OnceType once = LEVELDB_ONCE_INIT; +static Env* default_env; +static void InitDefaultEnv() { default_env = new Win32::Win32Env(); } + +Env* Env::Default() { + port::InitOnce(&once, InitDefaultEnv); + return default_env; +} + +} // namespace leveldb + +#endif // defined(LEVELDB_PLATFORM_WINDOWS) diff --git a/src/leveldb/util/filter_policy.cc b/src/leveldb/util/filter_policy.cc new file mode 100644 index 00000000..7b045c8c --- /dev/null +++ b/src/leveldb/util/filter_policy.cc @@ -0,0 +1,11 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/filter_policy.h" + +namespace leveldb { + +FilterPolicy::~FilterPolicy() { } + +} // namespace leveldb diff --git a/src/leveldb/util/hash.cc b/src/leveldb/util/hash.cc new file mode 100644 index 00000000..ed439ce7 --- /dev/null +++ b/src/leveldb/util/hash.cc @@ -0,0 +1,52 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include "util/coding.h" +#include "util/hash.h" + +// The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through +// between switch labels. The real definition should be provided externally. +// This one is a fallback version for unsupported compilers. +#ifndef FALLTHROUGH_INTENDED +#define FALLTHROUGH_INTENDED do { } while (0) +#endif + +namespace leveldb { + +uint32_t Hash(const char* data, size_t n, uint32_t seed) { + // Similar to murmur hash + const uint32_t m = 0xc6a4a793; + const uint32_t r = 24; + const char* limit = data + n; + uint32_t h = seed ^ (n * m); + + // Pick up four bytes at a time + while (data + 4 <= limit) { + uint32_t w = DecodeFixed32(data); + data += 4; + h += w; + h *= m; + h ^= (h >> 16); + } + + // Pick up remaining bytes + switch (limit - data) { + case 3: + h += static_cast(data[2]) << 16; + FALLTHROUGH_INTENDED; + case 2: + h += static_cast(data[1]) << 8; + FALLTHROUGH_INTENDED; + case 1: + h += static_cast(data[0]); + h *= m; + h ^= (h >> r); + break; + } + return h; +} + + +} // namespace leveldb diff --git a/src/leveldb/util/hash.h b/src/leveldb/util/hash.h new file mode 100644 index 00000000..8889d56b --- /dev/null +++ b/src/leveldb/util/hash.h @@ -0,0 +1,19 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Simple hash function used for internal data structures + +#ifndef STORAGE_LEVELDB_UTIL_HASH_H_ +#define STORAGE_LEVELDB_UTIL_HASH_H_ + +#include +#include + +namespace leveldb { + +extern uint32_t Hash(const char* data, size_t n, uint32_t seed); + +} + +#endif // STORAGE_LEVELDB_UTIL_HASH_H_ diff --git a/src/leveldb/util/hash_test.cc b/src/leveldb/util/hash_test.cc new file mode 100644 index 00000000..eaa1c92c --- /dev/null +++ b/src/leveldb/util/hash_test.cc @@ -0,0 +1,54 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/hash.h" +#include "util/testharness.h" + +namespace leveldb { + +class HASH { }; + +TEST(HASH, SignedUnsignedIssue) { + const unsigned char data1[1] = {0x62}; + const unsigned char data2[2] = {0xc3, 0x97}; + const unsigned char data3[3] = {0xe2, 0x99, 0xa5}; + const unsigned char data4[4] = {0xe1, 0x80, 0xb9, 0x32}; + const unsigned char data5[48] = { + 0x01, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x18, + 0x28, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }; + + ASSERT_EQ(Hash(0, 0, 0xbc9f1d34), 0xbc9f1d34); + ASSERT_EQ( + Hash(reinterpret_cast(data1), sizeof(data1), 0xbc9f1d34), + 0xef1345c4); + ASSERT_EQ( + Hash(reinterpret_cast(data2), sizeof(data2), 0xbc9f1d34), + 0x5b663814); + ASSERT_EQ( + Hash(reinterpret_cast(data3), sizeof(data3), 0xbc9f1d34), + 0x323c078f); + ASSERT_EQ( + Hash(reinterpret_cast(data4), sizeof(data4), 0xbc9f1d34), + 0xed21633a); + ASSERT_EQ( + Hash(reinterpret_cast(data5), sizeof(data5), 0x12345678), + 0xf333dabb); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/util/histogram.cc b/src/leveldb/util/histogram.cc new file mode 100644 index 00000000..bb95f583 --- /dev/null +++ b/src/leveldb/util/histogram.cc @@ -0,0 +1,139 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include "port/port.h" +#include "util/histogram.h" + +namespace leveldb { + +const double Histogram::kBucketLimit[kNumBuckets] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20, 25, 30, 35, 40, 45, + 50, 60, 70, 80, 90, 100, 120, 140, 160, 180, 200, 250, 300, 350, 400, 450, + 500, 600, 700, 800, 900, 1000, 1200, 1400, 1600, 1800, 2000, 2500, 3000, + 3500, 4000, 4500, 5000, 6000, 7000, 8000, 9000, 10000, 12000, 14000, + 16000, 18000, 20000, 25000, 30000, 35000, 40000, 45000, 50000, 60000, + 70000, 80000, 90000, 100000, 120000, 140000, 160000, 180000, 200000, + 250000, 300000, 350000, 400000, 450000, 500000, 600000, 700000, 800000, + 900000, 1000000, 1200000, 1400000, 1600000, 1800000, 2000000, 2500000, + 3000000, 3500000, 4000000, 4500000, 5000000, 6000000, 7000000, 8000000, + 9000000, 10000000, 12000000, 14000000, 16000000, 18000000, 20000000, + 25000000, 30000000, 35000000, 40000000, 45000000, 50000000, 60000000, + 70000000, 80000000, 90000000, 100000000, 120000000, 140000000, 160000000, + 180000000, 200000000, 250000000, 300000000, 350000000, 400000000, + 450000000, 500000000, 600000000, 700000000, 800000000, 900000000, + 1000000000, 1200000000, 1400000000, 1600000000, 1800000000, 2000000000, + 2500000000.0, 3000000000.0, 3500000000.0, 4000000000.0, 4500000000.0, + 5000000000.0, 6000000000.0, 7000000000.0, 8000000000.0, 9000000000.0, + 1e200, +}; + +void Histogram::Clear() { + min_ = kBucketLimit[kNumBuckets-1]; + max_ = 0; + num_ = 0; + sum_ = 0; + sum_squares_ = 0; + for (int i = 0; i < kNumBuckets; i++) { + buckets_[i] = 0; + } +} + +void Histogram::Add(double value) { + // Linear search is fast enough for our usage in db_bench + int b = 0; + while (b < kNumBuckets - 1 && kBucketLimit[b] <= value) { + b++; + } + buckets_[b] += 1.0; + if (min_ > value) min_ = value; + if (max_ < value) max_ = value; + num_++; + sum_ += value; + sum_squares_ += (value * value); +} + +void Histogram::Merge(const Histogram& other) { + if (other.min_ < min_) min_ = other.min_; + if (other.max_ > max_) max_ = other.max_; + num_ += other.num_; + sum_ += other.sum_; + sum_squares_ += other.sum_squares_; + for (int b = 0; b < kNumBuckets; b++) { + buckets_[b] += other.buckets_[b]; + } +} + +double Histogram::Median() const { + return Percentile(50.0); +} + +double Histogram::Percentile(double p) const { + double threshold = num_ * (p / 100.0); + double sum = 0; + for (int b = 0; b < kNumBuckets; b++) { + sum += buckets_[b]; + if (sum >= threshold) { + // Scale linearly within this bucket + double left_point = (b == 0) ? 0 : kBucketLimit[b-1]; + double right_point = kBucketLimit[b]; + double left_sum = sum - buckets_[b]; + double right_sum = sum; + double pos = (threshold - left_sum) / (right_sum - left_sum); + double r = left_point + (right_point - left_point) * pos; + if (r < min_) r = min_; + if (r > max_) r = max_; + return r; + } + } + return max_; +} + +double Histogram::Average() const { + if (num_ == 0.0) return 0; + return sum_ / num_; +} + +double Histogram::StandardDeviation() const { + if (num_ == 0.0) return 0; + double variance = (sum_squares_ * num_ - sum_ * sum_) / (num_ * num_); + return sqrt(variance); +} + +std::string Histogram::ToString() const { + std::string r; + char buf[200]; + snprintf(buf, sizeof(buf), + "Count: %.0f Average: %.4f StdDev: %.2f\n", + num_, Average(), StandardDeviation()); + r.append(buf); + snprintf(buf, sizeof(buf), + "Min: %.4f Median: %.4f Max: %.4f\n", + (num_ == 0.0 ? 0.0 : min_), Median(), max_); + r.append(buf); + r.append("------------------------------------------------------\n"); + const double mult = 100.0 / num_; + double sum = 0; + for (int b = 0; b < kNumBuckets; b++) { + if (buckets_[b] <= 0.0) continue; + sum += buckets_[b]; + snprintf(buf, sizeof(buf), + "[ %7.0f, %7.0f ) %7.0f %7.3f%% %7.3f%% ", + ((b == 0) ? 0.0 : kBucketLimit[b-1]), // left + kBucketLimit[b], // right + buckets_[b], // count + mult * buckets_[b], // percentage + mult * sum); // cumulative percentage + r.append(buf); + + // Add hash marks based on percentage; 20 marks for 100%. + int marks = static_cast(20*(buckets_[b] / num_) + 0.5); + r.append(marks, '#'); + r.push_back('\n'); + } + return r; +} + +} // namespace leveldb diff --git a/src/leveldb/util/histogram.h b/src/leveldb/util/histogram.h new file mode 100644 index 00000000..1ef9f3c8 --- /dev/null +++ b/src/leveldb/util/histogram.h @@ -0,0 +1,42 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_HISTOGRAM_H_ +#define STORAGE_LEVELDB_UTIL_HISTOGRAM_H_ + +#include + +namespace leveldb { + +class Histogram { + public: + Histogram() { } + ~Histogram() { } + + void Clear(); + void Add(double value); + void Merge(const Histogram& other); + + std::string ToString() const; + + private: + double min_; + double max_; + double num_; + double sum_; + double sum_squares_; + + enum { kNumBuckets = 154 }; + static const double kBucketLimit[kNumBuckets]; + double buckets_[kNumBuckets]; + + double Median() const; + double Percentile(double p) const; + double Average() const; + double StandardDeviation() const; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_HISTOGRAM_H_ diff --git a/src/leveldb/util/logging.cc b/src/leveldb/util/logging.cc new file mode 100644 index 00000000..ca6b3244 --- /dev/null +++ b/src/leveldb/util/logging.cc @@ -0,0 +1,72 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/logging.h" + +#include +#include +#include +#include +#include "leveldb/env.h" +#include "leveldb/slice.h" + +namespace leveldb { + +void AppendNumberTo(std::string* str, uint64_t num) { + char buf[30]; + snprintf(buf, sizeof(buf), "%llu", (unsigned long long) num); + str->append(buf); +} + +void AppendEscapedStringTo(std::string* str, const Slice& value) { + for (size_t i = 0; i < value.size(); i++) { + char c = value[i]; + if (c >= ' ' && c <= '~') { + str->push_back(c); + } else { + char buf[10]; + snprintf(buf, sizeof(buf), "\\x%02x", + static_cast(c) & 0xff); + str->append(buf); + } + } +} + +std::string NumberToString(uint64_t num) { + std::string r; + AppendNumberTo(&r, num); + return r; +} + +std::string EscapeString(const Slice& value) { + std::string r; + AppendEscapedStringTo(&r, value); + return r; +} + +bool ConsumeDecimalNumber(Slice* in, uint64_t* val) { + uint64_t v = 0; + int digits = 0; + while (!in->empty()) { + char c = (*in)[0]; + if (c >= '0' && c <= '9') { + ++digits; + const int delta = (c - '0'); + static const uint64_t kMaxUint64 = ~static_cast(0); + if (v > kMaxUint64/10 || + (v == kMaxUint64/10 && delta > kMaxUint64%10)) { + // Overflow + return false; + } + v = (v * 10) + delta; + in->remove_prefix(1); + } else { + break; + } + } + *val = v; + return (digits > 0); +} + +} // namespace leveldb diff --git a/src/leveldb/util/logging.h b/src/leveldb/util/logging.h new file mode 100644 index 00000000..1b450d24 --- /dev/null +++ b/src/leveldb/util/logging.h @@ -0,0 +1,43 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Must not be included from any .h files to avoid polluting the namespace +// with macros. + +#ifndef STORAGE_LEVELDB_UTIL_LOGGING_H_ +#define STORAGE_LEVELDB_UTIL_LOGGING_H_ + +#include +#include +#include +#include "port/port.h" + +namespace leveldb { + +class Slice; +class WritableFile; + +// Append a human-readable printout of "num" to *str +extern void AppendNumberTo(std::string* str, uint64_t num); + +// Append a human-readable printout of "value" to *str. +// Escapes any non-printable characters found in "value". +extern void AppendEscapedStringTo(std::string* str, const Slice& value); + +// Return a human-readable printout of "num" +extern std::string NumberToString(uint64_t num); + +// Return a human-readable version of "value". +// Escapes any non-printable characters found in "value". +extern std::string EscapeString(const Slice& value); + +// Parse a human-readable number from "*in" into *value. On success, +// advances "*in" past the consumed number and sets "*val" to the +// numeric value. Otherwise, returns false and leaves *in in an +// unspecified state. +extern bool ConsumeDecimalNumber(Slice* in, uint64_t* val); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_LOGGING_H_ diff --git a/src/leveldb/util/mutexlock.h b/src/leveldb/util/mutexlock.h new file mode 100644 index 00000000..1ff5a9ef --- /dev/null +++ b/src/leveldb/util/mutexlock.h @@ -0,0 +1,41 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_MUTEXLOCK_H_ +#define STORAGE_LEVELDB_UTIL_MUTEXLOCK_H_ + +#include "port/port.h" +#include "port/thread_annotations.h" + +namespace leveldb { + +// Helper class that locks a mutex on construction and unlocks the mutex when +// the destructor of the MutexLock object is invoked. +// +// Typical usage: +// +// void MyClass::MyMethod() { +// MutexLock l(&mu_); // mu_ is an instance variable +// ... some complex code, possibly with multiple return paths ... +// } + +class SCOPED_LOCKABLE MutexLock { + public: + explicit MutexLock(port::Mutex *mu) EXCLUSIVE_LOCK_FUNCTION(mu) + : mu_(mu) { + this->mu_->Lock(); + } + ~MutexLock() UNLOCK_FUNCTION() { this->mu_->Unlock(); } + + private: + port::Mutex *const mu_; + // No copying allowed + MutexLock(const MutexLock&); + void operator=(const MutexLock&); +}; + +} // namespace leveldb + + +#endif // STORAGE_LEVELDB_UTIL_MUTEXLOCK_H_ diff --git a/src/leveldb/util/options.cc b/src/leveldb/util/options.cc new file mode 100644 index 00000000..76af5b93 --- /dev/null +++ b/src/leveldb/util/options.cc @@ -0,0 +1,29 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/options.h" + +#include "leveldb/comparator.h" +#include "leveldb/env.h" + +namespace leveldb { + +Options::Options() + : comparator(BytewiseComparator()), + create_if_missing(false), + error_if_exists(false), + paranoid_checks(false), + env(Env::Default()), + info_log(NULL), + write_buffer_size(4<<20), + max_open_files(1000), + block_cache(NULL), + block_size(4096), + block_restart_interval(16), + compression(kSnappyCompression), + filter_policy(NULL) { +} + + +} // namespace leveldb diff --git a/src/leveldb/util/posix_logger.h b/src/leveldb/util/posix_logger.h new file mode 100644 index 00000000..c063c2b7 --- /dev/null +++ b/src/leveldb/util/posix_logger.h @@ -0,0 +1,98 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Logger implementation that can be shared by all environments +// where enough Posix functionality is available. + +#ifndef STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_ +#define STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_ + +#include +#include +#include +#include +#include "leveldb/env.h" + +namespace leveldb { + +class PosixLogger : public Logger { + private: + FILE* file_; + uint64_t (*gettid_)(); // Return the thread id for the current thread + public: + PosixLogger(FILE* f, uint64_t (*gettid)()) : file_(f), gettid_(gettid) { } + virtual ~PosixLogger() { + fclose(file_); + } + virtual void Logv(const char* format, va_list ap) { + const uint64_t thread_id = (*gettid_)(); + + // We try twice: the first time with a fixed-size stack allocated buffer, + // and the second time with a much larger dynamically allocated buffer. + char buffer[500]; + for (int iter = 0; iter < 2; iter++) { + char* base; + int bufsize; + if (iter == 0) { + bufsize = sizeof(buffer); + base = buffer; + } else { + bufsize = 30000; + base = new char[bufsize]; + } + char* p = base; + char* limit = base + bufsize; + + struct timeval now_tv; + gettimeofday(&now_tv, NULL); + const time_t seconds = now_tv.tv_sec; + struct tm t; + localtime_r(&seconds, &t); + p += snprintf(p, limit - p, + "%04d/%02d/%02d-%02d:%02d:%02d.%06d %llx ", + t.tm_year + 1900, + t.tm_mon + 1, + t.tm_mday, + t.tm_hour, + t.tm_min, + t.tm_sec, + static_cast(now_tv.tv_usec), + static_cast(thread_id)); + + // Print the message + if (p < limit) { + va_list backup_ap; + va_copy(backup_ap, ap); + p += vsnprintf(p, limit - p, format, backup_ap); + va_end(backup_ap); + } + + // Truncate to available space if necessary + if (p >= limit) { + if (iter == 0) { + continue; // Try again with larger buffer + } else { + p = limit - 1; + } + } + + // Add newline if necessary + if (p == base || p[-1] != '\n') { + *p++ = '\n'; + } + + assert(p <= limit); + fwrite(base, 1, p - base, file_); + fflush(file_); + if (base != buffer) { + delete[] base; + } + break; + } + } +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_ diff --git a/src/leveldb/util/random.h b/src/leveldb/util/random.h new file mode 100644 index 00000000..ddd51b1c --- /dev/null +++ b/src/leveldb/util/random.h @@ -0,0 +1,64 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_RANDOM_H_ +#define STORAGE_LEVELDB_UTIL_RANDOM_H_ + +#include + +namespace leveldb { + +// A very simple random number generator. Not especially good at +// generating truly random bits, but good enough for our needs in this +// package. +class Random { + private: + uint32_t seed_; + public: + explicit Random(uint32_t s) : seed_(s & 0x7fffffffu) { + // Avoid bad seeds. + if (seed_ == 0 || seed_ == 2147483647L) { + seed_ = 1; + } + } + uint32_t Next() { + static const uint32_t M = 2147483647L; // 2^31-1 + static const uint64_t A = 16807; // bits 14, 8, 7, 5, 2, 1, 0 + // We are computing + // seed_ = (seed_ * A) % M, where M = 2^31-1 + // + // seed_ must not be zero or M, or else all subsequent computed values + // will be zero or M respectively. For all other values, seed_ will end + // up cycling through every number in [1,M-1] + uint64_t product = seed_ * A; + + // Compute (product % M) using the fact that ((x << 31) % M) == x. + seed_ = static_cast((product >> 31) + (product & M)); + // The first reduction may overflow by 1 bit, so we may need to + // repeat. mod == M is not possible; using > allows the faster + // sign-bit-based test. + if (seed_ > M) { + seed_ -= M; + } + return seed_; + } + // Returns a uniformly distributed value in the range [0..n-1] + // REQUIRES: n > 0 + uint32_t Uniform(int n) { return Next() % n; } + + // Randomly returns true ~"1/n" of the time, and false otherwise. + // REQUIRES: n > 0 + bool OneIn(int n) { return (Next() % n) == 0; } + + // Skewed: pick "base" uniformly from range [0,max_log] and then + // return "base" random bits. The effect is to pick a number in the + // range [0,2^max_log-1] with exponential bias towards smaller numbers. + uint32_t Skewed(int max_log) { + return Uniform(1 << Uniform(max_log + 1)); + } +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_RANDOM_H_ diff --git a/src/leveldb/util/status.cc b/src/leveldb/util/status.cc new file mode 100644 index 00000000..a44f35b3 --- /dev/null +++ b/src/leveldb/util/status.cc @@ -0,0 +1,75 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include "port/port.h" +#include "leveldb/status.h" + +namespace leveldb { + +const char* Status::CopyState(const char* state) { + uint32_t size; + memcpy(&size, state, sizeof(size)); + char* result = new char[size + 5]; + memcpy(result, state, size + 5); + return result; +} + +Status::Status(Code code, const Slice& msg, const Slice& msg2) { + assert(code != kOk); + const uint32_t len1 = msg.size(); + const uint32_t len2 = msg2.size(); + const uint32_t size = len1 + (len2 ? (2 + len2) : 0); + char* result = new char[size + 5]; + memcpy(result, &size, sizeof(size)); + result[4] = static_cast(code); + memcpy(result + 5, msg.data(), len1); + if (len2) { + result[5 + len1] = ':'; + result[6 + len1] = ' '; + memcpy(result + 7 + len1, msg2.data(), len2); + } + state_ = result; +} + +std::string Status::ToString() const { + if (state_ == NULL) { + return "OK"; + } else { + char tmp[30]; + const char* type; + switch (code()) { + case kOk: + type = "OK"; + break; + case kNotFound: + type = "NotFound: "; + break; + case kCorruption: + type = "Corruption: "; + break; + case kNotSupported: + type = "Not implemented: "; + break; + case kInvalidArgument: + type = "Invalid argument: "; + break; + case kIOError: + type = "IO error: "; + break; + default: + snprintf(tmp, sizeof(tmp), "Unknown code(%d): ", + static_cast(code())); + type = tmp; + break; + } + std::string result(type); + uint32_t length; + memcpy(&length, state_, sizeof(length)); + result.append(state_ + 5, length); + return result; + } +} + +} // namespace leveldb diff --git a/src/leveldb/util/testharness.cc b/src/leveldb/util/testharness.cc new file mode 100644 index 00000000..402fab34 --- /dev/null +++ b/src/leveldb/util/testharness.cc @@ -0,0 +1,77 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/testharness.h" + +#include +#include +#include +#include + +namespace leveldb { +namespace test { + +namespace { +struct Test { + const char* base; + const char* name; + void (*func)(); +}; +std::vector* tests; +} + +bool RegisterTest(const char* base, const char* name, void (*func)()) { + if (tests == NULL) { + tests = new std::vector; + } + Test t; + t.base = base; + t.name = name; + t.func = func; + tests->push_back(t); + return true; +} + +int RunAllTests() { + const char* matcher = getenv("LEVELDB_TESTS"); + + int num = 0; + if (tests != NULL) { + for (size_t i = 0; i < tests->size(); i++) { + const Test& t = (*tests)[i]; + if (matcher != NULL) { + std::string name = t.base; + name.push_back('.'); + name.append(t.name); + if (strstr(name.c_str(), matcher) == NULL) { + continue; + } + } + fprintf(stderr, "==== Test %s.%s\n", t.base, t.name); + (*t.func)(); + ++num; + } + } + fprintf(stderr, "==== PASSED %d tests\n", num); + return 0; +} + +std::string TmpDir() { + std::string dir; + Status s = Env::Default()->GetTestDirectory(&dir); + ASSERT_TRUE(s.ok()) << s.ToString(); + return dir; +} + +int RandomSeed() { + const char* env = getenv("TEST_RANDOM_SEED"); + int result = (env != NULL ? atoi(env) : 301); + if (result <= 0) { + result = 301; + } + return result; +} + +} // namespace test +} // namespace leveldb diff --git a/src/leveldb/util/testharness.h b/src/leveldb/util/testharness.h new file mode 100644 index 00000000..da4fe68b --- /dev/null +++ b/src/leveldb/util/testharness.h @@ -0,0 +1,138 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_TESTHARNESS_H_ +#define STORAGE_LEVELDB_UTIL_TESTHARNESS_H_ + +#include +#include +#include +#include "leveldb/env.h" +#include "leveldb/slice.h" +#include "util/random.h" + +namespace leveldb { +namespace test { + +// Run some of the tests registered by the TEST() macro. If the +// environment variable "LEVELDB_TESTS" is not set, runs all tests. +// Otherwise, runs only the tests whose name contains the value of +// "LEVELDB_TESTS" as a substring. E.g., suppose the tests are: +// TEST(Foo, Hello) { ... } +// TEST(Foo, World) { ... } +// LEVELDB_TESTS=Hello will run the first test +// LEVELDB_TESTS=o will run both tests +// LEVELDB_TESTS=Junk will run no tests +// +// Returns 0 if all tests pass. +// Dies or returns a non-zero value if some test fails. +extern int RunAllTests(); + +// Return the directory to use for temporary storage. +extern std::string TmpDir(); + +// Return a randomization seed for this run. Typically returns the +// same number on repeated invocations of this binary, but automated +// runs may be able to vary the seed. +extern int RandomSeed(); + +// An instance of Tester is allocated to hold temporary state during +// the execution of an assertion. +class Tester { + private: + bool ok_; + const char* fname_; + int line_; + std::stringstream ss_; + + public: + Tester(const char* f, int l) + : ok_(true), fname_(f), line_(l) { + } + + ~Tester() { + if (!ok_) { + fprintf(stderr, "%s:%d:%s\n", fname_, line_, ss_.str().c_str()); + exit(1); + } + } + + Tester& Is(bool b, const char* msg) { + if (!b) { + ss_ << " Assertion failure " << msg; + ok_ = false; + } + return *this; + } + + Tester& IsOk(const Status& s) { + if (!s.ok()) { + ss_ << " " << s.ToString(); + ok_ = false; + } + return *this; + } + +#define BINARY_OP(name,op) \ + template \ + Tester& name(const X& x, const Y& y) { \ + if (! (x op y)) { \ + ss_ << " failed: " << x << (" " #op " ") << y; \ + ok_ = false; \ + } \ + return *this; \ + } + + BINARY_OP(IsEq, ==) + BINARY_OP(IsNe, !=) + BINARY_OP(IsGe, >=) + BINARY_OP(IsGt, >) + BINARY_OP(IsLe, <=) + BINARY_OP(IsLt, <) +#undef BINARY_OP + + // Attach the specified value to the error message if an error has occurred + template + Tester& operator<<(const V& value) { + if (!ok_) { + ss_ << " " << value; + } + return *this; + } +}; + +#define ASSERT_TRUE(c) ::leveldb::test::Tester(__FILE__, __LINE__).Is((c), #c) +#define ASSERT_OK(s) ::leveldb::test::Tester(__FILE__, __LINE__).IsOk((s)) +#define ASSERT_EQ(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsEq((a),(b)) +#define ASSERT_NE(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsNe((a),(b)) +#define ASSERT_GE(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsGe((a),(b)) +#define ASSERT_GT(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsGt((a),(b)) +#define ASSERT_LE(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsLe((a),(b)) +#define ASSERT_LT(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsLt((a),(b)) + +#define TCONCAT(a,b) TCONCAT1(a,b) +#define TCONCAT1(a,b) a##b + +#define TEST(base,name) \ +class TCONCAT(_Test_,name) : public base { \ + public: \ + void _Run(); \ + static void _RunIt() { \ + TCONCAT(_Test_,name) t; \ + t._Run(); \ + } \ +}; \ +bool TCONCAT(_Test_ignored_,name) = \ + ::leveldb::test::RegisterTest(#base, #name, &TCONCAT(_Test_,name)::_RunIt); \ +void TCONCAT(_Test_,name)::_Run() + +// Register the specified test. Typically not used directly, but +// invoked via the macro expansion of TEST. +extern bool RegisterTest(const char* base, const char* name, void (*func)()); + + +} // namespace test +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_TESTHARNESS_H_ diff --git a/src/leveldb/util/testutil.cc b/src/leveldb/util/testutil.cc new file mode 100644 index 00000000..bee56bf7 --- /dev/null +++ b/src/leveldb/util/testutil.cc @@ -0,0 +1,51 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/testutil.h" + +#include "util/random.h" + +namespace leveldb { +namespace test { + +Slice RandomString(Random* rnd, int len, std::string* dst) { + dst->resize(len); + for (int i = 0; i < len; i++) { + (*dst)[i] = static_cast(' ' + rnd->Uniform(95)); // ' ' .. '~' + } + return Slice(*dst); +} + +std::string RandomKey(Random* rnd, int len) { + // Make sure to generate a wide variety of characters so we + // test the boundary conditions for short-key optimizations. + static const char kTestChars[] = { + '\0', '\1', 'a', 'b', 'c', 'd', 'e', '\xfd', '\xfe', '\xff' + }; + std::string result; + for (int i = 0; i < len; i++) { + result += kTestChars[rnd->Uniform(sizeof(kTestChars))]; + } + return result; +} + + +extern Slice CompressibleString(Random* rnd, double compressed_fraction, + size_t len, std::string* dst) { + int raw = static_cast(len * compressed_fraction); + if (raw < 1) raw = 1; + std::string raw_data; + RandomString(rnd, raw, &raw_data); + + // Duplicate the random data until we have filled "len" bytes + dst->clear(); + while (dst->size() < len) { + dst->append(raw_data); + } + dst->resize(len); + return Slice(*dst); +} + +} // namespace test +} // namespace leveldb diff --git a/src/leveldb/util/testutil.h b/src/leveldb/util/testutil.h new file mode 100644 index 00000000..adad3fc1 --- /dev/null +++ b/src/leveldb/util/testutil.h @@ -0,0 +1,53 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_TESTUTIL_H_ +#define STORAGE_LEVELDB_UTIL_TESTUTIL_H_ + +#include "leveldb/env.h" +#include "leveldb/slice.h" +#include "util/random.h" + +namespace leveldb { +namespace test { + +// Store in *dst a random string of length "len" and return a Slice that +// references the generated data. +extern Slice RandomString(Random* rnd, int len, std::string* dst); + +// Return a random key with the specified length that may contain interesting +// characters (e.g. \x00, \xff, etc.). +extern std::string RandomKey(Random* rnd, int len); + +// Store in *dst a string of length "len" that will compress to +// "N*compressed_fraction" bytes and return a Slice that references +// the generated data. +extern Slice CompressibleString(Random* rnd, double compressed_fraction, + size_t len, std::string* dst); + +// A wrapper that allows injection of errors. +class ErrorEnv : public EnvWrapper { + public: + bool writable_file_error_; + int num_writable_file_errors_; + + ErrorEnv() : EnvWrapper(Env::Default()), + writable_file_error_(false), + num_writable_file_errors_(0) { } + + virtual Status NewWritableFile(const std::string& fname, + WritableFile** result) { + if (writable_file_error_) { + ++num_writable_file_errors_; + *result = NULL; + return Status::IOError(fname, "fake error"); + } + return target()->NewWritableFile(fname, result); + } +}; + +} // namespace test +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_TESTUTIL_H_ diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 00000000..5ce08afb --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,4046 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "alert.h" +#include "checkpoints.h" +#include "db.h" +#include "txdb.h" +#include "net.h" +#include "init.h" +#include "ui_interface.h" +#include "checkqueue.h" +#include "kernel.h" +#include +#include +#include + +#include "main.h" + +using namespace std; +using namespace boost; + + + +CCriticalSection cs_setpwalletRegistered; +set setpwalletRegistered; + +CCriticalSection cs_main; + +CTxMemPool mempool; +unsigned int nTransactionsUpdated = 0; + +map mapBlockIndex; +set > setStakeSeen; + +CBigNum bnProofOfWorkLimit(~uint256(0) >> 20); // "standard" scrypt target limit for proof of work, results with 0,000244140625 proof-of-work difficulty +CBigNum bnProofOfStakeLegacyLimit(~uint256(0) >> 20); // proof of stake target limit from block #15000 and until 20 June 2013, results with 0,00390625 proof of stake difficulty +CBigNum bnProofOfStakeLimit(~uint256(0) >> 20); // proof of stake target limit since 20 June 2013, equal to 0.03125 proof of stake difficulty +CBigNum bnProofOfStakeHardLimit(~uint256(0) >> 20); // disabled temporarily, will be used in the future to fix minimal proof of stake difficulty at 0.25 +uint256 nPoWBase = uint256("0x00000000ffff0000000000000000000000000000000000000000000000000000"); // difficulty-1 target + +CBigNum bnProofOfWorkLimitTestNet(~uint256(0) >> 16); + +unsigned int nStakeMinAge = 1 * 24 * nOneHour; // 1 days as zero time weight +unsigned int nStakeMaxAge = 3 * 24 * nOneHour; // 3 days as full weight +unsigned int nStakeTargetSpacing = 0.5 * 60; // 0.5-minute stakes spacing +int64_t nTargetSpacing = 0.5 * 60; // Same as the above +unsigned int nModifierInterval = 1 * nOneHour; // time to elapse before new modifier is computed + +int nCoinbaseMaturity = 60; + +CBlockIndex* pindexGenesisBlock = NULL; +int nBestHeight = -1; + +uint256 nBestChainTrust = 0; +uint256 nBestInvalidTrust = 0; + +uint256 hashBestChain = 0; +CBlockIndex* pindexBest = NULL; +int64_t nTimeBestReceived = 0; +int nScriptCheckThreads = 0; + +CMedianFilter cPeerBlockCounts(5, 0); // Amount of blocks that other nodes claim to have + +map mapOrphanBlocks; +multimap mapOrphanBlocksByPrev; +set > setStakeSeenOrphan; +map mapProofOfStake; + +map mapOrphanTransactions; +map > mapOrphanTransactionsByPrev; + +// Constant stuff for coinbase transactions we create: +CScript COINBASE_FLAGS; + +const string strMessageMagic = "XP Signed Message:\n"; + +// Settings +int64_t nTransactionFee = MIN_TX_FEE; +int64_t nMinimumInputValue = MIN_TXOUT_AMOUNT; + +// Ping and address broadcast intervals +int64_t nPingInterval = 30 * 60; +int64_t nBroadcastInterval = nOneDay; + +extern enum Checkpoints::CPMode CheckpointsMode; + +////////////////////////////////////////////////////////////////////////////// +// +// dispatching functions +// + +// These functions dispatch to one or all registered wallets + + +void RegisterWallet(CWallet* pwalletIn) +{ + { + LOCK(cs_setpwalletRegistered); + setpwalletRegistered.insert(pwalletIn); + } +} + +void UnregisterWallet(CWallet* pwalletIn) +{ + { + LOCK(cs_setpwalletRegistered); + setpwalletRegistered.erase(pwalletIn); + } +} + +// check whether the passed transaction is from us +bool static IsFromMe(CTransaction& tx) +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + if (pwallet->IsFromMe(tx)) + return true; + return false; +} + +// get the wallet transaction with the given hash (if it exists) +bool static GetTransaction(const uint256& hashTx, CWalletTx& wtx) +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + if (pwallet->GetTransaction(hashTx,wtx)) + return true; + return false; +} + +// erases transaction with the given hash from all wallets +void static EraseFromWallets(uint256 hash) +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->EraseFromWallet(hash); +} + +// make sure all wallets know about the given transaction, in the given block +void SyncWithWallets(const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fConnect) +{ + if (!fConnect) + { + // wallets need to refund inputs when disconnecting coinstake + if (tx.IsCoinStake()) + { + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + if (pwallet->IsFromMe(tx)) + pwallet->DisableTransaction(tx); + } + return; + } + + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->AddToWalletIfInvolvingMe(tx, pblock, fUpdate); +} + +// notify wallets about a new best chain +void static SetBestChain(const CBlockLocator& loc) +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->SetBestChain(loc); +} + +// notify wallets about an updated transaction +void static UpdatedTransaction(const uint256& hashTx) +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->UpdatedTransaction(hashTx); +} + +// dump all wallets +void static PrintWallets(const CBlock& block) +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->PrintWallet(block); +} + +// notify wallets about an incoming inventory (for request counts) +void static Inventory(const uint256& hash) +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->Inventory(hash); +} + +// ask wallets to resend their transactions +void ResendWalletTransactions() +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->ResendWalletTransactions(); +} + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// mapOrphanTransactions +// + +bool AddOrphanTx(const CTransaction& tx) +{ + uint256 hash = tx.GetHash(); + if (mapOrphanTransactions.count(hash)) + return false; + + // Ignore big transactions, to avoid a + // send-big-orphans memory exhaustion attack. If a peer has a legitimate + // large transaction with a missing parent then we assume + // it will rebroadcast it later, after the parent transaction(s) + // have been mined or received. + // 10,000 orphans, each of which is at most 5,000 bytes big is + // at most 500 megabytes of orphans: + + size_t nSize = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); + + if (nSize > 5000) + { + printf("ignoring large orphan tx (size: %" PRIszu ", hash: %s)\n", nSize, hash.ToString().substr(0,10).c_str()); + return false; + } + + mapOrphanTransactions[hash] = tx; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash); + + printf("stored orphan tx %s (mapsz %" PRIszu ")\n", hash.ToString().substr(0,10).c_str(), + mapOrphanTransactions.size()); + return true; +} + +void static EraseOrphanTx(uint256 hash) +{ + if (!mapOrphanTransactions.count(hash)) + return; + const CTransaction& tx = mapOrphanTransactions[hash]; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + mapOrphanTransactionsByPrev[txin.prevout.hash].erase(hash); + if (mapOrphanTransactionsByPrev[txin.prevout.hash].empty()) + mapOrphanTransactionsByPrev.erase(txin.prevout.hash); + } + mapOrphanTransactions.erase(hash); +} + +unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) +{ + unsigned int nEvicted = 0; + while (mapOrphanTransactions.size() > nMaxOrphans) + { + // Evict a random orphan: + uint256 randomhash = GetRandHash(); + map::iterator it = mapOrphanTransactions.lower_bound(randomhash); + if (it == mapOrphanTransactions.end()) + it = mapOrphanTransactions.begin(); + EraseOrphanTx(it->first); + ++nEvicted; + } + return nEvicted; +} + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// CTransaction and CTxIndex +// + +bool CTransaction::ReadFromDisk(CTxDB& txdb, const uint256& hash, CTxIndex& txindexRet) +{ + SetNull(); + if (!txdb.ReadTxIndex(hash, txindexRet)) + return false; + if (!ReadFromDisk(txindexRet.pos)) + return false; + return true; +} + +bool CTransaction::ReadFromDisk(CTxDB& txdb, COutPoint prevout, CTxIndex& txindexRet) +{ + if (!ReadFromDisk(txdb, prevout.hash, txindexRet)) + 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 CTransaction::IsStandard(string& strReason) const +{ + if (nVersion > CTransaction::CURRENT_VERSION) + { + strReason = "version"; + return false; + } + + unsigned int nDataOut = 0; + txnouttype whichType; + BOOST_FOREACH(const CTxIn& txin, vin) + { + // Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed + // keys. (remember the 520 byte limit on redeemScript size) That works + // out to a (15*(33+1))+3=513 byte redeemScript, 513+1+15*(73+1)=1624 + // bytes of scriptSig, which we round off to 1650 bytes for some minor + // future-proofing. That's also enough to spend a 20-of-20 + // CHECKMULTISIG scriptPubKey, though such a scriptPubKey is not + // considered standard) + if (txin.scriptSig.size() > 1650) + { + strReason = "scriptsig-size"; + return false; + } + if (!txin.scriptSig.IsPushOnly()) + { + strReason = "scriptsig-not-pushonly"; + return false; + } + if (!txin.scriptSig.HasCanonicalPushes()) { + strReason = "txin-scriptsig-not-canonicalpushes"; + return false; + } + } + BOOST_FOREACH(const CTxOut& txout, vout) { + if (!::IsStandard(txout.scriptPubKey, whichType)) { + strReason = "scriptpubkey"; + return false; + } + if (whichType == TX_NULL_DATA) + nDataOut++; + else { + if (txout.nValue == 0) { + strReason = "txout-value=0"; + return false; + } + if (!txout.scriptPubKey.HasCanonicalPushes()) { + strReason = "txout-scriptsig-not-canonicalpushes"; + return false; + } + } + } + + // only one OP_RETURN txout is permitted + if (nDataOut > 1) { + strReason = "multi-op-return"; + return false; + } + + return true; +} + +// +// Check transaction inputs, and make sure any +// pay-to-script-hash transactions are evaluating IsStandard scripts +// +// Why bother? To avoid denial-of-service attacks; an attacker +// can submit a standard HASH... OP_EQUAL transaction, +// which will get accepted into blocks. The redemption +// script can be anything; an attacker could use a very +// expensive-to-check-upon-redemption script like: +// DUP CHECKSIG DROP ... repeated 100 times... OP_1 +// +bool CTransaction::AreInputsStandard(const MapPrevTx& mapInputs) const +{ + if (IsCoinBase()) + return true; // Coinbases don't use vin normally + + for (unsigned int i = 0; i < vin.size(); i++) + { + const CTxOut& prev = GetOutputFor(vin[i], mapInputs); + + vector > vSolutions; + txnouttype whichType; + // get the scriptPubKey corresponding to this input: + const CScript& prevScript = prev.scriptPubKey; + if (!Solver(prevScript, whichType, vSolutions)) + return false; + int nArgsExpected = ScriptSigArgsExpected(whichType, vSolutions); + if (nArgsExpected < 0) + return false; + + // Transactions with extra stuff in their scriptSigs are + // non-standard. Note that this EvalScript() call will + // be quick, because if there are any operations + // beside "push data" in the scriptSig the + // IsStandard() call returns false + vector > stack; + if (!EvalScript(stack, vin[i].scriptSig, *this, i, false, 0)) + return false; + + if (whichType == TX_SCRIPTHASH) + { + if (stack.empty()) + return false; + CScript subscript(stack.back().begin(), stack.back().end()); + vector > vSolutions2; + txnouttype whichType2; + if (!Solver(subscript, whichType2, vSolutions2)) + return false; + if (whichType2 == TX_SCRIPTHASH) + return false; + + int tmpExpected; + tmpExpected = ScriptSigArgsExpected(whichType2, vSolutions2); + if (tmpExpected < 0) + return false; + nArgsExpected += tmpExpected; + } + + if (stack.size() != (unsigned int)nArgsExpected) + return false; + } + + return true; +} + +unsigned int +CTransaction::GetLegacySigOpCount() const +{ + unsigned int nSigOps = 0; + if (!IsCoinBase() || nTime < COINBASE_SIGOPS_SWITCH_TIME) + { + // Coinbase scriptsigs are never executed, so there is + // no sense in calculation of sigops. + BOOST_FOREACH(const CTxIn& txin, vin) + { + nSigOps += txin.scriptSig.GetSigOpCount(false); + } + } + BOOST_FOREACH(const CTxOut& txout, vout) + { + nSigOps += txout.scriptPubKey.GetSigOpCount(false); + } + return nSigOps; +} + + +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 < (int)pblock->vtx.size(); nIndex++) + if (pblock->vtx[nIndex] == *(CTransaction*)this) + break; + if (nIndex == (int)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; +} + + + + + + + +bool CTransaction::CheckTransaction() const +{ + // Basic checks that don't depend on any context + if (vin.empty()) + return DoS(10, error("CTransaction::CheckTransaction() : vin empty")); + if (vout.empty()) + return DoS(10, error("CTransaction::CheckTransaction() : vout empty")); + // Time (prevent mempool memory exhaustion attack) + if (nTime > FutureDrift(GetAdjustedTime())) + return DoS(10, error("CTransaction::CheckTransaction() : timestamp is too far into the future")); + // Size limits + if (::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) + return DoS(100, error("CTransaction::CheckTransaction() : size limits failed")); + + // Check for negative or overflow output values + int64_t nValueOut = 0; + for (unsigned int i = 0; i < vout.size(); i++) + { + const CTxOut& txout = vout[i]; + if (txout.IsEmpty() && !IsCoinBase() && !IsCoinStake()) + return DoS(100, error("CTransaction::CheckTransaction() : txout empty for user transaction")); + + if (txout.nValue < 0) + return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue is negative")); + if (txout.nValue > MAX_MONEY) + return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue too high")); + nValueOut += txout.nValue; + if (!MoneyRange(nValueOut)) + return DoS(100, error("CTransaction::CheckTransaction() : txout total out of range")); + } + + // Check for duplicate inputs + set vInOutPoints; + BOOST_FOREACH(const CTxIn& txin, vin) + { + if (vInOutPoints.count(txin.prevout)) + return false; + vInOutPoints.insert(txin.prevout); + } + + if (IsCoinBase()) + { + if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100) + return DoS(100, error("CTransaction::CheckTransaction() : coinbase script size is invalid")); + } + else + { + BOOST_FOREACH(const CTxIn& txin, vin) + if (txin.prevout.IsNull()) + return DoS(10, error("CTransaction::CheckTransaction() : prevout is null")); + } + + return true; +} + +int64_t CTransaction::GetMinFee(unsigned int nBlockSize, bool fAllowFree, enum GetMinFee_mode mode, unsigned int nBytes) const +{ + int64_t nMinTxFee = MIN_TX_FEE, nMinRelayTxFee = MIN_RELAY_TX_FEE; + + if(IsCoinStake()) + { + // Enforce 0.01 as minimum fee for coinstake + nMinTxFee = CENT; + nMinRelayTxFee = CENT; + } + + // Base fee is either nMinTxFee or nMinRelayTxFee + int64_t nBaseFee = (mode == GMF_RELAY) ? nMinRelayTxFee : nMinTxFee; + + unsigned int nNewBlockSize = nBlockSize + nBytes; + int64_t nMinFee = (1 + (int64_t)nBytes / 1000) * nBaseFee; + + if (fAllowFree) + { + if (nBlockSize == 1) + { + // Transactions under 1K are free + if (nBytes < 1000) + nMinFee = 0; + } + else + { + // Free transaction area + if (nNewBlockSize < 27000) + nMinFee = 0; + } + } + + // To limit dust spam, require additional MIN_TX_FEE/MIN_RELAY_TX_FEE for + // each non empty output which is less than 0.01 + // + // It's safe to ignore empty outputs here, because these inputs are allowed + // only for coinbase and coinstake transactions. + BOOST_FOREACH(const CTxOut& txout, vout) + if (txout.nValue < CENT && !txout.IsEmpty()) + nMinFee += nBaseFee; + + // Raise the price as the block approaches full + if (nBlockSize != 1 && nNewBlockSize >= MAX_BLOCK_SIZE_GEN/2) + { + if (nNewBlockSize >= MAX_BLOCK_SIZE_GEN) + return MAX_MONEY; + nMinFee *= MAX_BLOCK_SIZE_GEN / (MAX_BLOCK_SIZE_GEN - nNewBlockSize); + } + + if (!MoneyRange(nMinFee)) + nMinFee = MAX_MONEY; + + return nMinFee; +} + + +bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs, + bool* pfMissingInputs) +{ + if (pfMissingInputs) + *pfMissingInputs = false; + + if (!tx.CheckTransaction()) + return error("CTxMemPool::accept() : CheckTransaction failed"); + + // Coinbase is only valid in a block, not as a loose transaction + if (tx.IsCoinBase()) + return tx.DoS(100, error("CTxMemPool::accept() : coinbase as individual tx")); + + // ppcoin: coinstake is also only valid in a block, not as a loose transaction + if (tx.IsCoinStake()) + return tx.DoS(100, error("CTxMemPool::accept() : coinstake as individual tx")); + + // To help v0.1.5 clients who would see it as a negative number + if ((int64_t)tx.nLockTime > std::numeric_limits::max()) + return error("CTxMemPool::accept() : not accepting nLockTime beyond 2038 yet"); + + // Rather not work on nonstandard transactions (unless -testnet) + string strNonStd; + if (!fTestNet && !tx.IsStandard(strNonStd)) + return error("CTxMemPool::accept() : nonstandard transaction (%s)", strNonStd.c_str()); + + // Do we already have it? + uint256 hash = tx.GetHash(); + { + LOCK(cs); + if (mapTx.count(hash)) + return false; + } + if (fCheckInputs) + if (txdb.ContainsTx(hash)) + return false; + + // Check for conflicts with in-memory transactions + CTransaction* ptxOld = NULL; + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + COutPoint outpoint = tx.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 (!tx.IsNewerThan(*ptxOld)) + return false; + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + COutPoint outpoint = tx.vin[i].prevout; + if (!mapNextTx.count(outpoint) || mapNextTx[outpoint].ptx != ptxOld) + return false; + } + break; + } + } + + if (fCheckInputs) + { + MapPrevTx mapInputs; + map mapUnused; + bool fInvalid = false; + if (!tx.FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid)) + { + if (fInvalid) + return error("CTxMemPool::accept() : FetchInputs found invalid tx %s", hash.ToString().substr(0,10).c_str()); + if (pfMissingInputs) + *pfMissingInputs = true; + return false; + } + + // Check for non-standard pay-to-script-hash in inputs + if (!tx.AreInputsStandard(mapInputs) && !fTestNet) + return error("CTxMemPool::accept() : nonstandard transaction input"); + + // Note: if you modify this code to accept non-standard transactions, then + // you should add code here to check that the transaction does a + // reasonable number of ECDSA signature verifications. + + int64_t nFees = tx.GetValueIn(mapInputs)-tx.GetValueOut(); + unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); + + // Don't accept it if it can't get into a block + int64_t txMinFee = tx.GetMinFee(1000, true, GMF_RELAY, nSize); + if (nFees < txMinFee) + return error("CTxMemPool::accept() : not enough fees %s, %" PRId64 " < %" PRId64, + hash.ToString().c_str(), + nFees, txMinFee); + + // Continuously rate-limit free transactions + // This mitigates 'penny-flooding' -- sending thousands of free transactions just to + // be annoying or make others' transactions take longer to confirm. + if (nFees < MIN_RELAY_TX_FEE) + { + static CCriticalSection cs; + static double dFreeCount; + static int64_t nLastTime; + int64_t nNow = GetTime(); + + { + LOCK(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(tx)) + return error("CTxMemPool::accept() : free transaction rejected by rate limiter"); + if (fDebug) + printf("Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); + dFreeCount += nSize; + } + } + + // Check against previous transactions + // This is done last to help prevent CPU exhaustion denial-of-service attacks. + if (!tx.ConnectInputs(txdb, mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, false, true, STRICT_FLAGS)) + { + return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str()); + } + } + + // Store transaction in memory + { + LOCK(cs); + if (ptxOld) + { + printf("CTxMemPool::accept() : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str()); + remove(*ptxOld); + } + addUnchecked(hash, tx); + } + + ///// are we sure this is ok when loading transactions or restoring block txes + // If updated, erase old tx from wallet + if (ptxOld) + EraseFromWallets(ptxOld->GetHash()); + + printf("CTxMemPool::accept() : accepted %s (poolsz %" PRIszu ")\n", + hash.ToString().substr(0,10).c_str(), + mapTx.size()); + return true; +} + +bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMissingInputs) +{ + return mempool.accept(txdb, *this, fCheckInputs, pfMissingInputs); +} + +bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx) +{ + // Add to memory pool without checking anything. Don't call this directly, + // call CTxMemPool::accept to properly check the transaction first. + { + mapTx[hash] = tx; + for (unsigned int i = 0; i < tx.vin.size(); i++) + mapNextTx[tx.vin[i].prevout] = CInPoint(&mapTx[hash], i); + nTransactionsUpdated++; + } + return true; +} + + +bool CTxMemPool::remove(CTransaction &tx) +{ + // Remove transaction from memory pool + { + LOCK(cs); + uint256 hash = tx.GetHash(); + if (mapTx.count(hash)) + { + BOOST_FOREACH(const CTxIn& txin, tx.vin) + mapNextTx.erase(txin.prevout); + mapTx.erase(hash); + nTransactionsUpdated++; + } + } + return true; +} + +void CTxMemPool::clear() +{ + LOCK(cs); + mapTx.clear(); + mapNextTx.clear(); + ++nTransactionsUpdated; +} + +void CTxMemPool::queryHashes(std::vector& vtxid) +{ + vtxid.clear(); + + LOCK(cs); + vtxid.reserve(mapTx.size()); + for (map::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi) + vtxid.push_back((*mi).first); +} + + + + +int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) 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; + } + + pindexRet = pindex; + return pindexBest->nHeight - pindex->nHeight + 1; +} + + +int CMerkleTx::GetBlocksToMaturity() const +{ + if (!(IsCoinBase() || IsCoinStake())) + return 0; + return max(0, (nCoinbaseMaturity+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 CMerkleTx::AcceptToMemoryPool() +{ + CTxDB txdb("r"); + return AcceptToMemoryPool(txdb); +} + + + +bool CWalletTx::AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs) +{ + + { + LOCK(mempool.cs); + // Add previous supporting transactions first + BOOST_FOREACH(CMerkleTx& tx, vtxPrev) + { + if (!(tx.IsCoinBase() || tx.IsCoinStake())) + { + uint256 hash = tx.GetHash(); + if (!mempool.exists(hash) && !txdb.ContainsTx(hash)) + tx.AcceptToMemoryPool(txdb, fCheckInputs); + } + } + return AcceptToMemoryPool(txdb, fCheckInputs); + } + return false; +} + +bool CWalletTx::AcceptWalletTransaction() +{ + CTxDB txdb("r"); + return AcceptWalletTransaction(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; +} + +// Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock +bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock) +{ + { + LOCK(cs_main); + { + LOCK(mempool.cs); + if (mempool.exists(hash)) + { + tx = mempool.lookup(hash); + return true; + } + } + CTxDB txdb("r"); + CTxIndex txindex; + if (tx.ReadFromDisk(txdb, hash, txindex)) + { + CBlock block; + if (block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) + hashBlock = block.GetHash(); + return true; + } + // look for transaction in disconnected blocks to find orphaned CoinBase and CoinStake transactions + BOOST_FOREACH(PAIRTYPE(const uint256, CBlockIndex*)& item, mapBlockIndex) + { + CBlockIndex* pindex = item.second; + if (pindex == pindexBest || pindex->pnext != 0) + continue; + CBlock block; + if (!block.ReadFromDisk(pindex)) + continue; + BOOST_FOREACH(const CTransaction& txOrphan, block.vtx) + { + if (txOrphan.GetHash() == hash) + { + tx = txOrphan; + return true; + } + } + } + } + return false; +} + + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// CBlock and CBlockIndex +// + +static CBlockIndex* pblockindexFBBHLast; +CBlockIndex* FindBlockByHeight(int nHeight) +{ + CBlockIndex *pblockindex; + if (nHeight < nBestHeight / 2) + pblockindex = pindexGenesisBlock; + else + pblockindex = pindexBest; + if (pblockindexFBBHLast && abs(nHeight - pblockindex->nHeight) > abs(nHeight - pblockindexFBBHLast->nHeight)) + pblockindex = pblockindexFBBHLast; + while (pblockindex->nHeight > nHeight) + pblockindex = pblockindex->pprev; + while (pblockindex->nHeight < nHeight) + pblockindex = pblockindex->pnext; + pblockindexFBBHLast = pblockindex; + return pblockindex; +} + +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 static 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(); +} + +// ppcoin: find block wanted by given orphan block +uint256 WantedByOrphan(const CBlock* pblockOrphan) +{ + // Work back to the first block in the orphan chain + while (mapOrphanBlocks.count(pblockOrphan->hashPrevBlock)) + pblockOrphan = mapOrphanBlocks[pblockOrphan->hashPrevBlock]; + return pblockOrphan->hashPrevBlock; +} + +// select stake target limit according to hard-coded conditions +CBigNum inline GetProofOfStakeLimit(int nHeight, unsigned int nTime) +{ + if(fTestNet) // separate proof of stake target limit for testnet + return bnProofOfStakeLimit; + if(nTime > TARGETS_SWITCH_TIME) // 27 bits since 20 July 2013 + return bnProofOfStakeLimit; + if(nHeight + 1 > 15000) // 24 bits since block 15000 + return bnProofOfStakeLegacyLimit; + if(nHeight + 1 > 14060) // 31 bits since block 14060 until 15000 + return bnProofOfStakeHardLimit; + + return bnProofOfWorkLimit; // return bnProofOfWorkLimit of none matched +} + +// miner's coin base reward based on nBits +int64_t GetProofOfWorkReward(unsigned int nBits, int64_t nFees, int nHeight) +{ + int64_t nSubsidy = 100000 * COIN; + + if (nHeight == 1) + return 50000000000 * COIN; + + if (nHeight > 1500000) + return nFees; + + // Force block reward to zero when right shift is undefined. + //int halvings = nHeight / 840000; + //if (halvings >= 64) + // return nFees; + + //nSubsidy >>= halvings; + + if (fDebug && GetBoolArg("-printcreation")) + printf("GetProofOfWorkReward() : create=%s nBits=0x%08x nSubsidy=%" PRId64 "\n", FormatMoney(nSubsidy).c_str(), nBits, nSubsidy); + + return nSubsidy + nFees; +} + +// miner's coin stake reward based on nBits and coin age spent (coin-days) +int64_t GetProofOfStakeReward(int64_t nCoinAge, unsigned int nBits, int64_t nTime, bool bCoinYearOnly) +{ + int64_t nRewardCoinYear = 100 * CENT; + + int64_t nSubsidy = nCoinAge * nRewardCoinYear / 365; + + if (fDebug && GetBoolArg("-printcreation")) + printf("GetProofOfStakeReward(): create=%s nCoinAge=%"PRId64"\n", FormatMoney(nSubsidy).c_str(), nCoinAge); + + return nSubsidy; // + nFees; +} + +static const int64_t nTargetTimespan = 0.5 * 15 * 60; + +// Get proof of work blocks max spacing according to hard-coded conditions +// Not in use right now! +int64_t inline GetTargetSpacingWorkMax(int nHeight, unsigned int nTime) +{ + if(nTime > TARGETS_SWITCH_TIME) + return 3 * nStakeTargetSpacing; // 30 minutes on mainNet since 20 Jul 2013 00:00:00 + + if(fTestNet) + return 3 * nStakeTargetSpacing; // 15 minutes on testNet + + return 12 * nStakeTargetSpacing; // 2 hours otherwise +} + +// +// maximum nBits value could possible be required nTime after +// +unsigned int ComputeMaxBits(CBigNum bnTargetLimit, unsigned int nBase, int64_t nTime) +{ + CBigNum bnResult; + bnResult.SetCompact(nBase); + bnResult *= 2; + while (nTime > 0 && bnResult < bnTargetLimit) + { + // Maximum 200% adjustment per day... + bnResult *= 2; + nTime -= nOneDay; + } + if (bnResult > bnTargetLimit) + bnResult = bnTargetLimit; + return bnResult.GetCompact(); +} + +// +// minimum amount of work that could possibly be required nTime after +// minimum proof-of-work required was nBase +// +unsigned int ComputeMinWork(unsigned int nBase, int64_t nTime) +{ + return ComputeMaxBits(bnProofOfWorkLimit, nBase, nTime); +} + +// +// minimum amount of stake that could possibly be required nTime after +// minimum proof-of-stake required was nBase +// +unsigned int ComputeMinStake(unsigned int nBase, int64_t nTime, unsigned int nBlockTime) +{ + return ComputeMaxBits(GetProofOfStakeLimit(0, nBlockTime), nBase, nTime); +} + + +// ppcoin: find last block index up to pindex +const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfStake) +{ + while (pindex && pindex->pprev && (pindex->IsProofOfStake() != fProofOfStake)) + pindex = pindex->pprev; + return pindex; +} + +unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake) +{ + CBigNum bnTargetLimit = !fProofOfStake ? bnProofOfWorkLimit : GetProofOfStakeLimit(pindexLast->nHeight, pindexLast->nTime); + + if (pindexLast == NULL) + return bnTargetLimit.GetCompact(); // genesis block + + const CBlockIndex* pindexPrev = GetLastBlockIndex(pindexLast, fProofOfStake); + if (pindexPrev->pprev == NULL) + return bnTargetLimit.GetCompact(); // first block + const CBlockIndex* pindexPrevPrev = GetLastBlockIndex(pindexPrev->pprev, fProofOfStake); + if (pindexPrevPrev->pprev == NULL) + return bnTargetLimit.GetCompact(); // second block + + int64_t nActualSpacing = pindexPrev->GetBlockTime() - pindexPrevPrev->GetBlockTime(); + + // Don't allow zero or negative values. + if (nActualSpacing < 1) + nActualSpacing = 1; + + // Limit the impact of blocks that are unusually far in the future + if (nActualSpacing > 3 * nTargetSpacing) + nActualSpacing = 3 * nTargetSpacing; + + // ppcoin: target change every block + // ppcoin: retarget with exponential moving toward target spacing + CBigNum bnNew; + bnNew.SetCompact(pindexPrev->nBits); + // The below is intentionally commented out. We use the same time for POW and POS. + // int64_t nTargetSpacing = fProofOfStake? nStakeTargetSpacing : min(GetTargetSpacingWorkMax(pindexLast->nHeight, pindexLast->nTime), (int64_t) nStakeTargetSpacing * (1 + pindexLast->nHeight - pindexPrev->nHeight)); + int64_t nInterval = nTargetTimespan / nTargetSpacing; + bnNew *= ((nInterval - 1) * nTargetSpacing + nActualSpacing + nActualSpacing); + bnNew /= ((nInterval + 1) * nTargetSpacing); + + if (bnNew > bnTargetLimit) + bnNew = bnTargetLimit; + + 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; +} + +// Return maximum amount of blocks that other nodes claim to have +int GetNumBlocksOfPeers() +{ + return std::max(cPeerBlockCounts.median(), Checkpoints::GetTotalBlocksEstimate()); +} + +bool IsInitialBlockDownload() +{ + if (pindexBest == NULL || nBestHeight < Checkpoints::GetTotalBlocksEstimate()) + return true; + static int64_t nLastUpdate; + static CBlockIndex* pindexLastBest; + int64_t nCurrentTime = GetTime(); + if (pindexBest != pindexLastBest) + { + pindexLastBest = pindexBest; + nLastUpdate = nCurrentTime; + } + return (nCurrentTime - nLastUpdate < 10 && + pindexBest->GetBlockTime() < nCurrentTime - nOneDay); +} + +void static InvalidChainFound(CBlockIndex* pindexNew) +{ + if (pindexNew->nChainTrust > nBestInvalidTrust) + { + nBestInvalidTrust = pindexNew->nChainTrust; + CTxDB().WriteBestInvalidTrust(CBigNum(nBestInvalidTrust)); + uiInterface.NotifyBlocksChanged(); + } + + uint256 nBestInvalidBlockTrust = pindexNew->nChainTrust - pindexNew->pprev->nChainTrust; + uint256 nBestBlockTrust = pindexBest->nHeight != 0 ? (pindexBest->nChainTrust - pindexBest->pprev->nChainTrust) : pindexBest->nChainTrust; + + printf("InvalidChainFound: invalid block=%s height=%d trust=%s blocktrust=%" PRId64 " date=%s\n", + pindexNew->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->nHeight, + CBigNum(pindexNew->nChainTrust).ToString().c_str(), nBestInvalidBlockTrust.Get64(), + DateTimeStrFormat("%x %H:%M:%S", pindexNew->GetBlockTime()).c_str()); + printf("InvalidChainFound: current best=%s height=%d trust=%s blocktrust=%" PRId64 " date=%s\n", + hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, + CBigNum(pindexBest->nChainTrust).ToString().c_str(), + nBestBlockTrust.Get64(), + DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str()); +} + + +void CBlock::UpdateTime(const CBlockIndex* pindexPrev) +{ + nTime = max(GetBlockTime(), GetAdjustedTime()); +} + + + + + + + + + + + +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 + // This can fail if a duplicate of this transaction was in a chain that got + // reorganized away. This is only possible if this transaction was completely + // spent, so erasing it would be a no-op anyway. + txdb.EraseTxIndex(*this); + + return true; +} + + +bool CTransaction::FetchInputs(CTxDB& txdb, const map& mapTestPool, + bool fBlock, bool fMiner, MapPrevTx& inputsRet, bool& fInvalid) +{ + // FetchInputs can return false either because we just haven't seen some inputs + // (in which case the transaction should be stored as an orphan) + // or because the transaction is malformed (in which case the transaction should + // be dropped). If tx is definitely invalid, fInvalid will be set to true. + fInvalid = false; + + if (IsCoinBase()) + return true; // Coinbase transactions have no inputs to fetch. + + for (unsigned int i = 0; i < vin.size(); i++) + { + COutPoint prevout = vin[i].prevout; + if (inputsRet.count(prevout.hash)) + continue; // Got it already + + // Read txindex + CTxIndex& txindex = inputsRet[prevout.hash].first; + bool fFound = true; + if ((fBlock || fMiner) && mapTestPool.count(prevout.hash)) + { + // Get txindex from current proposed changes + txindex = mapTestPool.find(prevout.hash)->second; + } + else + { + // Read txindex from txdb + fFound = txdb.ReadTxIndex(prevout.hash, txindex); + } + if (!fFound && (fBlock || fMiner)) + return fMiner ? false : error("FetchInputs() : %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 = inputsRet[prevout.hash].second; + if (!fFound || txindex.pos == CDiskTxPos(1,1,1)) + { + // Get prev tx from single transactions in memory + { + LOCK(mempool.cs); + if (!mempool.exists(prevout.hash)) + return error("FetchInputs() : %s mempool Tx prev not found %s", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); + txPrev = mempool.lookup(prevout.hash); + } + if (!fFound) + txindex.vSpent.resize(txPrev.vout.size()); + } + else + { + // Get prev tx from disk + if (!txPrev.ReadFromDisk(txindex.pos)) + return error("FetchInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); + } + } + + // Make sure all prevout.n indexes are valid: + for (unsigned int i = 0; i < vin.size(); i++) + { + const COutPoint prevout = vin[i].prevout; + assert(inputsRet.count(prevout.hash) != 0); + const CTxIndex& txindex = inputsRet[prevout.hash].first; + const CTransaction& txPrev = inputsRet[prevout.hash].second; + if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size()) + { + // Revisit this if/when transaction replacement is implemented and allows + // adding inputs: + fInvalid = true; + return DoS(100, error("FetchInputs() : %s prevout.n out of range %d %" PRIszu " %" PRIszu " 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())); + } + } + + return true; +} + +const CTxOut& CTransaction::GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const +{ + MapPrevTx::const_iterator mi = inputs.find(input.prevout.hash); + if (mi == inputs.end()) + throw std::runtime_error("CTransaction::GetOutputFor() : prevout.hash not found"); + + const CTransaction& txPrev = (mi->second).second; + if (input.prevout.n >= txPrev.vout.size()) + throw std::runtime_error("CTransaction::GetOutputFor() : prevout.n out of range"); + + return txPrev.vout[input.prevout.n]; +} + +int64_t CTransaction::GetValueIn(const MapPrevTx& inputs) const +{ + if (IsCoinBase()) + return 0; + + int64_t nResult = 0; + for (unsigned int i = 0; i < vin.size(); i++) + { + nResult += GetOutputFor(vin[i], inputs).nValue; + } + return nResult; + +} + +unsigned int CTransaction::GetP2SHSigOpCount(const MapPrevTx& inputs) const +{ + if (IsCoinBase()) + return 0; + + unsigned int nSigOps = 0; + for (unsigned int i = 0; i < vin.size(); i++) + { + const CTxOut& prevout = GetOutputFor(vin[i], inputs); + if (prevout.scriptPubKey.IsPayToScriptHash()) + nSigOps += prevout.scriptPubKey.GetSigOpCount(vin[i].scriptSig); + } + return nSigOps; +} + +bool CScriptCheck::operator()() const { + const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; + if (!VerifyScript(scriptSig, scriptPubKey, *ptxTo, nIn, nFlags, nHashType)) + return error("CScriptCheck() : %s VerifySignature failed", ptxTo->GetHash().ToString().substr(0,10).c_str()); + return true; +} + +bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType) +{ + return CScriptCheck(txFrom, txTo, nIn, flags, nHashType)(); +} + +bool CTransaction::ConnectInputs(CTxDB& txdb, MapPrevTx inputs, map& mapTestPool, const CDiskTxPos& posThisTx, + const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fScriptChecks, unsigned int flags, std::vector *pvChecks) +{ + // Take over previous transactions' spent pointers + // fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain + // fMiner is true when called from the internal bitcoin miner + // ... both are false when called from CTransaction::AcceptToMemoryPool + + if (!IsCoinBase()) + { + int64_t nValueIn = 0; + int64_t nFees = 0; + for (unsigned int i = 0; i < vin.size(); i++) + { + COutPoint prevout = vin[i].prevout; + assert(inputs.count(prevout.hash) > 0); + CTxIndex& txindex = inputs[prevout.hash].first; + CTransaction& txPrev = inputs[prevout.hash].second; + + if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size()) + return DoS(100, error("ConnectInputs() : %s prevout.n out of range %d %" PRIszu " %" PRIszu " 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 or coinstake, check that it's matured + if (txPrev.IsCoinBase() || txPrev.IsCoinStake()) + for (const CBlockIndex* pindex = pindexBlock; pindex && pindexBlock->nHeight - pindex->nHeight < nCoinbaseMaturity; pindex = pindex->pprev) + if (pindex->nBlockPos == txindex.pos.nBlockPos && pindex->nFile == txindex.pos.nFile) + return error("ConnectInputs() : tried to spend %s at depth %d", txPrev.IsCoinBase() ? "coinbase" : "coinstake", pindexBlock->nHeight - pindex->nHeight); + + // ppcoin: check transaction timestamp + if (txPrev.nTime > nTime) + return DoS(100, error("ConnectInputs() : transaction timestamp earlier than input transaction")); + + // Check for negative or overflow input values + nValueIn += txPrev.vout[prevout.n].nValue; + if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) + return DoS(100, error("ConnectInputs() : txin values out of range")); + + } + + if (pvChecks) + pvChecks->reserve(vin.size()); + + // The first loop above does all the inexpensive checks. + // Only if ALL inputs pass do we perform expensive ECDSA signature checks. + // Helps prevent CPU exhaustion attacks. + for (unsigned int i = 0; i < vin.size(); i++) + { + COutPoint prevout = vin[i].prevout; + assert(inputs.count(prevout.hash) > 0); + CTxIndex& txindex = inputs[prevout.hash].first; + CTransaction& txPrev = inputs[prevout.hash].second; + + // Check for conflicts (double-spend) + // This doesn't trigger the DoS code on purpose; if it did, it would make it easier + // for an attacker to attempt to split the network. + 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()); + + // Skip ECDSA signature verification when connecting blocks (fBlock=true) + // before the last blockchain checkpoint. This is safe because block merkle hashes are + // still computed and checked, and any change will be caught at the next checkpoint. + if (fScriptChecks) + { + // Verify signature + CScriptCheck check(txPrev, *this, i, flags, 0); + if (pvChecks) + { + pvChecks->push_back(CScriptCheck()); + check.swap(pvChecks->back()); + } + else if (!check()) + { + if (flags & STRICT_FLAGS) + { + // Don't trigger DoS code in case of STRICT_FLAGS caused failure. + CScriptCheck check(txPrev, *this, i, flags & ~STRICT_FLAGS, 0); + if (check()) + return error("ConnectInputs() : %s strict VerifySignature failed", GetHash().ToString().substr(0,10).c_str()); + } + return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str())); + } + } + + // Mark outpoints as spent + txindex.vSpent[prevout.n] = posThisTx; + + // Write back + if (fBlock || fMiner) + { + mapTestPool[prevout.hash] = txindex; + } + } + + if (IsCoinStake()) + { + if (nTime > Checkpoints::GetLastCheckpointTime()) + { + unsigned int nTxSize = GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION); + + // coin stake tx earns reward instead of paying fee + uint64_t nCoinAge; + if (!GetCoinAge(txdb, nCoinAge)) + return error("ConnectInputs() : %s unable to get coin age for coinstake", GetHash().ToString().substr(0,10).c_str()); + + int64_t nReward = GetValueOut() - nValueIn; + int64_t nCalculatedReward = GetProofOfStakeReward(nCoinAge, pindexBlock->nBits, nTime) - GetMinFee(1, false, GMF_BLOCK, nTxSize) + CENT; + + if (nReward > nCalculatedReward) + return DoS(100, error("ConnectInputs() : coinstake pays too much(actual=%" PRId64 " vs calculated=%" PRId64 ")", nReward, nCalculatedReward)); + } + } + else + { + if (nValueIn < GetValueOut()) + return DoS(100, error("ConnectInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str())); + + // Tally transaction fees + int64_t nTxFee = nValueIn - GetValueOut(); + if (nTxFee < 0) + return DoS(100, error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str())); + + nFees += nTxFee; + if (!MoneyRange(nFees)) + return DoS(100, error("ConnectInputs() : nFees out of range")); + } + } + + return true; +} + + +bool CTransaction::ClientConnectInputs() +{ + if (IsCoinBase()) + return false; + + // Take over previous transactions' spent pointers + { + LOCK(mempool.cs); + int64_t nValueIn = 0; + for (unsigned int i = 0; i < vin.size(); i++) + { + // Get prev tx from single transactions in memory + COutPoint prevout = vin[i].prevout; + if (!mempool.exists(prevout.hash)) + return false; + CTransaction& txPrev = mempool.lookup(prevout.hash); + + if (prevout.n >= txPrev.vout.size()) + return false; + + // Verify signature + if (!VerifySignature(txPrev, *this, i, SCRIPT_VERIFY_NOCACHE | SCRIPT_VERIFY_P2SH, 0)) + return error("ClientConnectInputs() : VerifySignature failed"); + + ///// this is redundant with the mempool.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"); + } + + // ppcoin: clean up wallet after disconnecting coinstake + BOOST_FOREACH(CTransaction& tx, vtx) + SyncWithWallets(tx, this, false, false); + + return true; +} + +static CCheckQueue scriptcheckqueue(128); + +void ThreadScriptCheck(void*) { + vnThreadsRunning[THREAD_SCRIPTCHECK]++; + RenameThread("XP-scriptch"); + scriptcheckqueue.Thread(); + vnThreadsRunning[THREAD_SCRIPTCHECK]--; +} + +void ThreadScriptCheckQuit() { + scriptcheckqueue.Quit(); +} + +bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) +{ + // Check it again in case a previous version let a bad block in, but skip BlockSig checking + if (!CheckBlock(!fJustCheck, !fJustCheck, false)) + return false; + + // Do not allow blocks that contain transactions which 'overwrite' older transactions, + // unless those are already completely spent. + // If such overwrites are allowed, coinbases and transactions depending upon those + // can be duplicated to remove the ability to spend the first instance -- even after + // being sent to another address. + // See BIP30 and http://r6.ca/blog/20120206T005236Z.html for more information. + // This logic is not necessary for memory pool transactions, as AcceptToMemoryPool + // already refuses previously-known transaction ids entirely. + // This rule was originally applied all blocks whose timestamp was after March 15, 2012, 0:00 UTC. + // Now that the whole chain is irreversibly beyond that time it is applied to all blocks except the + // two in the chain that violate it. This prevents exploiting the issue against nodes in their + // initial block download. + bool fEnforceBIP30 = true; // Always active in XP + bool fScriptChecks = pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate(); + + //// issue here: it doesn't know the version + unsigned int nTxPos; + if (fJustCheck) + // FetchInputs treats CDiskTxPos(1,1,1) as a special "refer to memorypool" indicator + // Since we're just checking the block and not actually connecting it, it might not (and probably shouldn't) be on the disk to get the transaction from + nTxPos = 1; + else + nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION) - (2 * GetSizeOfCompactSize(0)) + GetSizeOfCompactSize(vtx.size()); + + map mapQueuedChanges; + CCheckQueueControl control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); + + int64_t nFees = 0; + int64_t nValueIn = 0; + int64_t nValueOut = 0; + unsigned int nSigOps = 0; + BOOST_FOREACH(CTransaction& tx, vtx) + { + uint256 hashTx = tx.GetHash(); + + if (fEnforceBIP30) { + CTxIndex txindexOld; + if (txdb.ReadTxIndex(hashTx, txindexOld)) { + BOOST_FOREACH(CDiskTxPos &pos, txindexOld.vSpent) + if (pos.IsNull()) + return false; + } + } + + nSigOps += tx.GetLegacySigOpCount(); + if (nSigOps > MAX_BLOCK_SIGOPS) + return DoS(100, error("ConnectBlock() : too many sigops")); + + CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos); + if (!fJustCheck) + nTxPos += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); + + MapPrevTx mapInputs; + if (tx.IsCoinBase()) + nValueOut += tx.GetValueOut(); + else + { + bool fInvalid; + if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs, fInvalid)) + return false; + + // Add in sigops done by pay-to-script-hash inputs; + // this is to prevent a "rogue miner" from creating + // an incredibly-expensive-to-validate block. + nSigOps += tx.GetP2SHSigOpCount(mapInputs); + if (nSigOps > MAX_BLOCK_SIGOPS) + return DoS(100, error("ConnectBlock() : too many sigops")); + + int64_t nTxValueIn = tx.GetValueIn(mapInputs); + int64_t nTxValueOut = tx.GetValueOut(); + nValueIn += nTxValueIn; + nValueOut += nTxValueOut; + if (!tx.IsCoinStake()) + nFees += nTxValueIn - nTxValueOut; + + std::vector vChecks; + if (!tx.ConnectInputs(txdb, mapInputs, mapQueuedChanges, posThisTx, pindex, true, false, fScriptChecks, SCRIPT_VERIFY_NOCACHE | SCRIPT_VERIFY_P2SH, nScriptCheckThreads ? &vChecks : NULL)) + return false; + control.Add(vChecks); + } + + mapQueuedChanges[hashTx] = CTxIndex(posThisTx, tx.vout.size()); + } + + if (!control.Wait()) + return DoS(100, false); + + if (IsProofOfWork()) + { + int64_t nBlockReward = GetProofOfWorkReward(nBits, nFees, pindex->pprev->nHeight + 1); + + // Check coinbase reward + if (vtx[0].GetValueOut() > nBlockReward) + return error("CheckBlock() : coinbase reward exceeded (actual=%" PRId64 " vs calculated=%" PRId64 ")", + vtx[0].GetValueOut(), + nBlockReward); + } + + // track money supply and mint amount info + pindex->nMint = nValueOut - nValueIn + nFees; + pindex->nMoneySupply = (pindex->pprev? pindex->pprev->nMoneySupply : 0) + nValueOut - nValueIn; + if (!txdb.WriteBlockIndex(CDiskBlockIndex(pindex))) + return error("Connect() : WriteBlockIndex for pindex failed"); + + // fees are not collected by proof-of-stake miners + // fees are destroyed to compensate the entire network + if (fDebug && IsProofOfStake() && GetBoolArg("-printcreation")) + printf("ConnectBlock() : destroy=%s nFees=%" PRId64 "\n", FormatMoney(nFees).c_str(), nFees); + + if (fJustCheck) + return true; + + // Write queued txindex changes + for (map::iterator mi = mapQueuedChanges.begin(); mi != mapQueuedChanges.end(); ++mi) + { + if (!txdb.UpdateTxIndex((*mi).first, (*mi).second)) + return error("ConnectBlock() : UpdateTxIndex failed"); + } + + // 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) + SyncWithWallets(tx, this, true); + + + return true; +} + +bool static 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()); + + printf("REORGANIZE: Disconnect %" PRIszu " blocks; %s..%s\n", vDisconnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexBest->GetBlockHash().ToString().substr(0,20).c_str()); + printf("REORGANIZE: Connect %" PRIszu " blocks; %s..%s\n", vConnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->GetBlockHash().ToString().substr(0,20).c_str()); + + // 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 %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str()); + + // Queue memory transactions to resurrect + BOOST_FOREACH(const CTransaction& tx, block.vtx) + if (!(tx.IsCoinBase() || tx.IsCoinStake())) + vResurrect.push_back(tx); + } + + // Connect longer branch + vector vDelete; + for (unsigned 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 + return error("Reorganize() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str()); + } + + // 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) + mempool.remove(tx); + + printf("REORGANIZE: done\n"); + + return true; +} + + +// Called from inside SetBestChain: attaches a block to the new best chain being built +bool CBlock::SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew) +{ + uint256 hash = GetHash(); + + // Adding to current best branch + if (!ConnectBlock(txdb, pindexNew) || !txdb.WriteHashBestChain(hash)) + { + txdb.TxnAbort(); + InvalidChainFound(pindexNew); + return false; + } + 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) + mempool.remove(tx); + + return true; +} + +bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) +{ + uint256 hash = GetHash(); + + if (!txdb.TxnBegin()) + return error("SetBestChain() : TxnBegin failed"); + + if (pindexGenesisBlock == NULL && hash == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)) + { + txdb.WriteHashBestChain(hash); + if (!txdb.TxnCommit()) + return error("SetBestChain() : TxnCommit failed"); + pindexGenesisBlock = pindexNew; + } + else if (hashPrevBlock == hashBestChain) + { + if (!SetBestChainInner(txdb, pindexNew)) + return error("SetBestChain() : SetBestChainInner failed"); + } + else + { + // the first block in the new chain that will cause it to become the new best chain + CBlockIndex *pindexIntermediate = pindexNew; + + // list of blocks that need to be connected afterwards + std::vector vpindexSecondary; + + // Reorganize is costly in terms of db load, as it works in a single db transaction. + // Try to limit how much needs to be done inside + while (pindexIntermediate->pprev && pindexIntermediate->pprev->nChainTrust > pindexBest->nChainTrust) + { + vpindexSecondary.push_back(pindexIntermediate); + pindexIntermediate = pindexIntermediate->pprev; + } + + if (!vpindexSecondary.empty()) + printf("Postponing %" PRIszu " reconnects\n", vpindexSecondary.size()); + + // Switch to new best branch + if (!Reorganize(txdb, pindexIntermediate)) + { + txdb.TxnAbort(); + InvalidChainFound(pindexNew); + return error("SetBestChain() : Reorganize failed"); + } + + // Connect further blocks + BOOST_REVERSE_FOREACH(CBlockIndex *pindex, vpindexSecondary) + { + CBlock block; + if (!block.ReadFromDisk(pindex)) + { + printf("SetBestChain() : ReadFromDisk failed\n"); + break; + } + if (!txdb.TxnBegin()) { + printf("SetBestChain() : TxnBegin 2 failed\n"); + break; + } + // errors now are not fatal, we still did a reorganisation to a new chain in a valid way + if (!block.SetBestChainInner(txdb, pindex)) + break; + } + } + + // Update best block in wallet (so we can detect restored wallets) + bool fIsInitialDownload = IsInitialBlockDownload(); + if (!fIsInitialDownload) + { + const CBlockLocator locator(pindexNew); + ::SetBestChain(locator); + } + + // New best block + hashBestChain = hash; + pindexBest = pindexNew; + pblockindexFBBHLast = NULL; + nBestHeight = pindexBest->nHeight; + nBestChainTrust = pindexNew->nChainTrust; + nTimeBestReceived = GetTime(); + nTransactionsUpdated++; + + uint256 nBestBlockTrust = pindexBest->nHeight != 0 ? (pindexBest->nChainTrust - pindexBest->pprev->nChainTrust) : pindexBest->nChainTrust; + + printf("SetBestChain: new best=%s height=%d trust=%s blocktrust=%" PRId64 " date=%s\n", + hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, + CBigNum(nBestChainTrust).ToString().c_str(), + nBestBlockTrust.Get64(), + DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str()); + + // Check the version of the last 100 blocks to see if we need to upgrade: + if (!fIsInitialDownload) + { + int nUpgraded = 0; + const CBlockIndex* pindex = pindexBest; + for (int i = 0; i < 100 && pindex != NULL; i++) + { + if (pindex->nVersion > CBlock::CURRENT_VERSION) + ++nUpgraded; + pindex = pindex->pprev; + } + if (nUpgraded > 0) + printf("SetBestChain: %d of last 100 blocks above version %d\n", nUpgraded, CBlock::CURRENT_VERSION); + if (nUpgraded > 100/2) + // strMiscWarning is read by GetWarnings(), called by Qt and the JSON-RPC code to warn the user: + strMiscWarning = _("Warning: This version is obsolete, upgrade required!"); + } + + std::string strCmd = GetArg("-blocknotify", ""); + + if (!fIsInitialDownload && !strCmd.empty()) + { + boost::replace_all(strCmd, "%s", hashBestChain.GetHex()); + boost::thread t(runCommand, strCmd); // thread runs free + } + + return true; +} + +// ppcoin: total coin age spent in transaction, in the unit of coin-days. +// Only those coins meeting minimum age requirement counts. As those +// transactions not in main chain are not currently indexed so we +// might not find out about their coin age. Older transactions are +// guaranteed to be in main chain by sync-checkpoint. This rule is +// introduced to help nodes establish a consistent view of the coin +// age (trust score) of competing branches. +bool CTransaction::GetCoinAge(CTxDB& txdb, uint64_t& nCoinAge) const +{ + CBigNum bnCentSecond = 0; // coin age in the unit of cent-seconds + nCoinAge = 0; + + if (IsCoinBase()) + return true; + + BOOST_FOREACH(const CTxIn& txin, vin) + { + // First try finding the previous transaction in database + CTransaction txPrev; + CTxIndex txindex; + if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex)) + continue; // previous transaction not in main chain + if (nTime < txPrev.nTime) + return false; // Transaction timestamp violation + + // Read block header + CBlock block; + if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) + return false; // unable to read block of previous transaction + if (block.GetBlockTime() + nStakeMinAge > nTime) + continue; // only count coins meeting min age requirement + + int64_t nValueIn = txPrev.vout[txin.prevout.n].nValue; + bnCentSecond += CBigNum(nValueIn) * (nTime-txPrev.nTime) / CENT; + + if (fDebug && GetBoolArg("-printcoinage")) + printf("coin age nValueIn=%" PRId64 " nTimeDiff=%d bnCentSecond=%s\n", nValueIn, nTime - txPrev.nTime, bnCentSecond.ToString().c_str()); + } + + CBigNum bnCoinDay = bnCentSecond * CENT / COIN / nOneDay; + if (fDebug && GetBoolArg("-printcoinage")) + printf("coin age bnCoinDay=%s\n", bnCoinDay.ToString().c_str()); + nCoinAge = bnCoinDay.getuint64(); + return true; +} + +// ppcoin: total coin age spent in block, in the unit of coin-days. +bool CBlock::GetCoinAge(uint64_t& nCoinAge) const +{ + nCoinAge = 0; + + CTxDB txdb("r"); + BOOST_FOREACH(const CTransaction& tx, vtx) + { + uint64_t nTxCoinAge; + if (tx.GetCoinAge(txdb, nTxCoinAge)) + nCoinAge += nTxCoinAge; + else + return false; + } + + if (nCoinAge == 0) // block coin age minimum 1 coin-day + nCoinAge = 1; + if (fDebug && GetBoolArg("-printcoinage")) + printf("block coin age total nCoinDays=%" PRId64 "\n", nCoinAge); + 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"); + pindexNew->phashBlock = &hash; + map::iterator miPrev = mapBlockIndex.find(hashPrevBlock); + if (miPrev != mapBlockIndex.end()) + { + pindexNew->pprev = (*miPrev).second; + pindexNew->nHeight = pindexNew->pprev->nHeight + 1; + } + + // ppcoin: compute chain trust score + pindexNew->nChainTrust = (pindexNew->pprev ? pindexNew->pprev->nChainTrust : 0) + pindexNew->GetBlockTrust(); + + // ppcoin: compute stake entropy bit for stake modifier + if (!pindexNew->SetStakeEntropyBit(GetStakeEntropyBit(pindexNew->nHeight))) + return error("AddToBlockIndex() : SetStakeEntropyBit() failed"); + + // ppcoin: record proof-of-stake hash value + if (pindexNew->IsProofOfStake()) + { + if (!mapProofOfStake.count(hash)) + return error("AddToBlockIndex() : hashProofOfStake not found in map"); + pindexNew->hashProofOfStake = mapProofOfStake[hash]; + } + + // ppcoin: compute stake modifier + uint64_t nStakeModifier = 0; + bool fGeneratedStakeModifier = false; + if (!ComputeNextStakeModifier(pindexNew, nStakeModifier, fGeneratedStakeModifier)) + return error("AddToBlockIndex() : ComputeNextStakeModifier() failed"); + pindexNew->SetStakeModifier(nStakeModifier, fGeneratedStakeModifier); + pindexNew->nStakeModifierChecksum = GetStakeModifierChecksum(pindexNew); + if (!CheckStakeModifierCheckpoints(pindexNew->nHeight, pindexNew->nStakeModifierChecksum)) + return error("AddToBlockIndex() : Rejected by stake modifier checkpoint height=%d, modifier=0x%016" PRIx64, pindexNew->nHeight, nStakeModifier); + + // Add to mapBlockIndex + map::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; + if (pindexNew->IsProofOfStake()) + setStakeSeen.insert(make_pair(pindexNew->prevoutStake, pindexNew->nStakeTime)); + pindexNew->phashBlock = &((*mi).first); + + // Write to disk block index + CTxDB txdb; + if (!txdb.TxnBegin()) + return false; + txdb.WriteBlockIndex(CDiskBlockIndex(pindexNew)); + if (!txdb.TxnCommit()) + return false; + + // New best + if (pindexNew->nChainTrust > nBestChainTrust) + if (!SetBestChain(txdb, pindexNew)) + return false; + + if (pindexNew == pindexBest) + { + // Notify UI to display prev block's coinbase if it was ours + static uint256 hashPrevBestCoinBase; + UpdatedTransaction(hashPrevBestCoinBase); + hashPrevBestCoinBase = vtx[0].GetHash(); + } + + static int8_t counter = 0; + if( (++counter & 0x0F) == 0 || !IsInitialBlockDownload()) // repaint every 16 blocks if not in initial block download + uiInterface.NotifyBlocksChanged(); + return true; +} + + + + +bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot, bool fCheckSig) const +{ + // These are checks that are independent of context + // that can be verified before saving an orphan block. + + set uniqueTx; // tx hashes + unsigned int nSigOps = 0; // total sigops + + // Size limits + if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) + return DoS(100, error("CheckBlock() : size limits failed")); + + bool fProofOfStake = IsProofOfStake(); + + // First transaction must be coinbase, the rest must not be + if (!vtx[0].IsCoinBase()) + return DoS(100, error("CheckBlock() : first tx is not coinbase")); + + if (!vtx[0].CheckTransaction()) + return DoS(vtx[0].nDoS, error("CheckBlock() : CheckTransaction failed on coinbase")); + + uniqueTx.insert(vtx[0].GetHash()); + nSigOps += vtx[0].GetLegacySigOpCount(); + + if (fProofOfStake) + { + // Proof-of-STake related checkings. Note that we know here that 1st transactions is coinstake. We don't need + // check the type of 1st transaction because it's performed earlier by IsProofOfStake() + + // nNonce must be zero for proof-of-stake blocks + if (nNonce != 0) + return DoS(100, error("CheckBlock() : non-zero nonce in proof-of-stake block")); + + // Coinbase output should be empty if proof-of-stake block + if (vtx[0].vout.size() != 1 || !vtx[0].vout[0].IsEmpty()) + return DoS(100, error("CheckBlock() : coinbase output not empty for proof-of-stake block")); + + // Check coinstake timestamp + if (GetBlockTime() != (int64_t)vtx[1].nTime) + return DoS(50, error("CheckBlock() : coinstake timestamp violation nTimeBlock=%" PRId64 " nTimeTx=%u", GetBlockTime(), vtx[1].nTime)); + + // XP: check proof-of-stake block signature + if (fCheckSig && !CheckBlockSignature()) + return DoS(100, error("CheckBlock() : bad proof-of-stake block signature")); + + if (!vtx[1].CheckTransaction()) + return DoS(vtx[1].nDoS, error("CheckBlock() : CheckTransaction failed on coinstake")); + + uniqueTx.insert(vtx[1].GetHash()); + nSigOps += vtx[1].GetLegacySigOpCount(); + } + else + { + // Check proof of work matches claimed amount + if (fCheckPOW && !CheckProofOfWork(GetHash(), nBits)) + return DoS(50, error("CheckBlock() : proof of work failed")); + + // Check timestamp + if (GetBlockTime() > FutureDrift(GetAdjustedTime())) + return error("CheckBlock() : block timestamp too far in the future"); + + // Check coinbase timestamp + if (GetBlockTime() < PastDrift((int64_t)vtx[0].nTime)) + return DoS(50, error("CheckBlock() : coinbase timestamp is too late")); + } + + // Iterate all transactions starting from second for proof-of-stake block + // or first for proof-of-work block + for (unsigned int i = fProofOfStake ? 2 : 1; i < vtx.size(); i++) + { + const CTransaction& tx = vtx[i]; + + // Reject coinbase transactions at non-zero index + if (tx.IsCoinBase()) + return DoS(100, error("CheckBlock() : coinbase at wrong index")); + + // Reject coinstake transactions at index != 1 + if (tx.IsCoinStake()) + return DoS(100, error("CheckBlock() : coinstake at wrong index")); + + // Check transaction timestamp + if (GetBlockTime() < (int64_t)tx.nTime) + return DoS(50, error("CheckBlock() : block timestamp earlier than transaction timestamp")); + + // Check transaction consistency + if (!tx.CheckTransaction()) + return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed")); + + // Add transaction hash into list of unique transaction IDs + uniqueTx.insert(tx.GetHash()); + + // Calculate sigops count + nSigOps += tx.GetLegacySigOpCount(); + } + + // Check for duplicate txids. This is caught by ConnectInputs(), + // but catching it earlier avoids a potential DoS attack: + if (uniqueTx.size() != vtx.size()) + return DoS(100, error("CheckBlock() : duplicate transaction")); + + // Reject block if validation would consume too much resources. + if (nSigOps > MAX_BLOCK_SIGOPS) + return DoS(100, error("CheckBlock() : out-of-bounds SigOpCount")); + + // Check merkle root + if (fCheckMerkleRoot && hashMerkleRoot != BuildMerkleTree()) + return DoS(100, 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 DoS(10, error("AcceptBlock() : prev block not found")); + CBlockIndex* pindexPrev = (*mi).second; + int nHeight = pindexPrev->nHeight+1; + + if (IsProofOfWork() && nHeight > LAST_POW_BLOCK) + return DoS(100, error("AcceptBlock() : reject proof-of-work at height %d", nHeight)); + + // Check proof-of-work or proof-of-stake + if (nBits != GetNextTargetRequired(pindexPrev, IsProofOfStake())) + return DoS(100, error("AcceptBlock() : incorrect %s", IsProofOfWork() ? "proof-of-work" : "proof-of-stake")); + + int64_t nMedianTimePast = pindexPrev->GetMedianTimePast(); + int64_t nMaxOffset = 4 * 86400; // Four days + + // Check timestamp against prev + if (GetBlockTime() <= nMedianTimePast || FutureDrift(GetBlockTime()) < pindexPrev->GetBlockTime()) + return error("AcceptBlock() : block's timestamp is too early"); + + // Don't accept blocks with future timestamps + if (pindexPrev->nHeight > 50000 && nMedianTimePast + nMaxOffset < GetBlockTime()) + return error("AcceptBlock() : block's timestamp is too far in the future"); + + // Check that all transactions are finalized + BOOST_FOREACH(const CTransaction& tx, vtx) + if (!tx.IsFinal(nHeight, GetBlockTime())) + return DoS(10, error("AcceptBlock() : contains a non-final transaction")); + + // Check that the block chain matches the known block chain up to a checkpoint + if (!Checkpoints::CheckHardened(nHeight, hash)) + return DoS(100, error("AcceptBlock() : rejected by hardened checkpoint lock-in at %d", nHeight)); + + bool cpSatisfies = Checkpoints::CheckSync(hash, pindexPrev); + + // Check that the block satisfies synchronized checkpoint + if (CheckpointsMode == Checkpoints::STRICT && !cpSatisfies) + return error("AcceptBlock() : rejected by synchronized checkpoint"); + + if (CheckpointsMode == Checkpoints::ADVISORY && !cpSatisfies) + strMiscWarning = _("WARNING: syncronized checkpoint violation detected, but skipped!"); + + // Enforce rule that the coinbase starts with serialized block height + CScript expect = CScript() << nHeight; + if (vtx[0].vin[0].scriptSig.size() < expect.size() || + !std::equal(expect.begin(), expect.end(), vtx[0].vin[0].scriptSig.begin())) + return DoS(100, error("AcceptBlock() : block height mismatch in coinbase")); + + // Write block to history file + if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION))) + return error("AcceptBlock() : out of disk space"); + unsigned int nFile = std::numeric_limits::max(); + 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 + int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(); + if (hashBestChain == hash) + { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) + pnode->PushInventory(CInv(MSG_BLOCK, hash)); + } + + // ppcoin: check pending sync-checkpoint + Checkpoints::AcceptPendingSyncCheckpoint(); + + return true; +} + +uint256 CBlockIndex::GetBlockTrust() const +{ + CBigNum bnTarget; + bnTarget.SetCompact(nBits); + + if (bnTarget <= 0) + return 0; + + // Return 1 for the first 12 blocks + if (pprev == NULL || pprev->nHeight < 12) + return 1; + + const CBlockIndex* currentIndex = pprev; + + if(IsProofOfStake()) + { + CBigNum bnNewTrust = (CBigNum(1)<<256) / (bnTarget+1); + + // Return 1/3 of score if parent block is not the PoW block + if (!pprev->IsProofOfWork()) + return (bnNewTrust / 3).getuint256(); + + int nPoWCount = 0; + + // Check last 12 blocks type + while (pprev->nHeight - currentIndex->nHeight < 12) + { + if (currentIndex->IsProofOfWork()) + nPoWCount++; + currentIndex = currentIndex->pprev; + } + + // Return 1/3 of score if less than 3 PoW blocks found + if (nPoWCount < 3) + return (bnNewTrust / 3).getuint256(); + + return bnNewTrust.getuint256(); + } + else + { + // Calculate work amount for block + CBigNum bnPoWTrust = CBigNum(nPoWBase) / (bnTarget+1); + + // Set nPowTrust to 1 if PoW difficulty is too low + if (bnPoWTrust < 1) + bnPoWTrust = 1; + + CBigNum bnLastBlockTrust = CBigNum(pprev->nChainTrust - pprev->pprev->nChainTrust); + + // Return nPoWTrust + 2/3 of previous block score if two parent blocks are not PoS blocks + if (!(pprev->IsProofOfStake() && pprev->pprev->IsProofOfStake())) + return (bnPoWTrust + 2 * bnLastBlockTrust / 3).getuint256(); + + int nPoSCount = 0; + + // Check last 12 blocks type + while (pprev->nHeight - currentIndex->nHeight < 12) + { + if (currentIndex->IsProofOfStake()) + nPoSCount++; + currentIndex = currentIndex->pprev; + } + + // Return nPoWTrust + 2/3 of previous block score if less than 7 PoS blocks found + if (nPoSCount < 7) + return (bnPoWTrust + 2 * bnLastBlockTrust / 3).getuint256(); + + bnTarget.SetCompact(pprev->nBits); + + if (bnTarget <= 0) + return 0; + + CBigNum bnNewTrust = (CBigNum(1)<<256) / (bnTarget+1); + + // Return nPoWTrust + full trust score for previous block nBits + return (bnPoWTrust + bnNewTrust).getuint256(); + } +} + +bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired, unsigned int nToCheck) +{ + unsigned int nFound = 0; + for (unsigned int i = 0; i < nToCheck && nFound < nRequired && pstart != NULL; i++) + { + if (pstart->nVersion >= minVersion) + ++nFound; + pstart = pstart->pprev; + } + return (nFound >= nRequired); +} + +bool static ReserealizeBlockSignature(CBlock* pblock) +{ + if (pblock->IsProofOfWork()) + { + pblock->vchBlockSig.clear(); + return true; + } + + return CKey::ReserealizeSignature(pblock->vchBlockSig); +} + +bool static IsCanonicalBlockSignature(CBlock* pblock) +{ + if (pblock->IsProofOfWork()) + return pblock->vchBlockSig.empty(); + + return IsDERSignature(pblock->vchBlockSig); +} + +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()); + + // Check proof-of-stake + // Limited duplicity on stake: prevents block flood attack + // Duplicate stake allowed only when there is orphan child block + if (pblock->IsProofOfStake() && setStakeSeen.count(pblock->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash) && !Checkpoints::WantedByPendingSyncCheckpoint(hash)) + return error("ProcessBlock() : duplicate proof-of-stake (%s, %d) for block %s", pblock->GetProofOfStake().first.ToString().c_str(), pblock->GetProofOfStake().second, hash.ToString().c_str()); + + // Strip the garbage from newly received blocks, if we found some + if (!IsCanonicalBlockSignature(pblock)) { + if (!ReserealizeBlockSignature(pblock)) + printf("WARNING: ProcessBlock() : ReserealizeBlockSignature FAILED\n"); + } + + // Preliminary checks + if (!pblock->CheckBlock(true, true, (pblock->nTime > Checkpoints::GetLastCheckpointTime()))) + return error("ProcessBlock() : CheckBlock FAILED"); + + // ppcoin: verify hash target and signature of coinstake tx + if (pblock->IsProofOfStake()) + { + uint256 hashProofOfStake = 0, targetProofOfStake = 0; + if (!CheckProofOfStake(pblock->vtx[1], pblock->nBits, hashProofOfStake, targetProofOfStake)) + { + printf("WARNING: ProcessBlock(): check proof-of-stake failed for block %s\n", hash.ToString().c_str()); + return false; // do not error here as we expect this during initial block download + } + if (!mapProofOfStake.count(hash)) // add to mapProofOfStake + mapProofOfStake.insert(make_pair(hash, hashProofOfStake)); + } + + CBlockIndex* pcheckpoint = Checkpoints::GetLastSyncCheckpoint(); + if (pcheckpoint && pblock->hashPrevBlock != hashBestChain && !Checkpoints::WantedByPendingSyncCheckpoint(hash)) + { + // Extra checks to prevent "fill up memory by spamming with bogus blocks" + int64_t deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime; + CBigNum bnNewBlock; + bnNewBlock.SetCompact(pblock->nBits); + CBigNum bnRequired; + + if (pblock->IsProofOfStake()) + bnRequired.SetCompact(ComputeMinStake(GetLastBlockIndex(pcheckpoint, true)->nBits, deltaTime, pblock->nTime)); + else + bnRequired.SetCompact(ComputeMinWork(GetLastBlockIndex(pcheckpoint, false)->nBits, deltaTime)); + + if (bnNewBlock > bnRequired) + { + if (pfrom) + pfrom->Misbehaving(100); + return error("ProcessBlock() : block with too little %s", pblock->IsProofOfStake()? "proof-of-stake" : "proof-of-work"); + } + } + + // ppcoin: ask for pending sync-checkpoint if any + if (!IsInitialBlockDownload()) + Checkpoints::AskForPendingSyncCheckpoint(pfrom); + + // 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()); + // ppcoin: check proof-of-stake + if (pblock->IsProofOfStake()) + { + // Limited duplicity on stake: prevents block flood attack + // Duplicate stake allowed only when there is orphan child block + if (setStakeSeenOrphan.count(pblock->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash) && !Checkpoints::WantedByPendingSyncCheckpoint(hash)) + return error("ProcessBlock() : duplicate proof-of-stake (%s, %d) for orphan block %s", pblock->GetProofOfStake().first.ToString().c_str(), pblock->GetProofOfStake().second, hash.ToString().c_str()); + else + setStakeSeenOrphan.insert(pblock->GetProofOfStake()); + } + 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)); + // ppcoin: getblocks may not obtain the ancestor block rejected + // earlier by duplicate-stake check so we ask for it again directly + if (!IsInitialBlockDownload()) + pfrom->AskFor(CInv(MSG_BLOCK, WantedByOrphan(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 (unsigned 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()); + setStakeSeenOrphan.erase(pblockOrphan->GetProofOfStake()); + delete pblockOrphan; + } + mapOrphanBlocksByPrev.erase(hashPrev); + } + + printf("ProcessBlock: ACCEPTED\n"); + + // ppcoin: if responsible for sync-checkpoint send it + if (pfrom && !CSyncCheckpoint::strMasterPrivKey.empty()) + Checkpoints::SendSyncCheckpoint(Checkpoints::AutoSelectSyncCheckpoint()); + + return true; +} + +// ppcoin: check block signature +bool CBlock::CheckBlockSignature() const +{ + if (vchBlockSig.empty()) + return false; + + txnouttype whichType; + vector vSolutions; + if (!Solver(vtx[1].vout[1].scriptPubKey, whichType, vSolutions)) + return false; + + if (whichType == TX_PUBKEY) + { + valtype& vchPubKey = vSolutions[0]; + CKey key; + if (!key.SetPubKey(vchPubKey)) + return false; + return key.Verify(GetHash(), vchBlockSig); + } + + return false; +} + +bool CheckDiskSpace(uint64_t nAdditionalBytes) +{ + uint64_t nFreeBytesAvailable = filesystem::space(GetDataDir()).available; + + // Check for nMinDiskSpace bytes (currently 50MB) + if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes) + { + fShutdown = true; + string strMessage = _("Warning: Disk space is low!"); + strMiscWarning = strMessage; + printf("*** %s\n", strMessage.c_str()); + uiInterface.ThreadSafeMessageBox(strMessage, "XP", CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION | CClientUIInterface::MODAL); + StartShutdown(); + return false; + } + return true; +} + +static filesystem::path BlockFilePath(unsigned int nFile) +{ + string strBlockFn = strprintf("blk%04u.dat", nFile); + return GetDataDir() / strBlockFn; +} + +FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode) +{ + if ((nFile < 1) || (nFile == std::numeric_limits::max())) + return NULL; + FILE* file = fopen(BlockFilePath(nFile).string().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; + while (true) + { + FILE* file = OpenBlockFile(nCurrentBlockFile, 0, "ab"); + if (!file) + return NULL; + if (fseek(file, 0, SEEK_END) != 0) + return NULL; + // FAT32 file size max 4GB, fseek and ftell max 2GB, so we must stay under 2GB + if (ftell(file) < (long)(0x7F000000 - MAX_SIZE)) + { + nFileRet = nCurrentBlockFile; + return file; + } + fclose(file); + nCurrentBlockFile++; + } +} + +void UnloadBlockIndex() +{ + mapBlockIndex.clear(); + setStakeSeen.clear(); + pindexGenesisBlock = NULL; + nBestHeight = 0; + nBestChainTrust = 0; + nBestInvalidTrust = 0; + hashBestChain = 0; + pindexBest = NULL; +} + +bool LoadBlockIndex(bool fAllowNew) +{ + if (fTestNet) + { + pchMessageStart[0] = 0xcd; + pchMessageStart[1] = 0xf2; + pchMessageStart[2] = 0xc0; + pchMessageStart[3] = 0xef; + + bnProofOfWorkLimit = bnProofOfWorkLimitTestNet; // 16 bits PoW target limit for testnet + nStakeMinAge = 2 * nOneHour; // test net min age is 2 hours + nModifierInterval = 20 * 60; // test modifier interval is 20 minutes + nCoinbaseMaturity = 10; // test maturity is 10 blocks + nStakeTargetSpacing = 5 * 60; // test block spacing is 5 minutes + } + + // + // Load block index + // + CTxDB txdb("cr+"); + if (!txdb.LoadBlockIndex()) + return false; + + // + // Init with genesis block + // + if (mapBlockIndex.empty()) + { + if (!fAllowNew) + return false; + + const char* pszTimestamp = "XP Genesis"; + CTransaction txNew; + txNew.nTime = 1472006325; + txNew.vin.resize(1); + txNew.vout.resize(1); + txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(9999) << vector((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp)); + txNew.vout[0].SetEmpty(); + CBlock block; + block.vtx.push_back(txNew); + block.hashPrevBlock = 0; + block.hashMerkleRoot = block.BuildMerkleTree(); + block.nVersion = 1; + block.nTime = 1472006328; + block.nBits = bnProofOfWorkLimit.GetCompact(); + block.nNonce = 261097; // !fTestNet ? 1575379 : 46534; + + + block.print(); + + //// debug print + assert(block.hashMerkleRoot == uint256("0xcc83c2272fa6b9d6fe6c322c9730bcae47278195a2d64a9d2f796bfb41eec001")); + assert(block.GetHash() == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)); + assert(block.CheckBlock()); + + // 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"); + + // initialize synchronized checkpoint + if (!Checkpoints::WriteSyncCheckpoint((!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet))) + return error("LoadBlockIndex() : failed to init sync checkpoint"); + + // upgrade time set to zero if txdb initialized + { + if (!txdb.WriteModifierUpgradeTime(0)) + return error("LoadBlockIndex() : failed to init upgrade info"); + printf(" Upgrade Info: ModifierUpgradeTime txdb initialization\n"); + } + } + + { + CTxDB txdb("r+"); + string strPubKey = ""; + if (!txdb.ReadCheckpointPubKey(strPubKey) || strPubKey != CSyncCheckpoint::strMasterPubKey) + { + // write checkpoint master key to db + txdb.TxnBegin(); + if (!txdb.WriteCheckpointPubKey(CSyncCheckpoint::strMasterPubKey)) + return error("LoadBlockIndex() : failed to write new checkpoint master key to db"); + if (!txdb.TxnCommit()) + return error("LoadBlockIndex() : failed to commit new checkpoint master key to db"); + if ((!fTestNet) && !Checkpoints::ResetSyncCheckpoint()) + return error("LoadBlockIndex() : failed to reset sync-checkpoint"); + } + + // upgrade time set to zero if blocktreedb initialized + if (txdb.ReadModifierUpgradeTime(nModifierUpgradeTime)) + { + if (nModifierUpgradeTime) + printf(" Upgrade Info: blocktreedb upgrade detected at timestamp %d\n", nModifierUpgradeTime); + else + printf(" Upgrade Info: no blocktreedb upgrade detected.\n"); + } + else + { + nModifierUpgradeTime = GetTime(); + printf(" Upgrade Info: upgrading blocktreedb at timestamp %u\n", nModifierUpgradeTime); + if (!txdb.WriteModifierUpgradeTime(nModifierUpgradeTime)) + return error("LoadBlockIndex() : failed to write upgrade info"); + } + +#ifndef USE_LEVELDB + txdb.Close(); +#endif + } + + return true; +} + + + +void PrintBlockTree() +{ + // pre-compute 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 %08x %s mint %7s tx %" PRIszu "", + pindex->nHeight, + pindex->nFile, + pindex->nBlockPos, + block.GetHash().ToString().c_str(), + block.nBits, + DateTimeStrFormat("%x %H:%M:%S", block.GetBlockTime()).c_str(), + FormatMoney(pindex->nMint).c_str(), + block.vtx.size()); + + PrintWallets(block); + + // put the main time-chain first + vector& vNext = mapNext[pindex]; + for (unsigned int i = 0; i < vNext.size(); i++) + { + if (vNext[i]->pnext) + { + swap(vNext[0], vNext[i]); + break; + } + } + + // iterate children + for (unsigned int i = 0; i < vNext.size(); i++) + vStack.push_back(make_pair(nCol+i, vNext[i])); + } +} + +bool LoadExternalBlockFile(FILE* fileIn) +{ + int64_t nStart = GetTimeMillis(); + + int nLoaded = 0; + { + LOCK(cs_main); + try { + CAutoFile blkdat(fileIn, SER_DISK, CLIENT_VERSION); + unsigned int nPos = 0; + while (nPos != std::numeric_limits::max() && blkdat.good() && !fRequestShutdown) + { + unsigned char pchData[65536]; + do { + fseek(blkdat, nPos, SEEK_SET); + int nRead = fread(pchData, 1, sizeof(pchData), blkdat); + if (nRead <= 8) + { + nPos = std::numeric_limits::max(); + break; + } + void* nFind = memchr(pchData, pchMessageStart[0], nRead+1-sizeof(pchMessageStart)); + if (nFind) + { + if (memcmp(nFind, pchMessageStart, sizeof(pchMessageStart))==0) + { + nPos += ((unsigned char*)nFind - pchData) + sizeof(pchMessageStart); + break; + } + nPos += ((unsigned char*)nFind - pchData) + 1; + } + else + nPos += sizeof(pchData) - sizeof(pchMessageStart) + 1; + } while(!fRequestShutdown); + if (nPos == std::numeric_limits::max()) + break; + fseek(blkdat, nPos, SEEK_SET); + unsigned int nSize; + blkdat >> nSize; + if (nSize > 0 && nSize <= MAX_BLOCK_SIZE) + { + CBlock block; + blkdat >> block; + if (ProcessBlock(NULL,&block)) + { + nLoaded++; + nPos += 4 + nSize; + } + } + } + } + catch (const std::exception&) { + printf("%s() : Deserialize or I/O error caught during load\n", + BOOST_CURRENT_FUNCTION); + } + } + printf("Loaded %i blocks from external file in %" PRId64 "ms\n", nLoaded, GetTimeMillis() - nStart); + return nLoaded > 0; +} + +////////////////////////////////////////////////////////////////////////////// +// +// CAlert +// + +extern map mapAlerts; +extern 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; + } + + // if detected unmet upgrade requirement enter safe mode + // Note: Modifier upgrade requires blockchain redownload if past protocol switch + if (IsFixedModifierInterval(nModifierUpgradeTime + nOneDay)) // 1 day margin + { + nPriority = 5000; + strStatusBar = strRPC = "WARNING: Blockchain redownload required approaching or past v.0.4.4.6u4 upgrade deadline."; + } + + // if detected invalid checkpoint enter safe mode + if (Checkpoints::hashInvalidCheckpoint != 0) + { + nPriority = 3000; + strStatusBar = strRPC = _("WARNING: Invalid checkpoint found! Displayed transactions may not be correct! You may need to upgrade, or notify developers."); + } + + // Alerts + { + LOCK(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 (nPriority > 1000) + strRPC = strStatusBar; + } + } + } + + if (strFor == "statusbar") + return strStatusBar; + else if (strFor == "rpc") + return strRPC; + assert(!"GetWarnings() : invalid parameter"); + return "error"; +} + + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// Messages +// + + +bool static AlreadyHave(CTxDB& txdb, const CInv& inv) +{ + switch (inv.type) + { + case MSG_TX: + { + bool txInMap = false; + { + LOCK(mempool.cs); + txInMap = (mempool.exists(inv.hash)); + } + return txInMap || + 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. +unsigned char pchMessageStart[4] = { 0xb4, 0xf8, 0xe2, 0xe5 }; + +bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) +{ + static map mapReuseKey; + RandAddSeedPerfmon(); + if (fDebug) + printf("received: %s (%" PRIszu " 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) + { + pfrom->Misbehaving(1); + return false; + } + + int64_t nTime; + CAddress addrMe; + CAddress addrFrom; + uint64_t nNonce = 1; + vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe; + if (pfrom->nVersion < MIN_PROTO_VERSION) + { + // Since February 20, 2012, the protocol is initiated at version 209, + // and earlier versions are no longer supported + printf("partner %s using obsolete version %i; disconnecting\n", pfrom->addr.ToString().c_str(), pfrom->nVersion); + pfrom->fDisconnect = true; + return false; + } + + if (pfrom->nVersion == 10300) + pfrom->nVersion = 300; + if (!vRecv.empty()) + vRecv >> addrFrom >> nNonce; + if (!vRecv.empty()) + vRecv >> pfrom->strSubVer; + if (!vRecv.empty()) + vRecv >> pfrom->nStartingHeight; + + if (pfrom->fInbound && addrMe.IsRoutable()) + { + pfrom->addrLocal = addrMe; + SeenLocal(addrMe); + } + + // 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; + } + + if (pfrom->nVersion < 60010) + { + printf("partner %s using a buggy client %d, disconnecting\n", pfrom->addr.ToString().c_str(), pfrom->nVersion); + pfrom->fDisconnect = true; + return true; + } + + // record my external IP reported by peer + if (addrFrom.IsRoutable() && addrMe.IsRoutable()) + addrSeenByPeer = addrMe; + + // Be shy and don't send version until we hear + if (pfrom->fInbound) + pfrom->PushVersion(); + + pfrom->fClient = !(pfrom->nServices & NODE_NETWORK); + + AddTimeData(pfrom->addr, nTime); + + // Change version + pfrom->PushMessage("verack"); + pfrom->vSend.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); + + if (!pfrom->fInbound) + { + // Advertise our address + if (!fNoListen && !IsInitialBlockDownload()) + { + CAddress addr = GetLocalAddress(&pfrom->addr); + if (addr.IsRoutable()) + pfrom->PushAddress(addr); + } + + // Get recent addresses + if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || addrman.size() < 1000) + { + pfrom->PushMessage("getaddr"); + pfrom->fGetAddr = true; + } + addrman.Good(pfrom->addr); + } else { + if (((CNetAddr)pfrom->addr) == (CNetAddr)addrFrom) + { + addrman.Add(addrFrom, addrFrom); + addrman.Good(addrFrom); + } + } + + // Ask the first connected node for block updates + static int nAskedForBlocks = 0; + if (!pfrom->fClient && !pfrom->fOneShot && + (pfrom->nStartingHeight > (nBestHeight - 144)) && + (pfrom->nVersion < NOBLKS_VERSION_START || + pfrom->nVersion >= NOBLKS_VERSION_END) && + (nAskedForBlocks < 1 || vNodes.size() <= 1)) + { + nAskedForBlocks++; + pfrom->PushGetBlocks(pindexBest, uint256(0)); + } + + // Relay alerts + { + LOCK(cs_mapAlerts); + BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) + item.second.RelayTo(pfrom); + } + + // Relay sync-checkpoint + { + LOCK(Checkpoints::cs_hashSyncCheckpoint); + if (!Checkpoints::checkpointMessage.IsNull()) + Checkpoints::checkpointMessage.RelayTo(pfrom); + } + + pfrom->fSuccessfullyConnected = true; + + printf("receive version message: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", pfrom->nVersion, pfrom->nStartingHeight, addrMe.ToString().c_str(), addrFrom.ToString().c_str(), pfrom->addr.ToString().c_str()); + + cPeerBlockCounts.input(pfrom->nStartingHeight); + + // ppcoin: ask for pending sync-checkpoint if any + if (!IsInitialBlockDownload()) + Checkpoints::AskForPendingSyncCheckpoint(pfrom); + } + + + else if (pfrom->nVersion == 0) + { + // Must have a version message before anything else + pfrom->Misbehaving(1); + return false; + } + + + else if (strCommand == "verack") + { + pfrom->vRecv.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); + } + + + else if (strCommand == "addr") + { + vector vAddr; + vRecv >> vAddr; + + // Don't want addr from older versions unless seeding + if (pfrom->nVersion < CADDR_TIME_VERSION && addrman.size() > 1000) + return true; + if (vAddr.size() > 1000) + { + pfrom->Misbehaving(20); + return error("message addr size() = %" PRIszu "", vAddr.size()); + } + + // Store the new addresses + vector vAddrOk; + int64_t nNow = GetAdjustedTime(); + int64_t nSince = nNow - 10 * 60; + BOOST_FOREACH(CAddress& addr, vAddr) + { + if (fShutdown) + return true; + if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) + addr.nTime = nNow - 5 * nOneDay; + pfrom->AddAddressKnown(addr); + bool fReachable = IsReachable(addr); + if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable()) + { + // Relay to a limited number of other nodes + { + LOCK(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) + hashSalt = GetRandHash(); + uint64_t hashAddr = addr.GetHash(); + uint256 hashRand = hashSalt ^ (hashAddr<<32) ^ ((GetTime()+hashAddr)/nOneDay); + hashRand = Hash(BEGIN(hashRand), END(hashRand)); + multimap mapMix; + BOOST_FOREACH(CNode* pnode, vNodes) + { + if (pnode->nVersion < CADDR_TIME_VERSION) + 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 = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s) + for (multimap::iterator mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi) + ((*mi).second)->PushAddress(addr); + } + } + // Do not store addresses outside our network + if (fReachable) + vAddrOk.push_back(addr); + } + addrman.Add(vAddrOk, pfrom->addr, 2 * nOneHour); + if (vAddr.size() < 1000) + pfrom->fGetAddr = false; + if (pfrom->fOneShot) + pfrom->fDisconnect = true; + } + + else if (strCommand == "inv") + { + vector vInv; + vRecv >> vInv; + if (vInv.size() > MAX_INV_SZ) + { + pfrom->Misbehaving(20); + return error("message inv size() = %" PRIszu "", vInv.size()); + } + + // find last block in inv vector + unsigned int nLastBlock = std::numeric_limits::max(); + for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) { + if (vInv[vInv.size() - 1 - nInv].type == MSG_BLOCK) { + nLastBlock = vInv.size() - 1 - nInv; + break; + } + } + CTxDB txdb("r"); + for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) + { + const CInv &inv = vInv[nInv]; + + if (fShutdown) + return true; + pfrom->AddInventoryKnown(inv); + + bool fAlreadyHave = AlreadyHave(txdb, inv); + if (fDebug) + 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])); + } else if (nInv == nLastBlock) { + // In case we are on a very long side-chain, it is possible that we already have + // the last block in an inv bundle sent in response to getblocks. Try to detect + // this situation and push another getblocks to continue. + pfrom->PushGetBlocks(mapBlockIndex[inv.hash], uint256(0)); + if (fDebug) + printf("force request: %s\n", inv.ToString().c_str()); + } + + // Track requests for our stuff + Inventory(inv.hash); + } + } + + + else if (strCommand == "getdata") + { + vector vInv; + vRecv >> vInv; + if (vInv.size() > MAX_INV_SZ) + { + pfrom->Misbehaving(20); + return error("message getdata size() = %" PRIszu "", vInv.size()); + } + + if (fDebugNet || (vInv.size() != 1)) + printf("received getdata (%" PRIszu " invsz)\n", vInv.size()); + + BOOST_FOREACH(const CInv& inv, vInv) + { + if (fShutdown) + return true; + if (fDebugNet || (vInv.size() == 1)) + 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) + { + // ppcoin: send latest proof-of-work block to allow the + // download node to accept as orphan (proof-of-stake + // block might be rejected by stake connection check) + vector vInv; + vInv.push_back(CInv(MSG_BLOCK, GetLastBlockIndex(pindexBest, false)->GetBlockHash())); + pfrom->PushMessage("inv", vInv); + pfrom->hashContinue = 0; + } + } + } + else if (inv.IsKnownType()) + { + // Send stream from relay memory + bool pushed = false; + { + LOCK(cs_mapRelay); + map::iterator mi = mapRelay.find(inv); + if (mi != mapRelay.end()) { + pfrom->PushMessage(inv.GetCommand(), (*mi).second); + pushed = true; + } + } + if (!pushed && inv.type == MSG_TX) { + LOCK(mempool.cs); + if (mempool.exists(inv.hash)) { + CTransaction tx = mempool.lookup(inv.hash); + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss.reserve(1000); + ss << tx; + pfrom->PushMessage("tx", ss); + } + } + } + + // Track requests for our stuff + Inventory(inv.hash); + } + } + + + 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; + 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()); + // ppcoin: tell downloading node about the latest block if it's + // without risk being rejected due to stake connection check + if (hashStop != hashBestChain && pindex->GetBlockTime() + nStakeMinAge > pindexBest->GetBlockTime()) + pfrom->PushInventory(CInv(MSG_BLOCK, hashBestChain)); + 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 == "checkpoint") + { + CSyncCheckpoint checkpoint; + vRecv >> checkpoint; + + if (checkpoint.ProcessSyncCheckpoint(pfrom)) + { + // Relay + pfrom->hashCheckpointKnown = checkpoint.hashCheckpoint; + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + checkpoint.RelayTo(pnode); + } + } + + 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; + printf("getheaders %d to %s\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,20).c_str()); + 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; + vector vEraseQueue; + CDataStream vMsg(vRecv); + CTxDB txdb("r"); + CTransaction tx; + vRecv >> tx; + + CInv inv(MSG_TX, tx.GetHash()); + pfrom->AddInventoryKnown(inv); + + bool fMissingInputs = false; + if (tx.AcceptToMemoryPool(txdb, true, &fMissingInputs)) + { + SyncWithWallets(tx, NULL, true); + RelayTransaction(tx, inv.hash); + mapAlreadyAskedFor.erase(inv); + vWorkQueue.push_back(inv.hash); + vEraseQueue.push_back(inv.hash); + + // Recursively process any orphan transactions that depended on this one + for (unsigned int i = 0; i < vWorkQueue.size(); i++) + { + uint256 hashPrev = vWorkQueue[i]; + for (set::iterator mi = mapOrphanTransactionsByPrev[hashPrev].begin(); + mi != mapOrphanTransactionsByPrev[hashPrev].end(); + ++mi) + { + const uint256& orphanTxHash = *mi; + CTransaction& orphanTx = mapOrphanTransactions[orphanTxHash]; + bool fMissingInputs2 = false; + + if (orphanTx.AcceptToMemoryPool(txdb, true, &fMissingInputs2)) + { + printf(" accepted orphan tx %s\n", orphanTxHash.ToString().substr(0,10).c_str()); + SyncWithWallets(tx, NULL, true); + RelayTransaction(orphanTx, orphanTxHash); + mapAlreadyAskedFor.erase(CInv(MSG_TX, orphanTxHash)); + vWorkQueue.push_back(orphanTxHash); + vEraseQueue.push_back(orphanTxHash); + } + else if (!fMissingInputs2) + { + // invalid orphan + vEraseQueue.push_back(orphanTxHash); + printf(" removed invalid orphan tx %s\n", orphanTxHash.ToString().substr(0,10).c_str()); + } + } + } + + BOOST_FOREACH(uint256 hash, vEraseQueue) + EraseOrphanTx(hash); + } + else if (fMissingInputs) + { + AddOrphanTx(tx); + + // DoS prevention: do not allow mapOrphanTransactions to grow unbounded + unsigned int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS); + if (nEvicted > 0) + printf("mapOrphan overflow, removed %u tx\n", nEvicted); + } + if (tx.nDoS) pfrom->Misbehaving(tx.nDoS); + } + + + else if (strCommand == "block") + { + CBlock block; + vRecv >> block; + uint256 hashBlock = block.GetHash(); + + printf("received block %s\n", hashBlock.ToString().substr(0,20).c_str()); + // block.print(); + + CInv inv(MSG_BLOCK, hashBlock); + pfrom->AddInventoryKnown(inv); + + if (ProcessBlock(pfrom, &block)) + mapAlreadyAskedFor.erase(inv); + if (block.nDoS) pfrom->Misbehaving(block.nDoS); + } + + + // This asymmetric behavior for inbound and outbound connections was introduced + // to prevent a fingerprinting attack: an attacker can send specific fake addresses + // to users' AddrMan and later request them by sending getaddr messages. + // Making users (which are behind NAT and can only make outgoing connections) ignore + // getaddr message mitigates the attack. + else if ((strCommand == "getaddr") && (pfrom->fInbound)) + { + // Don't return addresses older than nCutOff timestamp + int64_t nCutOff = GetTime() - (nNodeLifespan * nOneDay); + pfrom->vAddrToSend.clear(); + vector vAddr = addrman.GetAddr(); + BOOST_FOREACH(const CAddress &addr, vAddr) + if(addr.nTime > nCutOff) + pfrom->PushAddress(addr); + } + + + else if (strCommand == "mempool") + { + std::vector vtxid; + mempool.queryHashes(vtxid); + vector vInv; + for (unsigned int i = 0; i < vtxid.size(); i++) { + CInv inv(MSG_TX, vtxid[i]); + vInv.push_back(inv); + if (i == (MAX_INV_SZ - 1)) + break; + } + if (vInv.size() > 0) + pfrom->PushMessage("inv", vInv); + } + + + 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)) + pwalletMain->GetKeyFromPool(mapReuseKey[pfrom->addr], true); + + // Send back approval of order and pubkey to use + CScript scriptPubKey; + scriptPubKey << mapReuseKey[pfrom->addr] << OP_CHECKSIG; + pfrom->PushMessage("reply", hashReply, (int)0, scriptPubKey); + } + + + else if (strCommand == "reply") + { + uint256 hashReply; + vRecv >> hashReply; + + CRequestTracker tracker; + { + LOCK(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") + { + if (pfrom->nVersion > BIP0031_VERSION) + { + uint64_t nonce = 0; + vRecv >> nonce; + // Echo the message back with the nonce. This allows for two useful features: + // + // 1) A remote node can quickly check if the connection is operational + // 2) Remote nodes can measure the latency of the network thread. If this node + // is overloaded it won't respond to pings quickly and the remote node can + // avoid sending us more work, like chain download requests. + // + // The nonce stops the remote getting confused between different pings: without + // it, if the remote node sends a ping once per second and this node takes 5 + // seconds to respond to each, the 5th ping the remote sends would appear to + // return very quickly. + pfrom->PushMessage("pong", nonce); + } + } + + + else if (strCommand == "alert") + { + CAlert alert; + vRecv >> alert; + + uint256 alertHash = alert.GetHash(); + if (pfrom->setKnown.count(alertHash) == 0) + { + if (alert.ProcessAlert()) + { + // Relay + pfrom->setKnown.insert(alertHash); + { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + alert.RelayTo(pnode); + } + } + else { + // Small DoS penalty so peers that send us lots of + // duplicate/expired/invalid-signature/whatever alerts + // eventually get banned. + // This isn't a Misbehaving(100) (immediate ban) because the + // peer might be an older or different implementation with + // a different signature key, etc. + pfrom->Misbehaving(10); + } + } + } + + + 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 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 + // + + while (true) + { + // Don't bother if send buffer is too full to respond anyway + if (pfrom->vSend.size() >= SendBufferSize()) + break; + + // 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 ((int)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 %" PRIpdd " 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("ProcessMessages(%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 + uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize); + unsigned int nChecksum = 0; + memcpy(&nChecksum, &hash, sizeof(nChecksum)); + if (nChecksum != hdr.nChecksum) + { + printf("ProcessMessages(%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 + { + { + LOCK(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 under-length message on vRecv + printf("ProcessMessages(%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 over-long size + printf("ProcessMessages(%s, %u bytes) : Exception '%s' caught\n", strCommand.c_str(), nMessageSize, e.what()); + } + else + { + PrintExceptionContinue(&e, "ProcessMessages()"); + } + } + catch (std::exception& e) { + PrintExceptionContinue(&e, "ProcessMessages()"); + } catch (...) { + PrintExceptionContinue(NULL, "ProcessMessages()"); + } + + if (!fRet) + printf("ProcessMessage(%s, %u bytes) FAILED\n", strCommand.c_str(), nMessageSize); + } + + vRecv.Compact(); + return true; +} + + +bool SendMessages(CNode* pto, bool fSendTrickle) +{ + TRY_LOCK(cs_main, lockMain); + if (lockMain) { + // Don't send anything until we get their version message + if (pto->nVersion == 0) + return true; + + // Keep-alive ping. We send a nonce of zero because we don't use it anywhere + // right now. + if (pto->nLastSend && GetTime() - pto->nLastSend > nPingInterval && pto->vSend.empty()) { + uint64_t nonce = 0; + if (pto->nVersion > BIP0031_VERSION) + pto->PushMessage("ping", nonce); + else + pto->PushMessage("ping"); + } + + // Start block sync + if (pto->fStartSync) { + pto->fStartSync = false; + pto->PushGetBlocks(pindexBest, uint256(0)); + } + + // Resend wallet transactions that haven't gotten in a block yet + ResendWalletTransactions(); + + // Address refresh broadcast + static int64_t nLastRebroadcast; + if (!IsInitialBlockDownload() && (GetTime() - nLastRebroadcast > nBroadcastInterval)) + { + { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + { + // Periodically clear setAddrKnown to allow refresh broadcasts + if (nLastRebroadcast) + pnode->setAddrKnown.clear(); + + // Rebroadcast our address + if (!fNoListen) + { + CAddress addr = GetLocalAddress(&pnode->addr); + if (addr.IsRoutable()) + pnode->PushAddress(addr); + } + } + } + nLastRebroadcast = GetTime(); + } + + // + // 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; + { + LOCK(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) + hashSalt = GetRandHash(); + uint256 hashRand = inv.hash ^ hashSalt; + hashRand = Hash(BEGIN(hashRand), END(hashRand)); + bool fTrickleWait = ((hashRand & 3) != 0); + + // always trickle our own transactions + if (!fTrickleWait) + { + CWalletTx wtx; + if (GetTransaction(inv.hash, wtx)) + 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_t 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)) + { + if (fDebugNet) + printf("sending getdata: %s\n", inv.ToString().c_str()); + vGetData.push_back(inv); + if (vGetData.size() >= 1000) + { + pto->PushMessage("getdata", vGetData); + vGetData.clear(); + } + mapAlreadyAskedFor[inv] = nNow; + } + pto->mapAskFor.erase(pto->mapAskFor.begin()); + } + if (!vGetData.empty()) + pto->PushMessage("getdata", vGetData); + + } + return true; +} + + +class CMainCleanup +{ +public: + CMainCleanup() {} + ~CMainCleanup() { + // block headers + std::map::iterator it1 = mapBlockIndex.begin(); + for (; it1 != mapBlockIndex.end(); it1++) + delete (*it1).second; + mapBlockIndex.clear(); + + // orphan blocks + std::map::iterator it2 = mapOrphanBlocks.begin(); + for (; it2 != mapOrphanBlocks.end(); it2++) + delete (*it2).second; + mapOrphanBlocks.clear(); + + // orphan transactions + } +} instance_of_cmaincleanup; \ No newline at end of file diff --git a/src/main.h b/src/main.h new file mode 100644 index 00000000..596aea90 --- /dev/null +++ b/src/main.h @@ -0,0 +1,1655 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_MAIN_H +#define BITCOIN_MAIN_H + +#include + +#include "timestamps.h" +#include "bignum.h" +#include "sync.h" +#include "net.h" +#include "script.h" +#include "scrypt.h" + +#include +#include + +class CWallet; +class CBlock; +class CBlockIndex; +class CKeyItem; +class CReserveKey; +class COutPoint; + +class CAddress; +class CInv; +class CRequestTracker; +class CNode; + +// +// Global state +// + +static const int LAST_POW_BLOCK = 2147483646; // PoW always on + +static const unsigned int MAX_BLOCK_SIZE = 2000000; +static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2; +static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50; +static const unsigned int MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100; +static const unsigned int MAX_INV_SZ = 50000; + +static const int64_t MIN_TX_FEE = 0.00001 * COIN; +static const int64_t MIN_RELAY_TX_FEE = 0.00001 * COIN; +static const int64_t MIN_TXOUT_AMOUNT = 0.00001 * COIN; + +static const int64_t MAX_MONEY = 200000000000 * COIN; + +inline bool MoneyRange(int64_t nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } +// Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp. +static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC +// Maximum number of script-checking threads allowed +static const int MAX_SCRIPTCHECK_THREADS = 16; + +static const uint256 hashGenesisBlock("0x0000065a10945931644cdda436d5ad0742d085e9dee3e7fdd51b822626727e78"); +static const uint256 hashGenesisBlockTestNet("0x000c763e402f2436da9ed36c7286f62c3f6e5dbafce9ff289bd43d7459327eb"); + +inline int64_t PastDrift(int64_t nTime) { return nTime - ((0.5 * 3 + 10) * 60); } // up to this from the past +inline int64_t FutureDrift(int64_t nTime) { return nTime + 5 * 60; } // up to 5 mins from the future + +extern CScript COINBASE_FLAGS; +extern CCriticalSection cs_main; +extern std::map mapBlockIndex; +extern std::set > setStakeSeen; +extern CBlockIndex* pindexGenesisBlock; +extern unsigned int nNodeLifespan; +extern unsigned int nStakeMinAge; +extern int nCoinbaseMaturity; +extern int nBestHeight; +extern uint256 nBestChainTrust; +extern uint256 nBestInvalidTrust; +extern uint256 hashBestChain; +extern CBlockIndex* pindexBest; +extern unsigned int nTransactionsUpdated; +extern uint64_t nLastBlockTx; +extern uint64_t nLastBlockSize; +extern uint32_t nLastCoinStakeSearchInterval; +extern const std::string strMessageMagic; +extern int64_t nTimeBestReceived; +extern CCriticalSection cs_setpwalletRegistered; +extern std::set setpwalletRegistered; +extern unsigned char pchMessageStart[4]; +extern std::map mapOrphanBlocks; + +// Settings +extern int64_t nTransactionFee; +extern int64_t nMinimumInputValue; +extern bool fUseFastIndex; +extern int nScriptCheckThreads; +extern const uint256 entropyStore[38]; + +// Minimum disk space required - used in CheckDiskSpace() +static const uint64_t nMinDiskSpace = 52428800; + +class CReserveKey; +class CTxDB; +class CTxIndex; +class CScriptCheck; + +void RegisterWallet(CWallet* pwalletIn); +void UnregisterWallet(CWallet* pwalletIn); +void SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false, bool fConnect = true); +bool ProcessBlock(CNode* pfrom, CBlock* pblock); +bool CheckDiskSpace(uint64_t nAdditionalBytes=0); +FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode="rb"); +FILE* AppendBlockFile(unsigned int& nFileRet); + +void UnloadBlockIndex(); +bool LoadBlockIndex(bool fAllowNew=true); +void PrintBlockTree(); +CBlockIndex* FindBlockByHeight(int nHeight); +bool ProcessMessages(CNode* pfrom); +bool SendMessages(CNode* pto, bool fSendTrickle); +bool LoadExternalBlockFile(FILE* fileIn); + +// Run an instance of the script checking thread +void ThreadScriptCheck(void* parg); +// Stop the script checking threads +void ThreadScriptCheckQuit(); + +bool CheckProofOfWork(uint256 hash, unsigned int nBits); +unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake); +int64_t GetProofOfWorkReward(unsigned int nBits, int64_t nFees=0, int nHeight=0); +int64_t GetProofOfStakeReward(int64_t nCoinAge, unsigned int nBits, int64_t nTime, bool bCoinYearOnly=false); +unsigned int ComputeMinWork(unsigned int nBase, int64_t nTime); +unsigned int ComputeMinStake(unsigned int nBase, int64_t nTime, unsigned int nBlockTime); +int GetNumBlocksOfPeers(); +bool IsInitialBlockDownload(); +std::string GetWarnings(std::string strFor); +bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock); +uint256 WantedByOrphan(const CBlock* pblockOrphan); +const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfStake); +void ResendWalletTransactions(); + +bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); + + + + + + + +bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut); + +/** Position on disk for a particular transaction. */ +class CDiskTxPos +{ +public: + uint32_t nFile; + uint32_t nBlockPos; + uint32_t 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 = std::numeric_limits::max(); nBlockPos = 0; nTxPos = 0; } + bool IsNull() const { return (nFile == std::numeric_limits::max()); } + + friend bool operator==(const CDiskTxPos& a, const CDiskTxPos& b) + { + return (a.nFile == b.nFile && + a.nBlockPos == b.nBlockPos && + a.nTxPos == b.nTxPos); + } + + friend bool operator!=(const CDiskTxPos& a, const CDiskTxPos& b) + { + return !(a == b); + } + + + std::string ToString() const + { + if (IsNull()) + return "null"; + else + return strprintf("(nFile=%u, nBlockPos=%u, nTxPos=%u)", nFile, nBlockPos, nTxPos); + } + + void print() const + { + printf("%s", ToString().c_str()); + } +}; + + + +/** An inpoint - a combination of a transaction and an index n into its vin */ +class CInPoint +{ +public: + CTransaction* ptx; + uint32_t n; + + CInPoint() { SetNull(); } + CInPoint(CTransaction* ptxIn, unsigned int nIn) { ptx = ptxIn; n = nIn; } + void SetNull() { ptx = NULL; n = std::numeric_limits::max(); } + bool IsNull() const { return (ptx == NULL && n == std::numeric_limits::max()); } +}; + + + +/** An outpoint - a combination of a transaction hash and an index n into its vout */ +class COutPoint +{ +public: + uint256 hash; + uint32_t n; + + COutPoint() { SetNull(); } + COutPoint(uint256 hashIn, unsigned int nIn) { hash = hashIn; n = nIn; } + IMPLEMENT_SERIALIZE( READWRITE(FLATDATA(*this)); ) + void SetNull() { hash = 0; n = std::numeric_limits::max(); } + bool IsNull() const { return (hash == 0 && n == std::numeric_limits::max()); } + + friend bool operator<(const COutPoint& a, const COutPoint& b) + { + return (a.hash < b.hash || (a.hash == b.hash && a.n < b.n)); + } + + friend bool operator==(const COutPoint& a, const COutPoint& b) + { + return (a.hash == b.hash && a.n == b.n); + } + + friend bool operator!=(const COutPoint& a, const COutPoint& b) + { + return !(a == b); + } + + std::string ToString() const + { + return strprintf("COutPoint(%s, %u)", 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; + uint32_t nSequence; + + CTxIn() + { + nSequence = std::numeric_limits::max(); + } + + explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits::max()) + { + prevout = prevoutIn; + scriptSig = scriptSigIn; + nSequence = nSequenceIn; + } + + CTxIn(uint256 hashPrevTx, unsigned int nOut, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits::max()) + { + prevout = COutPoint(hashPrevTx, nOut); + scriptSig = scriptSigIn; + nSequence = nSequenceIn; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(prevout); + READWRITE(scriptSig); + READWRITE(nSequence); + ) + + bool IsFinal() const + { + return (nSequence == std::numeric_limits::max()); + } + + friend bool operator==(const CTxIn& a, const CTxIn& b) + { + return (a.prevout == b.prevout && + a.scriptSig == b.scriptSig && + a.nSequence == b.nSequence); + } + + friend bool operator!=(const CTxIn& a, const CTxIn& b) + { + return !(a == b); + } + + std::string ToStringShort() const + { + return strprintf(" %s %d", prevout.hash.ToString().c_str(), prevout.n); + } + + std::string ToString() const + { + std::string str; + str += "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 != std::numeric_limits::max()) + str += strprintf(", nSequence=%u", nSequence); + str += ")"; + return str; + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + + + +/** 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_t nValue; + CScript scriptPubKey; + + CTxOut() + { + SetNull(); + } + + CTxOut(int64_t nValueIn, CScript scriptPubKeyIn) + { + nValue = nValueIn; + scriptPubKey = scriptPubKeyIn; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(nValue); + READWRITE(scriptPubKey); + ) + + void SetNull() + { + nValue = -1; + scriptPubKey.clear(); + } + + bool IsNull() + { + return (nValue == -1); + } + + void SetEmpty() + { + nValue = 0; + scriptPubKey.clear(); + } + + bool IsEmpty() const + { + return (nValue == 0 && scriptPubKey.empty()); + } + + uint256 GetHash() const + { + return SerializeHash(*this); + } + + friend bool operator==(const CTxOut& a, const CTxOut& b) + { + return (a.nValue == b.nValue && + a.scriptPubKey == b.scriptPubKey); + } + + friend bool operator!=(const CTxOut& a, const CTxOut& b) + { + return !(a == b); + } + + std::string ToStringShort() const + { + return strprintf(" out %s %s", FormatMoney(nValue).c_str(), scriptPubKey.ToString(true).c_str()); + } + + std::string ToString() const + { + if (IsEmpty()) return "CTxOut(empty)"; + if (scriptPubKey.size() < 6) + return "CTxOut(error)"; + return strprintf("CTxOut(nValue=%s, scriptPubKey=%s)", FormatMoney(nValue).c_str(), scriptPubKey.ToString().c_str()); + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + + + +enum GetMinFee_mode +{ + GMF_BLOCK, + GMF_RELAY, + GMF_SEND, +}; + +typedef std::map > MapPrevTx; + +/** The basic transaction that is broadcasted on the network and contained in + * blocks. A transaction can contain multiple inputs and outputs. + */ +class CTransaction +{ +public: + static const int CURRENT_VERSION=1; + int nVersion; + uint32_t nTime; + std::vector vin; + std::vector vout; + uint32_t nLockTime; + + // Denial-of-service detection: + mutable int nDoS; + bool DoS(int nDoSIn, bool fIn) const { nDoS += nDoSIn; return fIn; } + + CTransaction() + { + SetNull(); + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(nTime); + READWRITE(vin); + READWRITE(vout); + READWRITE(nLockTime); + ) + + void SetNull() + { + nVersion = CTransaction::CURRENT_VERSION; + nTime = (uint32_t) GetAdjustedTime(); + vin.clear(); + vout.clear(); + nLockTime = 0; + nDoS = 0; // Denial-of-service prevention + } + + bool IsNull() const + { + return (vin.empty() && vout.empty()); + } + + uint256 GetHash() const + { + return SerializeHash(*this); + } + + bool IsFinal(int nBlockHeight=0, int64_t 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_t)nLockTime < ((int64_t)nLockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime)) + return true; + BOOST_FOREACH(const CTxIn& txin, vin) + if (!txin.IsFinal()) + return false; + return true; + } + + bool IsNewerThan(const CTransaction& old) const + { + if (vin.size() != old.vin.size()) + return false; + for (unsigned int i = 0; i < vin.size(); i++) + if (vin[i].prevout != old.vin[i].prevout) + return false; + + bool fNewer = false; + unsigned int nLowest = std::numeric_limits::max(); + for (unsigned 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() && vout.size() >= 1); + } + + bool IsCoinStake() const + { + // ppcoin: the coin stake transaction is marked with the first output empty + return (vin.size() > 0 && (!vin[0].prevout.IsNull()) && vout.size() >= 2 && vout[0].IsEmpty()); + } + + /** Check for standard transaction types + @return True if all outputs (scriptPubKeys) use only standard transaction forms + */ + bool IsStandard(std::string& strReason) const; + bool IsStandard() const + { + std::string strReason; + return IsStandard(strReason); + } + + /** Check for standard transaction types + @param[in] mapInputs Map of previous transactions that have outputs we're spending + @return True if all inputs (scriptSigs) use only standard transaction forms + @see CTransaction::FetchInputs + */ + bool AreInputsStandard(const MapPrevTx& mapInputs) const; + + /** Count ECDSA signature operations the old-fashioned (pre-0.6) way + @return number of sigops this transaction's outputs will produce when spent + @see CTransaction::FetchInputs + */ + unsigned int GetLegacySigOpCount() const; + + /** Count ECDSA signature operations in pay-to-script-hash inputs. + + @param[in] mapInputs Map of previous transactions that have outputs we're spending + @return maximum number of sigops required to validate this transaction's inputs + @see CTransaction::FetchInputs + */ + unsigned int GetP2SHSigOpCount(const MapPrevTx& mapInputs) const; + + /** Amount of bitcoins spent by this transaction. + @return sum of all outputs (note: does not include fees) + */ + int64_t GetValueOut() const + { + int64_t nValueOut = 0; + BOOST_FOREACH(const CTxOut& txout, vout) + { + nValueOut += txout.nValue; + if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut)) + throw std::runtime_error("CTransaction::GetValueOut() : value out of range"); + } + return nValueOut; + } + + /** Amount of bitcoins coming in to this transaction + Note that lightweight clients may not know anything besides the hash of previous transactions, + so may not be able to calculate this. + + @param[in] mapInputs Map of previous transactions that have outputs we're spending + @return Sum of value of all inputs (scriptSigs) + @see CTransaction::FetchInputs + */ + int64_t GetValueIn(const MapPrevTx& mapInputs) const; + + static bool AllowFree(double dPriority) + { + // Large (in bytes) low-priority (new, small-coin) transactions + // need a fee. + return dPriority > COIN * 144 / 250; + } + + int64_t GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=false, enum GetMinFee_mode mode=GMF_BLOCK, unsigned int nBytes = 0) const; + + bool ReadFromDisk(CDiskTxPos pos, FILE** pfileRet=NULL) + { + CAutoFile filein = CAutoFile(OpenBlockFile(pos.nFile, 0, pfileRet ? "rb+" : "rb"), SER_DISK, CLIENT_VERSION); + if (!filein) + return error("CTransaction::ReadFromDisk() : OpenBlockFile failed"); + + // Read transaction + if (fseek(filein, pos.nTxPos, SEEK_SET) != 0) + return error("CTransaction::ReadFromDisk() : fseek failed"); + + try { + filein >> *this; + } + catch (std::exception &e) { + (void)e; + return error("%s() : deserialize or I/O error", BOOST_CURRENT_FUNCTION); + } + + // 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.nTime == b.nTime && + a.vin == b.vin && + a.vout == b.vout && + a.nLockTime == b.nLockTime); + } + + friend bool operator!=(const CTransaction& a, const CTransaction& b) + { + return !(a == b); + } + + std::string ToStringShort() const + { + std::string str; + str += strprintf("%s %s", GetHash().ToString().c_str(), IsCoinBase()? "base" : (IsCoinStake()? "stake" : "user")); + return str; + } + + std::string ToString() const + { + std::string str; + str += IsCoinBase()? "Coinbase" : (IsCoinStake()? "Coinstake" : "CTransaction"); + str += strprintf("(hash=%s, nTime=%d, ver=%d, vin.size=%" PRIszu ", vout.size=%" PRIszu ", nLockTime=%d)\n", + GetHash().ToString().substr(0,10).c_str(), + nTime, + nVersion, + vin.size(), + vout.size(), + nLockTime); + for (unsigned int i = 0; i < vin.size(); i++) + str += " " + vin[i].ToString() + "\n"; + for (unsigned 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, const uint256& hash, CTxIndex& txindexRet); + bool ReadFromDisk(CTxDB& txdb, COutPoint prevout, CTxIndex& txindexRet); + bool ReadFromDisk(CTxDB& txdb, COutPoint prevout); + bool ReadFromDisk(COutPoint prevout); + bool DisconnectInputs(CTxDB& txdb); + + /** Fetch from memory and/or disk. inputsRet keys are transaction hashes. + + @param[in] txdb Transaction database + @param[in] mapTestPool List of pending changes to the transaction index database + @param[in] fBlock True if being called to add a new best-block to the chain + @param[in] fMiner True if being called by CreateNewBlock + @param[out] inputsRet Pointers to this transaction's inputs + @param[out] fInvalid returns true if transaction is invalid + @return Returns true if all inputs are in txdb or mapTestPool + */ + bool FetchInputs(CTxDB& txdb, const std::map& mapTestPool, + bool fBlock, bool fMiner, MapPrevTx& inputsRet, bool& fInvalid); + + /** Sanity check previous transactions, then, if all checks succeed, + mark them as spent by this transaction. + + @param[in] inputs Previous transactions (from FetchInputs) + @param[out] mapTestPool Keeps track of inputs that need to be updated on disk + @param[in] posThisTx Position of this transaction on disk + @param[in] pindexBlock + @param[in] fBlock true if called from ConnectBlock + @param[in] fMiner true if called from CreateNewBlock + @param[in] fScriptChecks enable scripts validation? + @param[in] flags STRICT_FLAGS script validation flags + @param[in] pvChecks NULL If pvChecks is not NULL, script checks are pushed onto it instead of being performed inline. + @return Returns true if all checks succeed + */ + bool ConnectInputs(CTxDB& txdb, MapPrevTx inputs, std::map& mapTestPool, const CDiskTxPos& posThisTx, const CBlockIndex* pindexBlock, + bool fBlock, bool fMiner, bool fScriptChecks=true, + unsigned int flags=STRICT_FLAGS, std::vector *pvChecks = NULL); + bool ClientConnectInputs(); + bool CheckTransaction() const; + bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL); + bool GetCoinAge(CTxDB& txdb, uint64_t& nCoinAge) const; // ppcoin: get transaction coin age + +protected: + const CTxOut& GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const; +}; + +/** Closure representing one script verification + * Note that this stores references to the spending transaction */ +class CScriptCheck +{ +private: + CScript scriptPubKey; + const CTransaction *ptxTo; + unsigned int nIn; + unsigned int nFlags; + int nHashType; + +public: + CScriptCheck() {} + CScriptCheck(const CTransaction& txFromIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, int nHashTypeIn) : + scriptPubKey(txFromIn.vout[txToIn.vin[nInIn].prevout.n].scriptPubKey), + ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), nHashType(nHashTypeIn) { } + + bool operator()() const; + + void swap(CScriptCheck &check) { + scriptPubKey.swap(check.scriptPubKey); + std::swap(ptxTo, check.ptxTo); + std::swap(nIn, check.nIn); + std::swap(nFlags, check.nFlags); + std::swap(nHashType, check.nHashType); + } +}; + + + + +/** A transaction with a merkle branch linking it to the block chain. */ +class CMerkleTx : public CTransaction +{ +public: + uint256 hashBlock; + std::vector vMerkleBranch; + int32_t nIndex; + + // memory only + mutable bool 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(CBlockIndex* &pindexRet) const; + int GetDepthInMainChain() const { CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); } + bool IsInMainChain() const { return GetDepthInMainChain() > 0; } + int GetBlocksToMaturity() const; + bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true); + bool AcceptToMemoryPool(); +}; + + + + +/** A txdb record that contains the disk location of a transaction and the + * locations of transactions that spend its outputs. vSpent is really only + * used as a flag, but having the location is very helpful for debugging. + */ +class CTxIndex +{ +public: + CDiskTxPos pos; + std::vector vSpent; + + CTxIndex() + { + SetNull(); + } + + CTxIndex(const CDiskTxPos& posIn, unsigned int nOutputs) + { + pos = posIn; + vSpent.resize(nOutputs); + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(pos); + READWRITE(vSpent); + ) + + void SetNull() + { + pos.SetNull(); + vSpent.clear(); + } + + bool IsNull() + { + return pos.IsNull(); + } + + friend bool operator==(const CTxIndex& a, const CTxIndex& b) + { + return (a.pos == b.pos && + a.vSpent == b.vSpent); + } + + friend bool operator!=(const CTxIndex& a, const CTxIndex& b) + { + return !(a == b); + } + int GetDepthInMainChain() const; + +}; + + +/** Nodes collect new transactions into a block, hash them into a hash tree, + * and scan through nonce values to make the block's hash satisfy proof-of-work + * requirements. When they solve the proof-of-work, they broadcast the block + * to everyone and the block is added to the block chain. The first transaction + * in the block is a special one that creates a new coin owned by the creator + * of the block. + * + * Blocks are appended to blk0001.dat files on disk. Their location on disk + * is indexed by CBlockIndex objects in memory. + */ +class CBlock +{ +public: + // header + static const int CURRENT_VERSION=6; + int32_t nVersion; + uint256 hashPrevBlock; + uint256 hashMerkleRoot; + uint32_t nTime; + uint32_t nBits; + uint32_t nNonce; + + // network and disk + std::vector vtx; + + // ppcoin: block signature - signed by one of the coin base txout[N]'s owner + std::vector vchBlockSig; + + // memory only + mutable std::vector vMerkleTree; + + // Denial-of-service detection: + mutable int nDoS; + bool DoS(int nDoSIn, bool fIn) const { nDoS += nDoSIn; return fIn; } + + CBlock() + { + SetNull(); + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(hashPrevBlock); + READWRITE(hashMerkleRoot); + READWRITE(nTime); + READWRITE(nBits); + READWRITE(nNonce); + + // ConnectBlock depends on vtx following header to generate CDiskTxPos + if (!(nType & (SER_GETHASH|SER_BLOCKHEADERONLY))) + { + READWRITE(vtx); + READWRITE(vchBlockSig); + } + else if (fRead) + { + const_cast(this)->vtx.clear(); + const_cast(this)->vchBlockSig.clear(); + } + ) + + void SetNull() + { + nVersion = CBlock::CURRENT_VERSION; + hashPrevBlock = 0; + hashMerkleRoot = 0; + nTime = 0; + nBits = 0; + nNonce = 0; + vtx.clear(); + vchBlockSig.clear(); + vMerkleTree.clear(); + nDoS = 0; + } + + bool IsNull() const + { + return (nBits == 0); + } + + uint256 GetHash() const + { + return SerializeHash(*this); + } + + int64_t GetBlockTime() const + { + return (int64_t)nTime; + } + + void UpdateTime(const CBlockIndex* pindexPrev); + + // ppcoin: entropy bit for stake modifier if chosen by modifier + unsigned int GetStakeEntropyBit(unsigned int nHeight) const + { + // Protocol switch to support p2pool at XP block #0 + if (nHeight >= 0 || fTestNet) + { + // Take last bit of block hash as entropy bit + unsigned int nEntropyBit = ((GetHash().Get64()) & 1ULL); + if (fDebug && GetBoolArg("-printstakemodifier")) + printf("GetStakeEntropyBit: nTime=%u hashBlock=%s nEntropyBit=%u\n", nTime, GetHash().ToString().c_str(), nEntropyBit); + return nEntropyBit; + } + + // Before XP block #0 - get from pregenerated table + int nBitNum = nHeight & 0xFF; + int nItemNum = nHeight / 0xFF; + + unsigned int nEntropyBit = (unsigned int) ((entropyStore[nItemNum] & (uint256(1) << nBitNum)) >> nBitNum).Get64(); + if (fDebug && GetBoolArg("-printstakemodifier")) + printf("GetStakeEntropyBit: from pregenerated table, nHeight=%d nEntropyBit=%u\n", nHeight, nEntropyBit); + return nEntropyBit; + } + + // ppcoin: two types of block: proof-of-work or proof-of-stake + bool IsProofOfStake() const + { + return (vtx.size() > 1 && vtx[1].IsCoinStake()); + } + + bool IsProofOfWork() const + { + return !IsProofOfStake(); + } + + std::pair GetProofOfStake() const + { + return IsProofOfStake()? std::make_pair(vtx[1].vin[0].prevout, vtx[1].nTime) : std::make_pair(COutPoint(), (unsigned int)0); + } + + // ppcoin: get max transaction timestamp + int64_t GetMaxTransactionTime() const + { + int64_t maxTransactionTime = 0; + BOOST_FOREACH(const CTransaction& tx, vtx) + maxTransactionTime = std::max(maxTransactionTime, (int64_t)tx.nTime); + return maxTransactionTime; + } + + uint256 BuildMerkleTree() const + { + vMerkleTree.clear(); + BOOST_FOREACH(const CTransaction& tx, vtx) + vMerkleTree.push_back(tx.GetHash()); + int j = 0; + for (int nSize = (int)vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) + { + for (int i = 0; i < nSize; i += 2) + { + int i2 = std::min(i+1, nSize-1); + vMerkleTree.push_back(Hash(BEGIN(vMerkleTree[j+i]), END(vMerkleTree[j+i]), + BEGIN(vMerkleTree[j+i2]), END(vMerkleTree[j+i2]))); + } + j += nSize; + } + return (vMerkleTree.empty() ? 0 : vMerkleTree.back()); + } + + std::vector GetMerkleBranch(int nIndex) const + { + if (vMerkleTree.empty()) + BuildMerkleTree(); + std::vector vMerkleBranch; + int j = 0; + for (int nSize = (int)vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) + { + int i = std::min(nIndex^1, nSize-1); + vMerkleBranch.push_back(vMerkleTree[j+i]); + nIndex >>= 1; + j += nSize; + } + return vMerkleBranch; + } + + static uint256 CheckMerkleBranch(uint256 hash, const std::vector& vMerkleBranch, int nIndex) + { + if (nIndex == -1) + return 0; + BOOST_FOREACH(const uint256& otherside, vMerkleBranch) + { + if (nIndex & 1) + hash = Hash(BEGIN(otherside), END(otherside), BEGIN(hash), END(hash)); + else + hash = Hash(BEGIN(hash), END(hash), BEGIN(otherside), END(otherside)); + nIndex >>= 1; + } + return hash; + } + + + bool WriteToDisk(unsigned int& nFileRet, unsigned int& nBlockPosRet) + { + // Open history file to append + CAutoFile fileout = CAutoFile(AppendBlockFile(nFileRet), SER_DISK, CLIENT_VERSION); + if (!fileout) + return error("CBlock::WriteToDisk() : AppendBlockFile failed"); + + // Write index header + unsigned int nSize = fileout.GetSerializeSize(*this); + fileout << FLATDATA(pchMessageStart) << nSize; + + // Write block + long fileOutPos = ftell(fileout); + if (fileOutPos < 0) + return error("CBlock::WriteToDisk() : ftell failed"); + nBlockPosRet = fileOutPos; + fileout << *this; + + // Flush stdio buffers and commit to disk before returning + fflush(fileout); + if (!IsInitialBlockDownload() || (nBestHeight+1) % 500 == 0) + FileCommit(fileout); + + return true; + } + + bool ReadFromDisk(unsigned int nFile, unsigned int nBlockPos, bool fReadTransactions=true) + { + SetNull(); + + // Open history file to read + CAutoFile filein = CAutoFile(OpenBlockFile(nFile, nBlockPos, "rb"), SER_DISK, CLIENT_VERSION); + if (!filein) + return error("CBlock::ReadFromDisk() : OpenBlockFile failed"); + if (!fReadTransactions) + filein.nType |= SER_BLOCKHEADERONLY; + + // Read block + try { + filein >> *this; + } + catch (std::exception &e) { + (void)e; + return error("%s() : deserialize or I/O error", BOOST_CURRENT_FUNCTION); + } + + // Check the header + if (fReadTransactions && IsProofOfWork() && !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=%" PRIszu ", vchBlockSig=%s)\n", + GetHash().ToString().c_str(), + nVersion, + hashPrevBlock.ToString().c_str(), + hashMerkleRoot.ToString().c_str(), + nTime, nBits, nNonce, + vtx.size(), + HexStr(vchBlockSig.begin(), vchBlockSig.end()).c_str()); + for (unsigned int i = 0; i < vtx.size(); i++) + { + printf(" "); + vtx[i].print(); + } + printf(" vMerkleTree: "); + for (unsigned 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 fJustCheck=false); + bool ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions=true); + bool SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew); + bool AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos); + bool CheckBlock(bool fCheckPOW=true, bool fCheckMerkleRoot=true, bool fCheckSig=true) const; + bool AcceptBlock(); + bool GetCoinAge(uint64_t& nCoinAge) const; // ppcoin: calculate total coin age spent in block + bool CheckBlockSignature() const; + +private: + bool SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew); +}; + + + + + + +/** 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; + uint32_t nFile; + uint32_t nBlockPos; + uint256 nChainTrust; // ppcoin: trust score of block chain + int32_t nHeight; + + int64_t nMint; + int64_t nMoneySupply; + + uint32_t nFlags; // ppcoin: block index flags + enum + { + BLOCK_PROOF_OF_STAKE = (1 << 0), // is proof-of-stake block + BLOCK_STAKE_ENTROPY = (1 << 1), // entropy bit for stake modifier + BLOCK_STAKE_MODIFIER = (1 << 2), // regenerated stake modifier + }; + + uint64_t nStakeModifier; // hash modifier for proof-of-stake + uint32_t nStakeModifierChecksum; // checksum of index; in-memeory only + + // proof-of-stake specific fields + COutPoint prevoutStake; + uint32_t nStakeTime; + uint256 hashProofOfStake; + + // block header + int32_t nVersion; + uint256 hashMerkleRoot; + uint32_t nTime; + uint32_t nBits; + uint32_t nNonce; + + CBlockIndex() + { + phashBlock = NULL; + pprev = NULL; + pnext = NULL; + nFile = 0; + nBlockPos = 0; + nHeight = 0; + nChainTrust = 0; + nMint = 0; + nMoneySupply = 0; + nFlags = 0; + nStakeModifier = 0; + nStakeModifierChecksum = 0; + hashProofOfStake = 0; + prevoutStake.SetNull(); + nStakeTime = 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; + nChainTrust = 0; + nMint = 0; + nMoneySupply = 0; + nFlags = 0; + nStakeModifier = 0; + nStakeModifierChecksum = 0; + hashProofOfStake = 0; + if (block.IsProofOfStake()) + { + SetProofOfStake(); + prevoutStake = block.vtx[1].vin[0].prevout; + nStakeTime = block.vtx[1].nTime; + } + else + { + prevoutStake.SetNull(); + nStakeTime = 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_t GetBlockTime() const + { + return (int64_t)nTime; + } + + uint256 GetBlockTrust() const; + + bool IsInMainChain() const + { + return (pnext || this == pindexBest); + } + + bool CheckIndex() const + { + return true; + } + + enum { nMedianTimeSpan=11 }; + + int64_t GetMedianTimePast() const + { + int64_t pmedian[nMedianTimeSpan]; + int64_t* pbegin = &pmedian[nMedianTimeSpan]; + int64_t* pend = &pmedian[nMedianTimeSpan]; + + const CBlockIndex* pindex = this; + for (int i = 0; i < nMedianTimeSpan && pindex; i++, pindex = pindex->pprev) + *(--pbegin) = pindex->GetBlockTime(); + + std::sort(pbegin, pend); + return pbegin[(pend - pbegin)/2]; + } + + int64_t 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(); + } + + /** + * Returns true if there are nRequired or more blocks of minVersion or above + * in the last nToCheck blocks, starting at pstart and going backwards. + */ + static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, + unsigned int nRequired, unsigned int nToCheck); + + + bool IsProofOfWork() const + { + return !(nFlags & BLOCK_PROOF_OF_STAKE); + } + + bool IsProofOfStake() const + { + return (nFlags & BLOCK_PROOF_OF_STAKE); + } + + void SetProofOfStake() + { + nFlags |= BLOCK_PROOF_OF_STAKE; + } + + unsigned int GetStakeEntropyBit() const + { + return ((nFlags & BLOCK_STAKE_ENTROPY) >> 1); + } + + bool SetStakeEntropyBit(unsigned int nEntropyBit) + { + if (nEntropyBit > 1) + return false; + nFlags |= (nEntropyBit? BLOCK_STAKE_ENTROPY : 0); + return true; + } + + bool GeneratedStakeModifier() const + { + return (nFlags & BLOCK_STAKE_MODIFIER) != 0; + } + + void SetStakeModifier(uint64_t nModifier, bool fGeneratedStakeModifier) + { + nStakeModifier = nModifier; + if (fGeneratedStakeModifier) + nFlags |= BLOCK_STAKE_MODIFIER; + } + + std::string ToString() const + { + return strprintf("CBlockIndex(nprev=%p, pnext=%p, nFile=%u, nBlockPos=%-6d nHeight=%d, nMint=%s, nMoneySupply=%s, nFlags=(%s)(%d)(%s), nStakeModifier=%016" PRIx64 ", nStakeModifierChecksum=%08x, hashProofOfStake=%s, prevoutStake=(%s), nStakeTime=%d merkle=%s, hashBlock=%s)", + pprev, pnext, nFile, nBlockPos, nHeight, + FormatMoney(nMint).c_str(), FormatMoney(nMoneySupply).c_str(), + GeneratedStakeModifier() ? "MOD" : "-", GetStakeEntropyBit(), IsProofOfStake()? "PoS" : "PoW", + nStakeModifier, nStakeModifierChecksum, + hashProofOfStake.ToString().c_str(), + prevoutStake.ToString().c_str(), nStakeTime, + hashMerkleRoot.ToString().c_str(), + GetBlockHash().ToString().c_str()); + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + + +/** Used to marshal pointers into hashes for db storage. */ +class CDiskBlockIndex : public CBlockIndex +{ +private: + uint256 blockHash; + +public: + uint256 hashPrev; + uint256 hashNext; + + CDiskBlockIndex() + { + hashPrev = 0; + hashNext = 0; + blockHash = 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); + READWRITE(nMint); + READWRITE(nMoneySupply); + READWRITE(nFlags); + READWRITE(nStakeModifier); + if (IsProofOfStake()) + { + READWRITE(prevoutStake); + READWRITE(nStakeTime); + READWRITE(hashProofOfStake); + } + else if (fRead) + { + const_cast(this)->prevoutStake.SetNull(); + const_cast(this)->nStakeTime = 0; + const_cast(this)->hashProofOfStake = 0; + } + + // block header + READWRITE(this->nVersion); + READWRITE(hashPrev); + READWRITE(hashMerkleRoot); + READWRITE(nTime); + READWRITE(nBits); + READWRITE(nNonce); + READWRITE(blockHash); + ) + + uint256 GetBlockHash() const + { + if (fUseFastIndex && (nTime < GetAdjustedTime() - nOneDay) && blockHash != 0) + return blockHash; + + CBlock block; + block.nVersion = nVersion; + block.hashPrevBlock = hashPrev; + block.hashMerkleRoot = hashMerkleRoot; + block.nTime = nTime; + block.nBits = nBits; + block.nNonce = nNonce; + + const_cast(this)->blockHash = block.GetHash(); + + return blockHash; + } + + std::string ToString() const + { + std::string str = "CDiskBlockIndex("; + str += CBlockIndex::ToString(); + str += strprintf("\n hashBlock=%s, hashPrev=%s, hashNext=%s)", + GetBlockHash().ToString().c_str(), + hashPrev.ToString().c_str(), + hashNext.ToString().c_str()); + return str; + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + + + + + + + +/** Describes a place in the block chain to another node such that if the + * other node doesn't have the same branch, it can find a recent common trunk. + * The further back it is, the further before the fork it may be. + */ +class CBlockLocator +{ +protected: + std::vector vHave; +public: + + CBlockLocator() + { + } + + explicit CBlockLocator(const CBlockIndex* pindex) + { + Set(pindex); + } + + explicit CBlockLocator(uint256 hashBlock) + { + std::map::iterator mi = mapBlockIndex.find(hashBlock); + if (mi != mapBlockIndex.end()) + Set((*mi).second); + } + + CBlockLocator(const std::vector& vHaveIn) + { + vHave = vHaveIn; + } + + 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((!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)); + } + + int GetDistanceBack() + { + // Retrace how far back it was in the sender's branch + int nDistance = 0; + int nStep = 1; + BOOST_FOREACH(const uint256& hash, vHave) + { + std::map::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) + { + CBlockIndex* pindex = (*mi).second; + if (pindex->IsInMainChain()) + return nDistance; + } + nDistance += nStep; + if (nDistance > 10) + nStep *= 2; + } + return nDistance; + } + + CBlockIndex* GetBlockIndex() + { + // Find the first block the caller has in the main chain + BOOST_FOREACH(const uint256& hash, vHave) + { + std::map::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) + { + CBlockIndex* pindex = (*mi).second; + if (pindex->IsInMainChain()) + return pindex; + } + } + return pindexGenesisBlock; + } + + uint256 GetBlockHash() + { + // Find the first block the caller has in the main chain + BOOST_FOREACH(const uint256& hash, vHave) + { + std::map::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) + { + CBlockIndex* pindex = (*mi).second; + if (pindex->IsInMainChain()) + return hash; + } + } + return (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet); + } + + int GetHeight() + { + CBlockIndex* pindex = GetBlockIndex(); + if (!pindex) + return 0; + return pindex->nHeight; + } +}; + + + + + + + + +class CTxMemPool +{ +public: + mutable CCriticalSection cs; + std::map mapTx; + std::map mapNextTx; + + bool accept(CTxDB& txdb, CTransaction &tx, + bool fCheckInputs, bool* pfMissingInputs); + bool addUnchecked(const uint256& hash, CTransaction &tx); + bool remove(CTransaction &tx); + void clear(); + void queryHashes(std::vector& vtxid); + + size_t size() + { + LOCK(cs); + return mapTx.size(); + } + + bool exists(uint256 hash) + { + return (mapTx.count(hash) != 0); + } + + CTransaction& lookup(uint256 hash) + { + return mapTx[hash]; + } +}; + +extern CTxMemPool mempool; + +#endif diff --git a/src/makefile.bsd b/src/makefile.bsd new file mode 100644 index 00000000..3b938f39 --- /dev/null +++ b/src/makefile.bsd @@ -0,0 +1,241 @@ +# Copyright (c) 2009-2010 Satoshi Nakamoto +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +USE_LEVELDB:=0 +USE_IPV6:=1 +ARCH:=$(uname -m) + +LINK:=$(CXX) + +DEFS=-DBOOST_SPIRIT_THREADSAFE -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS + +DEFS += $(addprefix -I,$(CURDIR) $(CURDIR)/obj $(BOOST_INCLUDE_PATH) $(BDB_INCLUDE_PATH) $(OPENSSL_INCLUDE_PATH)) +LIBS = $(addprefix -L,$(BOOST_LIB_PATH) $(BDB_LIB_PATH) $(OPENSSL_LIB_PATH)) + +LMODE = dynamic +LMODE2 = dynamic +ifdef STATIC + LMODE = static + ifeq (${STATIC}, all) + LMODE2 = static + endif +endif + +# for boost 1.37, add -mt to the boost libraries +LIBS += \ + -Wl,-B$(LMODE) \ + -l boost_system$(BOOST_LIB_SUFFIX) \ + -l boost_filesystem$(BOOST_LIB_SUFFIX) \ + -l boost_program_options$(BOOST_LIB_SUFFIX) \ + -l boost_thread$(BOOST_LIB_SUFFIX) \ + -l db_cxx$(BDB_LIB_SUFFIX) \ + -l ssl \ + -l crypto \ + -l execinfo + +ifneq (${USE_IPV6}, -) + DEFS += -DUSE_IPV6=$(USE_IPV6) +endif + +LIBS+= \ + -Wl,-B$(LMODE2) \ + -l z \ + -l pthread + + +# Hardening +# Make some classes of vulnerabilities unexploitable in case one is discovered. +# + # This is a workaround for Ubuntu bug #691722, the default -fstack-protector causes + # -fstack-protector-all to be ignored unless -fno-stack-protector is used first. + # see: https://bugs.launchpad.net/ubuntu/+source/gcc-4.5/+bug/691722 + HARDENING=-fno-stack-protector + + # Stack Canaries + # Put numbers at the beginning of each stack frame and check that they are the same. + # If a stack buffer if overflowed, it writes over the canary number and then on return + # when that number is checked, it won't be the same and the program will exit with + # a "Stack smashing detected" error instead of being exploited. + HARDENING+=-fstack-protector-all -Wstack-protector + + # Make some important things such as the global offset table read only as soon as + # the dynamic linker is finished building it. This will prevent overwriting of addresses + # which would later be jumped to. + LDHARDENING+=-Wl,-z,relro -Wl,-z,now + + # Build position independent code to take advantage of Address Space Layout Randomization + # offered by some kernels. + # see doc/build-unix.txt for more information. + ifdef PIE + HARDENING+=-fPIE + LDHARDENING+=-pie + endif + + # -D_FORTIFY_SOURCE=2 does some checking for potentially exploitable code patterns in + # the source such overflowing a statically defined buffer. + HARDENING+=-D_FORTIFY_SOURCE=2 +# + + +DEBUGFLAGS=-g + +ifeq (${ARCH}, i386) + EXT_OPTIONS=-msse2 -mssse3 +endif + +ifeq (${ARCH}, amd64) + EXT_OPTIONS=-mssse3 +endif + +xOPT_LEVEL=-O2 +ifeq (${USE_O3}, 1) + xOPT_LEVEL=-O3 +endif + +# CXXFLAGS can be specified on the make command line, so we use xCXXFLAGS that only +# adds some defaults in front. Unfortunately, CXXFLAGS=... $(CXXFLAGS) does not work. +xCXXFLAGS=$(xOPT_LEVEL) $(EXT_OPTIONS) -pthread -Wall -Wextra -Wno-ignored-qualifiers -Wformat -Wformat-security -Wno-unused-parameter \ + $(DEBUGFLAGS) $(DEFS) $(HARDENING) $(CXXFLAGS) + +# LDFLAGS can be specified on the make command line, so we use xLDFLAGS that only +# adds some defaults in front. Unfortunately, LDFLAGS=... $(LDFLAGS) does not work. +xLDFLAGS=$(LDHARDENING) $(LDFLAGS) + +OBJS= \ + obj/alert.o \ + obj/version.o \ + obj/checkpoints.o \ + obj/netbase.o \ + obj/addrman.o \ + obj/crypter.o \ + obj/base58.o \ + obj/key.o \ + obj/db.o \ + obj/init.o \ + obj/irc.o \ + obj/keystore.o \ + obj/main.o \ + obj/miner.o \ + obj/net.o \ + obj/ntp.o \ + obj/stun.o \ + obj/protocol.o \ + obj/bitcoinrpc.o \ + obj/rpcdump.o \ + obj/rpcnet.o \ + obj/rpcmining.o \ + obj/rpcwallet.o \ + obj/rpcblockchain.o \ + obj/rpcrawtransaction.o \ + obj/script.o \ + obj/sync.o \ + obj/util.o \ + obj/wallet.o \ + obj/walletdb.o \ + obj/noui.o \ + obj/kernel.o \ + obj/kernel_worker.o + +all: XPd + +# +# LevelDB support +# +ifeq (${USE_LEVELDB}, 1) +LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a +DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) -DUSE_LEVELDB +DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers) +OBJS += obj/txdb-leveldb.o +leveldb/libleveldb.a: + @echo "Building LevelDB ..."; cd leveldb; gmake libleveldb.a libmemenv.a; cd ..; +obj/txdb-leveldb.o: leveldb/libleveldb.a +endif +ifneq (${USE_LEVELDB}, 1) +OBJS += obj/txdb-bdb.o +endif + +ifeq (${USE_ASM}, 1) + +DEFS += -DUSE_ASM + +# Assembler implementation +OBJS += crypto/scrypt/asm/obj/scrypt-arm.o crypto/scrypt/asm/obj/scrypt-x86.o crypto/scrypt/asm/obj/scrypt-x86_64.o crypto/scrypt/asm/obj/asm-wrapper.o +OBJS += crypto/sha2/asm/obj/sha2-arm.o crypto/sha2/asm/obj/sha2-x86.o crypto/sha2/asm/obj/sha2-x86_64.o + +crypto/scrypt/asm/obj/scrypt-x86.o: crypto/scrypt/asm/scrypt-x86.S + $(CXX) -c $(xCXXFLAGS) -MMD -o $@ $< + +crypto/scrypt/asm/obj/scrypt-x86_64.o: crypto/scrypt/asm/scrypt-x86_64.S + $(CXX) -c $(xCXXFLAGS) -MMD -o $@ $< + +crypto/scrypt/asm/obj/scrypt-arm.o: crypto/scrypt/asm/scrypt-arm.S + $(CXX) -c $(xCXXFLAGS) -MMD -o $@ $< + +crypto/scrypt/asm/obj/asm-wrapper.o: crypto/scrypt/asm/asm-wrapper.cpp + $(CXX) -c $(xCXXFLAGS) -MMD -o $@ $< + +crypto/sha2/asm/obj/sha2-x86.o: crypto/sha2/asm/sha2-x86.S + $(CXX) -c $(xCXXFLAGS) -MMD -o $@ $< + +crypto/sha2/asm/obj/sha2-x86_64.o: crypto/sha2/asm/sha2-x86_64.S + $(CXX) -c $(xCXXFLAGS) -MMD -o $@ $< + +crypto/sha2/asm/obj/sha2-arm.o: crypto/sha2/asm/sha2-arm.S + $(CXX) -c $(xCXXFLAGS) -MMD -o $@ $< + +else +ifeq (${USE_SSE2}, 1) +# Intrinsic implementation +DEFS += -DUSE_SSE2 +OBJS += crypto/scrypt/intrin/obj/scrypt-sse2.o + +crypto/scrypt/intrin/obj/scrypt-sse2.o: crypto/scrypt/intrin/scrypt-sse2.cpp + $(CXX) -c $(xCXXFLAGS) -MMD -o $@ $< +else +# Generic implementation +OBJS += crypto/scrypt/generic/obj/scrypt-generic.o + +crypto/scrypt/generic/obj/scrypt-generic.o: crypto/scrypt/generic/scrypt-generic.cpp + $(CXX) -c $(xCXXFLAGS) -MMD -o $@ $< +endif +endif + +# auto-generated dependencies: +-include obj/*.P + +obj/build.h: FORCE + /bin/sh ../share/genbuild.sh obj/build.h +version.cpp: obj/build.h +DEFS += -DHAVE_BUILD_INFO + +obj/%.o: %.cpp + $(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $< + @cp $(@:%.o=%.d) $(@:%.o=%.P); \ + sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ + -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \ + rm -f $(@:%.o=%.d) + +XPd: $(OBJS:obj/%=obj/%) + $(LINK) $(xCXXFLAGS) -o $@ $^ $(xLDFLAGS) $(LIBS) + +clean: + -rm -f XPd + -rm -f obj/*.o + -rm -f obj/*.P + -rm -f obj/*.d + -rm -f crypto/scrypt/asm/obj/*.o + -rm -f crypto/scrypt/asm/obj/*.P + -rm -f crypto/scrypt/asm/obj/*.d + -rm -f crypto/scrypt/intrin/obj/*.o + -rm -f crypto/scrypt/intrin/obj/*.P + -rm -f crypto/scrypt/intrin/obj/*.d + -rm -f crypto/scrypt/generic/obj/*.o + -rm -f crypto/scrypt/generic/obj/*.P + -rm -f crypto/scrypt/generic/obj/*.d + -rm -f crypto/sha2/asm/obj/*.o + -rm -f crypto/sha2/asm/obj/*.P + -rm -f crypto/sha2/asm/obj/*.d + -rm -f obj/build.h + +FORCE: diff --git a/src/makefile.linux-mingw b/src/makefile.linux-mingw new file mode 100644 index 00000000..0e094d9d --- /dev/null +++ b/src/makefile.linux-mingw @@ -0,0 +1,200 @@ +# Copyright (c) 2009-2010 Satoshi Nakamoto +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +TARGET_PLATFORM:=i686 +#TARGET_PLATFORM:=x86_64 +CC:=$(TARGET_PLATFORM)-w64-mingw32-gcc +CXX:=$(TARGET_PLATFORM)-w64-mingw32-g++ +RANLIB:=$(TARGET_PLATFORM)-w64-mingw32-ranlib +STRIP:=$(TARGET_PLATFORM)-w64-mingw32-strip + +DEPSDIR:=/usr/$(TARGET_PLATFORM)-w64-mingw32 + +BOOST_LIB_PATH:=$(DEPSDIR)/boost_1_57_0/stage/lib +BDB_LIB_PATH:=$(DEPSDIR)/db-6.0.20.NC/build_unix +OPENSSL_LIB_PATH:=$(DEPSDIR)/openssl-1.0.1h + +BOOST_INCLUDE_PATH:=$(DEPSDIR)/boost_1_57_0 +BDB_INCLUDE_PATH:=$(DEPSDIR)/db-6.0.20.NC/build_unix +OPENSSL_INCLUDE_PATH:=$(DEPSDIR)/openssl-1.0.1h/include + +USE_LEVELDB:=0 +USE_IPV6:=1 + +INCLUDEPATHS= \ + -I"$(CURDIR)" \ + -I"$(CURDIR)"/obj \ + -I"$(BOOST_INCLUDE_PATH)" \ + -I"$(BDB_INCLUDE_PATH)" \ + -I"$(OPENSSL_INCLUDE_PATH)" + +LIBPATHS= \ + -L"$(BOOST_LIB_PATH)" \ + -L"$(BDB_LIB_PATH)" \ + -L"$(OPENSSL_LIB_PATH)" + +LIBS= \ + -l boost_system-mt \ + -l boost_filesystem-mt \ + -l boost_program_options-mt \ + -l boost_thread_win32-mt \ + -l boost_chrono-mt \ + -l db_cxx \ + -l ssl \ + -l crypto \ + -Wl,-Bstatic -lpthread -Wl,-Bdynamic + +xOPT_LEVEL=-O2 +ifeq (${USE_O3}, 1) + xOPT_LEVEL=-O3 +endif + +DEFS=-D_MT -DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS + +DEBUGFLAGS=-g +CFLAGS=$(xOPT_LEVEL) -msse2 -mssse3 -w -Wall -Wextra -Wno-ignored-qualifiers -Wformat -Wformat-security -Wno-unused-parameter $(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS) +LDFLAGS=-Wl,--dynamicbase -Wl,--nxcompat -static-libgcc -static-libstdc++ + +ifneq (${USE_IPV6}, -) + DEFS += -DUSE_IPV6=$(USE_IPV6) +endif + +LIBS += -l mingwthrd -l kernel32 -l user32 -l gdi32 -l comdlg32 -l winspool -l winmm -l shell32 -l comctl32 -l ole32 -l oleaut32 -l uuid -l rpcrt4 -l advapi32 -l ws2_32 -l mswsock -l shlwapi + +# TODO: make the mingw builds smarter about dependencies, like the linux/osx builds are +HEADERS = $(wildcard *.h) + +OBJS= \ + obj/alert.o \ + obj/version.o \ + obj/checkpoints.o \ + obj/netbase.o \ + obj/addrman.o \ + obj/crypter.o \ + obj/base58.o \ + obj/key.o \ + obj/db.o \ + obj/init.o \ + obj/irc.o \ + obj/keystore.o \ + obj/main.o \ + obj/miner.o \ + obj/net.o \ + obj/ntp.o \ + obj/stun.o \ + obj/protocol.o \ + obj/bitcoinrpc.o \ + obj/rpcdump.o \ + obj/rpcnet.o \ + obj/rpcmining.o \ + obj/rpcwallet.o \ + obj/rpcblockchain.o \ + obj/rpcrawtransaction.o \ + obj/script.o \ + obj/sync.o \ + obj/util.o \ + obj/wallet.o \ + obj/walletdb.o \ + obj/noui.o \ + obj/kernel.o \ + obj/kernel_worker.o + +all: XPd.exe + +# +# LevelDB support +# +ifeq (${USE_LEVELDB}, 1) +LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a +DEFS += -I"$(CURDIR)/leveldb/include" -DUSE_LEVELDB +DEFS += -I"$(CURDIR)/leveldb/helpers" +OBJS += obj/txdb-leveldb.o +leveldb/libleveldb.a: + @echo "Building LevelDB ..." && cd leveldb && CC=$(CC) CXX=$(CXX) TARGET_OS=OS_WINDOWS_CROSSCOMPILE CXXFLAGS="-I$(INCLUDEPATHS)" LDFLAGS="-L$(LIBPATHS)" $(MAKE) libleveldb.a libmemenv.a && $(RANLIB) libleveldb.a && $(RANLIB) libmemenv.a && cd .. +obj/txdb-leveldb.o: leveldb/libleveldb.a +else +OBJS += obj/txdb-bdb.o +endif + +ifeq (${USE_ASM}, 1) +# Assembler implementation +OBJS += crypto/scrypt/asm/obj/scrypt-arm.o crypto/scrypt/asm/obj/scrypt-x86.o crypto/scrypt/asm/obj/scrypt-x86_64.o crypto/scrypt/asm/obj/asm-wrapper.o +OBJS += crypto/sha2/asm/obj/sha2-arm.o crypto/sha2/asm/obj/sha2-x86.o crypto/sha2/asm/obj/sha2-x86_64.o + +crypto/scrypt/asm/obj/scrypt-x86.o: crypto/scrypt/asm/scrypt-x86.S + $(CXX) -c $(CFLAGS) -MMD -o $@ $< + +crypto/scrypt/asm/obj/scrypt-x86_64.o: crypto/scrypt/asm/scrypt-x86_64.S + $(CXX) -c $(CFLAGS) -MMD -o $@ $< + +crypto/scrypt/asm/obj/scrypt-arm.o: crypto/scrypt/asm/scrypt-arm.S + $(CXX) -c $(CFLAGS) -MMD -o $@ $< + +crypto/scrypt/asm/obj/asm-wrapper.o: crypto/scrypt/asm/asm-wrapper.cpp + $(CXX) -c $(CFLAGS) -MMD -o $@ $< + +crypto/sha2/asm/obj/sha2-x86.o: crypto/sha2/asm/sha2-x86.S + $(CXX) -c $(CFLAGS) -MMD -o $@ $< + +crypto/sha2/asm/obj/sha2-x86_64.o: crypto/sha2/asm/sha2-x86_64.S + $(CXX) -c $(CFLAGS) -MMD -o $@ $< + +crypto/sha2/asm/obj/sha2-arm.o: crypto/sha2/asm/sha2-arm.S + $(CXX) -c $(CFLAGS) -MMD -o $@ $< + +DEFS += -DUSE_ASM +else +ifeq (${USE_SSE2}, 1) +# Intrinsic implementation +DEFS += -DUSE_SSE2 +OBJS += crypto/scrypt/intrin/obj/scrypt-sse2.o + +crypto/scrypt/intrin/obj/scrypt-sse2.o: crypto/scrypt/intrin/scrypt-sse2.cpp $(HEADERS) + $(CXX) -c $(CFLAGS) -MMD -o $@ $< +else +ifneq (${USE_ASM}, 1) +# Generic implementation +OBJS += crypto/scrypt/generic/obj/scrypt-generic.o + +crypto/scrypt/generic/obj/scrypt-generic.o: crypto/scrypt/generic/scrypt-generic.cpp + $(CXX) -c $(CFLAGS) -MMD -o $@ $< +endif +endif +endif + + + +obj/build.h: FORCE + /bin/sh ../share/genbuild.sh obj/build.h +version.cpp: obj/build.h +DEFS += -DHAVE_BUILD_INFO + +obj/%.o: %.cpp $(HEADERS) + $(CXX) -c $(CFLAGS) -o $@ $< + +XPd.exe: $(OBJS:obj/%=obj/%) + $(CXX) $(CFLAGS) $(LDFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) -lshlwapi + $(STRIP) XPd.exe + +clean: + -rm -f XPd.exe + -rm -f obj/*.o + -rm -f obj/*.P + -rm -f obj/*.d + -rm -f crypto/scrypt/asm/obj/*.o + -rm -f crypto/scrypt/asm/obj/*.P + -rm -f crypto/scrypt/asm/obj/*.d + -rm -f crypto/scrypt/intrin/obj/*.o + -rm -f crypto/scrypt/intrin/obj/*.P + -rm -f crypto/scrypt/intrin/obj/*.d + -rm -f crypto/scrypt/generic/obj/*.o + -rm -f crypto/scrypt/generic/obj/*.P + -rm -f crypto/scrypt/generic/obj/*.d + -rm -f crypto/sha2/asm/obj/*.o + -rm -f crypto/sha2/asm/obj/*.P + -rm -f crypto/sha2/asm/obj/*.d + -rm -f obj/build.h + cd leveldb && TARGET_OS=OS_WINDOWS_CROSSCOMPILE $(MAKE) clean && cd .. + +FORCE: diff --git a/src/makefile.mingw b/src/makefile.mingw new file mode 100644 index 00000000..2e46ea8d --- /dev/null +++ b/src/makefile.mingw @@ -0,0 +1,168 @@ +# Copyright (c) 2009-2010 Satoshi Nakamoto +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +USE_LEVELDB:=0 +CC=gcc + + +USE_IPV6:=1 +USE_SSE2:=1 + +BOOST_SUFFIX?=-mgw49-mt-s-1_57 + +INCLUDEPATHS= \ + -I"$(CURDIR)" \ + -I"/c/deps/boost_1_57_0" \ + -I"/c/deps" \ + -I"/c/deps/db-6.0.20/build_unix" \ + -I"/c/deps/openssl-1.0.2/include" + +LIBPATHS= \ + -L"$(CURDIR)/leveldb" \ + -L"/c/deps/boost_1_57_0/stage/lib" \ + -L"/c/deps/db-6.0.20/build_unix" \ + -L"/c/deps/openssl-1.0.2" + +LIBS= \ + -l leveldb \ + -l memenv \ + -l boost_system$(BOOST_SUFFIX) \ + -l boost_filesystem$(BOOST_SUFFIX) \ + -l boost_program_options$(BOOST_SUFFIX) \ + -l boost_thread$(BOOST_SUFFIX) \ + -l boost_chrono$(BOOST_SUFFIX) \ + -l db_cxx \ + -l ssl \ + -l crypto + +xOPT_LEVEL=-O2 +ifeq (${USE_O3}, 1) + xOPT_LEVEL=-O3 +endif + +DEFS=-DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS +DEBUGFLAGS=-g +CFLAGS=-mthreads $(xOPT_LEVEL) -msse2 -mssse3 -w -Wall -Wextra -Wno-ignored-qualifiers -Wformat -Wformat-security -Wno-unused-parameter $(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS) +LDFLAGS=-Wl,--dynamicbase -Wl,--nxcompat -Wl,--large-address-aware -static + +ifneq (${USE_IPV6}, -) + DEFS += -DUSE_IPV6=$(USE_IPV6) +endif + +LIBS += -l kernel32 -l user32 -l gdi32 -l comdlg32 -l winspool -l winmm -l shell32 -l comctl32 -l ole32 -l oleaut32 -l uuid -l rpcrt4 -l advapi32 -l ws2_32 -l mswsock -l shlwapi + +# TODO: make the mingw builds smarter about dependencies, like the linux/osx builds are +HEADERS = $(wildcard *.h) + +OBJS= \ + obj/alert.o \ + obj/version.o \ + obj/checkpoints.o \ + obj/netbase.o \ + obj/addrman.o \ + obj/crypter.o \ + obj/base58.o \ + obj/key.o \ + obj/db.o \ + obj/init.o \ + obj/irc.o \ + obj/keystore.o \ + obj/main.o \ + obj/miner.o \ + obj/net.o \ + obj/ntp.o \ + obj/stun.o \ + obj/protocol.o \ + obj/bitcoinrpc.o \ + obj/rpcdump.o \ + obj/rpcnet.o \ + obj/rpcmining.o \ + obj/rpcwallet.o \ + obj/rpcblockchain.o \ + obj/rpcrawtransaction.o \ + obj/script.o \ + obj/sync.o \ + obj/util.o \ + obj/wallet.o \ + obj/walletdb.o \ + obj/noui.o \ + obj/kernel.o \ + obj/kernel_worker.o + +all: XPd.exe + +# +# LevelDB support +# +ifdef USE_LEVELDB +LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a +DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) -DUSE_LEVELDB +DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers) +OBJS += obj/txdb-leveldb.o +leveldb/libleveldb.a: + cd leveldb;TARGET_OS=NATIVE_WINDOWS make libleveldb.a libmemenv.a;; cd .. +obj/txdb-leveldb.o: leveldb/libleveldb.a +else +OBJS += obj/txdb-bdb.o +endif + +ifdef USE_ASM +# Assembler implementation +OBJS += crypto/scrypt/asm/obj/scrypt-arm.o crypto/scrypt/asm/obj/scrypt-x86.o crypto/scrypt/asm/obj/scrypt-x86_64.o crypto/scrypt/asm/obj/asm-wrapper.o +OBJS += crypto/sha2/asm/obj/sha2-arm.o crypto/sha2/asm/obj/sha2-x86.o crypto/sha2/asm/obj/sha2-x86_64.o + +crypto/scrypt/asm/obj/scrypt-x86.o: crypto/scrypt/asm/scrypt-x86.S + $(CXX) -c $(CFLAGS) -MMD -o $@ $< + +crypto/scrypt/asm/obj/scrypt-x86_64.o: crypto/scrypt/asm/scrypt-x86_64.S + $(CXX) -c $(CFLAGS) -MMD -o $@ $< + +crypto/scrypt/asm/obj/scrypt-arm.o: crypto/scrypt/asm/scrypt-arm.S + $(CXX) -c $(CFLAGS) -MMD -o $@ $< + +crypto/scrypt/asm/obj/asm-wrapper.o: crypto/scrypt/asm/asm-wrapper.cpp + $(CXX) -c $(CFLAGS) -MMD -o $@ $< + +crypto/sha2/asm/obj/sha2-x86.o: crypto/sha2/asm/sha2-x86.S + $(CXX) -c $(CFLAGS) -MMD -o $@ $< + +crypto/sha2/asm/obj/sha2-x86_64.o: crypto/sha2/asm/sha2-x86_64.S + $(CXX) -c $(CFLAGS) -MMD -o $@ $< + +crypto/sha2/asm/obj/sha2-arm.o: crypto/sha2/asm/sha2-arm.S + $(CXX) -c $(CFLAGS) -MMD -o $@ $< + +DEFS += -DUSE_ASM + +else +ifdef USE_SSE2 +# Intrinsic implementation +DEFS += -DUSE_SSE2 +OBJS += scrypt-intrin/obj/scrypt-sse2.o + +crypto/scrypt/intrin/obj/scrypt-sse2.o: crypto/scrypt/intrin/scrypt-sse2.cpp + $(CXX) -c $(CFLAGS) -MMD -o $@ $< +else +# Generic implementation +OBJS += obj/scrypt-generic.o + +crypto/scrypt/obj/scrypt-generic.o: crypto/scrypt/generic/scrypt-generic.cpp + $(CXX) -c $(CFLAGS) -MMD -o $@ $< +endif +endif + + +obj/%.o: %.cpp $(HEADERS) + g++ -c $(CFLAGS) -o $@ $< + +XPd.exe: $(OBJS:obj/%=obj/%) + g++ $(CFLAGS) $(LDFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) + +clean: + -del /Q XPd + -del /Q obj\* + -del /Q crypto\scrypt\asm\obj\* + -del /Q crypto\sha2\asm\obj\* + +FORCE: diff --git a/src/makefile.osx b/src/makefile.osx new file mode 100644 index 00000000..368dcaee --- /dev/null +++ b/src/makefile.osx @@ -0,0 +1,198 @@ +# -*- mode: Makefile; -*- +# Copyright (c) 2011 Bitcoin Developers +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# Mac OS X makefile for bitcoin +# Originally by Laszlo Hanyecz (solar@heliacal.net) + +CXX=llvm-g++ +DEPSDIR=/opt/local + +INCLUDEPATHS= \ + -I"$(CURDIR)" \ + -I"$(CURDIR)"/obj \ + -I"$(DEPSDIR)/include" \ + -I"$(DEPSDIR)/include/db48" + +LIBPATHS= \ + -L"$(DEPSDIR)/lib" \ + -L"$(DEPSDIR)/lib/db48" + +USE_LEVELDB:=0 +USE_IPV6:=1 + +LIBS= -dead_strip + +ifdef STATIC +# Build STATIC if you are redistributing the bitcoind binary +LIBS += \ + $(DEPSDIR)/lib/db48/libdb_cxx-4.8.a \ + $(DEPSDIR)/lib/libboost_system-mt.a \ + $(DEPSDIR)/lib/libboost_filesystem-mt.a \ + $(DEPSDIR)/lib/libboost_program_options-mt.a \ + $(DEPSDIR)/lib/libboost_thread-mt.a \ + $(DEPSDIR)/lib/libssl.a \ + $(DEPSDIR)/lib/libcrypto.a \ + -lz +else +LIBS += \ + -ldb_cxx-4.8 \ + -lboost_system-mt \ + -lboost_filesystem-mt \ + -lboost_program_options-mt \ + -lboost_thread-mt \ + -lssl \ + -lcrypto \ + -lz +endif + +DEFS=-DMAC_OSX -DMSG_NOSIGNAL=0 -DBOOST_SPIRIT_THREADSAFE -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS + +ifdef RELEASE +# Compile for maximum compatibility and smallest size. +# This requires that dependencies are compiled +# the same way. +CFLAGS = -O2 -msse2 -mssse3 +else +CFLAGS = -g -msse2 -mssse3 +endif + +# ppc doesn't work because we don't support big-endian +CFLAGS += -Wall -Wextra -Wformat -Wno-ignored-qualifiers -Wformat-security -Wno-unused-parameter \ + $(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS) + +OBJS= \ + obj/alert.o \ + obj/version.o \ + obj/checkpoints.o \ + obj/netbase.o \ + obj/addrman.o \ + obj/crypter.o \ + obj/base58.o \ + obj/key.o \ + obj/db.o \ + obj/init.o \ + obj/irc.o \ + obj/keystore.o \ + obj/main.o \ + obj/miner.o \ + obj/net.o \ + obj/ntp.o \ + obj/stun.o \ + obj/protocol.o \ + obj/bitcoinrpc.o \ + obj/rpcdump.o \ + obj/rpcnet.o \ + obj/rpcmining.o \ + obj/rpcwallet.o \ + obj/rpcblockchain.o \ + obj/rpcrawtransaction.o \ + obj/script.o \ + obj/sync.o \ + obj/util.o \ + obj/wallet.o \ + obj/walletdb.o \ + obj/noui.o \ + obj/kernel.o \ + obj/kernel_worker.o + +ifneq (${USE_IPV6}, -) + DEFS += -DUSE_IPV6=$(USE_IPV6) +endif + +all: XPd + +# +# LevelDB support +# +ifdef USE_LEVELDB +LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a +DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) -DUSE_LEVELDB +DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers) +OBJS += obj/txdb-leveldb.o +leveldb/libleveldb.a: + @echo "Building LevelDB ..."; cd leveldb; make; cd .. +obj/txdb-leveldb.o: leveldb/libleveldb.a +else +OBJS += obj/txdb-bdb.o +endif + +ifeq (${USE_ASM}, 1) +# Assembler implementation +OBJS += crypto/scrypt/asm/obj/scrypt-x86.o crypto/scrypt/asm/obj/scrypt-x86_64.o crypto/scrypt/asm/obj/asm-wrapper.o +OBJS += crypto/sha2/asm/obj/sha2-x86.o crypto/sha2/asm/obj/sha2-x86_64.o + +crypto/scrypt/asm/obj/scrypt-x86.o: crypto/scrypt/asm/scrypt-x86.S + $(CXX) -c $(CFLAGS) -MMD -o $@ $< + +crypto/scrypt/asm/obj/scrypt-x86_64.o: crypto/scrypt/asm/scrypt-x86_64.S + $(CXX) -c $(CFLAGS) -MMD -o $@ $< + +crypto/scrypt/asm/obj/asm-wrapper.o: crypto/scrypt/asm/asm-wrapper.cpp + $(CXX) -c $(CFLAGS) -MMD -o $@ $< + +crypto/sha2/asm/obj/sha2-x86.o: crypto/sha2/asm/sha2-x86.S + $(CXX) -c $(CFLAGS) -MMD -o $@ $< + +crypto/sha2/asm/obj/sha2-x86_64.o: crypto/sha2/asm/sha2-x86_64.S + $(CXX) -c $(CFLAGS) -MMD -o $@ $< + +DEFS += -DUSE_ASM + +else +ifeq (${USE_SSE2}, 1) +# Intrinsic implementation +DEFS += -DUSE_SSE2 +OBJS += crypto/scrypt/intrin/obj/scrypt-sse2.o + +crypto/scrypt/intrin/obj/scrypt-sse2.o: crypto/scrypt/intrin/scrypt-sse2.cpp + $(CXX) -c $(CFLAGS) -MMD -o $@ $< +else +# Generic implementation +OBJS += crypto/scrypt/generic/obj/scrypt-generic.o + +crypto/scrypt/generic/obj/scrypt-generic.o: crypto/scrypt/generic/scrypt-generic.cpp + $(CXX) -c $(CFLAGS) -MMD -o $@ $< +endif +endif + + +# auto-generated dependencies: +-include obj/*.P + +obj/build.h: FORCE + /bin/sh ../share/genbuild.sh obj/build.h +version.cpp: obj/build.h +DEFS += -DHAVE_BUILD_INFO + +obj/%.o: %.cpp + $(CXX) -c $(CFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $< + @cp $(@:%.o=%.d) $(@:%.o=%.P); \ + sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ + -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \ + rm -f $(@:%.o=%.d) + +XPd: $(OBJS:obj/%=obj/%) + $(CXX) $(CFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) + +clean: + -rm -f XPd + -rm -f obj/*.o + -rm -f obj/*.P + -rm -f obj/*.d + -rm -f crypto/scrypt/asm/obj/*.o + -rm -f crypto/scrypt/asm/obj/*.P + -rm -f crypto/scrypt/asm/obj/*.d + -rm -f crypto/scrypt/intrin/obj/*.o + -rm -f crypto/scrypt/intrin/obj/*.P + -rm -f crypto/scrypt/intrin/obj/*.d + -rm -f crypto/scrypt/generic/obj/*.o + -rm -f crypto/scrypt/generic/obj/*.P + -rm -f crypto/scrypt/generic/obj/*.d + -rm -f crypto/sha2/asm/obj/*.o + -rm -f crypto/sha2/asm/obj/*.P + -rm -f crypto/sha2/asm/obj/*.d + -rm -f obj/build.h + +FORCE: diff --git a/src/makefile.unix b/src/makefile.unix new file mode 100644 index 00000000..4ed61224 --- /dev/null +++ b/src/makefile.unix @@ -0,0 +1,243 @@ +# Copyright (c) 2009-2010 Satoshi Nakamoto +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +USE_LEVELDB:=0 +USE_IPV6:=1 + +LINK:=$(CXX) +ARCH:=$(shell uname -m) + +DEFS=-DBOOST_SPIRIT_THREADSAFE -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS + +DEFS += $(addprefix -I,$(CURDIR) $(CURDIR)/obj $(BOOST_INCLUDE_PATH) $(BDB_INCLUDE_PATH) $(OPENSSL_INCLUDE_PATH)) +LIBS = $(addprefix -L,$(BOOST_LIB_PATH) $(BDB_LIB_PATH) $(OPENSSL_LIB_PATH)) + +LMODE = dynamic +LMODE2 = dynamic +ifdef STATIC + LMODE = static + ifeq (${STATIC}, all) + LMODE2 = static + endif +endif + +# for boost 1.37, add -mt to the boost libraries +LIBS += \ + -Wl,-B$(LMODE) \ + -l boost_system$(BOOST_LIB_SUFFIX) \ + -l boost_filesystem$(BOOST_LIB_SUFFIX) \ + -l boost_program_options$(BOOST_LIB_SUFFIX) \ + -l boost_thread$(BOOST_LIB_SUFFIX) \ + -l db_cxx$(BDB_LIB_SUFFIX) \ + -l ssl \ + -l crypto + +ifneq (${USE_IPV6}, -) + DEFS += -DUSE_IPV6=$(USE_IPV6) +endif + +LIBS+= \ + -Wl,-B$(LMODE2) \ + -l z \ + -l dl \ + -l pthread + + +# Hardening +# Make some classes of vulnerabilities unexploitable in case one is discovered. +# + # This is a workaround for Ubuntu bug #691722, the default -fstack-protector causes + # -fstack-protector-all to be ignored unless -fno-stack-protector is used first. + # see: https://bugs.launchpad.net/ubuntu/+source/gcc-4.5/+bug/691722 + HARDENING=-fno-stack-protector + + # Stack Canaries + # Put numbers at the beginning of each stack frame and check that they are the same. + # If a stack buffer if overflowed, it writes over the canary number and then on return + # when that number is checked, it won't be the same and the program will exit with + # a "Stack smashing detected" error instead of being exploited. + HARDENING+=-fstack-protector-all -Wstack-protector + + # Make some important things such as the global offset table read only as soon as + # the dynamic linker is finished building it. This will prevent overwriting of addresses + # which would later be jumped to. + LDHARDENING+=-Wl,-z,relro -Wl,-z,now + + # Build position independent code to take advantage of Address Space Layout Randomization + # offered by some kernels. + # see doc/build-unix.txt for more information. + ifdef PIE + HARDENING+=-fPIE + LDHARDENING+=-pie + endif + + # -D_FORTIFY_SOURCE=2 does some checking for potentially exploitable code patterns in + # the source such overflowing a statically defined buffer. + HARDENING+=-D_FORTIFY_SOURCE=2 +# + + +DEBUGFLAGS=-g + + +ifeq (${ARCH}, i686) + EXT_OPTIONS=-msse2 -mssse3 +endif + +ifeq (${ARCH}, x86_64) + EXT_OPTIONS=-mssse3 +endif + +xOPT_LEVEL=-O2 +ifeq (${USE_O3}, 1) + xOPT_LEVEL=-O3 +endif + +# CXXFLAGS can be specified on the make command line, so we use xCXXFLAGS that only +# adds some defaults in front. Unfortunately, CXXFLAGS=... $(CXXFLAGS) does not work. +xCXXFLAGS=$(xOPT_LEVEL) $(EXT_OPTIONS) -pthread -Wall -Wextra -Wno-ignored-qualifiers -Wformat -Wformat-security -Wno-unused-parameter \ + $(DEBUGFLAGS) $(DEFS) $(HARDENING) $(CXXFLAGS) + +# LDFLAGS can be specified on the make command line, so we use xLDFLAGS that only +# adds some defaults in front. Unfortunately, LDFLAGS=... $(LDFLAGS) does not work. +xLDFLAGS=$(LDHARDENING) $(LDFLAGS) + +OBJS= \ + obj/alert.o \ + obj/version.o \ + obj/checkpoints.o \ + obj/netbase.o \ + obj/addrman.o \ + obj/crypter.o \ + obj/base58.o \ + obj/key.o \ + obj/db.o \ + obj/init.o \ + obj/irc.o \ + obj/keystore.o \ + obj/miner.o \ + obj/main.o \ + obj/net.o \ + obj/ntp.o \ + obj/stun.o \ + obj/protocol.o \ + obj/bitcoinrpc.o \ + obj/rpcdump.o \ + obj/rpcnet.o \ + obj/rpcmining.o \ + obj/rpcwallet.o \ + obj/rpcblockchain.o \ + obj/rpcrawtransaction.o \ + obj/script.o \ + obj/sync.o \ + obj/util.o \ + obj/wallet.o \ + obj/walletdb.o \ + obj/noui.o \ + obj/kernel.o \ + obj/kernel_worker.o + +all: XPd + +# +# LevelDB support +# +ifeq (${USE_LEVELDB}, 1) +LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a +DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) -DUSE_LEVELDB +DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers) +OBJS += obj/txdb-leveldb.o +leveldb/libleveldb.a: + @echo "Building LevelDB ..."; cd leveldb; make libleveldb.a libmemenv.a; cd ..; +obj/txdb-leveldb.o: leveldb/libleveldb.a +endif +ifneq (${USE_LEVELDB}, 1) +OBJS += obj/txdb-bdb.o +endif + +ifeq (${USE_ASM}, 1) +# Assembler implementation +OBJS += crypto/scrypt/asm/obj/scrypt-arm.o crypto/scrypt/asm/obj/scrypt-x86.o crypto/scrypt/asm/obj/scrypt-x86_64.o crypto/scrypt/asm/obj/asm-wrapper.o +OBJS += crypto/sha2/asm/obj/sha2-arm.o crypto/sha2/asm/obj/sha2-x86.o crypto/sha2/asm/obj/sha2-x86_64.o + +crypto/scrypt/asm/obj/scrypt-x86.o: crypto/scrypt/asm/scrypt-x86.S + $(CXX) -c $(xCXXFLAGS) -MMD -o $@ $< + +crypto/scrypt/asm/obj/scrypt-x86_64.o: crypto/scrypt/asm/scrypt-x86_64.S + $(CXX) -c $(xCXXFLAGS) -MMD -o $@ $< + +crypto/scrypt/asm/obj/scrypt-arm.o: crypto/scrypt/asm/scrypt-arm.S + $(CXX) -c $(xCXXFLAGS) -MMD -o $@ $< + +crypto/scrypt/asm/obj/asm-wrapper.o: crypto/scrypt/asm/asm-wrapper.cpp + $(CXX) -c $(xCXXFLAGS) -MMD -o $@ $< + +crypto/sha2/asm/obj/sha2-x86.o: crypto/sha2/asm/sha2-x86.S + $(CXX) -c $(xCXXFLAGS) -MMD -o $@ $< + +crypto/sha2/asm/obj/sha2-x86_64.o: crypto/sha2/asm/sha2-x86_64.S + $(CXX) -c $(xCXXFLAGS) -MMD -o $@ $< + +crypto/sha2/asm/obj/sha2-arm.o: crypto/sha2/asm/sha2-arm.S + $(CXX) -c $(xCXXFLAGS) -MMD -o $@ $< + +DEFS += -DUSE_ASM + +else +ifeq (${USE_SSE2}, 1) +# Intrinsic implementation +DEFS += -DUSE_SSE2 +OBJS += crypto/scrypt/intrin/obj/scrypt-sse2.o + +crypto/scrypt/intrin/obj/scrypt-sse2.o: crypto/scrypt/intrin/scrypt-sse2.cpp + $(CXX) -c $(xCXXFLAGS) -MMD -o $@ $< +else +# Generic implementation +OBJS += crypto/scrypt/generic/obj/scrypt-generic.o + +crypto/scrypt/generic/obj/scrypt-generic.o: crypto/scrypt/generic/scrypt-generic.cpp + $(CXX) -c $(xCXXFLAGS) -MMD -o $@ $< +endif +endif + + +# auto-generated dependencies: +-include obj/*.P + +obj/build.h: FORCE + /bin/sh ../share/genbuild.sh obj/build.h +version.cpp: obj/build.h +DEFS += -DHAVE_BUILD_INFO + + +obj/%.o: %.cpp + $(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $< + @cp $(@:%.o=%.d) $(@:%.o=%.P); \ + sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ + -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \ + rm -f $(@:%.o=%.d) + +XPd: $(OBJS:obj/%=obj/%) + $(LINK) $(xCXXFLAGS) -o $@ $^ $(xLDFLAGS) $(LIBS) + +clean: + -rm -f XPd + -rm -f obj/*.o + -rm -f obj/*.P + -rm -f obj/*.d + -rm -f crypto/scrypt/asm/obj/*.o + -rm -f crypto/scrypt/asm/obj/*.P + -rm -f crypto/scrypt/asm/obj/*.d + -rm -f crypto/scrypt/intrin/obj/*.o + -rm -f crypto/scrypt/intrin/obj/*.P + -rm -f crypto/scrypt/intrin/obj/*.d + -rm -f crypto/scrypt/generic/obj/*.o + -rm -f crypto/scrypt/generic/obj/*.P + -rm -f crypto/scrypt/generic/obj/*.d + -rm -f crypto/sha2/asm/obj/*.o + -rm -f crypto/sha2/asm/obj/*.P + -rm -f crypto/sha2/asm/obj/*.d + -rm -f obj/build.h + +FORCE: diff --git a/src/miner.cpp b/src/miner.cpp new file mode 100644 index 00000000..3f156224 --- /dev/null +++ b/src/miner.cpp @@ -0,0 +1,797 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2013 The XP developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "txdb.h" +#include "miner.h" +#include "kernel.h" +#include "kernel_worker.h" + +using namespace std; + +////////////////////////////////////////////////////////////////////////////// +// +// BitcoinMiner +// + +int64_t nReserveBalance = 0; +static unsigned int nMaxStakeSearchInterval = 60; +uint64_t nStakeInputsMapSize = 0; + +int static 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; +} + +static const unsigned int pSHA256InitState[8] = +{0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; + +void SHA256Transform(void* pstate, void* pinput, const void* pinit) +{ + SHA256_CTX ctx; + unsigned char data[64]; + + SHA256_Init(&ctx); + + for (int i = 0; i < 16; i++) + ((uint32_t*)data)[i] = ByteReverse(((uint32_t*)pinput)[i]); + + for (int i = 0; i < 8; i++) + ctx.h[i] = ((uint32_t*)pinit)[i]; + + SHA256_Update(&ctx, data, sizeof(data)); + for (int i = 0; i < 8; i++) + ((uint32_t*)pstate)[i] = ctx.h[i]; +} + +// Some explaining would be appreciated +class COrphan +{ +public: + CTransaction* ptx; + set setDependsOn; + double dPriority; + double dFeePerKb; + + COrphan(CTransaction* ptxIn) + { + ptx = ptxIn; + dPriority = dFeePerKb = 0; + } + + void print() const + { + printf("COrphan(hash=%s, dPriority=%.1f, dFeePerKb=%.1f)\n", + ptx->GetHash().ToString().substr(0,10).c_str(), dPriority, dFeePerKb); + BOOST_FOREACH(uint256 hash, setDependsOn) + printf(" setDependsOn %s\n", hash.ToString().substr(0,10).c_str()); + } +}; + + +uint64_t nLastBlockTx = 0; +uint64_t nLastBlockSize = 0; +uint32_t nLastCoinStakeSearchInterval = 0; + +// We want to sort transactions by priority and fee, so: +typedef boost::tuple TxPriority; +class TxPriorityCompare +{ + bool byFee; +public: + TxPriorityCompare(bool _byFee) : byFee(_byFee) { } + bool operator()(const TxPriority& a, const TxPriority& b) + { + if (byFee) + { + if (a.get<1>() == b.get<1>()) + return a.get<0>() < b.get<0>(); + return a.get<1>() < b.get<1>(); + } + else + { + if (a.get<0>() == b.get<0>()) + return a.get<1>() < b.get<1>(); + return a.get<0>() < b.get<0>(); + } + } +}; + +// CreateNewBlock: create new block (without proof-of-work/with provided coinstake) +CBlock* CreateNewBlock(CWallet* pwallet, CTransaction *txCoinStake) +{ + bool fProofOfStake = txCoinStake != NULL; + + // Create new block + auto_ptr pblock(new CBlock()); + if (!pblock.get()) + return NULL; + + // Create coinbase tx + CTransaction txCoinBase; + txCoinBase.vin.resize(1); + txCoinBase.vin[0].prevout.SetNull(); + txCoinBase.vout.resize(1); + + if (!fProofOfStake) + { + CReserveKey reservekey(pwallet); + txCoinBase.vout[0].scriptPubKey.SetDestination(reservekey.GetReservedKey().GetID()); + + // Add our coinbase tx as first transaction + pblock->vtx.push_back(txCoinBase); + } + else + { + // Coinbase output must be empty for Proof-of-Stake block + txCoinBase.vout[0].SetEmpty(); + + // Syncronize timestamps + pblock->nTime = txCoinBase.nTime = txCoinStake->nTime; + + // Add coinbase and coinstake transactions + pblock->vtx.push_back(txCoinBase); + pblock->vtx.push_back(*txCoinStake); + } + + // Largest block you're willing to create: + unsigned int nBlockMaxSize = GetArg("-blockmaxsize", MAX_BLOCK_SIZE_GEN/2); + // Limit to betweeen 1K and MAX_BLOCK_SIZE-1K for sanity: + nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE-1000), nBlockMaxSize)); + + // How much of the block should be dedicated to high-priority transactions, + // included regardless of the fees they pay + unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", 27000); + nBlockPrioritySize = std::min(nBlockMaxSize, nBlockPrioritySize); + + // Minimum block size you want to create; block will be filled with free transactions + // until there are no more or the block reaches this size: + unsigned int nBlockMinSize = GetArg("-blockminsize", 0); + nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize); + + // Fee-per-kilobyte amount considered the same as "free" + // Be careful setting this: if you set it to zero then + // a transaction spammer can cheaply fill blocks using + // 1-satoshi-fee transactions. It should be set above the real + // cost to you of processing a transaction. + int64_t nMinTxFee = MIN_TX_FEE; + if (mapArgs.count("-mintxfee")) + ParseMoney(mapArgs["-mintxfee"], nMinTxFee); + + CBlockIndex* pindexPrev = pindexBest; + + pblock->nBits = GetNextTargetRequired(pindexPrev, fProofOfStake); + + // Collect memory pool transactions into the block + int64_t nFees = 0; + { + LOCK2(cs_main, mempool.cs); + CBlockIndex* pindexPrev = pindexBest; + CTxDB txdb("r"); + + // Priority order to process transactions + list vOrphan; // list memory doesn't move + map > mapDependers; + + // This vector will be sorted into a priority queue: + vector vecPriority; + vecPriority.reserve(mempool.mapTx.size()); + for (map::iterator mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi) + { + CTransaction& tx = (*mi).second; + if (tx.IsCoinBase() || tx.IsCoinStake() || !tx.IsFinal()) + continue; + + COrphan* porphan = NULL; + double dPriority = 0; + int64_t nTotalIn = 0; + bool fMissingInputs = false; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + // Read prev transaction + CTransaction txPrev; + CTxIndex txindex; + if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex)) + { + // This should never happen; all transactions in the memory + // pool should connect to either transactions in the chain + // or other transactions in the memory pool. + if (!mempool.mapTx.count(txin.prevout.hash)) + { + printf("ERROR: mempool transaction missing input\n"); + if (fDebug) assert("mempool transaction missing input" == 0); + fMissingInputs = true; + if (porphan) + vOrphan.pop_back(); + break; + } + + // 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); + nTotalIn += mempool.mapTx[txin.prevout.hash].vout[txin.prevout.n].nValue; + continue; + } + int64_t nValueIn = txPrev.vout[txin.prevout.n].nValue; + nTotalIn += nValueIn; + + int nConf = txindex.GetDepthInMainChain(); + dPriority += (double)nValueIn * nConf; + } + if (fMissingInputs) continue; + + // Priority is sum(valuein * age) / txsize + unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); + dPriority /= nTxSize; + + // This is a more accurate fee-per-kilobyte than is used by the client code, because the + // client code rounds up the size to the nearest 1K. That's good, because it gives an + // incentive to create smaller transactions. + double dFeePerKb = double(nTotalIn-tx.GetValueOut()) / (double(nTxSize)/1000.0); + + if (porphan) + { + porphan->dPriority = dPriority; + porphan->dFeePerKb = dFeePerKb; + } + else + vecPriority.push_back(TxPriority(dPriority, dFeePerKb, &(*mi).second)); + } + + // Collect transactions into block + map mapTestPool; + uint64_t nBlockSize = 1000; + uint64_t nBlockTx = 0; + int nBlockSigOps = 100; + bool fSortedByFee = (nBlockPrioritySize <= 0); + + TxPriorityCompare comparer(fSortedByFee); + std::make_heap(vecPriority.begin(), vecPriority.end(), comparer); + + while (!vecPriority.empty()) + { + // Take highest priority transaction off the priority queue: + double dPriority = vecPriority.front().get<0>(); + double dFeePerKb = vecPriority.front().get<1>(); + CTransaction& tx = *(vecPriority.front().get<2>()); + + std::pop_heap(vecPriority.begin(), vecPriority.end(), comparer); + vecPriority.pop_back(); + + // Size limits + unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); + if (nBlockSize + nTxSize >= nBlockMaxSize) + continue; + + // Legacy limits on sigOps: + unsigned int nTxSigOps = tx.GetLegacySigOpCount(); + if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) + continue; + + // Timestamp limit + if (tx.nTime > GetAdjustedTime() || (fProofOfStake && tx.nTime > txCoinStake->nTime)) + continue; + + // Skip free transactions if we're past the minimum block size: + if (fSortedByFee && (dFeePerKb < nMinTxFee) && (nBlockSize + nTxSize >= nBlockMinSize)) + continue; + + // Prioritize by fee once past the priority size or we run out of high-priority + // transactions: + if (!fSortedByFee && + ((nBlockSize + nTxSize >= nBlockPrioritySize) || (dPriority < COIN * 144 / 250))) + { + fSortedByFee = true; + comparer = TxPriorityCompare(fSortedByFee); + std::make_heap(vecPriority.begin(), vecPriority.end(), comparer); + } + + // 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); + MapPrevTx mapInputs; + bool fInvalid; + if (!tx.FetchInputs(txdb, mapTestPoolTmp, false, true, mapInputs, fInvalid)) + continue; + + // Transaction fee + int64_t nTxFees = tx.GetValueIn(mapInputs)-tx.GetValueOut(); + int64_t nMinFee = tx.GetMinFee(nBlockSize, true, GMF_BLOCK, nTxSize); + if (nTxFees < nMinFee) + continue; + + // Sigops accumulation + nTxSigOps += tx.GetP2SHSigOpCount(mapInputs); + if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) + continue; + + if (!tx.ConnectInputs(txdb, mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, false, true, true, MANDATORY_SCRIPT_VERIFY_FLAGS)) + continue; + mapTestPoolTmp[tx.GetHash()] = CTxIndex(CDiskTxPos(1,1,1), tx.vout.size()); + swap(mapTestPool, mapTestPoolTmp); + + // Added + pblock->vtx.push_back(tx); + nBlockSize += nTxSize; + ++nBlockTx; + nBlockSigOps += nTxSigOps; + nFees += nTxFees; + + if (fDebug && GetBoolArg("-printpriority")) + { + printf("priority %.1f feeperkb %.1f txid %s\n", + dPriority, dFeePerKb, tx.GetHash().ToString().c_str()); + } + + // 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()) + { + vecPriority.push_back(TxPriority(porphan->dPriority, porphan->dFeePerKb, porphan->ptx)); + std::push_heap(vecPriority.begin(), vecPriority.end(), comparer); + } + } + } + } + } + + nLastBlockTx = nBlockTx; + nLastBlockSize = nBlockSize; + + if (!fProofOfStake) + { + pblock->vtx[0].vout[0].nValue = GetProofOfWorkReward(pblock->nBits, nFees, pindexPrev->nHeight + 1); + + if (fDebug) + printf("CreateNewBlock(): PoW reward %" PRIu64 "\n", pblock->vtx[0].vout[0].nValue); + } + + if (fDebug && GetBoolArg("-printpriority")) + printf("CreateNewBlock(): total size %" PRIu64 "\n", nBlockSize); + + // Fill in header + pblock->hashPrevBlock = pindexPrev->GetBlockHash(); + if (!fProofOfStake) + { + pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, pblock->GetMaxTransactionTime()); + pblock->nTime = max(pblock->GetBlockTime(), PastDrift(pindexPrev->GetBlockTime())); + pblock->UpdateTime(pindexPrev); + } + pblock->nNonce = 0; + } + + return pblock.release(); +} + + +void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce) +{ + // Update nExtraNonce + static uint256 hashPrevBlock; + if (hashPrevBlock != pblock->hashPrevBlock) + { + nExtraNonce = 0; + hashPrevBlock = pblock->hashPrevBlock; + } + ++nExtraNonce; + + unsigned int nHeight = pindexPrev->nHeight+1; // Height first in coinbase required for block.version=2 + pblock->vtx[0].vin[0].scriptSig = (CScript() << nHeight << CBigNum(nExtraNonce)) + COINBASE_FLAGS; + assert(pblock->vtx[0].vin[0].scriptSig.size() <= 100); + + pblock->hashMerkleRoot = pblock->BuildMerkleTree(); +} + + +void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1) +{ + // + // Pre-build 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 (unsigned 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, CWallet& wallet, CReserveKey& reservekey) +{ + uint256 hashBlock = pblock->GetHash(); + uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + + if(!pblock->IsProofOfWork()) + return error("CheckWork() : %s is not a proof-of-work block", hashBlock.GetHex().c_str()); + + if (hashBlock > hashTarget) + return error("CheckWork() : proof-of-work not meeting target"); + + //// debug print + printf("CheckWork() : new proof-of-work block found \n hash: %s \ntarget: %s\n", hashBlock.GetHex().c_str(), hashTarget.GetHex().c_str()); + pblock->print(); + printf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue).c_str()); + + // Found a solution + { + LOCK(cs_main); + if (pblock->hashPrevBlock != hashBestChain) + return error("CheckWork() : generated block is stale"); + + // Remove key from key pool + reservekey.KeepKey(); + + // Track how many getdata requests this block gets + { + LOCK(wallet.cs_wallet); + wallet.mapRequestCount[hashBlock] = 0; + } + + // Process this block the same as if we had received it from another node + if (!ProcessBlock(NULL, pblock)) + return error("CheckWork() : ProcessBlock, block not accepted"); + } + + return true; +} + +bool CheckStake(CBlock* pblock, CWallet& wallet) +{ + uint256 proofHash = 0, hashTarget = 0; + uint256 hashBlock = pblock->GetHash(); + + if(!pblock->IsProofOfStake()) + return error("CheckStake() : %s is not a proof-of-stake block", hashBlock.GetHex().c_str()); + + // verify hash target and signature of coinstake tx + if (!CheckProofOfStake(pblock->vtx[1], pblock->nBits, proofHash, hashTarget)) + return error("CheckStake() : proof-of-stake checking failed"); + + //// debug print + printf("CheckStake() : new proof-of-stake block found \n hash: %s \nproofhash: %s \ntarget: %s\n", hashBlock.GetHex().c_str(), proofHash.GetHex().c_str(), hashTarget.GetHex().c_str()); + pblock->print(); + printf("out %s\n", FormatMoney(pblock->vtx[1].GetValueOut()).c_str()); + + // Found a solution + { + LOCK(cs_main); + if (pblock->hashPrevBlock != hashBestChain) + return error("CheckStake() : generated block is stale"); + + // Track how many getdata requests this block gets + { + LOCK(wallet.cs_wallet); + wallet.mapRequestCount[hashBlock] = 0; + } + + // Process this block the same as if we had received it from another node + if (!ProcessBlock(NULL, pblock)) + return error("CheckStake() : ProcessBlock, block not accepted"); + } + + return true; +} + +// Precalculated SHA256 contexts and metadata +// (txid, vout.n) => (kernel, (tx.nTime, nAmount)) +typedef std::map, std::pair, std::pair > > MidstateMap; + +// Fill the inputs map with precalculated contexts and metadata +bool FillMap(CWallet *pwallet, uint32_t nUpperTime, MidstateMap &inputsMap) +{ + // Choose coins to use + int64_t nBalance = pwallet->GetBalance(); + + if (nBalance <= nReserveBalance) + return false; + + uint32_t nTime = GetAdjustedTime(); + + CTxDB txdb("r"); + { + LOCK2(cs_main, pwallet->cs_wallet); + + CoinsSet setCoins; + int64_t nValueIn = 0; + if (!pwallet->SelectCoinsSimple(nBalance - nReserveBalance, MIN_TX_FEE, MAX_MONEY, nUpperTime, nCoinbaseMaturity + 10, setCoins, nValueIn)) + return error("FillMap() : SelectCoinsSimple failed"); + + if (setCoins.empty()) + return false; + + CBlock block; + CTxIndex txindex; + + for(CoinsSet::const_iterator pcoin = setCoins.begin(); pcoin != setCoins.end(); pcoin++) + { + pair key = make_pair(pcoin->first->GetHash(), pcoin->second); + + // Skip existent inputs + if (inputsMap.find(key) != inputsMap.end()) + continue; + + // Trying to parse scriptPubKey + txnouttype whichType; + vector vSolutions; + if (!Solver(pcoin->first->vout[pcoin->second].scriptPubKey, whichType, vSolutions)) + continue; + + // Only support pay to public key and pay to address + if (whichType != TX_PUBKEY && whichType != TX_PUBKEYHASH) + continue; + + // Load transaction index item + if (!txdb.ReadTxIndex(pcoin->first->GetHash(), txindex)) + continue; + + // Read block header + if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) + continue; + + // Only load coins meeting min age requirement + if (nStakeMinAge + block.nTime > nTime - nMaxStakeSearchInterval) + continue; + + // Get stake modifier + uint64_t nStakeModifier = 0; + if (!GetKernelStakeModifier(block.GetHash(), nStakeModifier)) + continue; + + // Build static part of kernel + CDataStream ssKernel(SER_GETHASH, 0); + ssKernel << nStakeModifier; + ssKernel << block.nTime << (txindex.pos.nTxPos - txindex.pos.nBlockPos) << pcoin->first->nTime << pcoin->second; + + // (txid, vout.n) => (kernel, (tx.nTime, nAmount)) + inputsMap[key] = make_pair(std::vector(ssKernel.begin(), ssKernel.end()), make_pair(pcoin->first->nTime, pcoin->first->vout[pcoin->second].nValue)); + } + + nStakeInputsMapSize = inputsMap.size(); + + if (fDebug) + printf("FillMap() : Map of %" PRIu64 " precalculated contexts has been created by stake miner\n", nStakeInputsMapSize); + } + + return true; +} + +// Scan inputs map in order to find a solution +bool ScanMap(const MidstateMap &inputsMap, uint32_t nBits, MidstateMap::key_type &LuckyInput, std::pair &solution) +{ + static uint32_t nLastCoinStakeSearchTime = GetAdjustedTime(); // startup timestamp + uint32_t nSearchTime = GetAdjustedTime(); + + if (inputsMap.size() > 0 && nSearchTime > nLastCoinStakeSearchTime) + { + // Scanning interval (begintime, endtime) + std::pair interval; + + interval.first = nSearchTime; + interval.second = nSearchTime - min(nSearchTime-nLastCoinStakeSearchTime, nMaxStakeSearchInterval); + + // (txid, nout) => (kernel, (tx.nTime, nAmount)) + for(MidstateMap::const_iterator input = inputsMap.begin(); input != inputsMap.end(); input++) + { + unsigned char *kernel = (unsigned char *) &input->second.first[0]; + + // scan(State, Bits, Time, Amount, ...) + if (ScanKernelBackward(kernel, nBits, input->second.second.first, input->second.second.second, interval, solution)) + { + // Solution found + LuckyInput = input->first; // (txid, nout) + + return true; + } + } + + // Inputs map iteration can be big enough to consume few seconds while scanning. + // We're using dynamical calculation of scanning interval in order to compensate this delay. + nLastCoinStakeSearchInterval = nSearchTime - nLastCoinStakeSearchTime; + nLastCoinStakeSearchTime = nSearchTime; + } + + // No solutions were found + return false; +} + +// Stake miner thread +void ThreadStakeMiner(void* parg) +{ + SetThreadPriority(THREAD_PRIORITY_LOWEST); + + // Make this thread recognisable as the mining thread + RenameThread("XP-miner"); + CWallet* pwallet = (CWallet*)parg; + + MidstateMap inputsMap; + if (!FillMap(pwallet, GetAdjustedTime(), inputsMap)) + return; + + bool fTrySync = true; + + CBlockIndex* pindexPrev = pindexBest; + uint32_t nBits = GetNextTargetRequired(pindexPrev, true); + + printf("ThreadStakeMinter started\n"); + + try + { + vnThreadsRunning[THREAD_MINTER]++; + + MidstateMap::key_type LuckyInput; + std::pair solution; + + // Main miner loop + do + { + if (fShutdown) + goto _endloop; + + while (pwallet->IsLocked()) + { + Sleep(1000); + if (fShutdown) + goto _endloop; // Don't be afraid to use a goto if that's the best option. + } + + while (vNodes.empty() || IsInitialBlockDownload()) + { + fTrySync = true; + + Sleep(1000); + if (fShutdown) + goto _endloop; + } + + if (fTrySync) + { + // Don't try mine blocks unless we're at the top of chain and have at least two p2p connections. + fTrySync = false; + if (vNodes.size() < 2 || nBestHeight < GetNumBlocksOfPeers()) + { + Sleep(1000); + continue; + } + } + + if (ScanMap(inputsMap, nBits, LuckyInput, solution)) + { + SetThreadPriority(THREAD_PRIORITY_NORMAL); + + // Remove lucky input from the map + inputsMap.erase(inputsMap.find(LuckyInput)); + + CKey key; + CTransaction txCoinStake; + + // Create new coinstake transaction + if (!pwallet->CreateCoinStake(LuckyInput.first, LuckyInput.second, solution.second, nBits, txCoinStake, key)) + { + string strMessage = _("Warning: Unable to create coinstake transaction, see debug.log for the details. Mining thread has been stopped."); + strMiscWarning = strMessage; + printf("*** %s\n", strMessage.c_str()); + + break; + } + + // Now we have new coinstake, it's time to create the block ... + CBlock* pblock; + pblock = CreateNewBlock(pwallet, &txCoinStake); + if (!pblock) + { + string strMessage = _("Warning: Unable to allocate memory for the new block object. Mining thread has been stopped."); + strMiscWarning = strMessage; + printf("*** %s\n", strMessage.c_str()); + + break; + } + + unsigned int nExtraNonce = 0; + IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); + + // ... and sign it + if (!key.Sign(pblock->GetHash(), pblock->vchBlockSig)) + { + string strMessage = _("Warning: Proof-of-Stake miner is unable to sign the block (locked wallet?). Mining thread has been stopped."); + strMiscWarning = strMessage; + printf("*** %s\n", strMessage.c_str()); + + break; + } + + CheckStake(pblock, *pwallet); + SetThreadPriority(THREAD_PRIORITY_LOWEST); + Sleep(500); + } + + if (pindexPrev != pindexBest) + { + // The best block has been changed, we need to refill the map + if (FillMap(pwallet, GetAdjustedTime(), inputsMap)) + { + pindexPrev = pindexBest; + nBits = GetNextTargetRequired(pindexPrev, true); + } + else + { + // Clear existent data if FillMap failed + inputsMap.clear(); + } + } + + Sleep(500); + + _endloop: + (void)0; // do nothing + } + while(!fShutdown); + + vnThreadsRunning[THREAD_MINTER]--; + } + catch (std::exception& e) { + vnThreadsRunning[THREAD_MINTER]--; + PrintException(&e, "ThreadStakeMinter()"); + } catch (...) { + vnThreadsRunning[THREAD_MINTER]--; + PrintException(NULL, "ThreadStakeMinter()"); + } + printf("ThreadStakeMinter exiting, %d threads remaining\n", vnThreadsRunning[THREAD_MINTER]); +} diff --git a/src/miner.h b/src/miner.h new file mode 100644 index 00000000..dabb7ba7 --- /dev/null +++ b/src/miner.h @@ -0,0 +1,33 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2013 The XP developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef XP_MINER_H +#define XP_MINER_H + +#include "main.h" +#include "wallet.h" + +/* Generate a new block, without valid proof-of-work/with provided proof-of-stake */ +CBlock* CreateNewBlock(CWallet* pwallet, CTransaction *txAdd=NULL); + +/** Modify the extranonce in a block */ +void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce); + +/** Do mining precalculation */ +void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1); + +/** Check mined proof-of-work block */ +bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey); + +/** Check mined proof-of-stake block */ +bool CheckStake(CBlock* pblock, CWallet& wallet); + +/** Base sha256 mining transform */ +void SHA256Transform(void* pstate, void* pinput, const void* pinit); + +/** Stake miner thread */ +void ThreadStakeMiner(void* parg); + +#endif // XP_MINER_H diff --git a/src/ministun.h b/src/ministun.h new file mode 100644 index 00000000..d2ad7380 --- /dev/null +++ b/src/ministun.h @@ -0,0 +1,108 @@ +/* + * ministun.h + * Part of the ministun package + * + * STUN support code borrowed from Asterisk -- An open source telephony toolkit. + * Copyright (C) 1999 - 2006, Digium, Inc. + * Mark Spencer + * Standalone remake (c) 2009 Vladislav Grishenko + * + * This software is licensed under the terms of the GNU General + * Public License (GPL). Please see the file COPYING for details. + * + */ + +#define PACKAGE "ministun" +#define VERSION "0.1" + +#define STUN_SERVER "stun.xten.com" +#define STUN_PORT 3478 +#define STUN_COUNT 3 +#define STUN_TIMEOUT 3 + +#ifndef _MSC_VER + typedef struct { unsigned int id[4]; } __attribute__((packed)) stun_trans_id; + + struct stun_header { + unsigned short msgtype; + unsigned short msglen; + stun_trans_id id; + } __attribute__((packed)); + + struct stun_attr { + unsigned short attr; + unsigned short len; + } __attribute__((packed)); + + /* + * The format normally used for addresses carried by STUN messages. + */ + + struct stun_addr { + unsigned char unused; + unsigned char family; + unsigned short port; + unsigned int addr; + } __attribute__((packed)); +#else + typedef struct { unsigned int id[4]; } stun_trans_id; + +#pragma pack(push, 1) + struct stun_header { + unsigned short msgtype; + unsigned short msglen; + stun_trans_id id; + }; +#pragma pack(pop) + +#pragma pack(push, 1) + struct stun_attr { + unsigned short attr; + unsigned short len; + }; +#pragma pack(pop) + + /* + * The format normally used for addresses carried by STUN messages. + */ +#pragma pack(push, 1) + struct stun_addr { + unsigned char unused; + unsigned char family; + unsigned short port; + unsigned int addr; + }; +#pragma pack(pop) +#endif + +#define STUN_IGNORE (0) +#define STUN_ACCEPT (1) + +/* STUN message types + * 'BIND' refers to transactions used to determine the externally + * visible addresses. 'SEC' refers to transactions used to establish + * a session key for subsequent requests. + * 'SEC' functionality is not supported here. + */ + +#define STUN_BINDREQ 0x0001 +#define STUN_BINDRESP 0x0101 +#define STUN_BINDERR 0x0111 +#define STUN_SECREQ 0x0002 +#define STUN_SECRESP 0x0102 +#define STUN_SECERR 0x0112 + +/* Basic attribute types in stun messages. + * Messages can also contain custom attributes (codes above 0x7fff) + */ +#define STUN_MAPPED_ADDRESS 0x0001 +#define STUN_RESPONSE_ADDRESS 0x0002 +#define STUN_CHANGE_REQUEST 0x0003 +#define STUN_SOURCE_ADDRESS 0x0004 +#define STUN_CHANGED_ADDRESS 0x0005 +#define STUN_USERNAME 0x0006 +#define STUN_PASSWORD 0x0007 +#define STUN_MESSAGE_INTEGRITY 0x0008 +#define STUN_ERROR_CODE 0x0009 +#define STUN_UNKNOWN_ATTRIBUTES 0x000a +#define STUN_REFLECTED_FROM 0x000b diff --git a/src/mruset.h b/src/mruset.h new file mode 100644 index 00000000..a5273518 --- /dev/null +++ b/src/mruset.h @@ -0,0 +1,64 @@ +// Copyright (c) 2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_MRUSET_H +#define BITCOIN_MRUSET_H + +#include +#include + +/** STL-like set container that only keeps the most recent N elements. */ +template class mruset +{ +public: + typedef T key_type; + typedef T value_type; + typedef typename std::set::iterator iterator; + typedef typename std::set::const_iterator const_iterator; + typedef typename std::set::size_type size_type; + +protected: + std::set set; + std::deque queue; + size_type nMaxSize; + +public: + mruset(size_type nMaxSizeIn = 0) { nMaxSize = nMaxSizeIn; } + iterator begin() const { return set.begin(); } + iterator end() const { return set.end(); } + size_type size() const { return set.size(); } + bool empty() const { return set.empty(); } + iterator find(const key_type& k) const { return set.find(k); } + size_type count(const key_type& k) const { return set.count(k); } + bool inline friend operator==(const mruset& a, const mruset& b) { return a.set == b.set; } + bool inline friend operator==(const mruset& a, const std::set& b) { return a.set == b; } + bool inline friend operator<(const mruset& a, const mruset& b) { return a.set < b.set; } + std::pair insert(const key_type& x) + { + std::pair ret = set.insert(x); + if (ret.second) + { + if (nMaxSize && queue.size() == nMaxSize) + { + set.erase(queue.front()); + queue.pop_front(); + } + queue.push_back(x); + } + return ret; + } + size_type max_size() const { return nMaxSize; } + size_type max_size(size_type s) + { + if (s) + while (queue.size() > s) + { + set.erase(queue.front()); + queue.pop_front(); + } + nMaxSize = s; + return nMaxSize; + } +}; + +#endif diff --git a/src/net.cpp b/src/net.cpp new file mode 100644 index 00000000..7eefb34e --- /dev/null +++ b/src/net.cpp @@ -0,0 +1,1884 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "irc.h" +#include "db.h" +#include "net.h" +#include "init.h" +#include "addrman.h" +#include "ui_interface.h" +#include "miner.h" +#include "ntp.h" + +#ifdef WIN32 +#include +#endif + +using namespace std; +using namespace boost; + +static const int MAX_OUTBOUND_CONNECTIONS = 16; + +void ThreadMessageHandler2(void* parg); +void ThreadSocketHandler2(void* parg); +void ThreadOpenConnections2(void* parg); +void ThreadOpenAddedConnections2(void* parg); +void ThreadDNSAddressSeed2(void* parg); + +// Fix for ancient MinGW versions, that don't have defined these in ws2tcpip.h. +// Todo: Can be removed when our pull-tester is upgraded to a modern MinGW version. +#ifdef WIN32 +#ifndef PROTECTION_LEVEL_UNRESTRICTED +#define PROTECTION_LEVEL_UNRESTRICTED 10 +#endif +#ifndef IPV6_PROTECTION_LEVEL +#define IPV6_PROTECTION_LEVEL 23 +#endif +#endif + +struct LocalServiceInfo { + int nScore; + uint16_t nPort; +}; + +// +// Global state variables +// +bool fClient = false; +bool fDiscover = true; +uint64_t nLocalServices = (fClient ? 0 : NODE_NETWORK); +static CCriticalSection cs_mapLocalHost; +static map mapLocalHost; +static bool vfReachable[NET_MAX] = {}; +static bool vfLimited[NET_MAX] = {}; +static CNode* pnodeLocalHost = NULL; +static CNode* pnodeSync = NULL; +CAddress addrSeenByPeer(CService("0.0.0.0", nPortZero), nLocalServices); +uint64_t nLocalHostNonce = 0; +boost::array vnThreadsRunning; +static std::vector vhListenSocket; +CAddrMan addrman; + +vector vNodes; +CCriticalSection cs_vNodes; +map mapRelay; +deque > vRelayExpiration; +CCriticalSection cs_mapRelay; +map mapAlreadyAskedFor; + +static deque vOneShots; +CCriticalSection cs_vOneShots; + +set setservAddNodeAddresses; +CCriticalSection cs_setservAddNodeAddresses; + +vector vAddedNodes; +CCriticalSection cs_vAddedNodes; + +static CSemaphore *semOutbound = NULL; + +void AddOneShot(string strDest) +{ + LOCK(cs_vOneShots); + vOneShots.push_back(strDest); +} + +unsigned short GetListenPort() +{ + return (unsigned short)(GetArg("-port", GetDefaultPort())); +} + +void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd) +{ + // Filter out duplicate requests + if (pindexBegin == pindexLastGetBlocksBegin && hashEnd == hashLastGetBlocksEnd) + return; + pindexLastGetBlocksBegin = pindexBegin; + hashLastGetBlocksEnd = hashEnd; + + PushMessage("getblocks", CBlockLocator(pindexBegin), hashEnd); +} + +// find 'best' local address for a particular peer +bool GetLocal(CService& addr, const CNetAddr *paddrPeer) +{ + if (fNoListen) + return false; + + int nBestScore = -1; + int nBestReachability = -1; + { + LOCK(cs_mapLocalHost); + for (map::iterator it = mapLocalHost.begin(); it != mapLocalHost.end(); it++) + { + int nScore = (*it).second.nScore; + int nReachability = (*it).first.GetReachabilityFrom(paddrPeer); + if (nReachability > nBestReachability || (nReachability == nBestReachability && nScore > nBestScore)) + { + addr = CService((*it).first, (*it).second.nPort); + nBestReachability = nReachability; + nBestScore = nScore; + } + } + } + return nBestScore >= 0; +} + +// get best local address for a particular peer as a CAddress +CAddress GetLocalAddress(const CNetAddr *paddrPeer) +{ + CAddress ret(CService("0.0.0.0", nPortZero), 0); + CService addr; + if (GetLocal(addr, paddrPeer)) + { + ret = CAddress(addr); + ret.nServices = nLocalServices; + ret.nTime = GetAdjustedTime(); + } + return ret; +} + +bool RecvLine(SOCKET hSocket, string& strLine) +{ + strLine = ""; + while (true) + { + 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("socket closed\n"); + return false; + } + else + { + // socket error + int nErr = WSAGetLastError(); + printf("recv failed: %d\n", nErr); + return false; + } + } + } +} + +// used when scores of local addresses may have changed +// pushes better local address to peers +void static AdvertizeLocal() +{ + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + { + if (pnode->fSuccessfullyConnected) + { + CAddress addrLocal = GetLocalAddress(&pnode->addr); + if (addrLocal.IsRoutable() && (CService)addrLocal != (CService)pnode->addrLocal) + { + pnode->PushAddress(addrLocal); + pnode->addrLocal = addrLocal; + } + } + } +} + +void SetReachable(enum Network net, bool fFlag) +{ + LOCK(cs_mapLocalHost); + vfReachable[net] = fFlag; + if (net == NET_IPV6 && fFlag) + vfReachable[NET_IPV4] = true; +} + +// learn a new local address +bool AddLocal(const CService& addr, int nScore) +{ + if (!addr.IsRoutable()) + return false; + + if (!fDiscover && nScore < LOCAL_MANUAL) + return false; + + if (IsLimited(addr)) + return false; + + printf("AddLocal(%s,%i)\n", addr.ToString().c_str(), nScore); + + { + LOCK(cs_mapLocalHost); + bool fAlready = mapLocalHost.count(addr) > 0; + LocalServiceInfo &info = mapLocalHost[addr]; + if (!fAlready || nScore >= info.nScore) { + info.nScore = nScore + (fAlready ? 1 : 0); + info.nPort = addr.GetPort(); + } + SetReachable(addr.GetNetwork()); + } + + AdvertizeLocal(); + + return true; +} + +bool AddLocal(const CNetAddr &addr, int nScore) +{ + return AddLocal(CService(addr, GetListenPort()), nScore); +} + +/** Make a particular network entirely off-limits (no automatic connects to it) */ +void SetLimited(enum Network net, bool fLimited) +{ + if (net == NET_UNROUTABLE) + return; + LOCK(cs_mapLocalHost); + vfLimited[net] = fLimited; +} + +bool IsLimited(enum Network net) +{ + LOCK(cs_mapLocalHost); + return vfLimited[net]; +} + +bool IsLimited(const CNetAddr &addr) +{ + return IsLimited(addr.GetNetwork()); +} + +/** vote for a local address */ +bool SeenLocal(const CService& addr) +{ + { + LOCK(cs_mapLocalHost); + if (mapLocalHost.count(addr) == 0) + return false; + mapLocalHost[addr].nScore++; + } + + AdvertizeLocal(); + + return true; +} + +/** check whether a given address is potentially local */ +bool IsLocal(const CService& addr) +{ + LOCK(cs_mapLocalHost); + return mapLocalHost.count(addr) > 0; +} + +/** check whether a given address is in a network we can probably connect to */ +bool IsReachable(const CNetAddr& addr) +{ + LOCK(cs_mapLocalHost); + enum Network net = addr.GetNetwork(); + return vfReachable[net] && !vfLimited[net]; +} + +extern int GetExternalIPbySTUN(uint64_t rnd, struct sockaddr_in *mapped, const char **srv); + +// We now get our external IP from the IRC server first and only use this as a backup +bool GetMyExternalIP(CNetAddr& ipRet) +{ + struct sockaddr_in mapped; + uint64_t rnd = std::numeric_limits::max(); + const char *srv; + int rc = GetExternalIPbySTUN(rnd, &mapped, &srv); + if(rc >= 0) { + ipRet = CNetAddr(mapped.sin_addr); + printf("GetExternalIPbySTUN(%" PRIu64 ") returned %s in attempt %d; Server=%s\n", rnd, ipRet.ToStringIP().c_str(), rc, srv); + return true; + } + return false; +} + +void ThreadGetMyExternalIP(void* parg) +{ + // Make this thread recognisable as the external IP detection thread + RenameThread("XP-ext-ip"); + + CNetAddr addrLocalHost; + if (GetMyExternalIP(addrLocalHost)) + { + printf("GetMyExternalIP() returned %s\n", addrLocalHost.ToStringIP().c_str()); + AddLocal(addrLocalHost, LOCAL_HTTP); + } +} + + + + + +void AddressCurrentlyConnected(const CService& addr) +{ + addrman.Connected(addr); +} + + + + +uint64_t CNode::nTotalBytesRecv = 0; +uint64_t CNode::nTotalBytesSent = 0; +CCriticalSection CNode::cs_totalBytesRecv; +CCriticalSection CNode::cs_totalBytesSent; + +CNode* FindNode(const CNetAddr& ip) +{ + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + if ((CNetAddr)pnode->addr == ip) + return (pnode); + return NULL; +} + +CNode* FindNode(std::string addrName) +{ + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + if (pnode->addrName == addrName) + return (pnode); + return NULL; +} + +CNode* FindNode(const CService& addr) +{ + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + if ((CService)pnode->addr == addr) + return (pnode); + return NULL; +} + +CNode* ConnectNode(CAddress addrConnect, const char *pszDest, int64_t nTimeout) +{ + if (pszDest == NULL) { + if (IsLocal(addrConnect)) + return NULL; + + // Look for an existing connection + CNode* pnode = FindNode((CService)addrConnect); + if (pnode) + { + if (nTimeout != 0) + pnode->AddRef(nTimeout); + else + pnode->AddRef(); + return pnode; + } + } + + + /// debug print + printf("trying connection %s lastseen=%.1fhrs\n", + pszDest ? pszDest : addrConnect.ToString().c_str(), + pszDest ? 0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0); + + // Connect + SOCKET hSocket; + if (pszDest ? ConnectSocketByName(addrConnect, hSocket, pszDest, GetDefaultPort()) : ConnectSocket(addrConnect, hSocket)) + { + addrman.Attempt(addrConnect); + + /// debug print + printf("connected %s\n", pszDest ? pszDest : addrConnect.ToString().c_str()); + + // Set to non-blocking +#ifdef WIN32 + u_long nOne = 1; + if (ioctlsocket(hSocket, FIONBIO, &nOne) == SOCKET_ERROR) + printf("ConnectSocket() : ioctlsocket non-blocking setting failed, error %d\n", WSAGetLastError()); +#else + if (fcntl(hSocket, F_SETFL, O_NONBLOCK) == SOCKET_ERROR) + printf("ConnectSocket() : fcntl non-blocking setting failed, error %d\n", errno); +#endif + + // Add node + CNode* pnode = new CNode(hSocket, addrConnect, pszDest ? pszDest : "", false); + if (nTimeout != 0) + pnode->AddRef(nTimeout); + else + pnode->AddRef(); + + { + LOCK(cs_vNodes); + vNodes.push_back(pnode); + } + + pnode->nTimeConnected = GetTime(); + return pnode; + } + else + { + return NULL; + } +} + +void CNode::CloseSocketDisconnect() +{ + fDisconnect = true; + if (hSocket != INVALID_SOCKET) + { + printf("disconnecting node %s\n", addrName.c_str()); + closesocket(hSocket); + hSocket = INVALID_SOCKET; + vRecv.clear(); + } + + // in case this fails, we'll empty the recv buffer when the CNode is deleted + TRY_LOCK(cs_vRecv, lockRecv); + if (lockRecv) + vRecv.clear(); + + // if this was the sync node, we'll need a new one + if (this == pnodeSync) + pnodeSync = NULL; +} + +void CNode::Cleanup() +{ +} + + +void CNode::PushVersion() +{ + int64_t nTime = GetAdjustedTime(); + CAddress addrYou, addrMe; + + bool fHidden = false; + if (addr.IsTor()) { + if (mapArgs.count("-torname")) { + // Our hidden service address + CService addrTorName(mapArgs["-torname"], GetListenPort()); + + if (addrTorName.IsValid()) { + addrYou = addr; + addrMe = CAddress(addrTorName); + fHidden = true; + } + } + } + + if (!fHidden) { + addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0", nPortZero))); + addrMe = GetLocalAddress(&addr); + } + + RAND_bytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce)); + printf("send version message: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString().c_str(), addrYou.ToString().c_str(), addr.ToString().c_str()); + PushMessage("version", PROTOCOL_VERSION, nLocalServices, nTime, addrYou, addrMe, + nLocalHostNonce, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector()), nBestHeight); +} + + + + + +std::map CNode::setBanned; +CCriticalSection CNode::cs_setBanned; + +void CNode::ClearBanned() +{ + setBanned.clear(); +} + +bool CNode::IsBanned(CNetAddr ip) +{ + bool fResult = false; + { + LOCK(cs_setBanned); + std::map::iterator i = setBanned.find(ip); + if (i != setBanned.end()) + { + int64_t t = (*i).second; + if (GetTime() < t) + fResult = true; + } + } + return fResult; +} + +bool CNode::Misbehaving(int howmuch) +{ + if (addr.IsLocal()) + { + printf("Warning: Local node %s misbehaving (delta: %d)!\n", addrName.c_str(), howmuch); + return false; + } + + nMisbehavior += howmuch; + if (nMisbehavior >= GetArgInt("-banscore", 100)) + { + int64_t banTime = GetTime()+GetArg("-bantime", nOneDay); // Default 24-hour ban + printf("Misbehaving: %s (%d -> %d) DISCONNECTING\n", addr.ToString().c_str(), nMisbehavior-howmuch, nMisbehavior); + { + LOCK(cs_setBanned); + if (setBanned[addr] < banTime) + setBanned[addr] = banTime; + } + CloseSocketDisconnect(); + return true; + } else + printf("Misbehaving: %s (%d -> %d)\n", addr.ToString().c_str(), nMisbehavior-howmuch, nMisbehavior); + return false; +} + +#undef X +#define X(name) stats.name = name +void CNode::copyStats(CNodeStats &stats) +{ + X(nServices); + X(nLastSend); + X(nLastRecv); + X(nTimeConnected); + X(addrName); + X(nVersion); + X(strSubVer); + X(fInbound); + X(nReleaseTime); + X(nStartingHeight); + X(nMisbehavior); + X(nSendBytes); + X(nRecvBytes); + stats.fSyncNode = (this == pnodeSync); +} +#undef X + + + + + + + + + + +void ThreadSocketHandler(void* parg) +{ + // Make this thread recognisable as the networking thread + RenameThread("XP-net"); + + try + { + vnThreadsRunning[THREAD_SOCKETHANDLER]++; + ThreadSocketHandler2(parg); + vnThreadsRunning[THREAD_SOCKETHANDLER]--; + } + catch (std::exception& e) { + vnThreadsRunning[THREAD_SOCKETHANDLER]--; + PrintException(&e, "ThreadSocketHandler()"); + } catch (...) { + vnThreadsRunning[THREAD_SOCKETHANDLER]--; + throw; // support pthread_cancel() + } + printf("ThreadSocketHandler exited\n"); +} + +static list vNodesDisconnected; + +void ThreadSocketHandler2(void* parg) +{ + printf("ThreadSocketHandler started\n"); + size_t nPrevNodeCount = 0; + while (true) + { + // + // Disconnect nodes + // + { + LOCK(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()); + + // release outbound grant (if any) + pnode->grantOutbound.Release(); + + // 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_LOCK(pnode->cs_vSend, lockSend); + if (lockSend) + { + TRY_LOCK(pnode->cs_vRecv, lockRecv); + if (lockRecv) + { + TRY_LOCK(pnode->cs_mapRequests, lockReq); + if (lockReq) + { + TRY_LOCK(pnode->cs_inventory, lockInv); + if (lockInv) + fDelete = true; + } + } + } + } + if (fDelete) + { + vNodesDisconnected.remove(pnode); + delete pnode; + } + } + } + } + if (vNodes.size() != nPrevNodeCount) + { + nPrevNodeCount = vNodes.size(); + uiInterface.NotifyNumConnectionsChanged(vNodes.size()); + } + + + // + // 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; + bool have_fds = false; + + BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) { + FD_SET(hListenSocket, &fdsetRecv); + hSocketMax = max(hSocketMax, hListenSocket); + have_fds = true; + } + { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + { + if (pnode->hSocket == INVALID_SOCKET) + continue; + FD_SET(pnode->hSocket, &fdsetRecv); + FD_SET(pnode->hSocket, &fdsetError); + hSocketMax = max(hSocketMax, pnode->hSocket); + have_fds = true; + { + TRY_LOCK(pnode->cs_vSend, lockSend); + if (lockSend && !pnode->vSend.empty()) + FD_SET(pnode->hSocket, &fdsetSend); + } + } + } + + vnThreadsRunning[THREAD_SOCKETHANDLER]--; + int nSelect = select(have_fds ? hSocketMax + 1 : 0, + &fdsetRecv, &fdsetSend, &fdsetError, &timeout); + vnThreadsRunning[THREAD_SOCKETHANDLER]++; + if (fShutdown) + return; + if (nSelect == SOCKET_ERROR) + { + if (have_fds) + { + int nErr = WSAGetLastError(); + printf("socket select error %d\n", nErr); + for (unsigned int i = 0; i <= hSocketMax; i++) + FD_SET(i, &fdsetRecv); + } + FD_ZERO(&fdsetSend); + FD_ZERO(&fdsetError); + Sleep(timeout.tv_usec/1000); + } + + + // + // Accept new connections + // + BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) + if (hListenSocket != INVALID_SOCKET && FD_ISSET(hListenSocket, &fdsetRecv)) + { +#ifdef USE_IPV6 + struct sockaddr_storage sockaddr; +#else + struct sockaddr sockaddr; +#endif + socklen_t len = sizeof(sockaddr); + SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len); + CAddress addr; + int nInbound = 0; + + if (hSocket != INVALID_SOCKET) + if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) + printf("Warning: Unknown socket family\n"); + + { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + if (pnode->fInbound) + nInbound++; + } + + if (hSocket == INVALID_SOCKET) + { + int nErr = WSAGetLastError(); + if (nErr != WSAEWOULDBLOCK) + printf("socket error accept failed: %d\n", nErr); + } + else if (nInbound >= GetArgInt("-maxconnections", 125) - MAX_OUTBOUND_CONNECTIONS) + { + { + LOCK(cs_setservAddNodeAddresses); + if (!setservAddNodeAddresses.count(addr)) + closesocket(hSocket); + } + } + else if (CNode::IsBanned(addr)) + { + printf("connection from %s dropped (banned)\n", addr.ToString().c_str()); + closesocket(hSocket); + } + else + { + printf("accepted connection %s\n", addr.ToString().c_str()); + CNode* pnode = new CNode(hSocket, addr, "", true); + pnode->AddRef(); + { + LOCK(cs_vNodes); + vNodes.push_back(pnode); + } + } + } + + + // + // Service each socket + // + vector vNodesCopy; + { + LOCK(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_LOCK(pnode->cs_vRecv, lockRecv); + if (lockRecv) + { + CDataStream& vRecv = pnode->vRecv; + uint64_t nPos = vRecv.size(); + + if (nPos > ReceiveBufferSize()) { + if (!pnode->fDisconnect) + printf("socket recv flood control disconnect (%" PRIszu " 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(); + pnode->nRecvBytes += nBytes; + pnode->RecordBytesRecv(nBytes); + } + 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_LOCK(pnode->cs_vSend, lockSend); + if (lockSend) + { + 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(); + pnode->nSendBytes += nBytes; + pnode->RecordBytesSent(nBytes); + } + 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(); + } + } + } + } + } + + // + // 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; + } + } + } + { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodesCopy) + pnode->Release(); + } + + Sleep(10); + } +} + +// DNS seeds +// Each pair gives a source name and a seed name. +// The first name is used as information source for addrman. +// The second name should resolve to a list of seed addresses. +static const char *strDNSSeed[][2] = { + {"seed1.cryptolife.net", "seed1.cryptolife.net"}, + {"seed2.cryptolife.net", "seed2.cryptolife.net"}, + {"seed3.cryptolife.net", "seed3.cryptolife.net"}, + {"electrum1.cryptolife.net", "electrum1.cryptolife.net"}, + {"wallet.cryptolife.net", "wallet.cryptolife.net"}, + {"explore.cryptolife.net", "explore.cryptolife.net"} +}; + +void ThreadDNSAddressSeed(void* parg) +{ + // Make this thread recognisable as the DNS seeding thread + RenameThread("XP-dnsseed"); + + try + { + vnThreadsRunning[THREAD_DNSSEED]++; + ThreadDNSAddressSeed2(parg); + vnThreadsRunning[THREAD_DNSSEED]--; + } + catch (std::exception& e) { + vnThreadsRunning[THREAD_DNSSEED]--; + PrintException(&e, "ThreadDNSAddressSeed()"); + } catch (...) { + vnThreadsRunning[THREAD_DNSSEED]--; + throw; // support pthread_cancel() + } + printf("ThreadDNSAddressSeed exited\n"); +} + +void ThreadDNSAddressSeed2(void* parg) +{ + printf("ThreadDNSAddressSeed started\n"); + int found = 0; + + if (!fTestNet) + { + printf("Loading addresses from DNS seeds (could take a while)\n"); + + for (unsigned int seed_idx = 0; seed_idx < ARRAYLEN(strDNSSeed); seed_idx++) { + if (HaveNameProxy()) { + AddOneShot(strDNSSeed[seed_idx][1]); + } else { + vector vaddr; + vector vAdd; + if (LookupHost(strDNSSeed[seed_idx][1], vaddr)) + { + BOOST_FOREACH(CNetAddr& ip, vaddr) + { + CAddress addr = CAddress(CService(ip, GetDefaultPort())); + addr.nTime = GetTime() - 3*nOneDay - GetRand(4*nOneDay); // use a random age between 3 and 7 days old + vAdd.push_back(addr); + found++; + } + } + addrman.Add(vAdd, CNetAddr(strDNSSeed[seed_idx][0], true)); + } + } + } + + printf("%d addresses found from DNS seeds\n", found); +} + + + + + + + + + + + + +uint32_t pnSeed[] = +{ + 0xa52bf0da, 0x30aa43d8, 0x614488d5, 0x517b6fd5, 0xd4bf62d4, 0xb7d638d4, 0xbc12bcd1, 0xa2501bc6, + 0xfde617c6, 0x3337b1c6, 0x1dcd71c3, 0x2c1544c1, 0xe05f6ac1, 0x852f63c0, 0x3e2363c0, 0x15f563c0, + 0x430d63c0, 0x50d6a9c0, 0xf0a679c0, 0xefdeedbf, 0x7aaee8bc, 0x3a3dbbbc, 0xef218abc, 0x0bef78bc, + 0x8baa3eb2, 0x2bf913b2, 0x24ed9fb2, 0xb42289b2, 0x718a09b0, 0xe9433eb0, 0x559425b0, 0xc97e1fb0, + 0x18e1d4b0, 0x8f6cc1b0, 0xac3838ae, 0x86c0ffad, 0x6b0272a7, 0xa463f8a2, 0x6f17f3a2, 0xb3d6f3a2, + 0x9cd8f997, 0xd513fb94, 0x39e64880, 0x3859dd6f, 0x0b08fe6d, 0xe601fe6d, 0xeb44a26d, 0xfe53186c, + 0x203c2e68, 0x1c542868, 0x0caa8368, 0xb8748368, 0xccca4762, 0xc637555f, 0x638a545f, 0x59b2205f, + 0x52568c5f, 0xba568c5f, 0x8a568c5f, 0x31b0f45e, 0x54a0f45e, 0x37d6f15e, 0xc520175e, 0x7620175e, + 0xc310175e, 0x8e33b45e, 0x7abb5f5d, 0xd3014c5d, 0xa1e1485d, 0x9947645d, 0xfab8ff5c, 0xa979e65b, + 0xa879e65b, 0x9f79e65b, 0x9fa3d25b, 0x112a925b, 0x7b92905b, 0x60647a5b, 0x1e389d5a, 0x851afa59, + 0x0185ef59, 0x26549b59, 0x1c9efe57, 0xc54c1256, 0x1ad51955, 0x19d21955, 0x73c41955, 0x3f74ee55, + 0x633eea55, 0x6883d655, 0xfb72c655, 0x5360a653, 0x17c1ea52, 0xc661c852, 0x1ecdc852, 0x006a9752, + 0xf72d9252, 0x82650551, 0x36f1c851, 0x33f1c851, 0xd5c1864f, 0xb6bf1b4e, 0x96da184e, 0x40d0234d, + 0x9a96ab4c, 0x8fc2a84c, 0xb5dbd048, 0xf4644447, 0x2d51af47, 0xa9625445, 0x83f05243, 0x89672941, + 0x3a8bad3e, 0xf0a05436, 0x6ab7c936, 0x49971d32, 0xadd4482e, 0xcffd212e, 0x6730bc2e, 0x839aa12e, + 0x68d9692e, 0xc7183b25, 0x6c47bb25, 0x2490bb25, 0xad651c1f, 0x048a861f, 0x6937811f, 0x064b2d05, + 0x4d226805, +}; + +const char* pchTorSeed[] = +{ + "seedp4knqnoei57u.onion", + "seedr3hhlepyi7fd.onion", + "seed3uuomkclbiz4.onion", + "seedeh7qck3ouff5.onion", + "5rg3vq4jagckeckf.onion", + "seedt3sraf53ajiy.onion", + "seedg4qyccsg42oq.onion", + "novaqrtoywpg7jly.onion", + "seed3d5wolqbgrcb.onion", + "seed24u5dwph3qw4.onion", + "mj26ulzbs2oskgym.onion", + "eqon4usunavt76m7.onion", + "seedd3aldwpslzl3.onion" +}; + +void DumpAddresses() +{ + int64_t nStart = GetTimeMillis(); + + CAddrDB adb; + adb.Write(addrman); + + printf("Flushed %d addresses to peers.dat %" PRId64 "ms\n", + addrman.size(), GetTimeMillis() - nStart); +} + +void ThreadDumpAddress2(void* parg) +{ + printf("ThreadDumpAddress started\n"); + + vnThreadsRunning[THREAD_DUMPADDRESS]++; + while (!fShutdown) + { + DumpAddresses(); + vnThreadsRunning[THREAD_DUMPADDRESS]--; + Sleep(600000); + vnThreadsRunning[THREAD_DUMPADDRESS]++; + } + vnThreadsRunning[THREAD_DUMPADDRESS]--; +} + +void ThreadDumpAddress(void* parg) +{ + // Make this thread recognisable as the address dumping thread + RenameThread("XP-adrdump"); + + try + { + ThreadDumpAddress2(parg); + } + catch (std::exception& e) { + PrintException(&e, "ThreadDumpAddress()"); + } + printf("ThreadDumpAddress exited\n"); +} + +void ThreadOpenConnections(void* parg) +{ + // Make this thread recognisable as the connection opening thread + RenameThread("XP-opencon"); + + try + { + vnThreadsRunning[THREAD_OPENCONNECTIONS]++; + ThreadOpenConnections2(parg); + vnThreadsRunning[THREAD_OPENCONNECTIONS]--; + } + catch (std::exception& e) { + vnThreadsRunning[THREAD_OPENCONNECTIONS]--; + PrintException(&e, "ThreadOpenConnections()"); + } catch (...) { + vnThreadsRunning[THREAD_OPENCONNECTIONS]--; + PrintException(NULL, "ThreadOpenConnections()"); + } + printf("ThreadOpenConnections exited\n"); +} + +void static ProcessOneShot() +{ + string strDest; + { + LOCK(cs_vOneShots); + if (vOneShots.empty()) + return; + strDest = vOneShots.front(); + vOneShots.pop_front(); + } + CAddress addr; + CSemaphoreGrant grant(*semOutbound, true); + if (grant) { + if (!OpenNetworkConnection(addr, &grant, strDest.c_str(), true)) + AddOneShot(strDest); + } +} + +void ThreadOpenConnections2(void* parg) +{ + printf("ThreadOpenConnections started\n"); + + // Connect to specific addresses + if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) + { + for (int64_t nLoop = 0;; nLoop++) + { + ProcessOneShot(); + BOOST_FOREACH(string strAddr, mapMultiArgs["-connect"]) + { + CAddress addr; + OpenNetworkConnection(addr, NULL, strAddr.c_str()); + for (int i = 0; i < 10 && i < nLoop; i++) + { + Sleep(500); + if (fShutdown) + return; + } + } + Sleep(500); + } + } + + // Initiate network connections + int64_t nStart = GetTime(); + while (true) + { + ProcessOneShot(); + + vnThreadsRunning[THREAD_OPENCONNECTIONS]--; + Sleep(500); + vnThreadsRunning[THREAD_OPENCONNECTIONS]++; + if (fShutdown) + return; + + + vnThreadsRunning[THREAD_OPENCONNECTIONS]--; + CSemaphoreGrant grant(*semOutbound); + vnThreadsRunning[THREAD_OPENCONNECTIONS]++; + if (fShutdown) + return; + + // Add seed nodes if IRC isn't working + if (!IsLimited(NET_IPV4) && addrman.size()==0 && (GetTime() - nStart > 60) && !fTestNet) + { + std::vector vAdd; + for (unsigned 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. + // Seed nodes are given a random 'last seen time' of between one and two + // weeks ago. + const int64_t nOneWeek = 7*24*60*60; + struct in_addr ip; + memcpy(&ip, &pnSeed[i], sizeof(ip)); + CAddress addr(CService(ip, GetDefaultPort())); + addr.nTime = GetTime()-GetRand(nOneWeek)-nOneWeek; + vAdd.push_back(addr); + } + addrman.Add(vAdd, CNetAddr("127.0.0.1")); + } + + // Add Tor nodes if we have connection with onion router + if (mapArgs.count("-tor")) + { + std::vector vAdd; + for (unsigned int i = 0; i < ARRAYLEN(pchTorSeed); i++) + { + const int64_t nOneWeek = 7*24*60*60; + CAddress addr(CService(pchTorSeed[i], GetDefaultPort())); + addr.nTime = GetTime()-GetRand(nOneWeek)-nOneWeek; + vAdd.push_back(addr); + } + addrman.Add(vAdd, CNetAddr("dummyaddress.onion")); + } + + // + // Choose an address to connect to based on most recently seen + // + CAddress addrConnect; + + // Only connect out to one peer per network group (/16 for IPv4). + // Do this here so we don't have to critsect vNodes inside mapAddresses critsect. + int nOutbound = 0; + set > setConnected; + { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) { + if (!pnode->fInbound) { + setConnected.insert(pnode->addr.GetGroup()); + nOutbound++; + } + } + } + + int64_t nANow = GetAdjustedTime(); + + int nTries = 0; + while (true) + { + // use an nUnkBias between 10 (no outgoing connections) and 90 (8 outgoing connections) + CAddress addr = addrman.Select(10 + min(nOutbound,8)*10); + + // if we selected an invalid address, restart + if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr)) + break; + + // If we didn't find an appropriate destination after trying 100 addresses fetched from addrman, + // stop this loop, and let the outer loop run again (which sleeps, adds seed nodes, recalculates + // already-connected network ranges, ...) before trying new addrman addresses. + nTries++; + if (nTries > 100) + break; + + if (IsLimited(addr)) + continue; + + // only consider very recently tried nodes after 30 failed attempts + if (nANow - addr.nLastTry < 600 && nTries < 30) + continue; + + // do not allow non-default ports, unless after 50 invalid addresses selected already + if (addr.GetPort() != GetDefaultPort() && nTries < 50) + continue; + + addrConnect = addr; + break; + } + + if (addrConnect.IsValid()) + OpenNetworkConnection(addrConnect, &grant); + } +} + +void ThreadOpenAddedConnections(void* parg) +{ + // Make this thread recognisable as the connection opening thread + RenameThread("XP-opencon"); + + try + { + vnThreadsRunning[THREAD_ADDEDCONNECTIONS]++; + ThreadOpenAddedConnections2(parg); + vnThreadsRunning[THREAD_ADDEDCONNECTIONS]--; + } + catch (std::exception& e) { + vnThreadsRunning[THREAD_ADDEDCONNECTIONS]--; + PrintException(&e, "ThreadOpenAddedConnections()"); + } catch (...) { + vnThreadsRunning[THREAD_ADDEDCONNECTIONS]--; + PrintException(NULL, "ThreadOpenAddedConnections()"); + } + printf("ThreadOpenAddedConnections exited\n"); +} + +void ThreadOpenAddedConnections2(void* parg) +{ + printf("ThreadOpenAddedConnections started\n"); + + { + LOCK(cs_vAddedNodes); + vAddedNodes = mapMultiArgs["-addnode"]; + } + + if (HaveNameProxy()) { + while(!fShutdown) { + list lAddresses(0); + { + LOCK(cs_vAddedNodes); + BOOST_FOREACH(string& strAddNode, vAddedNodes) + lAddresses.push_back(strAddNode); + } + BOOST_FOREACH(string& strAddNode, lAddresses) { + CAddress addr; + CSemaphoreGrant grant(*semOutbound); + OpenNetworkConnection(addr, &grant, strAddNode.c_str()); + Sleep(500); + } + vnThreadsRunning[THREAD_ADDEDCONNECTIONS]--; + Sleep(120000); // Retry every 2 minutes + vnThreadsRunning[THREAD_ADDEDCONNECTIONS]++; + } + return; + } + + for (uint32_t i = 0; true; i++) + { + list lAddresses(0); + { + LOCK(cs_vAddedNodes); + BOOST_FOREACH(string& strAddNode, vAddedNodes) + lAddresses.push_back(strAddNode); + } + + list > lservAddressesToAdd(0); + BOOST_FOREACH(string& strAddNode, lAddresses) + { + vector vservNode(0); + if (Lookup(strAddNode.c_str(), vservNode, GetDefaultPort(), fNameLookup, 0)) + { + lservAddressesToAdd.push_back(vservNode); + { + LOCK(cs_setservAddNodeAddresses); + BOOST_FOREACH(CService& serv, vservNode) + setservAddNodeAddresses.insert(serv); + } + } + } + // Attempt to connect to each IP for each addnode entry until at least one is successful per addnode entry + // (keeping in mind that addnode entries can have many IPs if fNameLookup) + { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + for (list >::iterator it = lservAddressesToAdd.begin(); it != lservAddressesToAdd.end(); it++) + { + BOOST_FOREACH(CService& addrNode, *(it)) + if (pnode->addr == addrNode) + { + it = lservAddressesToAdd.erase(it); + if(it != lservAddressesToAdd.begin()) + it--; + break; + } + if (it == lservAddressesToAdd.end()) + break; + } + } + BOOST_FOREACH(vector& vserv, lservAddressesToAdd) + { + if (vserv.size() == 0) + continue; + CSemaphoreGrant grant(*semOutbound); + OpenNetworkConnection(CAddress(vserv[i % vserv.size()]), &grant); + Sleep(500); + if (fShutdown) + return; + } + if (fShutdown) + return; + vnThreadsRunning[THREAD_ADDEDCONNECTIONS]--; + Sleep(120000); // Retry every 2 minutes + vnThreadsRunning[THREAD_ADDEDCONNECTIONS]++; + if (fShutdown) + return; + } +} + +// if successful, this moves the passed grant to the constructed node +bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound, const char *strDest, bool fOneShot) +{ + // + // Initiate outbound network connection + // + if (fShutdown) + return false; + if (!strDest) + if (IsLocal(addrConnect) || + FindNode((CNetAddr)addrConnect) || CNode::IsBanned(addrConnect) || + FindNode(addrConnect.ToStringIPPort().c_str())) + return false; + if (strDest && FindNode(strDest)) + return false; + + vnThreadsRunning[THREAD_OPENCONNECTIONS]--; + CNode* pnode = ConnectNode(addrConnect, strDest); + vnThreadsRunning[THREAD_OPENCONNECTIONS]++; + if (fShutdown) + return false; + if (!pnode) + return false; + if (grantOutbound) + grantOutbound->MoveTo(pnode->grantOutbound); + pnode->fNetworkNode = true; + if (fOneShot) + pnode->fOneShot = true; + + return true; +} + +// for now, use a very simple selection metric: the node from which we received +// most recently +static int64_t NodeSyncScore(const CNode *pnode) { + return pnode->nLastRecv; +} + +void static StartSync(const vector &vNodes) { + CNode *pnodeNewSync = NULL; + int64_t nBestScore = 0; + + // Iterate over all nodes + BOOST_FOREACH(CNode* pnode, vNodes) { + // check preconditions for allowing a sync + if (!pnode->fClient && !pnode->fOneShot && + !pnode->fDisconnect && pnode->fSuccessfullyConnected && + (pnode->nStartingHeight > (nBestHeight - 144)) && + (pnode->nVersion < NOBLKS_VERSION_START || pnode->nVersion >= NOBLKS_VERSION_END)) { + // if ok, compare node's score with the best so far + int64_t nScore = NodeSyncScore(pnode); + if (pnodeNewSync == NULL || nScore > nBestScore) { + pnodeNewSync = pnode; + nBestScore = nScore; + } + } + } + // if a new sync candidate was found, start sync! + if (pnodeNewSync) { + pnodeNewSync->fStartSync = true; + pnodeSync = pnodeNewSync; + } +} + +void ThreadMessageHandler(void* parg) +{ + // Make this thread recognisable as the message handling thread + RenameThread("XP-msghand"); + + try + { + vnThreadsRunning[THREAD_MESSAGEHANDLER]++; + ThreadMessageHandler2(parg); + vnThreadsRunning[THREAD_MESSAGEHANDLER]--; + } + catch (std::exception& e) { + vnThreadsRunning[THREAD_MESSAGEHANDLER]--; + PrintException(&e, "ThreadMessageHandler()"); + } catch (...) { + vnThreadsRunning[THREAD_MESSAGEHANDLER]--; + PrintException(NULL, "ThreadMessageHandler()"); + } + printf("ThreadMessageHandler exited\n"); +} + +void ThreadMessageHandler2(void* parg) +{ + printf("ThreadMessageHandler started\n"); + SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL); + while (!fShutdown) + { + bool fHaveSyncNode = false; + vector vNodesCopy; + { + LOCK(cs_vNodes); + vNodesCopy = vNodes; + BOOST_FOREACH(CNode* pnode, vNodesCopy) { + pnode->AddRef(); + if (pnode == pnodeSync) + fHaveSyncNode = true; + } + } + + if (!fHaveSyncNode) + StartSync(vNodesCopy); + + // 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_LOCK(pnode->cs_vRecv, lockRecv); + if (lockRecv) + ProcessMessages(pnode); + } + if (fShutdown) + return; + + // Send messages + { + TRY_LOCK(pnode->cs_vSend, lockSend); + if (lockSend) + SendMessages(pnode, pnode == pnodeTrickle); + } + if (fShutdown) + return; + } + + { + LOCK(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[THREAD_MESSAGEHANDLER]--; + Sleep(100); + if (fRequestShutdown) + StartShutdown(); + vnThreadsRunning[THREAD_MESSAGEHANDLER]++; + if (fShutdown) + return; + } +} + + + + + + +bool BindListenPort(const CService &addrBind, string& strError) +{ + strError = ""; + int nOne = 1; + + // Create socket for listening for incoming connections +#ifdef USE_IPV6 + struct sockaddr_storage sockaddr; +#else + struct sockaddr sockaddr; +#endif + socklen_t len = sizeof(sockaddr); + if (!addrBind.GetSockAddr((struct sockaddr*)&sockaddr, &len)) + { + strError = strprintf("Error: bind address family for %s not supported", addrBind.ToString().c_str()); + printf("%s\n", strError.c_str()); + return false; + } + + SOCKET hListenSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, 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; + } + +#ifndef WIN32 +#ifdef SO_NOSIGPIPE + // Different way of disabling SIGPIPE on BSD + setsockopt(hListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&nOne, sizeof(int)); +#endif + // 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 WIN32 + // Set to non-blocking, 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; + } + +#ifdef USE_IPV6 + // some systems don't have IPV6_V6ONLY but are always v6only; others do have the option + // and enable it by default or not. Try to enable it, if possible. + if (addrBind.IsIPv6()) { +#ifdef IPV6_V6ONLY +#ifdef WIN32 + setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&nOne, sizeof(int)); +#else + setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&nOne, sizeof(int)); +#endif +#endif +#ifdef WIN32 + int nProtLevel = PROTECTION_LEVEL_UNRESTRICTED; + setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, (const char*)&nProtLevel, sizeof(int)); +#endif + } +#endif + + if (::bind(hListenSocket, (struct sockaddr*)&sockaddr, len) == SOCKET_ERROR) + { + int nErr = WSAGetLastError(); + if (nErr == WSAEADDRINUSE) + strError = strprintf(_("Unable to bind to %s on this computer. XP is probably already running."), addrBind.ToString().c_str()); + else + strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %d, %s)"), addrBind.ToString().c_str(), nErr, strerror(nErr)); + printf("%s\n", strError.c_str()); + closesocket(hListenSocket); + return false; + } + printf("Bound to %s\n", addrBind.ToString().c_str()); + + // 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()); + closesocket(hListenSocket); + return false; + } + + vhListenSocket.push_back(hListenSocket); + + if (addrBind.IsRoutable() && fDiscover) + AddLocal(addrBind, LOCAL_BIND); + + return true; +} + +void static Discover() +{ + if (!fDiscover) + return; + +#ifdef WIN32 + // Get local host IP + char pszHostName[1000] = ""; + if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR) + { + vector vaddr; + if (LookupHost(pszHostName, vaddr)) + { + BOOST_FOREACH (const CNetAddr &addr, vaddr) + { + AddLocal(addr, LOCAL_IF); + } + } + } +#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; + if (ifa->ifa_addr->sa_family == AF_INET) + { + struct sockaddr_in* s4 = (struct sockaddr_in*)(ifa->ifa_addr); + CNetAddr addr(s4->sin_addr); + if (AddLocal(addr, LOCAL_IF)) + printf("IPv4 %s: %s\n", ifa->ifa_name, addr.ToString().c_str()); + } +#ifdef USE_IPV6 + else if (ifa->ifa_addr->sa_family == AF_INET6) + { + struct sockaddr_in6* s6 = (struct sockaddr_in6*)(ifa->ifa_addr); + CNetAddr addr(s6->sin6_addr); + if (AddLocal(addr, LOCAL_IF)) + printf("IPv6 %s: %s\n", ifa->ifa_name, addr.ToString().c_str()); + } +#endif + } + freeifaddrs(myaddrs); + } +#endif + + // Don't use external IPv4 discovery, when -onlynet="IPv6" + if (!IsLimited(NET_IPV4)) + NewThread(ThreadGetMyExternalIP, NULL); +} + +void StartNode(void* parg) +{ + // Make this thread recognisable as the startup thread + RenameThread("XP-start"); + + if (semOutbound == NULL) { + // initialize semaphore + int nMaxOutbound = min(MAX_OUTBOUND_CONNECTIONS, GetArgInt("-maxconnections", 125)); + semOutbound = new CSemaphore(nMaxOutbound); + } + + if (pnodeLocalHost == NULL) + pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress(CService("127.0.0.1", nPortZero), nLocalServices)); + + Discover(); + + // + // Start threads + // + + if (!GetBoolArg("-dnsseed", true)) + printf("DNS seeding disabled\n"); + else + if (!NewThread(ThreadDNSAddressSeed, NULL)) + printf("Error: NewThread(ThreadDNSAddressSeed) failed\n"); + + // Get addresses from IRC and advertise ours + if (!GetBoolArg("-irc", true)) + printf("IRC seeding disabled\n"); + else + if (!NewThread(ThreadIRCSeed, NULL)) + printf("Error: NewThread(ThreadIRCSeed) failed\n"); + + // Send and receive from sockets, accept connections + if (!NewThread(ThreadSocketHandler, NULL)) + printf("Error: NewThread(ThreadSocketHandler) failed\n"); + + // Initiate outbound connections from -addnode + if (!NewThread(ThreadOpenAddedConnections, NULL)) + printf("Error: NewThread(ThreadOpenAddedConnections) failed\n"); + + // Initiate outbound connections + if (!NewThread(ThreadOpenConnections, NULL)) + printf("Error: NewThread(ThreadOpenConnections) failed\n"); + + // Process messages + if (!NewThread(ThreadMessageHandler, NULL)) + printf("Error: NewThread(ThreadMessageHandler) failed\n"); + + // Dump network addresses + if (!NewThread(ThreadDumpAddress, NULL)) + printf("Error; NewThread(ThreadDumpAddress) failed\n"); + + // Mine proof-of-stake blocks in the background + if (!NewThread(ThreadStakeMiner, pwalletMain)) + printf("Error: NewThread(ThreadStakeMiner) failed\n"); + + // Trusted NTP server, it's localhost by default. + strTrustedUpstream = GetArg("-ntp", "localhost"); + + // Start periodical NTP sampling thread + NewThread(ThreadNtpSamples, NULL); + +} + +bool StopNode() +{ + printf("StopNode()\n"); + fShutdown = true; + nTransactionsUpdated++; + int64_t nStart = GetTime(); + { + LOCK(cs_main); + ThreadScriptCheckQuit(); + } + if (semOutbound) + for (int i=0; ipost(); + do + { + int nThreadsRunning = 0; + for (int n = 0; n < THREAD_MAX; n++) + nThreadsRunning += vnThreadsRunning[n]; + if (nThreadsRunning == 0) + break; + if (GetTime() - nStart > 20) + break; + Sleep(20); + } while(true); + if (vnThreadsRunning[THREAD_SOCKETHANDLER] > 0) printf("ThreadSocketHandler still running\n"); + if (vnThreadsRunning[THREAD_OPENCONNECTIONS] > 0) printf("ThreadOpenConnections still running\n"); + if (vnThreadsRunning[THREAD_MESSAGEHANDLER] > 0) printf("ThreadMessageHandler still running\n"); + if (vnThreadsRunning[THREAD_RPCLISTENER] > 0) printf("ThreadRPCListener still running\n"); + if (vnThreadsRunning[THREAD_RPCHANDLER] > 0) printf("ThreadsRPCServer still running\n"); + if (vnThreadsRunning[THREAD_DNSSEED] > 0) printf("ThreadDNSAddressSeed still running\n"); + if (vnThreadsRunning[THREAD_ADDEDCONNECTIONS] > 0) printf("ThreadOpenAddedConnections still running\n"); + if (vnThreadsRunning[THREAD_DUMPADDRESS] > 0) printf("ThreadDumpAddresses still running\n"); + if (vnThreadsRunning[THREAD_MINTER] > 0) printf("ThreadStakeMinter still running\n"); + if (vnThreadsRunning[THREAD_SCRIPTCHECK] > 0) printf("ThreadScriptCheck still running\n"); + while (vnThreadsRunning[THREAD_MESSAGEHANDLER] > 0 || vnThreadsRunning[THREAD_RPCHANDLER] > 0 || vnThreadsRunning[THREAD_SCRIPTCHECK] > 0) + Sleep(20); + Sleep(50); + DumpAddresses(); + + return true; +} + +class CNetCleanup +{ +public: + CNetCleanup() + { + } + ~CNetCleanup() + { + // Close sockets + BOOST_FOREACH(CNode* pnode, vNodes) + if (pnode->hSocket != INVALID_SOCKET) + closesocket(pnode->hSocket); + BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) + if (hListenSocket != INVALID_SOCKET) + if (closesocket(hListenSocket) == SOCKET_ERROR) + printf("closesocket(hListenSocket) failed with error %d\n", WSAGetLastError()); + + // clean up some globals (to help leak detection) + BOOST_FOREACH(CNode *pnode, vNodes) + delete pnode; + BOOST_FOREACH(CNode *pnode, vNodesDisconnected) + delete pnode; + vNodes.clear(); + vNodesDisconnected.clear(); + delete semOutbound; + semOutbound = NULL; + delete pnodeLocalHost; + pnodeLocalHost = NULL; + +#ifdef WIN32 + // Shutdown Windows Sockets + WSACleanup(); +#endif + } +} +instance_of_cnetcleanup; + +void RelayTransaction(const CTransaction& tx, const uint256& hash) +{ + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss.reserve(10000); + ss << tx; + RelayTransaction(tx, hash, ss); +} + +void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataStream& ss) +{ + CInv inv(MSG_TX, hash); + { + LOCK(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.insert(std::make_pair(inv, ss)); + vRelayExpiration.push_back(std::make_pair(GetTime() + 15 * 60, inv)); + } + + RelayInventory(inv); +} + +void CNode::RecordBytesRecv(uint64_t bytes) +{ + LOCK(cs_totalBytesRecv); + nTotalBytesRecv += bytes; +} + +void CNode::RecordBytesSent(uint64_t bytes) +{ + LOCK(cs_totalBytesSent); + nTotalBytesSent += bytes; +} + +uint64_t CNode::GetTotalBytesRecv() +{ + LOCK(cs_totalBytesRecv); + return nTotalBytesRecv; +} + +uint64_t CNode::GetTotalBytesSent() +{ + LOCK(cs_totalBytesSent); + return nTotalBytesSent; +} diff --git a/src/net.h b/src/net.h new file mode 100644 index 00000000..56c7b2f0 --- /dev/null +++ b/src/net.h @@ -0,0 +1,688 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_NET_H +#define BITCOIN_NET_H + +#include +#include +#ifndef Q_MOC_RUN +#include +#include +#endif +#include + +#ifndef WIN32 +#include +#endif + +#include "mruset.h" +#include "netbase.h" +#include "protocol.h" +#include "addrman.h" +#include "hash.h" + +class CRequestTracker; +class CNode; +class CBlockIndex; +extern int nBestHeight; + +const uint16_t nSocksDefault = 9050; +const uint16_t nPortZero = 0; + + +inline uint64_t ReceiveBufferSize() { return 1000*GetArg("-maxreceivebuffer", 5*1000); } +inline uint64_t SendBufferSize() { return 1000*GetArg("-maxsendbuffer", 1*1000); } + +void AddOneShot(std::string strDest); +bool RecvLine(SOCKET hSocket, std::string& strLine); +bool GetMyExternalIP(CNetAddr& ipRet); +void AddressCurrentlyConnected(const CService& addr); +CNode* FindNode(const CNetAddr& ip); +CNode* FindNode(const CService& ip); +CNode* ConnectNode(CAddress addrConnect, const char *strDest = NULL, int64_t nTimeout=0); +bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false); +void MapPort(); +unsigned short GetListenPort(); +bool BindListenPort(const CService &bindAddr, std::string& strError=REF(std::string())); +void StartNode(void* parg); +bool StopNode(); + +enum +{ + LOCAL_NONE, // unknown + LOCAL_IF, // address a local interface listens on + LOCAL_BIND, // address explicit bound to + LOCAL_IRC, // address reported by IRC (deprecated) + LOCAL_HTTP, // address reported by whatismyip.com and similar + LOCAL_MANUAL, // address explicitly specified (-externalip=) + + LOCAL_MAX +}; + +void SetLimited(enum Network net, bool fLimited = true); +bool IsLimited(enum Network net); +bool IsLimited(const CNetAddr& addr); +bool AddLocal(const CService& addr, int nScore = LOCAL_NONE); +bool AddLocal(const CNetAddr& addr, int nScore = LOCAL_NONE); +bool SeenLocal(const CService& addr); +bool IsLocal(const CService& addr); +bool GetLocal(CService &addr, const CNetAddr *paddrPeer = NULL); +bool IsReachable(const CNetAddr &addr); +void SetReachable(enum Network net, bool fFlag = true); +CAddress GetLocalAddress(const CNetAddr *paddrPeer = NULL); + + +enum +{ + MSG_TX = 1, + MSG_BLOCK, +}; + +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; + } +}; + + +/** Thread types */ +enum threadId +{ + THREAD_SOCKETHANDLER, + THREAD_OPENCONNECTIONS, + THREAD_MESSAGEHANDLER, + THREAD_RPCLISTENER, + THREAD_DNSSEED, + THREAD_ADDEDCONNECTIONS, + THREAD_DUMPADDRESS, + THREAD_RPCHANDLER, + THREAD_MINTER, + THREAD_SCRIPTCHECK, + THREAD_NTP, + + THREAD_MAX +}; + +extern bool fClient; +extern bool fDiscover; +extern uint64_t nLocalServices; +extern uint64_t nLocalHostNonce; +extern CAddress addrSeenByPeer; +extern boost::array vnThreadsRunning; +extern CAddrMan addrman; + +extern std::vector vNodes; +extern CCriticalSection cs_vNodes; +extern std::vector vAddedNodes; +extern CCriticalSection cs_vAddedNodes; +extern std::map mapRelay; +extern std::deque > vRelayExpiration; +extern CCriticalSection cs_mapRelay; +extern std::map mapAlreadyAskedFor; + + + + +class CNodeStats +{ +public: + uint64_t nServices; + int64_t nLastSend; + int64_t nLastRecv; + int64_t nTimeConnected; + std::string addrName; + int32_t nVersion; + std::string strSubVer; + bool fInbound; + int64_t nReleaseTime; + int32_t nStartingHeight; + int32_t nMisbehavior; + uint64_t nSendBytes; + uint64_t nRecvBytes; + bool fSyncNode; +}; + + + + + +/** Information about a peer */ +class CNode +{ +public: + // socket + uint64_t nServices; + SOCKET hSocket; + CDataStream vSend; + CDataStream vRecv; + uint64_t nSendBytes; + uint64_t nRecvBytes; + CCriticalSection cs_vSend; + CCriticalSection cs_vRecv; + int64_t nLastSend; + int64_t nLastRecv; + int64_t nLastSendEmpty; + int64_t nTimeConnected; + int32_t nHeaderStart; + uint32_t nMessageStart; + CAddress addr; + std::string addrName; + CService addrLocal; + int32_t nVersion; + std::string strSubVer; + bool fOneShot; + bool fClient; + bool fInbound; + bool fNetworkNode; + bool fSuccessfullyConnected; + bool fDisconnect; + CSemaphoreGrant grantOutbound; +protected: + int nRefCount; + + // Denial-of-service detection/prevention + // Key is IP address, value is banned-until-time + static std::map setBanned; + static CCriticalSection cs_setBanned; + int nMisbehavior; + +public: + int64_t nReleaseTime; + std::map mapRequests; + CCriticalSection cs_mapRequests; + uint256 hashContinue; + CBlockIndex* pindexLastGetBlocksBegin; + uint256 hashLastGetBlocksEnd; + int32_t nStartingHeight; + bool fStartSync; + + // flood relay + std::vector vAddrToSend; + std::set setAddrKnown; + bool fGetAddr; + std::set setKnown; + uint256 hashCheckpointKnown; // ppcoin: known sent sync-checkpoint + + // inventory based relay + mruset setInventoryKnown; + std::vector vInventoryToSend; + CCriticalSection cs_inventory; + std::multimap mapAskFor; + + CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn = "", bool fInboundIn=false) : vSend(SER_NETWORK, MIN_PROTO_VERSION), vRecv(SER_NETWORK, MIN_PROTO_VERSION) + { + nServices = 0; + hSocket = hSocketIn; + nLastSend = 0; + nLastRecv = 0; + nSendBytes = 0; + nRecvBytes = 0; + nLastSendEmpty = GetTime(); + nTimeConnected = GetTime(); + nHeaderStart = -1; + nMessageStart = std::numeric_limits::max(); + addr = addrIn; + addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn; + nVersion = 0; + strSubVer = ""; + fOneShot = false; + 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; + fStartSync = false; + fGetAddr = false; + nMisbehavior = 0; + hashCheckpointKnown = 0; + setInventoryKnown.max_size((size_t)SendBufferSize() / 1000); + + // Be shy and don't send version until we hear + if (hSocket != INVALID_SOCKET && !fInbound) + PushVersion(); + } + + ~CNode() + { + if (hSocket != INVALID_SOCKET) + { + closesocket(hSocket); + hSocket = INVALID_SOCKET; + } + } + + +private: + // Network usage totals + static CCriticalSection cs_totalBytesRecv; + static CCriticalSection cs_totalBytesSent; + static uint64_t nTotalBytesRecv; + static uint64_t nTotalBytesSent; + CNode(const CNode&); + void operator=(const CNode&); +public: + + + int GetRefCount() + { + return std::max(nRefCount, 0) + (GetTime() < nReleaseTime ? 1 : 0); + } + + CNode* AddRef(int64_t 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) + { + { + LOCK(cs_inventory); + setInventoryKnown.insert(inv); + } + } + + void PushInventory(const CInv& inv) + { + { + LOCK(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_t& nRequestTime = mapAlreadyAskedFor[inv]; + if (fDebugNet) + printf("askfor %s %" PRId64 " (%s)\n", inv.ToString().c_str(), nRequestTime, DateTimeStrFormat("%H:%M:%S", nRequestTime/1000000).c_str()); + + // Make sure not to reuse time indexes to keep things in the same order + int64_t nNow = (GetTime() - 1) * 1000000; + static int64_t nLastTime; + ++nLastTime; + nNow = std::max(nNow, nLastTime); + nLastTime = nNow; + + // 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) + { + ENTER_CRITICAL_SECTION(cs_vSend); + if (nHeaderStart != -1) + AbortMessage(); + nHeaderStart = (int32_t)vSend.size(); + vSend << CMessageHeader(pszCommand, 0); + nMessageStart = (uint32_t)vSend.size(); + if (fDebug) + printf("sending: %s ", pszCommand); + } + + void AbortMessage() + { + if (nHeaderStart < 0) + return; + vSend.resize(nHeaderStart); + nHeaderStart = -1; + nMessageStart = std::numeric_limits::max(); + LEAVE_CRITICAL_SECTION(cs_vSend); + + if (fDebug) + printf("(aborted)\n"); + } + + void EndMessage() + { + if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) + { + printf("dropmessages DROPPING SEND MESSAGE\n"); + AbortMessage(); + return; + } + + if (nHeaderStart < 0) + return; + + // Set the size + uint32_t nSize = (uint32_t) vSend.size() - nMessageStart; + memcpy((char*)&vSend[nHeaderStart] + CMessageHeader::MESSAGE_SIZE_OFFSET, &nSize, sizeof(nSize)); + + // Set the checksum + uint256 hash = Hash(vSend.begin() + nMessageStart, vSend.end()); + uint32_t nChecksum = 0; + memcpy(&nChecksum, &hash, sizeof(nChecksum)); + assert(nMessageStart - nHeaderStart >= CMessageHeader::CHECKSUM_OFFSET + sizeof(nChecksum)); + memcpy((char*)&vSend[nHeaderStart] + CMessageHeader::CHECKSUM_OFFSET, &nChecksum, sizeof(nChecksum)); + + if (fDebug) { + printf("(%d bytes)\n", nSize); + } + + nHeaderStart = -1; + nMessageStart = std::numeric_limits::max(); + LEAVE_CRITICAL_SECTION(cs_vSend); + } + + void EndMessageAbortIfEmpty() + { + if (nHeaderStart < 0) + return; + int nSize = (int) vSend.size() - nMessageStart; + if (nSize > 0) + EndMessage(); + else + AbortMessage(); + } + + + + void PushVersion(); + + + 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)); + + { + LOCK(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)); + + { + LOCK(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)); + + { + LOCK(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(); + + + // Denial-of-service detection/prevention + // The idea is to detect peers that are behaving + // badly and disconnect/ban them, but do it in a + // one-coding-mistake-won't-shatter-the-entire-network + // way. + // IMPORTANT: There should be nothing I can give a + // node that it will forward on that will make that + // node's peers drop it. If there is, an attacker + // can isolate a node and/or try to split the network. + // Dropping a node for sending stuff that is invalid + // now but might be valid in a later version is also + // dangerous, because it can cause a network split + // between nodes running old code and nodes running + // new code. + static void ClearBanned(); // needed for unit testing + static bool IsBanned(CNetAddr ip); + bool Misbehaving(int howmuch); // 1 == a little, 100 == a lot + void copyStats(CNodeStats &stats); + // Network stats + static void RecordBytesRecv(uint64_t bytes); + static void RecordBytesSent(uint64_t bytes); + + static uint64_t GetTotalBytesRecv(); + static uint64_t GetTotalBytesSent(); +}; + +inline void RelayInventory(const CInv& inv) +{ + // Put on lists to offer to the other nodes + { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + pnode->PushInventory(inv); + } +} + +class CTransaction; +void RelayTransaction(const CTransaction& tx, const uint256& hash); +void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataStream& ss); + + +#endif diff --git a/src/netbase.cpp b/src/netbase.cpp new file mode 100644 index 00000000..1e7172e0 --- /dev/null +++ b/src/netbase.cpp @@ -0,0 +1,1187 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "netbase.h" +#include "util.h" +#include "sync.h" +#include "hash.h" + +#ifndef WIN32 +#ifdef ANDROID +#include +#else +#include +#endif +#endif + +#ifdef _MSC_VER +#include +typedef SSIZE_T ssize_t; +#endif + +#include // for to_lower() +#include // for startswith() and endswith() + +using namespace std; + +// Settings +static proxyType proxyInfo[NET_MAX]; +static proxyType nameproxyInfo; +static CCriticalSection cs_proxyInfos; +int nConnectTimeout = 5000; +bool fNameLookup = false; + +static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; + +enum Network ParseNetwork(std::string net) { + boost::to_lower(net); + if (net == "ipv4") return NET_IPV4; + if (net == "ipv6") return NET_IPV6; + if (net == "tor" || net == "onion") return NET_TOR; + if (net == "i2p") return NET_I2P; + return NET_UNROUTABLE; +} + +void SplitHostPort(std::string in, uint16_t &portOut, std::string &hostOut) { + size_t colon = in.find_last_of(':'); + // if a : is found, and it either follows a [...], or no other : is in the string, treat it as port separator + bool fHaveColon = colon != in.npos; + bool fBracketed = fHaveColon && (in[0]=='[' && in[colon-1]==']'); // if there is a colon, and in[0]=='[', colon is not 0, so in[colon-1] is safe + bool fMultiColon = fHaveColon && (in.find_last_of(':',colon-1) != in.npos); + if (fHaveColon && (colon==0 || fBracketed || !fMultiColon)) { + char *endp = NULL; + int n = strtol(in.c_str() + colon + 1, &endp, 10); + if (endp && *endp == 0 && n >= 0) { + in = in.substr(0, colon); + if (n > 0 && n < 0x10000) + portOut = n; + } + } + if (in.size()>0 && in[0] == '[' && in[in.size()-1] == ']') + hostOut = in.substr(1, in.size()-2); + else + hostOut = in; +} + +bool static LookupIntern(const char *pszName, std::vector& vIP, unsigned int nMaxSolutions, bool fAllowLookup) +{ + vIP.clear(); + + { + CNetAddr addr; + if (addr.SetSpecial(std::string(pszName))) { + vIP.push_back(addr); + return true; + } + } + + struct addrinfo aiHint; + memset(&aiHint, 0, sizeof(struct addrinfo)); + + aiHint.ai_socktype = SOCK_STREAM; + aiHint.ai_protocol = IPPROTO_TCP; +#ifdef USE_IPV6 + aiHint.ai_family = AF_UNSPEC; +#else + aiHint.ai_family = AF_INET; +#endif +#ifdef WIN32 + aiHint.ai_flags = fAllowLookup ? 0 : AI_NUMERICHOST; +#else + aiHint.ai_flags = fAllowLookup ? AI_ADDRCONFIG : AI_NUMERICHOST; +#endif + struct addrinfo *aiRes = NULL; + int nErr = getaddrinfo(pszName, NULL, &aiHint, &aiRes); + if (nErr) + return false; + + struct addrinfo *aiTrav = aiRes; + while (aiTrav != NULL && (nMaxSolutions == 0 || vIP.size() < nMaxSolutions)) + { + switch (aiTrav->ai_family) + { + case (AF_INET): + assert(aiTrav->ai_addrlen >= sizeof(sockaddr_in)); + vIP.push_back(CNetAddr(((struct sockaddr_in*)(aiTrav->ai_addr))->sin_addr)); + break; + +#ifdef USE_IPV6 + case (AF_INET6): + assert(aiTrav->ai_addrlen >= sizeof(sockaddr_in6)); + vIP.push_back(CNetAddr(((struct sockaddr_in6*)(aiTrav->ai_addr))->sin6_addr)); + break; +#endif + } + + aiTrav = aiTrav->ai_next; + } + + freeaddrinfo(aiRes); + + return (vIP.size() > 0); +} + +bool LookupHost(const char *pszName, std::vector& vIP, unsigned int nMaxSolutions, bool fAllowLookup) +{ + std::string strHost(pszName); + if (strHost.empty()) + return false; + if (boost::algorithm::starts_with(strHost, "[") && boost::algorithm::ends_with(strHost, "]")) + { + strHost = strHost.substr(1, strHost.size() - 2); + } + + return LookupIntern(strHost.c_str(), vIP, nMaxSolutions, fAllowLookup); +} + +bool LookupHostNumeric(const char *pszName, std::vector& vIP, unsigned int nMaxSolutions) +{ + return LookupHost(pszName, vIP, nMaxSolutions, false); +} + +bool Lookup(const char *pszName, std::vector& vAddr, uint16_t portDefault, bool fAllowLookup, unsigned int nMaxSolutions) +{ + if (pszName[0] == 0) + return false; + uint16_t port = portDefault; + std::string hostname = ""; + SplitHostPort(std::string(pszName), port, hostname); + + std::vector vIP; + bool fRet = LookupIntern(hostname.c_str(), vIP, nMaxSolutions, fAllowLookup); + if (!fRet) + return false; + vAddr.resize(vIP.size()); + for (unsigned int i = 0; i < vIP.size(); i++) + vAddr[i] = CService(vIP[i], port); + return true; +} + +bool Lookup(const char *pszName, CService& addr, uint16_t portDefault, bool fAllowLookup) +{ + std::vector vService; + bool fRet = Lookup(pszName, vService, portDefault, fAllowLookup, 1); + if (!fRet) + return false; + addr = vService[0]; + return true; +} + +bool LookupNumeric(const char *pszName, CService& addr, uint16_t portDefault) +{ + return Lookup(pszName, addr, portDefault, false); +} + +bool static Socks4(const CService &addrDest, SOCKET& hSocket) +{ + printf("SOCKS4 connecting %s\n", addrDest.ToString().c_str()); + if (!addrDest.IsIPv4()) + { + closesocket(hSocket); + return error("Proxy destination is not IPv4"); + } + char pszSocks4IP[] = "\4\1\0\0\0\0\0\0user"; + struct sockaddr_in addr; + socklen_t len = sizeof(addr); + if (!addrDest.GetSockAddr((struct sockaddr*)&addr, &len) || addr.sin_family != AF_INET) + { + closesocket(hSocket); + return error("Cannot get proxy destination address"); + } + memcpy(pszSocks4IP + 2, &addr.sin_port, 2); + memcpy(pszSocks4IP + 4, &addr.sin_addr, 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("SOCKS4 connected %s\n", addrDest.ToString().c_str()); + return true; +} + +bool static Socks5(string strDest, uint16_t port, SOCKET& hSocket) +{ + printf("SOCKS5 connecting %s\n", strDest.c_str()); + if (strDest.size() > 255) + { + closesocket(hSocket); + return error("Hostname too long"); + } + char pszSocks5Init[] = "\5\1\0"; + ssize_t nSize = sizeof(pszSocks5Init) - 1; + + ssize_t ret = send(hSocket, pszSocks5Init, nSize, MSG_NOSIGNAL); + if (ret != nSize) + { + closesocket(hSocket); + return error("Error sending to proxy"); + } + char pchRet1[2]; + if (recv(hSocket, pchRet1, 2, 0) != 2) + { + closesocket(hSocket); + return error("Error reading proxy response"); + } + if (pchRet1[0] != 0x05 || pchRet1[1] != 0x00) + { + closesocket(hSocket); + return error("Proxy failed to initialize"); + } + string strSocks5("\5\1"); + strSocks5 += '\000'; strSocks5 += '\003'; + strSocks5 += static_cast(std::min((int)strDest.size(), 255)); + strSocks5 += strDest; + strSocks5 += static_cast((port >> 8) & 0xFF); + strSocks5 += static_cast((port >> 0) & 0xFF); + ret = send(hSocket, strSocks5.c_str(), strSocks5.size(), MSG_NOSIGNAL); + if (ret != (ssize_t)strSocks5.size()) + { + closesocket(hSocket); + return error("Error sending to proxy"); + } + char pchRet2[4]; + if (recv(hSocket, pchRet2, 4, 0) != 4) + { + closesocket(hSocket); + return error("Error reading proxy response"); + } + if (pchRet2[0] != 0x05) + { + closesocket(hSocket); + return error("Proxy failed to accept request"); + } + if (pchRet2[1] != 0x00) + { + closesocket(hSocket); + switch (pchRet2[1]) + { + case 0x01: return error("Proxy error: general failure"); + case 0x02: return error("Proxy error: connection not allowed"); + case 0x03: return error("Proxy error: network unreachable"); + case 0x04: return error("Proxy error: host unreachable"); + case 0x05: return error("Proxy error: connection refused"); + case 0x06: return error("Proxy error: TTL expired"); + case 0x07: return error("Proxy error: protocol error"); + case 0x08: return error("Proxy error: address type not supported"); + default: return error("Proxy error: unknown"); + } + } + if (pchRet2[2] != 0x00) + { + closesocket(hSocket); + return error("Error: malformed proxy response"); + } + char pchRet3[256]; + switch (pchRet2[3]) + { + case 0x01: ret = recv(hSocket, pchRet3, 4, 0) != 4; break; + case 0x04: ret = recv(hSocket, pchRet3, 16, 0) != 16; break; + case 0x03: + { + ret = recv(hSocket, pchRet3, 1, 0) != 1; + if (ret) { + closesocket(hSocket); + return error("Error reading from proxy"); + } + int nRecv = pchRet3[0]; + ret = recv(hSocket, pchRet3, nRecv, 0) != nRecv; + break; + } + default: closesocket(hSocket); return error("Error: malformed proxy response"); + } + if (ret) + { + closesocket(hSocket); + return error("Error reading from proxy"); + } + if (recv(hSocket, pchRet3, 2, 0) != 2) + { + closesocket(hSocket); + return error("Error reading from proxy"); + } + printf("SOCKS5 connected %s\n", strDest.c_str()); + return true; +} + +bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRet, int nTimeout) +{ + hSocketRet = INVALID_SOCKET; + +#ifdef USE_IPV6 + struct sockaddr_storage sockaddr; +#else + struct sockaddr sockaddr; +#endif + socklen_t len = sizeof(sockaddr); + if (!addrConnect.GetSockAddr((struct sockaddr*)&sockaddr, &len)) { + printf("Cannot connect to %s: unsupported network\n", addrConnect.ToString().c_str()); + return false; + } + + SOCKET hSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP); + if (hSocket == INVALID_SOCKET) + return false; +#ifdef SO_NOSIGPIPE + int set = 1; + setsockopt(hSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(int)); +#endif + +#ifdef WIN32 + 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, len) == 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 WIN32 + 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(): %s\n",strerror(nRet)); + closesocket(hSocket); + return false; + } + } +#ifdef WIN32 + else if (WSAGetLastError() != WSAEISCONN) +#else + else +#endif + { + printf("connect() failed: %i\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 WIN32 + 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; + } + + hSocketRet = hSocket; + return true; +} + +bool SetProxy(enum Network net, CService addrProxy, int nSocksVersion) { + assert(net >= 0 && net < NET_MAX); + if (nSocksVersion != 0 && nSocksVersion != 4 && nSocksVersion != 5) + return false; + if (nSocksVersion != 0 && !addrProxy.IsValid()) + return false; + LOCK(cs_proxyInfos); + proxyInfo[net] = std::make_pair(addrProxy, nSocksVersion); + return true; +} + +bool GetProxy(enum Network net, proxyType &proxyInfoOut) { + assert(net >= 0 && net < NET_MAX); + LOCK(cs_proxyInfos); + if (!proxyInfo[net].second) + return false; + proxyInfoOut = proxyInfo[net]; + return true; +} + +bool SetNameProxy(CService addrProxy, int nSocksVersion) { + if (nSocksVersion != 0 && nSocksVersion != 5) + return false; + if (nSocksVersion != 0 && !addrProxy.IsValid()) + return false; + LOCK(cs_proxyInfos); + nameproxyInfo = std::make_pair(addrProxy, nSocksVersion); + return true; +} + +bool GetNameProxy(proxyType &nameproxyInfoOut) { + LOCK(cs_proxyInfos); + if (!nameproxyInfo.second) + return false; + nameproxyInfoOut = nameproxyInfo; + return true; +} + +bool HaveNameProxy() { + LOCK(cs_proxyInfos); + return nameproxyInfo.second != 0; +} + +bool IsProxy(const CNetAddr &addr) { + LOCK(cs_proxyInfos); + for (int i = 0; i < NET_MAX; i++) { + if (proxyInfo[i].second && (addr == (CNetAddr)proxyInfo[i].first)) + return true; + } + return false; +} + +bool ConnectSocket(const CService &addrDest, SOCKET& hSocketRet, int nTimeout) +{ + proxyType proxy; + + // no proxy needed + if (!GetProxy(addrDest.GetNetwork(), proxy)) + return ConnectSocketDirectly(addrDest, hSocketRet, nTimeout); + + SOCKET hSocket = INVALID_SOCKET; + + // first connect to proxy server + if (!ConnectSocketDirectly(proxy.first, hSocket, nTimeout)) + return false; + + // do socks negotiation + switch (proxy.second) { + case 4: + if (!Socks4(addrDest, hSocket)) + return false; + break; + case 5: + if (!Socks5(addrDest.ToStringIP(), addrDest.GetPort(), hSocket)) + return false; + break; + default: + closesocket(hSocket); + return false; + } + + hSocketRet = hSocket; + return true; +} + +bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, uint16_t portDefault, int nTimeout) +{ + string strDest; + uint16_t port = portDefault; + SplitHostPort(string(pszDest), port, strDest); + + SOCKET hSocket = INVALID_SOCKET; + + proxyType nameproxy; + GetNameProxy(nameproxy); + + CService addrResolved(CNetAddr(strDest, fNameLookup && !nameproxy.second), port); + if (addrResolved.IsValid()) { + addr = addrResolved; + return ConnectSocket(addr, hSocketRet, nTimeout); + } + addr = CService("0.0.0.0:0"); + if (!nameproxy.second) + return false; + if (!ConnectSocketDirectly(nameproxy.first, hSocket, nTimeout)) + return false; + + switch(nameproxy.second) { + default: + case 4: + closesocket(hSocket); + return false; + case 5: + if (!Socks5(strDest, port, hSocket)) + return false; + break; + } + + hSocketRet = hSocket; + return true; +} + +void CNetAddr::Init() +{ + memset(ip, 0, sizeof(ip)); +} + +void CNetAddr::SetIP(const CNetAddr& ipIn) +{ + memcpy(ip, ipIn.ip, sizeof(ip)); +} + +static const unsigned char pchOnionCat[] = {0xFD,0x87,0xD8,0x7E,0xEB,0x43}; +static const unsigned char pchGarliCat[] = {0xFD,0x60,0xDB,0x4D,0xDD,0xB5}; + +bool CNetAddr::SetSpecial(const std::string &strName) +{ + if (strName.size()>6 && strName.substr(strName.size() - 6, 6) == ".onion") { + std::vector vchAddr = DecodeBase32(strName.substr(0, strName.size() - 6).c_str()); + if (vchAddr.size() != 16-sizeof(pchOnionCat)) + return false; + memcpy(ip, pchOnionCat, sizeof(pchOnionCat)); + for (unsigned int i=0; i<16-sizeof(pchOnionCat); i++) + ip[i + sizeof(pchOnionCat)] = vchAddr[i]; + return true; + } + if (strName.size()>11 && strName.substr(strName.size() - 11, 11) == ".oc.b32.i2p") { + std::vector vchAddr = DecodeBase32(strName.substr(0, strName.size() - 11).c_str()); + if (vchAddr.size() != 16-sizeof(pchGarliCat)) + return false; + memcpy(ip, pchOnionCat, sizeof(pchGarliCat)); + for (unsigned int i=0; i<16-sizeof(pchGarliCat); i++) + ip[i + sizeof(pchGarliCat)] = vchAddr[i]; + return true; + } + return false; +} + +CNetAddr::CNetAddr() +{ + Init(); +} + +CNetAddr::CNetAddr(const struct in_addr& ipv4Addr) +{ + memcpy(ip, pchIPv4, 12); + memcpy(ip+12, &ipv4Addr, 4); +} + +#ifdef USE_IPV6 +CNetAddr::CNetAddr(const struct in6_addr& ipv6Addr) +{ + memcpy(ip, &ipv6Addr, 16); +} +#endif + +CNetAddr::CNetAddr(const char *pszIp, bool fAllowLookup) +{ + Init(); + std::vector vIP; + if (LookupHost(pszIp, vIP, 1, fAllowLookup)) + *this = vIP[0]; +} + +CNetAddr::CNetAddr(const std::string &strIp, bool fAllowLookup) +{ + Init(); + std::vector vIP; + if (LookupHost(strIp.c_str(), vIP, 1, fAllowLookup)) + *this = vIP[0]; +} + +uint8_t CNetAddr::GetByte(int n) const +{ + return ip[15-n]; +} + +bool CNetAddr::IsIPv4() const +{ + return (memcmp(ip, pchIPv4, sizeof(pchIPv4)) == 0); +} + +bool CNetAddr::IsIPv6() const +{ + return (!IsIPv4() && !IsTor() && !IsI2P()); +} + +bool CNetAddr::IsRFC1918() const +{ + return IsIPv4() && ( + GetByte(3) == 10 || + (GetByte(3) == 192 && GetByte(2) == 168) || + (GetByte(3) == 172 && (GetByte(2) >= 16 && GetByte(2) <= 31))); +} + +bool CNetAddr::IsRFC3927() const +{ + return IsIPv4() && (GetByte(3) == 169 && GetByte(2) == 254); +} + +bool CNetAddr::IsRFC3849() const +{ + return GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x0D && GetByte(12) == 0xB8; +} + +bool CNetAddr::IsRFC3964() const +{ + return (GetByte(15) == 0x20 && GetByte(14) == 0x02); +} + +bool CNetAddr::IsRFC6052() const +{ + static const unsigned char pchRFC6052[] = {0,0x64,0xFF,0x9B,0,0,0,0,0,0,0,0}; + return (memcmp(ip, pchRFC6052, sizeof(pchRFC6052)) == 0); +} + +bool CNetAddr::IsRFC4380() const +{ + return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0 && GetByte(12) == 0); +} + +bool CNetAddr::IsRFC4862() const +{ + static const unsigned char pchRFC4862[] = {0xFE,0x80,0,0,0,0,0,0}; + return (memcmp(ip, pchRFC4862, sizeof(pchRFC4862)) == 0); +} + +bool CNetAddr::IsRFC4193() const +{ + return ((GetByte(15) & 0xFE) == 0xFC); +} + +bool CNetAddr::IsRFC6145() const +{ + static const unsigned char pchRFC6145[] = {0,0,0,0,0,0,0,0,0xFF,0xFF,0,0}; + return (memcmp(ip, pchRFC6145, sizeof(pchRFC6145)) == 0); +} + +bool CNetAddr::IsRFC4843() const +{ + return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x10); +} + +bool CNetAddr::IsTor() const +{ + return (memcmp(ip, pchOnionCat, sizeof(pchOnionCat)) == 0); +} + +bool CNetAddr::IsI2P() const +{ + return (memcmp(ip, pchGarliCat, sizeof(pchGarliCat)) == 0); +} + +bool CNetAddr::IsLocal() const +{ + // IPv4 loopback + if (IsIPv4() && (GetByte(3) == 127 || GetByte(3) == 0)) + return true; + + // IPv6 loopback (::1/128) + static const unsigned char pchLocal[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}; + if (memcmp(ip, pchLocal, 16) == 0) + return true; + + return false; +} + +bool CNetAddr::IsMulticast() const +{ + return (IsIPv4() && (GetByte(3) & 0xF0) == 0xE0) + || (GetByte(15) == 0xFF); +} + +bool CNetAddr::IsValid() const +{ + // Cleanup 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(ip, pchIPv4+3, sizeof(pchIPv4)-3) == 0) + return false; + + // unspecified IPv6 address (::/128) + unsigned char ipNone[16] = {}; + if (memcmp(ip, ipNone, 16) == 0) + return false; + + // documentation IPv6 address + if (IsRFC3849()) + return false; + + if (IsIPv4()) + { + // INADDR_NONE + uint32_t ipNone = INADDR_NONE; + if (memcmp(ip+12, &ipNone, 4) == 0) + return false; + + // 0 + ipNone = 0; + if (memcmp(ip+12, &ipNone, 4) == 0) + return false; + } + + return true; +} + +bool CNetAddr::IsRoutable() const +{ + return IsValid() && !(IsRFC1918() || IsRFC3927() || IsRFC4862() || (IsRFC4193() && !IsTor() && !IsI2P()) || IsRFC4843() || IsLocal()); +} + +enum Network CNetAddr::GetNetwork() const +{ + if (!IsRoutable()) + return NET_UNROUTABLE; + + if (IsIPv4()) + return NET_IPV4; + + if (IsTor()) + return NET_TOR; + + if (IsI2P()) + return NET_I2P; + + return NET_IPV6; +} + +std::string CNetAddr::ToStringIP() const +{ + if (IsTor()) + return EncodeBase32(&ip[6], 10) + ".onion"; + if (IsI2P()) + return EncodeBase32(&ip[6], 10) + ".oc.b32.i2p"; + CService serv(*this, (uint16_t)0); +#ifdef USE_IPV6 + struct sockaddr_storage sockaddr; +#else + struct sockaddr sockaddr; +#endif + socklen_t socklen = sizeof(sockaddr); + if (serv.GetSockAddr((struct sockaddr*)&sockaddr, &socklen)) { + char name[1025] = ""; + if (!getnameinfo((const struct sockaddr*)&sockaddr, socklen, name, sizeof(name), NULL, 0, NI_NUMERICHOST)) + return std::string(name); + } + if (IsIPv4()) + return strprintf("%u.%u.%u.%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0)); + else + return strprintf("%x:%x:%x:%x:%x:%x:%x:%x", + GetByte(15) << 8 | GetByte(14), GetByte(13) << 8 | GetByte(12), + GetByte(11) << 8 | GetByte(10), GetByte(9) << 8 | GetByte(8), + GetByte(7) << 8 | GetByte(6), GetByte(5) << 8 | GetByte(4), + GetByte(3) << 8 | GetByte(2), GetByte(1) << 8 | GetByte(0)); +} + +std::string CNetAddr::ToString() const +{ + return ToStringIP(); +} + +bool operator==(const CNetAddr& a, const CNetAddr& b) +{ + return (memcmp(a.ip, b.ip, 16) == 0); +} + +bool operator!=(const CNetAddr& a, const CNetAddr& b) +{ + return (memcmp(a.ip, b.ip, 16) != 0); +} + +bool operator<(const CNetAddr& a, const CNetAddr& b) +{ + return (memcmp(a.ip, b.ip, 16) < 0); +} + +bool CNetAddr::GetInAddr(struct in_addr* pipv4Addr) const +{ + if (!IsIPv4()) + return false; + memcpy(pipv4Addr, ip+12, 4); + return true; +} + +#ifdef USE_IPV6 +bool CNetAddr::GetIn6Addr(struct in6_addr* pipv6Addr) const +{ + memcpy(pipv6Addr, ip, 16); + return true; +} +#endif + +// get canonical identifier of an address' group +// no two connections will be attempted to addresses with the same group +std::vector CNetAddr::GetGroup() const +{ + std::vector vchRet; + uint8_t nClass = NET_IPV6; + int nStartByte = 0; + int nBits = 16; + + // all local addresses belong to the same group + if (IsLocal()) + { + nClass = 255; + nBits = 0; + } + + // all unroutable addresses belong to the same group + if (!IsRoutable()) + { + nClass = NET_UNROUTABLE; + nBits = 0; + } + // for IPv4 addresses, '1' + the 16 higher-order bits of the IP + // includes mapped IPv4, SIIT translated IPv4, and the well-known prefix + else if (IsIPv4() || IsRFC6145() || IsRFC6052()) + { + nClass = NET_IPV4; + nStartByte = 12; + } + // for 6to4 tunnelled addresses, use the encapsulated IPv4 address + else if (IsRFC3964()) + { + nClass = NET_IPV4; + nStartByte = 2; + } + // for Teredo-tunnelled IPv6 addresses, use the encapsulated IPv4 address + else if (IsRFC4380()) + { + vchRet.push_back(NET_IPV4); + vchRet.push_back(GetByte(3) ^ 0xFF); + vchRet.push_back(GetByte(2) ^ 0xFF); + return vchRet; + } + else if (IsTor()) + { + nClass = NET_TOR; + nStartByte = 6; + nBits = 4; + } + else if (IsI2P()) + { + nClass = NET_I2P; + nStartByte = 6; + nBits = 4; + } + // for he.net, use /36 groups + else if (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x04 && GetByte(12) == 0x70) + nBits = 36; + // for the rest of the IPv6 network, use /32 groups + else + nBits = 32; + + vchRet.push_back(nClass); + while (nBits >= 8) + { + vchRet.push_back(GetByte(15 - nStartByte)); + nStartByte++; + nBits -= 8; + } + if (nBits > 0) + vchRet.push_back(GetByte(15 - nStartByte) | ((1 << nBits) - 1)); + + return vchRet; +} + +uint64_t CNetAddr::GetHash() const +{ + uint256 hash = Hash(&ip[0], &ip[16]); + uint64_t nRet; + memcpy(&nRet, &hash, sizeof(nRet)); + return nRet; +} + +void CNetAddr::print() const +{ + printf("CNetAddr(%s)\n", ToString().c_str()); +} + +// private extensions to enum Network, only returned by GetExtNetwork, +// and only used in GetReachabilityFrom +static const int NET_UNKNOWN = NET_MAX + 0; +static const int NET_TEREDO = NET_MAX + 1; +int static GetExtNetwork(const CNetAddr *addr) +{ + if (addr == NULL) + return NET_UNKNOWN; + if (addr->IsRFC4380()) + return NET_TEREDO; + return addr->GetNetwork(); +} + +/** Calculates a metric for how reachable (*this) is from a given partner */ +int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const +{ + enum Reachability { + REACH_UNREACHABLE, + REACH_DEFAULT, + REACH_TEREDO, + REACH_IPV6_WEAK, + REACH_IPV4, + REACH_IPV6_STRONG, + REACH_PRIVATE + }; + + if (!IsRoutable()) + return REACH_UNREACHABLE; + + int ourNet = GetExtNetwork(this); + int theirNet = GetExtNetwork(paddrPartner); + bool fTunnel = IsRFC3964() || IsRFC6052() || IsRFC6145(); + + switch(theirNet) { + case NET_IPV4: + switch(ourNet) { + default: return REACH_DEFAULT; + case NET_IPV4: return REACH_IPV4; + } + case NET_IPV6: + switch(ourNet) { + default: return REACH_DEFAULT; + case NET_TEREDO: return REACH_TEREDO; + case NET_IPV4: return REACH_IPV4; + case NET_IPV6: return fTunnel ? REACH_IPV6_WEAK : REACH_IPV6_STRONG; // only prefer giving our IPv6 address if it's not tunnelled + } + case NET_TOR: + switch(ourNet) { + default: return REACH_DEFAULT; + case NET_IPV4: return REACH_IPV4; // Tor users can connect to IPv4 as well + case NET_TOR: return REACH_PRIVATE; + } + case NET_I2P: + switch(ourNet) { + default: return REACH_DEFAULT; + case NET_I2P: return REACH_PRIVATE; + } + case NET_TEREDO: + switch(ourNet) { + default: return REACH_DEFAULT; + case NET_TEREDO: return REACH_TEREDO; + case NET_IPV6: return REACH_IPV6_WEAK; + case NET_IPV4: return REACH_IPV4; + } + case NET_UNKNOWN: + case NET_UNROUTABLE: + default: + switch(ourNet) { + default: return REACH_DEFAULT; + case NET_TEREDO: return REACH_TEREDO; + case NET_IPV6: return REACH_IPV6_WEAK; + case NET_IPV4: return REACH_IPV4; + case NET_I2P: return REACH_PRIVATE; // assume connections from unroutable addresses are + case NET_TOR: return REACH_PRIVATE; // either from Tor/I2P, or don't care about our address + } + } +} + +void CService::Init() +{ + port = 0; +} + +CService::CService() +{ + Init(); +} + +CService::CService(const CNetAddr& cip, uint16_t portIn) : CNetAddr(cip), port(portIn) +{ +} + +CService::CService(const struct in_addr& ipv4Addr, uint16_t portIn) : CNetAddr(ipv4Addr), port(portIn) +{ +} + +#ifdef USE_IPV6 +CService::CService(const struct in6_addr& ipv6Addr, uint16_t portIn) : CNetAddr(ipv6Addr), port(portIn) +{ +} +#endif + +CService::CService(const struct sockaddr_in& addr) : CNetAddr(addr.sin_addr), port(ntohs(addr.sin_port)) +{ + assert(addr.sin_family == AF_INET); +} + +#ifdef USE_IPV6 +CService::CService(const struct sockaddr_in6 &addr) : CNetAddr(addr.sin6_addr), port(ntohs(addr.sin6_port)) +{ + assert(addr.sin6_family == AF_INET6); +} +#endif + +bool CService::SetSockAddr(const struct sockaddr *paddr) +{ + switch (paddr->sa_family) { + case AF_INET: + *this = CService(*(const struct sockaddr_in*)paddr); + return true; +#ifdef USE_IPV6 + case AF_INET6: + *this = CService(*(const struct sockaddr_in6*)paddr); + return true; +#endif + default: + return false; + } +} + +CService::CService(const char *pszIpPort, bool fAllowLookup) +{ + Init(); + CService ip; + if (Lookup(pszIpPort, ip, 0, fAllowLookup)) + *this = ip; +} + +CService::CService(const char *pszIpPort, uint16_t portDefault, bool fAllowLookup) +{ + Init(); + CService ip; + if (Lookup(pszIpPort, ip, portDefault, fAllowLookup)) + *this = ip; +} + +CService::CService(const std::string &strIpPort, bool fAllowLookup) +{ + Init(); + CService ip; + if (Lookup(strIpPort.c_str(), ip, 0, fAllowLookup)) + *this = ip; +} + +CService::CService(const std::string &strIpPort, uint16_t portDefault, bool fAllowLookup) +{ + Init(); + CService ip; + if (Lookup(strIpPort.c_str(), ip, portDefault, fAllowLookup)) + *this = ip; +} + +unsigned short CService::GetPort() const +{ + return port; +} + +bool operator==(const CService& a, const CService& b) +{ + return (CNetAddr)a == (CNetAddr)b && a.port == b.port; +} + +bool operator!=(const CService& a, const CService& b) +{ + return (CNetAddr)a != (CNetAddr)b || a.port != b.port; +} + +bool operator<(const CService& a, const CService& b) +{ + return (CNetAddr)a < (CNetAddr)b || ((CNetAddr)a == (CNetAddr)b && a.port < b.port); +} + +bool CService::GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const +{ + if (IsIPv4()) { + if (*addrlen < (socklen_t)sizeof(struct sockaddr_in)) + return false; + *addrlen = sizeof(struct sockaddr_in); + struct sockaddr_in *paddrin = (struct sockaddr_in*)paddr; + memset(paddrin, 0, *addrlen); + if (!GetInAddr(&paddrin->sin_addr)) + return false; + paddrin->sin_family = AF_INET; + paddrin->sin_port = htons(port); + return true; + } +#ifdef USE_IPV6 + if (IsIPv6()) { + if (*addrlen < (socklen_t)sizeof(struct sockaddr_in6)) + return false; + *addrlen = sizeof(struct sockaddr_in6); + struct sockaddr_in6 *paddrin6 = (struct sockaddr_in6*)paddr; + memset(paddrin6, 0, *addrlen); + if (!GetIn6Addr(&paddrin6->sin6_addr)) + return false; + paddrin6->sin6_family = AF_INET6; + paddrin6->sin6_port = htons(port); + return true; + } +#endif + return false; +} + +std::vector CService::GetKey() const +{ + std::vector vKey; + vKey.resize(18); + memcpy(&vKey[0], ip, 16); + vKey[16] = port / 0x100; + vKey[17] = port & 0x0FF; + return vKey; +} + +std::string CService::ToStringPort() const +{ + return strprintf("%u", port); +} + +std::string CService::ToStringIPPort() const +{ + if (IsIPv4() || IsTor() || IsI2P()) { + return ToStringIP() + ":" + ToStringPort(); + } else { + return "[" + ToStringIP() + "]:" + ToStringPort(); + } +} + +std::string CService::ToString() const +{ + return ToStringIPPort(); +} + +void CService::print() const +{ + printf("CService(%s)\n", ToString().c_str()); +} + +void CService::SetPort(unsigned short portIn) +{ + port = portIn; +} diff --git a/src/netbase.h b/src/netbase.h new file mode 100644 index 00000000..9300003c --- /dev/null +++ b/src/netbase.h @@ -0,0 +1,153 @@ +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_NETBASE_H +#define BITCOIN_NETBASE_H + +#include +#include + +#include "serialize.h" +#include "compat.h" + +extern int nConnectTimeout; + +#ifdef WIN32 +// In MSVC, this is defined as a macro, undefine it to prevent a compile and link error +#undef SetPort +#endif + +enum Network +{ + NET_UNROUTABLE, + NET_IPV4, + NET_IPV6, + NET_TOR, + NET_I2P, + + NET_MAX, +}; + +extern int nConnectTimeout; +extern bool fNameLookup; + +/** IP address (IPv6, or IPv4 using mapped IPv6 range (::FFFF:0:0/96)) */ +class CNetAddr +{ + protected: + unsigned char ip[16]; // in network byte order + + public: + CNetAddr(); + CNetAddr(const struct in_addr& ipv4Addr); + explicit CNetAddr(const char *pszIp, bool fAllowLookup = false); + explicit CNetAddr(const std::string &strIp, bool fAllowLookup = false); + void Init(); + void SetIP(const CNetAddr& ip); + bool SetSpecial(const std::string &strName); // for Tor and I2P addresses + bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0) + bool IsIPv6() const; // IPv6 address (not mapped IPv4, not Tor/I2P) + bool IsRFC1918() const; // IPv4 private networks (10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12) + bool IsRFC3849() const; // IPv6 documentation address (2001:0DB8::/32) + bool IsRFC3927() const; // IPv4 autoconfig (169.254.0.0/16) + bool IsRFC3964() const; // IPv6 6to4 tunnelling (2002::/16) + bool IsRFC4193() const; // IPv6 unique local (FC00::/15) + bool IsRFC4380() const; // IPv6 Teredo tunnelling (2001::/32) + bool IsRFC4843() const; // IPv6 ORCHID (2001:10::/28) + bool IsRFC4862() const; // IPv6 autoconfig (FE80::/64) + bool IsRFC6052() const; // IPv6 well-known prefix (64:FF9B::/96) + bool IsRFC6145() const; // IPv6 IPv4-translated address (::FFFF:0:0:0/96) + bool IsTor() const; + bool IsI2P() const; + bool IsLocal() const; + bool IsRoutable() const; + bool IsValid() const; + bool IsMulticast() const; + enum Network GetNetwork() const; + std::string ToString() const; + std::string ToStringIP() const; + uint8_t GetByte(int n) const; + uint64_t GetHash() const; + bool GetInAddr(struct in_addr* pipv4Addr) const; + std::vector GetGroup() const; + int GetReachabilityFrom(const CNetAddr *paddrPartner = NULL) const; + void print() const; + +#ifdef USE_IPV6 + CNetAddr(const struct in6_addr& pipv6Addr); + bool GetIn6Addr(struct in6_addr* pipv6Addr) const; +#endif + + friend bool operator==(const CNetAddr& a, const CNetAddr& b); + friend bool operator!=(const CNetAddr& a, const CNetAddr& b); + friend bool operator<(const CNetAddr& a, const CNetAddr& b); + + IMPLEMENT_SERIALIZE + ( + READWRITE(FLATDATA(ip)); + ) +}; + +/** A combination of a network address (CNetAddr) and a (TCP) port */ +class CService : public CNetAddr +{ + protected: + unsigned short port; // host order + + public: + CService(); + CService(const CNetAddr& ip, uint16_t port); + CService(const struct in_addr& ipv4Addr, uint16_t port); + CService(const struct sockaddr_in& addr); + explicit CService(const char *pszIpPort, uint16_t portDefault, bool fAllowLookup = false); + explicit CService(const char *pszIpPort, bool fAllowLookup = false); + explicit CService(const std::string& strIpPort, uint16_t portDefault, bool fAllowLookup = false); + explicit CService(const std::string& strIpPort, bool fAllowLookup = false); + void Init(); + void SetPort(uint16_t portIn); + unsigned short GetPort() const; + bool GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const; + bool SetSockAddr(const struct sockaddr* paddr); + friend bool operator==(const CService& a, const CService& b); + friend bool operator!=(const CService& a, const CService& b); + friend bool operator<(const CService& a, const CService& b); + std::vector GetKey() const; + std::string ToString() const; + std::string ToStringPort() const; + std::string ToStringIPPort() const; + void print() const; + +#ifdef USE_IPV6 + CService(const struct in6_addr& ipv6Addr, uint16_t port); + CService(const struct sockaddr_in6& addr); +#endif + + IMPLEMENT_SERIALIZE + ( + CService* pthis = const_cast(this); + READWRITE(FLATDATA(ip)); + unsigned short portN = htons(port); + READWRITE(portN); + if (fRead) + pthis->port = ntohs(portN); + ) +}; + +typedef std::pair proxyType; + +enum Network ParseNetwork(std::string net); +void SplitHostPort(std::string in, uint16_t &portOut, std::string &hostOut); +bool SetProxy(enum Network net, CService addrProxy, int nSocksVersion = 5); +bool GetProxy(enum Network net, proxyType &proxyInfoOut); +bool IsProxy(const CNetAddr &addr); +bool SetNameProxy(CService addrProxy, int nSocksVersion = 5); +bool HaveNameProxy(); +bool LookupHost(const char *pszName, std::vector& vIP, unsigned int nMaxSolutions = 0, bool fAllowLookup = true); +bool LookupHostNumeric(const char *pszName, std::vector& vIP, unsigned int nMaxSolutions = 0); +bool Lookup(const char *pszName, CService& addr, uint16_t portDefault = 0, bool fAllowLookup = true); +bool Lookup(const char *pszName, std::vector& vAddr, uint16_t portDefault = 0, bool fAllowLookup = true, unsigned int nMaxSolutions = 0); +bool LookupNumeric(const char *pszName, CService& addr, uint16_t portDefault = 0); +bool ConnectSocket(const CService &addr, SOCKET& hSocketRet, int nTimeout = nConnectTimeout); +bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, uint16_t portDefault = 0, int nTimeout = nConnectTimeout); + +#endif diff --git a/src/noui.cpp b/src/noui.cpp new file mode 100644 index 00000000..965b39d1 --- /dev/null +++ b/src/noui.cpp @@ -0,0 +1,28 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "ui_interface.h" +#include "init.h" +#include "bitcoinrpc.h" + +#include + +static int noui_ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style) +{ + printf("%s: %s\n", caption.c_str(), message.c_str()); + fprintf(stderr, "%s: %s\n", caption.c_str(), message.c_str()); + return 4; +} + +static bool noui_ThreadSafeAskFee(int64_t nFeeRequired, const std::string& strCaption) +{ + return true; +} + +void noui_connect() +{ + // Connect bitcoind signal handlers + uiInterface.ThreadSafeMessageBox.connect(noui_ThreadSafeMessageBox); + uiInterface.ThreadSafeAskFee.connect(noui_ThreadSafeAskFee); +} diff --git a/src/ntp.cpp b/src/ntp.cpp new file mode 100644 index 00000000..d0894bf9 --- /dev/null +++ b/src/ntp.cpp @@ -0,0 +1,535 @@ +#ifdef WIN32 +#include +#else +#include +#include +#include +#include +#include +#endif +#ifndef WIN32 +#include +#endif + +#include "netbase.h" +#include "net.h" +//#include "util.h" +#include "ui_interface.h" + +extern int GetRandInt(int nMax); + +/* + * NTP uses two fixed point formats. The first (l_fp) is the "long" + * format and is 64 bits long with the decimal between bits 31 and 32. + * This is used for time stamps in the NTP packet header (in network + * byte order) and for internal computations of offsets (in local host + * byte order). We use the same structure for both signed and unsigned + * values, which is a big hack but saves rewriting all the operators + * twice. Just to confuse this, we also sometimes just carry the + * fractional part in calculations, in both signed and unsigned forms. + * Anyway, an l_fp looks like: + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Integral Part | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Fractional Part | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * REF http://www.eecis.udel.edu/~mills/database/rfc/rfc2030.txt + */ + + +typedef struct { + union { + uint32_t Xl_ui; + int32_t Xl_i; + } Ul_i; + union { + uint32_t Xl_uf; + int32_t Xl_f; + } Ul_f; +} l_fp; + + +inline void Ntp2Unix(const uint32_t &n, time_t &u) { + // Ntp's time scale starts in 1900, Unix in 1970. + + u = n - 0x83aa7e80; // 2208988800 1970 - 1900 in seconds +} + +inline void ntohl_fp(l_fp *n, l_fp *h) { + (h)->Ul_i.Xl_ui = ntohl((n)->Ul_i.Xl_ui); + (h)->Ul_f.Xl_uf = ntohl((n)->Ul_f.Xl_uf); +} + +struct pkt { + uint8_t li_vn_mode; /* leap indicator, version and mode */ + uint8_t stratum; /* peer stratum */ + uint8_t ppoll; /* peer poll interval */ + int8_t precision; /* peer clock precision */ + uint32_t rootdelay; /* distance to primary clock */ + uint32_t rootdispersion; /* clock dispersion */ + uint32_t refid; /* reference clock ID */ + l_fp ref; /* time peer clock was last updated */ + l_fp org; /* originate time stamp */ + l_fp rec; /* receive time stamp */ + l_fp xmt; /* transmit time stamp */ + + uint32_t exten[1]; /* misused */ + uint8_t mac[5 * sizeof(uint32_t)]; /* mac */ +}; + +const int nServersCount = 162; + +std::string NtpServers[162] = { + // Microsoft + "time.windows.com", + + // Google + "time1.google.com", + "time2.google.com", + "time3.google.com", + "time4.google.com", + + // Hurricane Electric + "clock.sjc.he.net", + "clock.nyc.he.net", + + // SixXS + "ntp.sixxs.net", + "ntp.eu.sixxs.net", + "ntp.us.sixxs.net", + "ntp.ap.sixxs.net", + + // Russian Federation + "ntp.karelia.pro", + "ntp.alpet.me", + "aviel.alpet.me", + "ntp.sampo.ru", + "ntp.szt.ru", + "ntp.ix.ru", + "ntp1.stratum2.ru", + "ntp2.stratum2.ru", + "ntp3.stratum2.ru", + "ntp4.stratum2.ru", + "ntp5.stratum2.ru", + "ntp6.stratum2.ru", + "ntp7.stratum2.ru", + "ntp1.stratum1.ru", + "ntp2.stratum1.ru", + "ntp3.stratum1.ru", + "ntp4.stratum1.ru", + "ntp1.vniiftri.ru", + "ntp2.vniiftri.ru", + "ntp3.vniiftri.ru", + "ntp4.vniiftri.ru", + "ntp21.vniiftri.ru", + "ntp1.niiftri.irkutsk.ru", + "ntp2.niiftri.irkutsk.ru", + "vniiftri.khv.ru", + "vniiftri2.khv.ru", + "ntp0.zenon.net", + "ntp.mobatime.ru", + "0.ru.pool.ntp.org", + "1.ru.pool.ntp.org", + "2.ru.pool.ntp.org", + "3.ru.pool.ntp.org", + + // United States + "tock.cs.unlv.edu", + "timex.cs.columbia.edu", + "tick.cs.unlv.edu", + "sundial.columbia.edu", + "ntp-1.ece.cmu.edu", + "ntp-2.ece.cmu.edu", + "ntp1.cs.wisc.edu", + "ntp2.cs.wisc.edu", + "ntp3.cs.wisc.edu", + "ntp-01.caltech.edu", + "ntp-02.caltech.edu", + "ntp-03.caltech.edu", + "ntp-04.caltech.edu", + "nist1-pa.ustiming.org", + "time.nist.gov", + "time-a.nist.gov", + "time-b.nist.gov", + "time-c.nist.gov", + "time-d.nist.gov", + "time-nw.nist.gov", + "nist1-macon.macon.ga.us", + "nist.netservicesgroup.com", + "nisttime.carsoncity.k12.mi.us", + "nist1-lnk.binary.net", + "wwv.nist.gov", + "time-a.timefreq.bldrdoc.gov", + "time-b.timefreq.bldrdoc.gov", + "time-c.timefreq.bldrdoc.gov", + "utcnist.colorado.edu", + "utcnist2.colorado.edu", + "ntp-nist.ldsbc.net", + "nist1-lv.ustiming.org", + "time-nw.nist.gov", + "nist-time-server.eoni.com", + "nist-time-server.eoni.com", + "ntp1.bu.edu", + "ntp2.bu.edu", + "ntp3.bu.edu", + "0.us.pool.ntp.org", + "1.us.pool.ntp.org", + "2.us.pool.ntp.org", + "3.us.pool.ntp.org", + "wwv.otc.psu.edu", + "otc1.psu.edu", + "otc2.psu.edu", + "now.okstate.edu", + "ntp.colby.edu", + "bonehed.lcs.mit.edu", + "ntp-s1.cise.ufl.edu", + + // South Africa + "ntp1.meraka.csir.co.za", + "ntp.is.co.za", + "ntp2.is.co.za", + "igubu.saix.net", + "ntp1.neology.co.za", + "ntp2.neology.co.za", + "tick.meraka.csir.co.za", + "tock.meraka.csir.co.za", + "ntp.time.org.za", + "ntp1.meraka.csir.co.za", + "ntp2.meraka.csir.co.za", + "0.za.pool.ntp.org", + "1.za.pool.ntp.org", + "2.za.pool.ntp.org", + "3.za.pool.ntp.org", + + // Italy + "ntp0.ien.it", + "ntp1.ien.it", + "ntp2.ien.it", + "ntp1.inrim.it", + "ntp2.inrim.it", + "0.it.pool.ntp.org", + "1.it.pool.ntp.org", + "2.it.pool.ntp.org", + "3.it.pool.ntp.org", + + // Netherlands + "ntp0.nl.net", + "ntp1.nl.net", + "ntp2.nl.net", + "ntp.utwente.nl", + "0.nl.pool.ntp.org", + "1.nl.pool.ntp.org", + "2.nl.pool.ntp.org", + "3.nl.pool.ntp.org", + + // United Kingdom + "ntp2d.mcc.ac.uk", + "ntp2c.mcc.ac.uk", + "ntp2b.mcc.ac.uk", + "ntp.exnet.com", + "ntp.cis.strath.ac.uk", + "ntppub.le.ac.uk", + "0.uk.pool.ntp.org", + "1.uk.pool.ntp.org", + "2.uk.pool.ntp.org", + "3.uk.pool.ntp.org", + + // Canada + "chime.utoronto.ca", + "tick.utoronto.ca", + "time.nrc.ca", + "timelord.uregina.ca", + "tock.utoronto.ca", + "www1.cmc.ec.gc.ca", + "www2.cmc.ec.gc.ca", + "0.ca.pool.ntp.org", + "1.ca.pool.ntp.org", + "2.ca.pool.ntp.org", + "3.ca.pool.ntp.org", + + // Japan + "ntp.nict.jp", + "0.jp.pool.ntp.org", + "1.jp.pool.ntp.org", + "2.jp.pool.ntp.org", + "3.jp.pool.ntp.org", + + // Australia + "ntp.cs.mu.oz.au", + "augean.eleceng.adelaide.edu.au", + "0.au.pool.ntp.org", + "1.au.pool.ntp.org", + "2.au.pool.ntp.org", + "3.au.pool.ntp.org", + + // Slovenia + "time.ijs.si", + + // Austria + "0.at.pool.ntp.org", + "1.at.pool.ntp.org", + "2.at.pool.ntp.org", + "3.at.pool.ntp.org", + + // ??? + "clepsydra.dec.com", + + // ... To be continued +}; + +bool InitWithHost(const std::string &strHostName, SOCKET &sockfd, socklen_t &servlen, struct sockaddr *pcliaddr) { + + sockfd = INVALID_SOCKET; + + std::vector vIP; + bool fRet = LookupHost(strHostName.c_str(), vIP, 10, true); + if (!fRet) { + return false; + } + + struct sockaddr_in servaddr; + servaddr.sin_family = AF_INET; + servaddr.sin_port = htons(123); + + bool found = false; + for(unsigned int i = 0; i < vIP.size(); i++) { + if ((found = vIP[i].GetInAddr(&servaddr.sin_addr)) != false) { + break; + } + } + + if (!found) { + return false; + } + + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + + if (sockfd == INVALID_SOCKET) + return false; // socket initialization error + + if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) == -1 ) { + return false; // "connection" error + } + + + *pcliaddr = *((struct sockaddr *) &servaddr); + servlen = sizeof(servaddr); + + return true; +} + +bool InitWithRandom(SOCKET &sockfd, socklen_t &servlen, struct sockaddr *pcliaddr) { + + for (int nAttempt = 0; nAttempt < nServersCount; nAttempt++) { + int nServerNum = GetRandInt(nServersCount); + if (InitWithHost(NtpServers[nServerNum], sockfd, servlen, pcliaddr)) { + return true; + } + } + + return false; +} + +int64_t DoReq(SOCKET sockfd, socklen_t servlen, struct sockaddr cliaddr) { + + +#ifdef WIN32 + u_long nOne = 1; + if (ioctlsocket(sockfd, FIONBIO, &nOne) == SOCKET_ERROR) { + printf("ConnectSocket() : ioctlsocket non-blocking setting failed, error %d\n", WSAGetLastError()); +#else + if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == SOCKET_ERROR) { + printf("ConnectSocket() : fcntl non-blocking setting failed, error %d\n", errno); +#endif + return -2; + } + + struct timeval timeout = {10, 0}; + struct pkt *msg = new pkt; + struct pkt *prt = new pkt; + time_t seconds_transmit; + int len = 48; + + msg->li_vn_mode=227; + msg->stratum=0; + msg->ppoll=4; + msg->precision=0; + msg->rootdelay=0; + msg->rootdispersion=0; + + msg->ref.Ul_i.Xl_i=0; + msg->ref.Ul_f.Xl_f=0; + msg->org.Ul_i.Xl_i=0; + msg->org.Ul_f.Xl_f=0; + msg->rec.Ul_i.Xl_i=0; + msg->rec.Ul_f.Xl_f=0; + msg->xmt.Ul_i.Xl_i=0; + msg->xmt.Ul_f.Xl_f=0; + + int retcode = sendto(sockfd, (char *) msg, len, 0, &cliaddr, servlen); + if (retcode < 0) { + printf("sendto() failed: %d\n", retcode); + seconds_transmit = -3; + goto _end; + } + + fd_set fdset; + FD_ZERO(&fdset); + FD_SET(sockfd, &fdset); + + retcode = select(sockfd + 1, &fdset, NULL, NULL, &timeout); + if (retcode <= 0) { + printf("recvfrom() error\n"); + seconds_transmit = -4; + goto _end; + } + + recvfrom(sockfd, (char *) msg, len, 0, NULL, NULL); + ntohl_fp(&msg->xmt, &prt->xmt); + Ntp2Unix(prt->xmt.Ul_i.Xl_ui, seconds_transmit); + + _end: + + delete msg; + delete prt; + + return seconds_transmit; +} + +int64_t NtpGetTime(CNetAddr& ip) { + struct sockaddr cliaddr; + + SOCKET sockfd; + socklen_t servlen; + + if (!InitWithRandom(sockfd, servlen, &cliaddr)) + return -1; + + ip = CNetAddr(((sockaddr_in *)&cliaddr)->sin_addr); + int64_t nTime = DoReq(sockfd, servlen, cliaddr); + + closesocket(sockfd); + + return nTime; +} + +int64_t NtpGetTime(const std::string &strHostName) +{ + struct sockaddr cliaddr; + + SOCKET sockfd; + socklen_t servlen; + + if (!InitWithHost(strHostName, sockfd, servlen, &cliaddr)) + return -1; + + int64_t nTime = DoReq(sockfd, servlen, cliaddr); + + closesocket(sockfd); + + return nTime; +} + +// NTP server, which we unconditionally trust. This may be your own installation of ntpd somewhere, for example. +// "localhost" means "trust no one" +std::string strTrustedUpstream = "localhost"; + +// Current offset +int64_t nNtpOffset = INT64_MAX; + +int64_t GetNtpOffset() { + return nNtpOffset; +} + +void ThreadNtpSamples(void* parg) { + const int64_t nMaxOffset = 86400; // Not a real limit, just sanity threshold. + + printf("Trying to find NTP server at localhost...\n"); + + std::string strLocalHost = "127.0.0.1"; + if (NtpGetTime(strLocalHost) == GetTime()) { + printf("There is NTP server active at localhost, we don't need NTP thread.\n"); + + nNtpOffset = 0; + return; + } + + printf("ThreadNtpSamples started\n"); + vnThreadsRunning[THREAD_NTP]++; + + // Make this thread recognisable as time synchronization thread + RenameThread("XP-ntp-samples"); + + CMedianFilter vTimeOffsets(200,0); + + while (!fShutdown) { + if (strTrustedUpstream != "localhost") { + // Trying to get new offset sample from trusted NTP server. + int64_t nClockOffset = NtpGetTime(strTrustedUpstream) - GetTime(); + + if (abs64(nClockOffset) < nMaxOffset) { + // Everything seems right, remember new trusted offset. + printf("ThreadNtpSamples: new offset sample from %s, offset=%" PRId64 ".\n", strTrustedUpstream.c_str(), nClockOffset); + nNtpOffset = nClockOffset; + } + else { + // Something went wrong, disable trusted offset sampling. + nNtpOffset = INT64_MAX; + strTrustedUpstream = "localhost"; + + int nSleepMinutes = 1 + GetRandInt(9); // Sleep for 1-10 minutes. + for (int i = 0; i < nSleepMinutes * 60 && !fShutdown; i++) + Sleep(1000); + + continue; + } + } + else { + // Now, trying to get 2-4 samples from random NTP servers. + int nSamplesCount = 2 + GetRandInt(2); + + for (int i = 0; i < nSamplesCount; i++) { + CNetAddr ip; + int64_t nClockOffset = NtpGetTime(ip) - GetTime(); + + if (abs64(nClockOffset) < nMaxOffset) { // Skip the deliberately wrong timestamps + printf("ThreadNtpSamples: new offset sample from %s, offset=%" PRId64 ".\n", ip.ToString().c_str(), nClockOffset); + vTimeOffsets.input(nClockOffset); + } + } + + if (vTimeOffsets.size() > 1) { + nNtpOffset = vTimeOffsets.median(); + } + else { + // Not enough offsets yet, try to collect additional samples later. + nNtpOffset = INT64_MAX; + int nSleepMinutes = 1 + GetRandInt(4); // Sleep for 1-5 minutes. + for (int i = 0; i < nSleepMinutes * 60 && !fShutdown; i++) + Sleep(1000); + continue; + } + } + + if (GetNodesOffset() == INT_MAX && abs64(nNtpOffset) > 40 * 60) + { + // If there is not enough node offsets data and NTP time offset is greater than 40 minutes then give a warning. + std::string strMessage = _("Warning: Please check that your computer's date and time are correct! If your clock is wrong XP will not work properly."); + strMiscWarning = strMessage; + printf("*** %s\n", strMessage.c_str()); + uiInterface.ThreadSafeMessageBox(strMessage+" ", std::string("XP"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION); + } + + printf("nNtpOffset = %+" PRId64 " (%+" PRId64 " minutes)\n", nNtpOffset, nNtpOffset/60); + + int nSleepHours = 1 + GetRandInt(5); // Sleep for 1-6 hours. + for (int i = 0; i < nSleepHours * 3600 && !fShutdown; i++) + Sleep(1000); + } + + vnThreadsRunning[THREAD_NTP]--; + printf("ThreadNtpSamples exited\n"); +} diff --git a/src/ntp.h b/src/ntp.h new file mode 100644 index 00000000..8cf3be64 --- /dev/null +++ b/src/ntp.h @@ -0,0 +1,13 @@ +// Get time from random server and return server address. +int64_t NtpGetTime(CNetAddr& ip); + +// Get time from provided server. +int64_t NtpGetTime(const std::string &strHostName); + +extern std::string strTrustedUpstream; + +// NTP time samples thread. +void ThreadNtpSamples(void* parg); + +// NTP offset +int64_t GetNtpOffset(); diff --git a/src/obj/.gitignore b/src/obj/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/src/obj/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/src/protocol.cpp b/src/protocol.cpp new file mode 100644 index 00000000..5a7dd4a7 --- /dev/null +++ b/src/protocol.cpp @@ -0,0 +1,150 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "protocol.h" +#include "util.h" +#include "netbase.h" + +#ifndef WIN32 +# include +#endif + +static const char* ppszTypeName[] = +{ + "ERROR", + "tx", + "block", +}; + +CMessageHeader::CMessageHeader() +{ + memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)); + memset(pchCommand, 0, sizeof(pchCommand)); + pchCommand[1] = 1; + nMessageSize = std::numeric_limits::max(); + nChecksum = 0; +} + +CMessageHeader::CMessageHeader(const char* pszCommand, unsigned int nMessageSizeIn) +{ + memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)); + strncpy(pchCommand, pszCommand, COMMAND_SIZE); + nMessageSize = nMessageSizeIn; + nChecksum = 0; +} + +std::string CMessageHeader::GetCommand() const +{ + if (pchCommand[COMMAND_SIZE-1] == 0) + return std::string(pchCommand, pchCommand + strlen(pchCommand)); + else + return std::string(pchCommand, pchCommand + COMMAND_SIZE); +} + +bool CMessageHeader::IsValid() const +{ + // Check start string + if (memcmp(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)) != 0) + return false; + + // Check the command string for errors + for (const 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; +} + + + +CAddress::CAddress() : CService() +{ + Init(); +} + +CAddress::CAddress(CService ipIn, uint64_t nServicesIn) : CService(ipIn) +{ + Init(); + nServices = nServicesIn; +} + +void CAddress::Init() +{ + nServices = NODE_NETWORK; + nTime = 100000000; + nLastTry = 0; +} + +CInv::CInv() +{ + type = 0; + hash = 0; +} + +CInv::CInv(int typeIn, const uint256& hashIn) +{ + type = typeIn; + hash = hashIn; +} + +CInv::CInv(const std::string& strType, const uint256& hashIn) +{ + unsigned 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; +} + +bool operator<(const CInv& a, const CInv& b) +{ + return (a.type < b.type || (a.type == b.type && a.hash < b.hash)); +} + +bool CInv::IsKnownType() const +{ + return (type >= 1 && type < (int)ARRAYLEN(ppszTypeName)); +} + +const char* CInv::GetCommand() const +{ + if (!IsKnownType()) + throw std::out_of_range(strprintf("CInv::GetCommand() : type=%d unknown type", type)); + return ppszTypeName[type]; +} + +std::string CInv::ToString() const +{ + return strprintf("%s %s", GetCommand(), hash.ToString().substr(0,20).c_str()); +} + +void CInv::print() const +{ + printf("CInv(%s)\n", ToString().c_str()); +} + diff --git a/src/protocol.h b/src/protocol.h new file mode 100644 index 00000000..70f2c991 --- /dev/null +++ b/src/protocol.h @@ -0,0 +1,138 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef __cplusplus +# error This header can only be compiled as C++. +#endif + +#ifndef __INCLUDED_PROTOCOL_H__ +#define __INCLUDED_PROTOCOL_H__ + +#include "serialize.h" +#include "netbase.h" +#include +#include +#include "uint256.h" + +extern bool fTestNet; +static inline unsigned short GetDefaultPort(const bool testnet = fTestNet) +{ + return testnet ? 17778 : 28192; +} + + +extern unsigned char pchMessageStart[4]; + +/** Message header. + * (4) message start. + * (12) command. + * (4) size. + * (4) checksum. + */ +class CMessageHeader +{ + public: + CMessageHeader(); + CMessageHeader(const char* pszCommand, unsigned int nMessageSizeIn); + + std::string GetCommand() const; + bool IsValid() const; + + IMPLEMENT_SERIALIZE + ( + READWRITE(FLATDATA(pchMessageStart)); + READWRITE(FLATDATA(pchCommand)); + READWRITE(nMessageSize); + READWRITE(nChecksum); + ) + + // TODO: make private (improves encapsulation) + public: + enum { + MESSAGE_START_SIZE=sizeof(::pchMessageStart), + COMMAND_SIZE=12, + MESSAGE_SIZE_SIZE=sizeof(int), + CHECKSUM_SIZE=sizeof(int), + + MESSAGE_SIZE_OFFSET=MESSAGE_START_SIZE+COMMAND_SIZE, + CHECKSUM_OFFSET=MESSAGE_SIZE_OFFSET+MESSAGE_SIZE_SIZE + }; + char pchMessageStart[MESSAGE_START_SIZE]; + char pchCommand[COMMAND_SIZE]; + unsigned int nMessageSize; + unsigned int nChecksum; +}; + +/** nServices flags */ +enum +{ + NODE_NETWORK = (1 << 0), +}; + +/** A CService with information about it as peer */ +class CAddress : public CService +{ + public: + CAddress(); + explicit CAddress(CService ipIn, uint64_t nServicesIn=NODE_NETWORK); + + void Init(); + + IMPLEMENT_SERIALIZE + ( + CAddress* pthis = const_cast(this); + CService* pip = (CService*)pthis; + if (fRead) + pthis->Init(); + if (nType & SER_DISK) + READWRITE(nVersion); + if ((nType & SER_DISK) || + (nVersion >= CADDR_TIME_VERSION && !(nType & SER_GETHASH))) + READWRITE(nTime); + READWRITE(nServices); + READWRITE(*pip); + ) + + void print() const; + + // TODO: make private (improves encapsulation) + public: + uint64_t nServices; + + // disk and network only + unsigned int nTime; + + // memory only + int64_t nLastTry; +}; + +/** inv message data */ +class CInv +{ + public: + CInv(); + CInv(int typeIn, const uint256& hashIn); + CInv(const std::string& strType, const uint256& hashIn); + + IMPLEMENT_SERIALIZE + ( + READWRITE(type); + READWRITE(hash); + ) + + friend bool operator<(const CInv& a, const CInv& b); + + bool IsKnownType() const; + const char* GetCommand() const; + std::string ToString() const; + void print() const; + + // TODO: make private (improves encapsulation) + public: + int type; + uint256 hash; +}; + +#endif // __INCLUDED_PROTOCOL_H__ diff --git a/src/qt/aboutdialog.cpp b/src/qt/aboutdialog.cpp new file mode 100644 index 00000000..a66287db --- /dev/null +++ b/src/qt/aboutdialog.cpp @@ -0,0 +1,50 @@ +#include "aboutdialog.h" +#include "ui_aboutdialog.h" + +#include "dialogwindowflags.h" + +#include "clientmodel.h" + +#include "version.h" + +#include + +AboutDialog::AboutDialog(QWidget *parent) : + QWidget(parent, DIALOGWINDOWHINTS), + ui(new Ui::AboutDialog) +{ + ui->setupUi(this); +} + +void AboutDialog::setModel(ClientModel *model) +{ + if(model) + { + ui->versionLabel->setText(model->formatFullVersion()); + } +} + +AboutDialog::~AboutDialog() +{ + delete ui; +} + +void AboutDialog::on_buttonBox_accepted() +{ + close(); +} + +void AboutDialog::keyPressEvent(QKeyEvent *event) +{ +#ifdef ANDROID + if(event->key() == Qt::Key_Back) + { + close(); + } +#else + if(event->key() == Qt::Key_Escape) + { + close(); + } +#endif +} \ No newline at end of file diff --git a/src/qt/aboutdialog.h b/src/qt/aboutdialog.h new file mode 100644 index 00000000..714970f6 --- /dev/null +++ b/src/qt/aboutdialog.h @@ -0,0 +1,30 @@ +#ifndef ABOUTDIALOG_H +#define ABOUTDIALOG_H + +#include + +namespace Ui { + class AboutDialog; +} +class ClientModel; + +/** "About" dialog box */ +class AboutDialog : public QWidget +{ + Q_OBJECT + +public: + explicit AboutDialog(QWidget *parent = 0); + ~AboutDialog(); + + void setModel(ClientModel *model); +private: + Ui::AboutDialog *ui; + + void keyPressEvent(QKeyEvent *); + +private slots: + void on_buttonBox_accepted(); +}; + +#endif // ABOUTDIALOG_H diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp new file mode 100644 index 00000000..e5b55c7f --- /dev/null +++ b/src/qt/addressbookpage.cpp @@ -0,0 +1,372 @@ +#include "addressbookpage.h" +#include "ui_addressbookpage.h" + +#include "addresstablemodel.h" +#include "optionsmodel.h" +#include "bitcoingui.h" +#include "editaddressdialog.h" +#include "csvmodelwriter.h" +#include "guiutil.h" +#include "qrcodedialog.h" + +#include +#include +#include +#include + + +AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget *parent) : + QDialog(parent), + ui(new Ui::AddressBookPage), + model(0), + optionsModel(0), + mode(mode), + tab(tab) +{ + ui->setupUi(this); + +#ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac + ui->newAddressButton->setIcon(QIcon()); + ui->copyToClipboard->setIcon(QIcon()); + ui->deleteButton->setIcon(QIcon()); +#endif + + ui->showQRCode->setVisible(false); + + switch(mode) + { + case ForSending: + connect(ui->tableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(accept())); + ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers); + ui->tableView->setFocus(); + break; + case ForEditing: + ui->buttonBox->setVisible(false); + break; + } + switch(tab) + { + case SendingTab: + ui->labelExplanation->setVisible(false); + ui->deleteButton->setVisible(true); + ui->signMessage->setVisible(false); + break; + case ReceivingTab: + ui->deleteButton->setVisible(false); + ui->signMessage->setVisible(true); + break; + } + + // Context menu actions + QAction *copyLabelAction = new QAction(tr("Copy &Label"), this); + QAction *copyAddressAction = new QAction(ui->copyToClipboard->text(), this); + QAction *editAction = new QAction(tr("&Edit"), this); + QAction *showQRCodeAction = new QAction(ui->showQRCode->text(), this); + QAction *signMessageAction = new QAction(ui->signMessage->text(), this); + QAction *verifyMessageAction = new QAction(ui->verifyMessage->text(), this); + deleteAction = new QAction(ui->deleteButton->text(), this); + + // Build context menu + contextMenu = new QMenu(); + contextMenu->addAction(copyAddressAction); + contextMenu->addAction(copyLabelAction); + contextMenu->addAction(editAction); + if(tab == SendingTab) + contextMenu->addAction(deleteAction); + contextMenu->addSeparator(); + contextMenu->addAction(showQRCodeAction); + if(tab == ReceivingTab) + contextMenu->addAction(signMessageAction); + else if(tab == SendingTab) + contextMenu->addAction(verifyMessageAction); + + // Connect signals for context menu actions + connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(on_copyToClipboard_clicked())); + connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(onCopyLabelAction())); + connect(editAction, SIGNAL(triggered()), this, SLOT(onEditAction())); + connect(deleteAction, SIGNAL(triggered()), this, SLOT(on_deleteButton_clicked())); + connect(showQRCodeAction, SIGNAL(triggered()), this, SLOT(on_showQRCode_clicked())); + connect(signMessageAction, SIGNAL(triggered()), this, SLOT(on_signMessage_clicked())); + connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(on_verifyMessage_clicked())); + + connect(ui->tableView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint))); + + // Pass through accept action from button box + connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept())); +} + +AddressBookPage::~AddressBookPage() +{ + delete ui; +} + +void AddressBookPage::setModel(AddressTableModel *model) +{ + this->model = model; + if(!model) + return; + + proxyModel = new QSortFilterProxyModel(this); + proxyModel->setSourceModel(model); + proxyModel->setDynamicSortFilter(true); + proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); + proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + switch(tab) + { + case ReceivingTab: + // Receive filter + proxyModel->setFilterRole(AddressTableModel::TypeRole); + proxyModel->setFilterFixedString(AddressTableModel::Receive); + break; + case SendingTab: + // Send filter + 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( + AddressTableModel::Address, 320); +#if QT_VERSION < 0x050000 + ui->tableView->horizontalHeader()->setResizeMode( + AddressTableModel::Label, QHeaderView::Stretch); +#else + ui->tableView->horizontalHeader()->setSectionResizeMode( + AddressTableModel::Label, QHeaderView::Stretch); +#endif + connect(ui->tableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(selectionChanged())); + + // Select row for newly created address + connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(selectNewAddress(QModelIndex,int,int))); + + selectionChanged(); +} + +void AddressBookPage::setOptionsModel(OptionsModel *optionsModel) +{ + this->optionsModel = optionsModel; +} + +void AddressBookPage::on_copyToClipboard_clicked() +{ + GUIUtil::copyEntryData(ui->tableView, AddressTableModel::Address); +} + +void AddressBookPage::onCopyLabelAction() +{ + GUIUtil::copyEntryData(ui->tableView, AddressTableModel::Label); +} + +void AddressBookPage::onEditAction() +{ + if(!ui->tableView->selectionModel()) + return; + QModelIndexList indexes = ui->tableView->selectionModel()->selectedRows(); + if(indexes.isEmpty()) + return; + + EditAddressDialog dlg( + tab == SendingTab ? + EditAddressDialog::EditSendingAddress : + EditAddressDialog::EditReceivingAddress); + dlg.setModel(model); + QModelIndex origIndex = proxyModel->mapToSource(indexes.at(0)); + dlg.loadRow(origIndex.row()); + dlg.exec(); +} + +void AddressBookPage::on_signMessage_clicked() +{ + QTableView *table = ui->tableView; + QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); + QString addr; + + foreach (QModelIndex index, indexes) + { + QVariant address = index.data(); + addr = address.toString(); + } + + emit signMessage(addr); +} + +void AddressBookPage::on_verifyMessage_clicked() +{ + QTableView *table = ui->tableView; + QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); + QString addr; + + foreach (QModelIndex index, indexes) + { + QVariant address = index.data(); + addr = address.toString(); + } + + emit verifyMessage(addr); +} + +void AddressBookPage::on_newAddressButton_clicked() +{ + if(!model) + return; + EditAddressDialog dlg( + tab == SendingTab ? + EditAddressDialog::NewSendingAddress : + EditAddressDialog::NewReceivingAddress, this); + dlg.setModel(model); + if(dlg.exec()) + { + newAddressToSelect = dlg.getAddress(); + } +} + +void AddressBookPage::on_deleteButton_clicked() +{ + QTableView *table = ui->tableView; + if(!table->selectionModel()) + return; + QModelIndexList indexes = table->selectionModel()->selectedRows(); + if(!indexes.isEmpty()) + { + table->model()->removeRow(indexes.at(0).row()); + } +} + +void AddressBookPage::selectionChanged() +{ + // Set button states based on selected tab and selection + QTableView *table = ui->tableView; + if(!table->selectionModel()) + return; + + if(table->selectionModel()->hasSelection()) + { + switch(tab) + { + case SendingTab: + // In sending tab, allow deletion of selection + ui->deleteButton->setEnabled(true); + ui->deleteButton->setVisible(true); + deleteAction->setEnabled(true); + ui->signMessage->setEnabled(false); + ui->signMessage->setVisible(false); + ui->verifyMessage->setEnabled(true); + ui->verifyMessage->setVisible(true); + break; + case ReceivingTab: + // Deleting receiving addresses, however, is not allowed + ui->deleteButton->setEnabled(false); + ui->deleteButton->setVisible(false); + deleteAction->setEnabled(false); + ui->signMessage->setEnabled(true); + ui->signMessage->setVisible(true); + ui->verifyMessage->setEnabled(false); + ui->verifyMessage->setVisible(false); + break; + } + ui->copyToClipboard->setEnabled(true); + ui->showQRCode->setEnabled(true); + } + else + { + ui->deleteButton->setEnabled(false); + ui->showQRCode->setEnabled(false); + ui->copyToClipboard->setEnabled(false); + ui->signMessage->setEnabled(false); + ui->verifyMessage->setEnabled(false); + } +} + +void AddressBookPage::done(int retval) +{ + QTableView *table = ui->tableView; + if(!table->selectionModel() || !table->model()) + return; + // 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 + QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); + + foreach (QModelIndex index, indexes) + { + QVariant address = table->model()->data(index); + returnValue = address.toString(); + } + + if(returnValue.isEmpty()) + { + // If no address entry selected, return rejected + retval = Rejected; + } + + QDialog::done(retval); +} + +void AddressBookPage::exportClicked() +{ + // CSV is currently the only supported format + QString filename = GUIUtil::getSaveFileName( + this, + tr("Export Address Book Data"), QString(), + tr("Comma separated file (*.csv)")); + + if (filename.isNull()) return; + + 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); + } +} + +void AddressBookPage::on_showQRCode_clicked() +{ + QTableView *table = ui->tableView; + QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); + + foreach (QModelIndex index, indexes) + { + QString address = index.data().toString(), label = index.sibling(index.row(), 0).data(Qt::EditRole).toString(); + + QRCodeDialog *dialog = new QRCodeDialog(address, label, tab == ReceivingTab, this); + if(optionsModel) + dialog->setModel(optionsModel); + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->show(); + } +} + +void AddressBookPage::contextualMenu(const QPoint &point) +{ + QModelIndex index = ui->tableView->indexAt(point); + if(index.isValid()) + { + contextMenu->exec(QCursor::pos()); + } +} + +void AddressBookPage::selectNewAddress(const QModelIndex &parent, int begin, int end) +{ + QModelIndex idx = proxyModel->mapFromSource(model->index(begin, AddressTableModel::Address, parent)); + if(idx.isValid() && (idx.data(Qt::EditRole).toString() == newAddressToSelect)) + { + // Select row of newly created address, once + ui->tableView->setFocus(); + ui->tableView->selectRow(idx.row()); + newAddressToSelect.clear(); + } +} diff --git a/src/qt/addressbookpage.h b/src/qt/addressbookpage.h new file mode 100644 index 00000000..df874869 --- /dev/null +++ b/src/qt/addressbookpage.h @@ -0,0 +1,85 @@ +#ifndef ADDRESSBOOKPAGE_H +#define ADDRESSBOOKPAGE_H + +#include + +namespace Ui { + class AddressBookPage; +} +class AddressTableModel; +class OptionsModel; + +QT_BEGIN_NAMESPACE +class QTableView; +class QItemSelection; +class QSortFilterProxyModel; +class QMenu; +class QModelIndex; +QT_END_NAMESPACE + +/** Widget that shows a list of sending or receiving addresses. + */ +class AddressBookPage : public QDialog +{ + Q_OBJECT + +public: + enum Tabs { + SendingTab = 0, + ReceivingTab = 1 + }; + + enum Mode { + ForSending, /**< Open address book to pick address for sending */ + ForEditing /**< Open address book for editing */ + }; + + explicit AddressBookPage(Mode mode, Tabs tab, QWidget *parent = 0); + ~AddressBookPage(); + + void setModel(AddressTableModel *model); + void setOptionsModel(OptionsModel *optionsModel); + const QString &getReturnValue() const { return returnValue; } + +public slots: + void done(int retval); + void exportClicked(); + +private: + Ui::AddressBookPage *ui; + AddressTableModel *model; + OptionsModel *optionsModel; + Mode mode; + Tabs tab; + QString returnValue; + QSortFilterProxyModel *proxyModel; + QMenu *contextMenu; + QAction *deleteAction; + QString newAddressToSelect; + +private slots: + void on_deleteButton_clicked(); + void on_newAddressButton_clicked(); + /** Copy address of currently selected address entry to clipboard */ + void on_copyToClipboard_clicked(); + void on_signMessage_clicked(); + void on_verifyMessage_clicked(); + void selectionChanged(); + void on_showQRCode_clicked(); + /** Spawn contextual menu (right mouse menu) for address book entry */ + void contextualMenu(const QPoint &point); + + /** Copy label of currently selected address entry to clipboard */ + void onCopyLabelAction(); + /** Edit currently selected address entry */ + void onEditAction(); + + /** New entry/entries were added to address table */ + void selectNewAddress(const QModelIndex &parent, int begin, int end); + +signals: + void signMessage(QString addr); + void verifyMessage(QString addr); +}; + +#endif // ADDRESSBOOKDIALOG_H diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp new file mode 100644 index 00000000..03b09cdc --- /dev/null +++ b/src/qt/addresstablemodel.cpp @@ -0,0 +1,426 @@ +#include "addresstablemodel.h" +#include "guiutil.h" +#include "walletmodel.h" + +#include "wallet.h" +#include "base58.h" + +#include +#include + +const QString AddressTableModel::Send = "S"; +const QString AddressTableModel::Receive = "R"; + +struct AddressTableEntry +{ + enum Type { + Sending, + Receiving + }; + + Type type; + QString label; + QString address; + + AddressTableEntry() {} + AddressTableEntry(Type type, const QString &label, const QString &address): + type(type), label(label), address(address) {} +}; + +struct AddressTableEntryLessThan +{ + bool operator()(const AddressTableEntry &a, const AddressTableEntry &b) const + { + return a.address < b.address; + } + bool operator()(const AddressTableEntry &a, const QString &b) const + { + return a.address < b; + } + bool operator()(const QString &a, const AddressTableEntry &b) const + { + return a < b.address; + } +}; + +// Private implementation +class AddressTablePriv +{ +public: + CWallet *wallet; + QList cachedAddressTable; + AddressTableModel *parent; + + AddressTablePriv(CWallet *wallet, AddressTableModel *parent): + wallet(wallet), parent(parent) {} + + void refreshAddressTable() + { + cachedAddressTable.clear(); + { + LOCK(wallet->cs_wallet); + BOOST_FOREACH(const PAIRTYPE(CTxDestination, std::string)& item, wallet->mapAddressBook) + { + const CBitcoinAddress& address = item.first; + const std::string& strName = item.second; + bool fMine = IsMine(*wallet, address.Get()); + cachedAddressTable.append(AddressTableEntry(fMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending, + QString::fromStdString(strName), + QString::fromStdString(address.ToString()))); + } + } + // qLowerBound() and qUpperBound() require our cachedAddressTable list to be sorted in asc order + qSort(cachedAddressTable.begin(), cachedAddressTable.end(), AddressTableEntryLessThan()); + } + + void updateEntry(const QString &address, const QString &label, bool isMine, int status) + { + // Find address / label in model + QList::iterator lower = qLowerBound( + cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan()); + QList::iterator upper = qUpperBound( + cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan()); + int lowerIndex = (lower - cachedAddressTable.begin()); + int upperIndex = (upper - cachedAddressTable.begin()); + bool inModel = (lower != upper); + AddressTableEntry::Type newEntryType = isMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending; + + switch(status) + { + case CT_NEW: + if(inModel) + { + OutputDebugStringF("Warning: AddressTablePriv::updateEntry: Got CT_NOW, but entry is already in model\n"); + break; + } + parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex); + cachedAddressTable.insert(lowerIndex, AddressTableEntry(newEntryType, label, address)); + parent->endInsertRows(); + break; + case CT_UPDATED: + if(!inModel) + { + OutputDebugStringF("Warning: AddressTablePriv::updateEntry: Got CT_UPDATED, but entry is not in model\n"); + break; + } + lower->type = newEntryType; + lower->label = label; + parent->emitDataChanged(lowerIndex); + break; + case CT_DELETED: + if(!inModel) + { + OutputDebugStringF("Warning: AddressTablePriv::updateEntry: Got CT_DELETED, but entry is not in model\n"); + break; + } + parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1); + cachedAddressTable.erase(lower, upper); + parent->endRemoveRows(); + break; + } + } + + int size() + { + return cachedAddressTable.size(); + } + + AddressTableEntry *index(int idx) + { + if(idx >= 0 && idx < cachedAddressTable.size()) + { + return &cachedAddressTable[idx]; + } + else + { + return 0; + } + } +}; + +AddressTableModel::AddressTableModel(CWallet *wallet, WalletModel *parent) : + QAbstractTableModel(parent),walletModel(parent),wallet(wallet),priv(0) +{ + columns << tr("Label") << tr("Address"); + priv = new AddressTablePriv(wallet, this); + priv->refreshAddressTable(); +} + +AddressTableModel::~AddressTableModel() +{ + delete priv; +} + +int AddressTableModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return priv->size(); +} + +int AddressTableModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return columns.length(); +} + +QVariant AddressTableModel::data(const QModelIndex &index, int role) const +{ + if(!index.isValid()) + return QVariant(); + + AddressTableEntry *rec = static_cast(index.internalPointer()); + + if(role == Qt::DisplayRole || role == Qt::EditRole) + { + switch(index.column()) + { + case Label: + if(rec->label.isEmpty() && role == Qt::DisplayRole) + { + return tr("(no label)"); + } + else + { + return rec->label; + } + case Address: + return rec->address; + } + } + else if (role == Qt::FontRole) + { + QFont font; + if(index.column() == Address) + { + font = GUIUtil::bitcoinAddressFont(); + } + return font; + } + else if (role == TypeRole) + { + switch(rec->type) + { + case AddressTableEntry::Sending: + return Send; + case AddressTableEntry::Receiving: + return Receive; + default: break; + } + } + return QVariant(); +} + +bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if(!index.isValid()) + return false; + AddressTableEntry *rec = static_cast(index.internalPointer()); + + editStatus = OK; + + if(role == Qt::EditRole) + { + switch(index.column()) + { + case Label: + // Do nothing, if old label == new label + if(rec->label == value.toString()) + { + editStatus = NO_CHANGES; + return false; + } + wallet->SetAddressBookName(CBitcoinAddress(rec->address.toStdString()).Get(), value.toString().toStdString()); + break; + case Address: + // Do nothing, if old address == new address + if(CBitcoinAddress(rec->address.toStdString()) == CBitcoinAddress(value.toString().toStdString())) + { + editStatus = NO_CHANGES; + return false; + } + // Refuse to set invalid address, set error status and return false + else if(!walletModel->validateAddress(value.toString())) + { + editStatus = INVALID_ADDRESS; + return false; + } + // Check for duplicate addresses to prevent accidental deletion of addresses, if you try + // to paste an existing address over another address (with a different label) + else if(wallet->mapAddressBook.count(CBitcoinAddress(value.toString().toStdString()).Get())) + { + editStatus = DUPLICATE_ADDRESS; + return false; + } + // Double-check that we're not overwriting a receiving address + else if(rec->type == AddressTableEntry::Sending) + { + { + LOCK(wallet->cs_wallet); + // Remove old entry + wallet->DelAddressBookName(CBitcoinAddress(rec->address.toStdString()).Get()); + // Add new entry with new address + wallet->SetAddressBookName(CBitcoinAddress(value.toString().toStdString()).Get(), rec->label.toStdString()); + } + } + break; + } + return true; + } + return false; +} + +QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if(orientation == Qt::Horizontal) + { + if(role == Qt::DisplayRole) + { + return columns[section]; + } + } + return QVariant(); +} + +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); + AddressTableEntry *data = priv->index(row); + if(data) + { + return createIndex(row, column, priv->index(row)); + } + else + { + return QModelIndex(); + } +} + +void AddressTableModel::updateEntry(const QString &address, const QString &label, bool isMine, int status) +{ + // Update address book model from Bitcoin core + priv->updateEntry(address, label, isMine, status); +} + +QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address) +{ + 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 addresses + { + LOCK(wallet->cs_wallet); + if(wallet->mapAddressBook.count(CBitcoinAddress(strAddress).Get())) + { + editStatus = DUPLICATE_ADDRESS; + return QString(); + } + } + } + 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(); + } + CPubKey newKey; + if(!wallet->GetKeyFromPool(newKey, true)) + { + editStatus = KEY_GENERATION_FAILURE; + return QString(); + } + strAddress = CBitcoinAddress(newKey.GetID()).ToString(); + } + else + { + return QString(); + } + + // Add entry + { + LOCK(wallet->cs_wallet); + wallet->SetAddressBookName(CBitcoinAddress(strAddress).Get(), strLabel); + } + return QString::fromStdString(strAddress); +} + +bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent) +{ + Q_UNUSED(parent); + AddressTableEntry *rec = priv->index(row); + if(count != 1 || !rec || rec->type == AddressTableEntry::Receiving) + { + // Can only remove one row at a time, and cannot remove rows not in model. + // Also refuse to remove receiving addresses. + return false; + } + { + LOCK(wallet->cs_wallet); + wallet->DelAddressBookName(CBitcoinAddress(rec->address.toStdString()).Get()); + } + return true; +} + +/* Look up label for address in address book, if not found return empty string. + */ +QString AddressTableModel::labelForAddress(const QString &address) const +{ + { + LOCK(wallet->cs_wallet); + CBitcoinAddress address_parsed(address.toStdString()); + std::map::iterator mi = wallet->mapAddressBook.find(address_parsed.Get()); + 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(); + } +} + +void AddressTableModel::emitDataChanged(int idx) +{ + emit dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length()-1, QModelIndex())); +} diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h new file mode 100644 index 00000000..ae3e3b2f --- /dev/null +++ b/src/qt/addresstablemodel.h @@ -0,0 +1,92 @@ +#ifndef ADDRESSTABLEMODEL_H +#define ADDRESSTABLEMODEL_H + +#include +#include + +class AddressTablePriv; +class CWallet; +class WalletModel; + +/** + Qt model of the address book in the core. This allows views to access and modify the address book. + */ +class AddressTableModel : public QAbstractTableModel +{ + Q_OBJECT +public: + explicit AddressTableModel(CWallet *wallet, WalletModel *parent = 0); + ~AddressTableModel(); + + enum ColumnIndex { + Label = 0, /**< User specified label */ + Address = 1 /**< Bitcoin address */ + }; + + enum RoleIndex { + TypeRole = Qt::UserRole /**< Type of address (#Send or #Receive) */ + }; + + /** Return status of edit/insert operation */ + enum EditStatus { + OK, /**< Everything ok */ + NO_CHANGES, /**< No changes were made during edit operation */ + INVALID_ADDRESS, /**< Unparseable address */ + DUPLICATE_ADDRESS, /**< Address already in address book */ + WALLET_UNLOCK_FAILURE, /**< Wallet could not be unlocked to create new receiving address */ + KEY_GENERATION_FAILURE /**< Generating a new public key for a receiving address failed */ + }; + + static const QString Send; /**< Specifies send address */ + static const QString Receive; /**< Specifies receive address */ + + /** @name Methods overridden 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()); + Qt::ItemFlags flags(const QModelIndex &index) const; + /*@}*/ + + /* 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); + + /* 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; + + EditStatus getEditStatus() const { return editStatus; } + +private: + WalletModel *walletModel; + CWallet *wallet; + AddressTablePriv *priv; + QStringList columns; + EditStatus editStatus; + + /** Notify listeners that data changed. */ + void emitDataChanged(int index); + +signals: + void defaultAddressChanged(const QString &address); + +public slots: + /* Update address list from core. + */ + void updateEntry(const QString &address, const QString &label, bool isMine, int status); + + friend class AddressTablePriv; +}; + +#endif // ADDRESSTABLEMODEL_H diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp new file mode 100644 index 00000000..35d9e262 --- /dev/null +++ b/src/qt/askpassphrasedialog.cpp @@ -0,0 +1,269 @@ +#include "askpassphrasedialog.h" +#include "ui_askpassphrasedialog.h" + +#include "guiconstants.h" +#include "dialogwindowflags.h" +#include "walletmodel.h" + +#include +#include +#include + +extern bool fWalletUnlockMintOnly; + +AskPassphraseDialog::AskPassphraseDialog(Mode mode, QWidget *parent) : + QDialog(parent, DIALOGWINDOWHINTS), + ui(new Ui::AskPassphraseDialog), + mode(mode), + model(0), + fCapsLock(false) +{ + ui->setupUi(this); + ui->passEdit1->setMaxLength(MAX_PASSPHRASE_SIZE); + ui->passEdit2->setMaxLength(MAX_PASSPHRASE_SIZE); + ui->passEdit3->setMaxLength(MAX_PASSPHRASE_SIZE); + + // Setup Caps Lock detection. + ui->passEdit1->installEventFilter(this); + ui->passEdit2->installEventFilter(this); + ui->passEdit3->installEventFilter(this); + + 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 + case UnlockMining: + 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; + } + + 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() +{ + SecureString oldpass, newpass1, newpass2; + if(!model) + return; + oldpass.reserve(MAX_PASSPHRASE_SIZE); + newpass1.reserve(MAX_PASSPHRASE_SIZE); + newpass2.reserve(MAX_PASSPHRASE_SIZE); + // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) + // Alternately, find a way to make this input mlock()'d to begin with. + oldpass.assign(ui->passEdit1->text().toStdString().c_str()); + newpass1.assign(ui->passEdit2->text().toStdString().c_str()); + newpass2.assign(ui->passEdit3->text().toStdString().c_str()); + + 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 COINS!") + "

" + tr("Are 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("XP will close now to finish the encryption process. " + "Remember that encrypting your wallet cannot fully protect " + "your coins from being stolen by malware infecting your computer.") + + "

" + + tr("IMPORTANT: Any previous backups you have made of your wallet file " + "should be replaced with the newly generated, encrypted wallet file. " + "For security reasons, previous backups of the unencrypted wallet file " + "will become useless as soon as you start using the new, encrypted wallet.") + + "
"); + QApplication::quit(); + } + 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 UnlockMining: + 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 + fWalletUnlockMintOnly = true; + } + 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 + { + QMessageBox::warning(this, tr("Wallet decrypted"), + "" + + tr("XP will close now to finish the decryption process. ") + + ""); + QApplication::quit(); + } + break; + case ChangePass: + if(newpass1 == newpass2) + { + if(model->changePassphrase(oldpass, newpass1)) + { + QMessageBox::information(this, tr("Wallet encrypted"), + tr("Wallet passphrase was successfully 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 acceptable + 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 UnlockMining: + 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); +} + +bool AskPassphraseDialog::event(QEvent *event) +{ + // Detect Caps Lock key press. + if (event->type() == QEvent::KeyPress) { + QKeyEvent *ke = static_cast(event); + if (ke->key() == Qt::Key_CapsLock) { + fCapsLock = !fCapsLock; + } + if (fCapsLock) { + ui->capsLabel->setText(tr("Warning: The Caps Lock key is on!")); + } else { + ui->capsLabel->clear(); + } + } + return QWidget::event(event); +} + +bool AskPassphraseDialog::eventFilter(QObject *object, QEvent *event) +{ + /* Detect Caps Lock. + * There is no good OS-independent way to check a key state in Qt, but we + * can detect Caps Lock by checking for the following condition: + * Shift key is down and the result is a lower case character, or + * Shift key is not down and the result is an upper case character. + */ + if (event->type() == QEvent::KeyPress) { + QKeyEvent *ke = static_cast(event); + QString str = ke->text(); + if (str.length() != 0) { + const QChar *psz = str.unicode(); + bool fShift = (ke->modifiers() & Qt::ShiftModifier) != 0; + if ((fShift && psz->isLower()) || (!fShift && psz->isUpper())) { + fCapsLock = true; + ui->capsLabel->setText(tr("Warning: The Caps Lock key is on!")); + } else if (psz->isLetter()) { + fCapsLock = false; + ui->capsLabel->clear(); + } + } + } + return QDialog::eventFilter(object, event); +} diff --git a/src/qt/askpassphrasedialog.h b/src/qt/askpassphrasedialog.h new file mode 100644 index 00000000..5909fb6c --- /dev/null +++ b/src/qt/askpassphrasedialog.h @@ -0,0 +1,46 @@ +#ifndef ASKPASSPHRASEDIALOG_H +#define ASKPASSPHRASEDIALOG_H + +#include + +namespace Ui { + class AskPassphraseDialog; +} + +class WalletModel; + +/** Multifunctional dialog to ask for passphrases. Used for encryption, unlocking, and changing the passphrase. + */ +class AskPassphraseDialog : public QDialog +{ + Q_OBJECT + +public: + enum Mode { + Encrypt, /**< Ask passphrase twice and encrypt */ + Unlock, /**< Ask passphrase and unlock */ + UnlockMining, /**< Ask passphrase and unlock for mining */ + ChangePass, /**< Ask old passphrase + new passphrase twice */ + Decrypt /**< Ask passphrase and decrypt wallet */ + }; + + explicit AskPassphraseDialog(Mode mode, QWidget *parent = 0); + ~AskPassphraseDialog(); + + void accept(); + + void setModel(WalletModel *model); + +private: + Ui::AskPassphraseDialog *ui; + Mode mode; + WalletModel *model; + bool fCapsLock; + +private slots: + void textChanged(); + bool event(QEvent *event); + bool eventFilter(QObject *, QEvent *event); +}; + +#endif // ASKPASSPHRASEDIALOG_H diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp new file mode 100644 index 00000000..16f69372 --- /dev/null +++ b/src/qt/bitcoin.cpp @@ -0,0 +1,282 @@ +/* + * W.J. van der Laan 2011-2012 + */ +#include "bitcoingui.h" +#include "clientmodel.h" +#include "walletmodel.h" +#include "optionsmodel.h" +#include "guiutil.h" +#include "guiconstants.h" + +#include "init.h" +#include "ui_interface.h" +#include "qtipcserver.h" +#include "intro.h" + +#include +#include +#if QT_VERSION < 0x050000 +#include +#endif +#include +#include +#include +#include +#include + +#if defined(BITCOIN_NEED_QT_PLUGINS) && !defined(_BITCOIN_QT_PLUGINS_INCLUDED) +#define _BITCOIN_QT_PLUGINS_INCLUDED +#define __INSURE__ +#include +Q_IMPORT_PLUGIN(qcncodecs) +Q_IMPORT_PLUGIN(qjpcodecs) +Q_IMPORT_PLUGIN(qtwcodecs) +Q_IMPORT_PLUGIN(qkrcodecs) +Q_IMPORT_PLUGIN(qtaccessiblewidgets) +#endif + +// Need a global reference for the notifications to find the GUI +static BitcoinGUI *guiref; +static QSplashScreen *splashref; + +/** Set up translations */ +static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTranslator, QTranslator &translatorBase, QTranslator &translator) +{ + QSettings settings; + // Get desired locale (e.g. "de_DE") + // 1) System default language + QString lang_territory = QLocale::system().name(); + // 2) Language from QSettings + QString lang_territory_qsettings = settings.value("language", "").toString(); + if(!lang_territory_qsettings.isEmpty()) + lang_territory = lang_territory_qsettings; + // 3) -lang command line argument + lang_territory = QString::fromStdString(GetArg("-lang", lang_territory.toStdString())); + // Convert to "de" only by truncating "_DE" + QString lang = lang_territory; + lang.truncate(lang_territory.lastIndexOf('_')); + // Load language files for configured locale: + // - First load the translator for the base language, without territory + // - Then load the more specific locale translator + // Load e.g. qt_de.qm + if (qtTranslatorBase.load("qt_" + lang, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) + QApplication::installTranslator(&qtTranslatorBase); + // Load e.g. qt_de_DE.qm + if (qtTranslator.load("qt_" + lang_territory, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) + QApplication::installTranslator(&qtTranslator); + // Load e.g. bitcoin_de.qm (shortcut "de" needs to be defined in bitcoin.qrc) + if (translatorBase.load(lang, ":/translations/")) + QApplication::installTranslator(&translatorBase); + // Load e.g. bitcoin_de_DE.qm (shortcut "de_DE" needs to be defined in bitcoin.qrc) + if (translator.load(lang_territory, ":/translations/")) + QApplication::installTranslator(&translator); +} + +static void ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style) +{ + // Message from network thread + if(guiref) + { + bool modal = (style & CClientUIInterface::MODAL); + // in case of modal message, use blocking connection to wait for user to click OK + QMetaObject::invokeMethod(guiref, "error", + modal ? GUIUtil::blockingGUIThreadConnection() : Qt::QueuedConnection, + Q_ARG(QString, QString::fromStdString(caption)), + Q_ARG(QString, QString::fromStdString(message)), + Q_ARG(bool, modal)); + } + else + { + printf("%s: %s\n", caption.c_str(), message.c_str()); + fprintf(stderr, "%s: %s\n", caption.c_str(), message.c_str()); + } +} + +static bool ThreadSafeAskFee(int64_t nFeeRequired, const std::string& strCaption) +{ + if(!guiref) + return false; + if(nFeeRequired < MIN_TX_FEE || nFeeRequired <= nTransactionFee || fDaemon) + return true; + bool payFee = false; + + QMetaObject::invokeMethod(guiref, "askFee", GUIUtil::blockingGUIThreadConnection(), + Q_ARG(qint64, nFeeRequired), + Q_ARG(bool*, &payFee)); + + return payFee; +} + +static void ThreadSafeHandleURI(const std::string& strURI) +{ + if(!guiref) + return; + + QMetaObject::invokeMethod(guiref, "handleURI", GUIUtil::blockingGUIThreadConnection(), + Q_ARG(QString, QString::fromStdString(strURI))); +} + +static void InitMessage(const std::string &message) +{ + if(splashref) + { + splashref->showMessage(QString::fromStdString(message), Qt::AlignBottom|Qt::AlignHCenter, QColor(255,255,200)); + QApplication::instance()->processEvents(); + } +} + +static void QueueShutdown() +{ + QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection); +} + +/* + Translate string to current locale using Qt. + */ +static std::string Translate(const char* psz) +{ + return QCoreApplication::translate("bitcoin-core", psz).toStdString(); +} + +/* Handle runaway exceptions. Shows a message box with the problem and quits the program. + */ +static void handleRunawayException(std::exception *e) +{ + PrintExceptionContinue(e, "Runaway exception"); + QMessageBox::critical(0, "Runaway exception", BitcoinGUI::tr("A fatal error occurred. XP can no longer continue safely and will quit.") + QString("\n\n") + QString::fromStdString(strMiscWarning)); + exit(1); +} + +#ifndef BITCOIN_QT_TEST +int main(int argc, char *argv[]) +{ + // Do this early as we don't want to bother initializing if we are just calling IPC + ipcScanRelay(argc, argv); +#if QT_VERSION < 0x050000 + // Internal string conversion is all UTF-8 + QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8")); + QTextCodec::setCodecForCStrings(QTextCodec::codecForTr()); +#endif + Q_INIT_RESOURCE(bitcoin); + QApplication app(argc, argv); + + // Application identification (must be set before OptionsModel is initialized, + // as it is used to locate QSettings) + app.setOrganizationName("XP"); + app.setOrganizationDomain("XP.su"); + if(GetBoolArg("-testnet")) // Separate UI settings for testnet + app.setApplicationName("XP-Qt-testnet"); + else + app.setApplicationName("XP-Qt"); + // Now that QSettings are accessible, initialize translations + QTranslator qtTranslatorBase, qtTranslator, translatorBase, translator; + initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator); + + // Command-line options take precedence: + ParseParameters(argc, argv); + + // User language is set up: pick a data directory + Intro::pickDataDirectory(); + + // Install global event filter that makes sure that long tooltips can be word-wrapped + app.installEventFilter(new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app)); + + // ... then bitcoin.conf: + if (!boost::filesystem::is_directory(GetDataDir(false))) + { + QMessageBox::critical(0, "XP", + QObject::tr("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(mapArgs["-datadir"]))); + return 1; + } + ReadConfigFile(mapArgs, mapMultiArgs); + + // ... then GUI settings: + OptionsModel optionsModel; + + // Subscribe to global signals from core + uiInterface.ThreadSafeMessageBox.connect(ThreadSafeMessageBox); + uiInterface.ThreadSafeAskFee.connect(ThreadSafeAskFee); + uiInterface.ThreadSafeHandleURI.connect(ThreadSafeHandleURI); + uiInterface.InitMessage.connect(InitMessage); + uiInterface.QueueShutdown.connect(QueueShutdown); + uiInterface.Translate.connect(Translate); + + // Show help message immediately after parsing command-line options (for "-lang") and setting locale, + // but before showing splash screen. + if (mapArgs.count("-?") || mapArgs.count("--help")) + { + GUIUtil::HelpMessageBox help; + help.showOrPrint(); + return 1; + } + + QSplashScreen splash(QPixmap(":/images/splash"), 0); + if (GetBoolArg("-splash", true) && !GetBoolArg("-min")) + { + splash.show(); + splash.setAutoFillBackground(true); + splashref = &splash; + } + + app.processEvents(); + + app.setQuitOnLastWindowClosed(false); + + try + { + // Regenerate startup link, to fix links to old versions + if (GUIUtil::GetStartOnSystemStartup()) + GUIUtil::SetStartOnSystemStartup(true); + + BitcoinGUI window; + guiref = &window; + if(AppInit2()) + { + { + // Put this in a block, so that the Model objects are cleaned up before + // calling Shutdown(). + + if (splashref) + splash.finish(&window); + + ClientModel clientModel(&optionsModel); + WalletModel walletModel(pwalletMain, &optionsModel); + + window.setClientModel(&clientModel); + window.setWalletModel(&walletModel); + + // If -min option passed, start window minimized. + if(GetBoolArg("-min")) + { + window.showMinimized(); + } + else + { + window.show(); + } + + // Place this here as guiref has to be defined if we don't want to lose URIs + ipcInit(argc, argv); + + app.exec(); + + window.hide(); + window.setClientModel(0); + window.setWalletModel(0); + guiref = 0; + } + // Shutdown the core and its threads, but don't exit Bitcoin-Qt here + Shutdown(NULL); + } + else + { + return 1; + } + } catch (std::exception& e) { + handleRunawayException(&e); + } catch (...) { + handleRunawayException(NULL); + } + return 0; +} +#endif // BITCOIN_QT_TEST diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc new file mode 100644 index 00000000..3415f72a --- /dev/null +++ b/src/qt/bitcoin.qrc @@ -0,0 +1,60 @@ + + + res/icons/bitcoin.png + res/icons/address-book.png + res/icons/quit.png + res/icons/send.png + res/icons/bitcoin.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/mining_active.png + res/icons/mining_inactive.png + res/icons/transaction0.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/icons/configure.png + res/icons/receive.png + res/icons/editpaste.png + res/icons/editcopy.png + res/icons/add.png + res/icons/bitcoin.png + res/icons/bitcoin.png + res/icons/edit.png + res/icons/history.png + res/icons/overview.png + 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/icons/lock_closed.png + res/icons/lock_open.png + res/icons/key.png + res/icons/filesave.png + res/icons/qrcode.png + res/icons/debugwindow.png + res/icons/dump.png + res/icons/import.png + + + res/images/about.png + res/images/splash2.jpg + + + res/movies/update_spinner.mng + + + locale/bitcoin_en.qm + locale/bitcoin_ru.qm + locale/bitcoin_uk.qm + + diff --git a/src/qt/bitcoinaddressvalidator.cpp b/src/qt/bitcoinaddressvalidator.cpp new file mode 100644 index 00000000..5136ea0c --- /dev/null +++ b/src/qt/bitcoinaddressvalidator.cpp @@ -0,0 +1,77 @@ +#include "bitcoinaddressvalidator.h" + +/* Base58 characters are: + "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" + + This is: + - All numbers except for '0' + - All upper-case letters except for 'I' and 'O' + - All lower-case letters except for 'l' + + User friendly Base58 input can map + - 'l' and 'I' to '1' + - '0' and 'O' to 'o' +*/ + +BitcoinAddressValidator::BitcoinAddressValidator(QObject *parent) : + QValidator(parent) +{ +} + +QValidator::State BitcoinAddressValidator::validate(QString &input, int &pos) const +{ + // Correction + for(int idx=0; idx= '0' && ch<='9') || + (ch >= 'a' && ch<='z') || + (ch >= 'A' && ch<='Z')) && + ch != 'l' && ch != 'I' && ch != '0' && ch != 'O') + { + // Alphanumeric and not a 'forbidden' character + } + else + { + state = QValidator::Invalid; + } + } + + // Empty address is "intermediate" input + if(input.isEmpty()) + { + state = QValidator::Intermediate; + } + + return state; +} diff --git a/src/qt/bitcoinaddressvalidator.h b/src/qt/bitcoinaddressvalidator.h new file mode 100644 index 00000000..9710d122 --- /dev/null +++ b/src/qt/bitcoinaddressvalidator.h @@ -0,0 +1,24 @@ +#ifndef BITCOINADDRESSVALIDATOR_H +#define BITCOINADDRESSVALIDATOR_H + +#include + +/** Base48 entry widget validator. + Corrects near-miss characters and refuses characters that are no part of base48. + */ +class BitcoinAddressValidator : public QValidator +{ + Q_OBJECT +public: + explicit BitcoinAddressValidator(QObject *parent = 0); + + State validate(QString &input, int &pos) const; + + static const int MaxAddressLength = 35; +signals: + +public slots: + +}; + +#endif // BITCOINADDRESSVALIDATOR_H diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp new file mode 100644 index 00000000..9514ec84 --- /dev/null +++ b/src/qt/bitcoinamountfield.cpp @@ -0,0 +1,168 @@ +#include "bitcoinamountfield.h" +#include "qvaluecombobox.h" +#include "bitcoinunits.h" + +#include "guiconstants.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +BitcoinAmountField::BitcoinAmountField(QWidget *parent): + QWidget(parent), amount(0), currentUnit(-1) +{ + amount = new QDoubleSpinBox(this); + amount->setLocale(QLocale::c()); + amount->setDecimals(8); + amount->installEventFilter(this); + amount->setMaximumWidth(170); + amount->setSingleStep(0.001); + + QHBoxLayout *layout = new QHBoxLayout(this); + layout->addWidget(amount); + unit = new QValueComboBox(this); + unit->setModel(new BitcoinUnits(this)); + layout->addWidget(unit); + layout->addStretch(1); + layout->setContentsMargins(0,0,0,0); + + setLayout(layout); + + setFocusPolicy(Qt::TabFocus); + setFocusProxy(amount); + + // If one if the widgets changes, the combined content changes as well + connect(amount, SIGNAL(valueChanged(QString)), this, SIGNAL(textChanged())); + connect(unit, SIGNAL(currentIndexChanged(int)), this, SLOT(unitChanged(int))); + + // Set default based on configuration + unitChanged(unit->currentIndex()); +} + +void BitcoinAmountField::setText(const QString &text) +{ + if (text.isEmpty()) + amount->clear(); + else + amount->setValue(text.toDouble()); +} + +void BitcoinAmountField::clear() +{ + amount->clear(); + unit->setCurrentIndex(0); +} + +bool BitcoinAmountField::validate() +{ + bool valid = true; + if (amount->value() == 0.0) + valid = false; + if (valid && !BitcoinUnits::parse(currentUnit, text(), 0)) + valid = false; + + setValid(valid); + + return valid; +} + +void BitcoinAmountField::setValid(bool valid) +{ + if (valid) + amount->setStyleSheet(""); + else + amount->setStyleSheet(STYLE_INVALID); +} + +QString BitcoinAmountField::text() const +{ + if (amount->text().isEmpty()) + return QString(); + else + return amount->text(); +} + +bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event) +{ + if (event->type() == QEvent::FocusIn) + { + // Clear invalid flag on focus + setValid(true); + } + else if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) + { + QKeyEvent *keyEvent = static_cast(event); + if (keyEvent->key() == Qt::Key_Comma) + { + // Translate a comma into a period + QKeyEvent periodKeyEvent(event->type(), Qt::Key_Period, keyEvent->modifiers(), ".", keyEvent->isAutoRepeat(), keyEvent->count()); + qApp->sendEvent(object, &periodKeyEvent); + return true; + } + } + return QWidget::eventFilter(object, event); +} + +QWidget *BitcoinAmountField::setupTabChain(QWidget *prev) +{ + QWidget::setTabOrder(prev, amount); + return amount; +} + +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->setDecimals(BitcoinUnits::decimals(currentUnit)); + amount->setMaximum(qPow(10, BitcoinUnits::amountDigits(currentUnit)) - qPow(10, -amount->decimals())); + + if(valid) + { + // If value was valid, re-place it in the widget with the new unit + setValue(currentValue); + } + else + { + // If current value is invalid, just clear field + setText(""); + } + setValid(true); +} + +void BitcoinAmountField::setDisplayUnit(int newUnit) +{ + unit->setValue(newUnit); +} diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h new file mode 100644 index 00000000..66792e00 --- /dev/null +++ b/src/qt/bitcoinamountfield.h @@ -0,0 +1,60 @@ +#ifndef BITCOINFIELD_H +#define BITCOINFIELD_H + +#include + +QT_BEGIN_NAMESPACE +class QDoubleSpinBox; +class QValueComboBox; +QT_END_NAMESPACE + +/** Widget for entering bitcoin amounts. + */ +class BitcoinAmountField: public QWidget +{ + Q_OBJECT + Q_PROPERTY(qint64 value READ value WRITE setValue NOTIFY textChanged USER true) +public: + explicit BitcoinAmountField(QWidget *parent = 0); + + qint64 value(bool *valid=0) const; + void setValue(qint64 value); + + /** Mark current value as invalid in UI. */ + void setValid(bool valid); + /** Perform input validation, mark field as invalid if entered value is not valid. */ + bool validate(); + + /** Change unit used to display amount. */ + void setDisplayUnit(int unit); + + /** Make field empty and ready for new input. */ + void clear(); + + /** Qt messes up the tab chain by default in some cases (issue https://bugreports.qt-project.org/browse/QTBUG-10907), + in these cases we have to set it up manually. + */ + QWidget *setupTabChain(QWidget *prev); + +signals: + void textChanged(); + +protected: + /** Intercept focus-in event and ',' key presses */ + bool eventFilter(QObject *object, QEvent *event); + +private: + QDoubleSpinBox *amount; + QValueComboBox *unit; + int currentUnit; + + void setText(const QString &text); + QString text() const; + +private slots: + void unitChanged(int idx); + +}; + + +#endif // BITCOINFIELD_H diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp new file mode 100644 index 00000000..b4743f68 --- /dev/null +++ b/src/qt/bitcoingui.cpp @@ -0,0 +1,1235 @@ +/* + * Qt4 bitcoin GUI. + * + * W.J. van der Laan 2011-2012 + * The Bitcoin Developers 2011-2012 + */ +#include "bitcoingui.h" +#include "transactiontablemodel.h" +#include "addressbookpage.h" +#include "sendcoinsdialog.h" +#include "signverifymessagedialog.h" +#include "secondauthdialog.h" +#include "multisigdialog.h" +#include "optionsdialog.h" +#include "aboutdialog.h" +#include "clientmodel.h" +#include "walletmodel.h" +#include "editaddressdialog.h" +#include "optionsmodel.h" +#include "transactiondescdialog.h" +#include "addresstablemodel.h" +#include "transactionview.h" +#include "overviewpage.h" +#include "bitcoinunits.h" +#include "guiconstants.h" +#include "askpassphrasedialog.h" +#include "notificator.h" +#include "guiutil.h" +#include "ui_interface.h" +#include "rpcconsole.h" +#include "mintingview.h" + +#ifdef Q_OS_MAC +#include "macdockiconhandler.h" +#endif + +#include +#if QT_VERSION < 0x050000 +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if QT_VERSION < 0x050000 +#include +#else +#include +#endif +#include +#include +#if QT_VERSION < 0x050000 +#include +#endif +#include +#include + +#include + +extern bool fWalletUnlockMintOnly; +extern uint64_t nStakeInputsMapSize; + +BitcoinGUI::BitcoinGUI(QWidget *parent): + QMainWindow(parent), + clientModel(0), + walletModel(0), + signVerifyMessageDialog(0), + secondAuthDialog(0), + multisigPage(0), + encryptWalletAction(0), + lockWalletAction(0), + unlockWalletAction(0), + unlockWalletMiningAction(0), + changePassphraseAction(0), + aboutQtAction(0), + trayIcon(0), + notificator(0), + rpcConsole(0), + aboutDialog(0), + optionsDialog(0) +{ + resize(850, 550); + setWindowTitle(tr("XP") + " - " + tr("Wallet")); +#ifndef Q_OS_MAC + qApp->setWindowIcon(QIcon(":icons/bitcoin")); + setWindowIcon(QIcon(":icons/bitcoin")); +#else + setUnifiedTitleAndToolBarOnMac(true); + QApplication::setAttribute(Qt::AA_DontShowIconsInMenus); +#endif + // Accept D&D of URIs + setAcceptDrops(true); + + // Create actions for the toolbar, menu bar and tray/dock icon + createActions(); + + // Create application menu bar + createMenuBar(); + + // Create the toolbars + createToolBars(); + + // Create the tray icon (or setup the dock icon) + createTrayIcon(); + + // Create tabs + overviewPage = new OverviewPage(); + + transactionsPage = new QWidget(this); + QVBoxLayout *vbox = new QVBoxLayout(); + transactionView = new TransactionView(this); + vbox->addWidget(transactionView); + transactionsPage->setLayout(vbox); + + mintingPage = new QWidget(this); + QVBoxLayout *vboxMinting = new QVBoxLayout(); + mintingView = new MintingView(this); + vboxMinting->addWidget(mintingView); + mintingPage->setLayout(vboxMinting); + + addressBookPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::SendingTab); + + receiveCoinsPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::ReceivingTab); + + sendCoinsPage = new SendCoinsDialog(this); + + signVerifyMessageDialog = new SignVerifyMessageDialog(0); + + secondAuthDialog = new SecondAuthDialog(0); + + multisigPage = new MultisigDialog(0); + + centralWidget = new QStackedWidget(this); + centralWidget->addWidget(overviewPage); + centralWidget->addWidget(transactionsPage); + centralWidget->addWidget(mintingPage); + centralWidget->addWidget(addressBookPage); + centralWidget->addWidget(receiveCoinsPage); + centralWidget->addWidget(sendCoinsPage); + setCentralWidget(centralWidget); + + // Create status bar + statusBar(); + + // Status bar notification icons + QFrame *frameBlocks = new QFrame(); + frameBlocks->setContentsMargins(0,0,0,0); + frameBlocks->setMinimumWidth(72); + frameBlocks->setMaximumWidth(72); + QHBoxLayout *frameBlocksLayout = new QHBoxLayout(frameBlocks); + frameBlocksLayout->setContentsMargins(3,0,3,0); + frameBlocksLayout->setSpacing(3); + labelEncryptionIcon = new QLabel(); + labelMiningIcon = new QLabel(); + labelConnectionsIcon = new QLabel(); + labelBlocksIcon = new QLabel(); + frameBlocksLayout->addStretch(); + frameBlocksLayout->addWidget(labelEncryptionIcon); + frameBlocksLayout->addStretch(); + frameBlocksLayout->addWidget(labelMiningIcon); + frameBlocksLayout->addStretch(); + frameBlocksLayout->addWidget(labelConnectionsIcon); + frameBlocksLayout->addStretch(); + frameBlocksLayout->addWidget(labelBlocksIcon); + frameBlocksLayout->addStretch(); + + // Progress bar and label for blocks download + progressBarLabel = new QLabel(); + progressBarLabel->setVisible(false); + progressBar = new QProgressBar(); + progressBar->setAlignment(Qt::AlignCenter); + progressBar->setVisible(false); + + // Override style sheet for progress bar for styles that have a segmented progress bar, + // as they make the text unreadable (workaround for issue #1071) + // See https://qt-project.org/doc/qt-4.8/gallery.html + QString curStyle = qApp->style()->metaObject()->className(); + if(curStyle == "QWindowsStyle" || curStyle == "QWindowsXPStyle") + { + progressBar->setStyleSheet("QProgressBar { background-color: #e8e8e8; border: 1px solid grey; border-radius: 7px; padding: 1px; text-align: center; } QProgressBar::chunk { background: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #FF8000, stop: 1 orange); border-radius: 7px; margin: 0px; }"); + } + + statusBar()->addWidget(progressBarLabel); + statusBar()->addWidget(progressBar); + statusBar()->addPermanentWidget(frameBlocks); + + syncIconMovie = new QMovie(":/movies/update_spinner", "mng", this); + + // Clicking on a transaction on the overview page simply sends you to transaction history page + connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), this, SLOT(gotoHistoryPage())); + connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), transactionView, SLOT(focusTransaction(QModelIndex))); + + // Double-clicking on a transaction on the transaction history page shows details + connect(transactionView, SIGNAL(doubleClicked(QModelIndex)), transactionView, SLOT(showDetails())); + + rpcConsole = new RPCConsole(0); + connect(openRPCConsoleAction, SIGNAL(triggered()), rpcConsole, SLOT(show())); + connect(openRPCConsoleAction, SIGNAL(triggered()), rpcConsole, SLOT(raise())); + + aboutDialog = new AboutDialog(0); + optionsDialog = new OptionsDialog(0); + + // Clicking on "Verify Message" in the address book sends you to the verify message tab + connect(addressBookPage, SIGNAL(verifyMessage(QString)), this, SLOT(gotoVerifyMessageTab(QString))); + // Clicking on "Sign Message" in the receive coins page sends you to the sign message tab + connect(receiveCoinsPage, SIGNAL(signMessage(QString)), this, SLOT(gotoSignMessageTab(QString))); + + gotoOverviewPage(); +} + +BitcoinGUI::~BitcoinGUI() +{ + if(trayIcon) // Hide tray icon, as deleting will let it linger until quit (on Ubuntu) + trayIcon->hide(); +#ifdef Q_OS_MAC + delete appMenuBar; +#endif + + delete rpcConsole; + delete aboutDialog; + delete optionsDialog; + delete multisigPage; + delete secondAuthDialog; + delete signVerifyMessageDialog; +} + +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); + overviewAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_1)); + tabGroup->addAction(overviewAction); + + sendCoinsAction = new QAction(QIcon(":/icons/send"), tr("&Send coins"), this); + sendCoinsAction->setToolTip(tr("Send coins to a XP address")); + sendCoinsAction->setCheckable(true); + sendCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_2)); + tabGroup->addAction(sendCoinsAction); + + 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); + receiveCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_3)); + tabGroup->addAction(receiveCoinsAction); + + historyAction = new QAction(QIcon(":/icons/history"), tr("&Transactions"), this); + historyAction->setToolTip(tr("Browse transaction history")); + historyAction->setCheckable(true); + historyAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_4)); + tabGroup->addAction(historyAction); + + mintingAction = new QAction(QIcon(":/icons/history"), tr("&Minting"), this); + mintingAction->setToolTip(tr("Show your minting capacity")); + mintingAction->setCheckable(true); + mintingAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_5)); + tabGroup->addAction(mintingAction); + + 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); + addressBookAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_6)); + tabGroup->addAction(addressBookAction); + + multisigAction = new QAction(QIcon(":/icons/send"), tr("Multisig"), this); + multisigAction->setStatusTip(tr("Open window for working with multisig addresses")); + tabGroup->addAction(multisigAction); + + connect(overviewAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); + connect(overviewAction, SIGNAL(triggered()), this, SLOT(gotoOverviewPage())); + connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); + connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(gotoSendCoinsPage())); + connect(receiveCoinsAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); + connect(receiveCoinsAction, SIGNAL(triggered()), this, SLOT(gotoReceiveCoinsPage())); + connect(historyAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); + connect(historyAction, SIGNAL(triggered()), this, SLOT(gotoHistoryPage())); + connect(mintingAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); + connect(mintingAction, SIGNAL(triggered()), this, SLOT(gotoMintingPage())); + connect(addressBookAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); + connect(addressBookAction, SIGNAL(triggered()), this, SLOT(gotoAddressBookPage())); + connect(multisigAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); + connect(multisigAction, SIGNAL(triggered()), this, SLOT(gotoMultisigPage())); + + quitAction = new QAction(QIcon(":/icons/quit"), tr("E&xit"), this); + quitAction->setStatusTip(tr("Quit application")); + quitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q)); + quitAction->setMenuRole(QAction::QuitRole); + aboutAction = new QAction(QIcon(":/icons/bitcoin"), tr("&About XP"), this); + aboutAction->setStatusTip(tr("Show information about XP")); + aboutAction->setMenuRole(QAction::AboutRole); +#if QT_VERSION < 0x050000 + aboutQtAction = new QAction(QIcon(":/trolltech/qmessagebox/images/qtlogo-64.png"), tr("About &Qt"), this); +#else + aboutQtAction = new QAction(QIcon(":/qt-project.org/qmessagebox/images/qtlogo-64.png"), tr("About &Qt"), this); +#endif + aboutQtAction->setStatusTip(tr("Show information about Qt")); + aboutQtAction->setMenuRole(QAction::AboutQtRole); + optionsAction = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); + optionsAction->setStatusTip(tr("Modify configuration options for XP")); + optionsAction->setMenuRole(QAction::PreferencesRole); + toggleHideAction = new QAction(QIcon(":/icons/bitcoin"), tr("&Show / Hide"), this); + encryptWalletAction = new QAction(QIcon(":/icons/lock_closed"), tr("&Encrypt Wallet..."), this); + encryptWalletAction->setStatusTip(tr("Encrypt or decrypt wallet")); + encryptWalletAction->setCheckable(true); + backupWalletAction = new QAction(QIcon(":/icons/filesave"), tr("&Backup Wallet..."), this); + backupWalletAction->setStatusTip(tr("Backup wallet to another location")); + dumpWalletAction = new QAction(QIcon(":/icons/dump"), tr("&Dump Wallet..."), this); + dumpWalletAction->setStatusTip(tr("Dump keys to a text file")); + importWalletAction = new QAction(QIcon(":/icons/import"), tr("&Import Wallet..."), this); + importWalletAction->setStatusTip(tr("Import keys into a wallet")); + changePassphraseAction = new QAction(QIcon(":/icons/key"), tr("&Change Passphrase..."), this); + changePassphraseAction->setStatusTip(tr("Change the passphrase used for wallet encryption")); + signMessageAction = new QAction(QIcon(":/icons/edit"), tr("Sign &message..."), this); + signMessageAction->setStatusTip(tr("Sign messages with your XP addresses to prove you own them")); + verifyMessageAction = new QAction(QIcon(":/icons/transaction_0"), tr("&Verify message..."), this); + verifyMessageAction->setStatusTip(tr("Verify messages to ensure they were signed with specified XP addresses")); + secondAuthAction = new QAction(QIcon(":/icons/key"), tr("Second &auth..."), this); + secondAuthAction->setStatusTip(tr("Second auth with your XP addresses")); + + lockWalletAction = new QAction(QIcon(":/icons/lock_closed"), tr("&Lock wallet"), this); + lockWalletAction->setStatusTip(tr("Lock wallet")); + lockWalletAction->setCheckable(true); + + unlockWalletAction = new QAction(QIcon(":/icons/lock_open"), tr("Unlo&ck wallet"), this); + unlockWalletAction->setStatusTip(tr("Unlock wallet")); + unlockWalletAction->setCheckable(true); + + unlockWalletMiningAction = new QAction(QIcon(":/icons/mining_active"), tr("Unlo&ck wallet for mining"), this); + unlockWalletMiningAction->setStatusTip(tr("Unlock wallet for mining")); + unlockWalletMiningAction->setCheckable(true); + + exportAction = new QAction(QIcon(":/icons/export"), tr("&Export..."), this); + exportAction->setStatusTip(tr("Export the data in the current tab to a file")); + openRPCConsoleAction = new QAction(QIcon(":/icons/debugwindow"), tr("&Debug window"), this); + openRPCConsoleAction->setStatusTip(tr("Open debugging and diagnostic console")); + + connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); + connect(aboutAction, SIGNAL(triggered()), this, SLOT(aboutClicked())); + connect(aboutQtAction, SIGNAL(triggered()), qApp, SLOT(aboutQt())); + connect(optionsAction, SIGNAL(triggered()), this, SLOT(optionsClicked())); + connect(toggleHideAction, SIGNAL(triggered()), this, SLOT(toggleHidden())); + connect(encryptWalletAction, SIGNAL(triggered(bool)), this, SLOT(encryptWallet(bool))); + connect(lockWalletAction, SIGNAL(triggered(bool)), this, SLOT(lockWallet())); + connect(unlockWalletAction, SIGNAL(triggered(bool)), this, SLOT(unlockWallet())); + connect(unlockWalletMiningAction, SIGNAL(triggered(bool)), this, SLOT(unlockWalletMining(bool))); + connect(backupWalletAction, SIGNAL(triggered()), this, SLOT(backupWallet())); + connect(dumpWalletAction, SIGNAL(triggered()), this, SLOT(dumpWallet())); + connect(importWalletAction, SIGNAL(triggered()), this, SLOT(importWallet())); + connect(changePassphraseAction, SIGNAL(triggered()), this, SLOT(changePassphrase())); + connect(signMessageAction, SIGNAL(triggered()), this, SLOT(gotoSignMessageTab())); + connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(gotoVerifyMessageTab())); + connect(secondAuthAction, SIGNAL(triggered()), this, SLOT(gotoSecondAuthPage())); +} + +void BitcoinGUI::createMenuBar() +{ +#ifdef Q_OS_MAC + // Create a decoupled menu bar on Mac which stays even if the window is closed + appMenuBar = new QMenuBar(); +#else + // Get the main window's menu bar on other platforms + appMenuBar = menuBar(); +#endif + + // Configure the menus + QMenu *file = appMenuBar->addMenu(tr("&File")); + file->addAction(backupWalletAction); + file->addSeparator(); + file->addAction(dumpWalletAction); + file->addAction(importWalletAction); + file->addAction(exportAction); + file->addAction(signMessageAction); + file->addAction(verifyMessageAction); + file->addAction(secondAuthAction); + file->addAction(multisigAction); + file->addSeparator(); + file->addAction(quitAction); + + QMenu *settings = appMenuBar->addMenu(tr("&Settings")); + QMenu *securityMenu = settings->addMenu(QIcon(":/icons/key"), tr("&Wallet security")); + securityMenu->addAction(encryptWalletAction); + securityMenu->addAction(changePassphraseAction); + securityMenu->addAction(unlockWalletAction); + securityMenu->addAction(unlockWalletMiningAction); + securityMenu->addAction(lockWalletAction); + settings->addAction(optionsAction); + + QMenu *help = appMenuBar->addMenu(tr("&Help")); + help->addAction(openRPCConsoleAction); + help->addSeparator(); + help->addAction(aboutAction); + help->addAction(aboutQtAction); +} + +void BitcoinGUI::createToolBars() +{ + QToolBar *toolbar = addToolBar(tr("Tabs toolbar")); + toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + toolbar->addAction(overviewAction); + toolbar->addAction(sendCoinsAction); + toolbar->addAction(receiveCoinsAction); + toolbar->addAction(historyAction); + toolbar->addAction(mintingAction); + toolbar->addAction(addressBookAction); + + QToolBar *toolbar2 = addToolBar(tr("Actions toolbar")); + toolbar2->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + toolbar2->addAction(exportAction); + toolbar2->setVisible(false); + +} + +void BitcoinGUI::setClientModel(ClientModel *clientModel) +{ + this->clientModel = clientModel; + if(clientModel) + { + // Replace some strings and icons, when using the testnet + if(clientModel->isTestNet()) + { + setWindowTitle(windowTitle() + QString(" ") + tr("[testnet]")); +#ifndef Q_OS_MAC + qApp->setWindowIcon(QIcon(":icons/bitcoin_testnet")); + setWindowIcon(QIcon(":icons/bitcoin_testnet")); +#else + MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin_testnet")); +#endif + if(trayIcon) + { + trayIcon->setToolTip(tr("XP client") + QString(" ") + tr("[testnet]")); + trayIcon->setIcon(QIcon(":/icons/toolbar_testnet")); + toggleHideAction->setIcon(QIcon(":/icons/toolbar_testnet")); + } + + aboutAction->setIcon(QIcon(":/icons/toolbar_testnet")); + } + + // Keep up to date with client + setNumConnections(clientModel->getNumConnections()); + connect(clientModel, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int))); + + setNumBlocks(clientModel->getNumBlocks(), clientModel->getNumBlocksOfPeers()); + connect(clientModel, SIGNAL(numBlocksChanged(int,int)), this, SLOT(setNumBlocks(int,int))); + + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(updateMining())); + timer->start(10*1000); //10 seconds + + // Report errors from network/worker thread + connect(clientModel, SIGNAL(error(QString,QString,bool)), this, SLOT(error(QString,QString,bool))); + + rpcConsole->setClientModel(clientModel); + addressBookPage->setOptionsModel(clientModel->getOptionsModel()); + receiveCoinsPage->setOptionsModel(clientModel->getOptionsModel()); + } +} + +void BitcoinGUI::setWalletModel(WalletModel *walletModel) +{ + this->walletModel = walletModel; + if(walletModel) + { + // Report errors from wallet thread + connect(walletModel, SIGNAL(error(QString,QString,bool)), this, SLOT(error(QString,QString,bool))); + + // Put transaction list in tabs + transactionView->setModel(walletModel); + mintingView->setModel(walletModel); + + overviewPage->setModel(walletModel); + addressBookPage->setModel(walletModel->getAddressTableModel()); + receiveCoinsPage->setModel(walletModel->getAddressTableModel()); + sendCoinsPage->setModel(walletModel); + signVerifyMessageDialog->setModel(walletModel); + secondAuthDialog->setModel(walletModel); + multisigPage->setModel(walletModel); + + setEncryptionStatus(walletModel->getEncryptionStatus()); + connect(walletModel, SIGNAL(encryptionStatusChanged(int)), this, SLOT(setEncryptionStatus(int))); + connect(walletModel, SIGNAL(encryptionStatusChanged(int)), this, SLOT(updateMining())); + + // Balloon pop-up 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() +{ + QMenu *trayIconMenu; +#ifndef Q_OS_MAC + trayIcon = new QSystemTrayIcon(this); + trayIconMenu = new QMenu(this); + trayIcon->setContextMenu(trayIconMenu); + trayIcon->setToolTip(tr("XP client")); + trayIcon->setIcon(QIcon(":/icons/toolbar")); + connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), + this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason))); + trayIcon->show(); +#else + // Note: On Mac, the dock icon is used to provide the tray's functionality. + MacDockIconHandler *dockIconHandler = MacDockIconHandler::instance(); + dockIconHandler->setMainWindow((QMainWindow *)this); + trayIconMenu = dockIconHandler->dockMenu(); +#endif + + // Configuration of the tray icon (or dock icon) icon menu + trayIconMenu->addAction(toggleHideAction); + trayIconMenu->addSeparator(); + trayIconMenu->addAction(sendCoinsAction); + trayIconMenu->addAction(multisigAction); + trayIconMenu->addAction(receiveCoinsAction); + trayIconMenu->addSeparator(); + trayIconMenu->addAction(signMessageAction); + trayIconMenu->addAction(verifyMessageAction); + trayIconMenu->addAction(secondAuthAction); + trayIconMenu->addSeparator(); + trayIconMenu->addAction(optionsAction); + trayIconMenu->addAction(openRPCConsoleAction); +#ifndef Q_OS_MAC + // This is built-in on Mac + trayIconMenu->addSeparator(); + trayIconMenu->addAction(quitAction); +#endif + notificator = new Notificator(QApplication::applicationName(), trayIcon, this); +} + +#ifndef Q_OS_MAC +void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason) +{ + if(reason == QSystemTrayIcon::Trigger) + { + // Click on system tray icon triggers show/hide of the main window + toggleHideAction->trigger(); + } +} +#endif + +void BitcoinGUI::optionsClicked() +{ + if(!clientModel || !clientModel->getOptionsModel()) + return; + + optionsDialog->setModel(clientModel->getOptionsModel()); + optionsDialog->setWindowModality(Qt::ApplicationModal); + optionsDialog->show(); +} + +void BitcoinGUI::aboutClicked() +{ + aboutDialog->setModel(clientModel); + aboutDialog->setWindowModality(Qt::ApplicationModal); + aboutDialog->show(); +} + +void BitcoinGUI::setNumConnections(int count) +{ + QString icon; + switch(count) + { + 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; + } + labelConnectionsIcon->setPixmap(QIcon(icon).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelConnectionsIcon->setToolTip(tr("%n active connection(s) to XP network", "", count)); +} + +void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks) +{ + // don't show / hide progress bar and its label if we have no connection to the network + if (!clientModel || clientModel->getNumConnections() == 0) + { + progressBarLabel->setVisible(false); + progressBar->setVisible(false); + + return; + } + + QString strStatusBarWarnings = clientModel->getStatusBarWarnings(); + QString tooltip; + + if(count < nTotalBlocks) + { + int nRemainingBlocks = nTotalBlocks - count; + float nPercentageDone = count / (nTotalBlocks * 0.01f); + + if (strStatusBarWarnings.isEmpty()) + { + progressBarLabel->setText(tr("Synchronizing with network...")); + progressBarLabel->setVisible(true); + progressBar->setFormat(tr("~%n block(s) remaining", "", nRemainingBlocks)); + progressBar->setMaximum(nTotalBlocks); + progressBar->setValue(count); + progressBar->setVisible(true); + } + + tooltip = tr("Downloaded %1 of %2 blocks of transaction history (%3% done).").arg(count).arg(nTotalBlocks).arg(nPercentageDone, 0, 'f', 2); + } + else + { + if (strStatusBarWarnings.isEmpty()) + progressBarLabel->setVisible(false); + + progressBar->setVisible(false); + tooltip = tr("Downloaded %1 blocks of transaction history.").arg(count); + } + + // Override progressBarLabel text and hide progress bar, when we have warnings to display + if (!strStatusBarWarnings.isEmpty()) + { + progressBarLabel->setText(strStatusBarWarnings); + progressBarLabel->setVisible(true); + progressBar->setVisible(false); + } + + tooltip = tr("Current PoW difficulty is %1.").arg(clientModel->getDifficulty(false)) + QString("
") + tooltip; + tooltip = tr("Current PoS difficulty is %1.").arg(clientModel->getDifficulty(true)) + QString("
") + tooltip; + + QDateTime lastBlockDate = clientModel->getLastBlockDate(); + int secs = lastBlockDate.secsTo(QDateTime::currentDateTime()); + QString text; + + // Represent time from last generated block in human readable text + if(secs <= 0) + { + // Fully up to date. Leave text empty. + } + else if(secs < 60) + { + text = tr("%n second(s) ago","",secs); + } + 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)); + } + + // Set icon state: spinning if catching up, tick otherwise + if(secs < 90*60 && count >= nTotalBlocks) + { + tooltip = tr("Up to date") + QString(".
") + tooltip; + labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + + overviewPage->showOutOfSyncWarning(false); + } + else + { + tooltip = tr("Catching up...") + QString("
") + tooltip; + labelBlocksIcon->setMovie(syncIconMovie); + syncIconMovie->start(); + + overviewPage->showOutOfSyncWarning(true); + } + + if(!text.isEmpty()) + { + tooltip += QString("
"); + tooltip += tr("Last received block was generated %1.").arg(text); + } + + // Don't word-wrap this (fixed-width) tooltip + tooltip = QString("") + tooltip + QString(""); + + labelBlocksIcon->setToolTip(tooltip); + progressBarLabel->setToolTip(tooltip); + progressBar->setToolTip(tooltip); +} + +void BitcoinGUI::updateMining() +{ + if(!walletModel) + return; + + labelMiningIcon->setPixmap(QIcon(":/icons/mining_inactive").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + + if (!clientModel->getNumConnections()) + { + labelMiningIcon->setToolTip(tr("Wallet is offline")); + return; + } + + if (walletModel->getEncryptionStatus() == WalletModel::Locked) + { + labelMiningIcon->setToolTip(tr("Wallet is locked")); + return; + } + + if (clientModel->inInitialBlockDownload() || clientModel->getNumBlocksOfPeers() > clientModel->getNumBlocks()) + { + labelMiningIcon->setToolTip(tr("Blockchain download is in progress")); + return; + } + + if (nStakeInputsMapSize > 0) + { + labelMiningIcon->setPixmap(QIcon(":/icons/mining_active").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + + uint64_t nNetworkWeight = clientModel->getPoSKernelPS(); + + labelMiningIcon->setToolTip(QString("")+tr("Stake miner is active
%1 inputs being used for mining
Network weight is %3").arg(nStakeInputsMapSize).arg(nNetworkWeight)+QString("<\nobr>")); + } + else + labelMiningIcon->setToolTip(tr("No suitable inputs were found")); +} + +void BitcoinGUI::message(const QString &title, const QString &message, unsigned int style, const QString &detail) +{ + QString strTitle = tr("XP") + " - "; + // Default to information icon + int nMBoxIcon = QMessageBox::Information; + int nNotifyIcon = Notificator::Information; + + + // Check for usage of predefined title + switch (style) { + case CClientUIInterface::MSG_ERROR: + strTitle += tr("Error"); + break; + case CClientUIInterface::MSG_WARNING: + strTitle += tr("Warning"); + break; + case CClientUIInterface::MSG_INFORMATION: + strTitle += tr("Information"); + break; + default: + strTitle += title; // Use supplied title + } + + // Check for error/warning icon + if (style & CClientUIInterface::ICON_ERROR) { + nMBoxIcon = QMessageBox::Critical; + nNotifyIcon = Notificator::Critical; + } + else if (style & CClientUIInterface::ICON_WARNING) { + nMBoxIcon = QMessageBox::Warning; + nNotifyIcon = Notificator::Warning; + } + + // Display message + if (style & CClientUIInterface::MODAL) { + // Check for buttons, use OK as default, if none was supplied + QMessageBox::StandardButton buttons; + buttons = QMessageBox::Ok; + + QMessageBox mBox((QMessageBox::Icon)nMBoxIcon, strTitle, message, buttons); + + if(!detail.isEmpty()) { mBox.setDetailedText(detail); } + + mBox.exec(); + } + else + notificator->notify((Notificator::Class)nNotifyIcon, strTitle, message); +} + + +void BitcoinGUI::changeEvent(QEvent *e) +{ + QMainWindow::changeEvent(e); +#ifndef Q_OS_MAC // Ignored on Mac + if(e->type() == QEvent::WindowStateChange) + { + if(clientModel && clientModel->getOptionsModel()->getMinimizeToTray()) + { + QWindowStateChangeEvent *wsevt = static_cast(e); + if(!(wsevt->oldState() & Qt::WindowMinimized) && isMinimized()) + { + QTimer::singleShot(0, this, SLOT(hide())); + e->ignore(); + } + } + } +#endif +} + +void BitcoinGUI::closeEvent(QCloseEvent *event) +{ + if(clientModel) + { +#ifndef Q_OS_MAC // Ignored on Mac + if(!clientModel->getOptionsModel()->getMinimizeOnClose()) + { + qApp->quit(); + } +#endif + } + // close rpcConsole in case it was open to make some space for the shutdown window + rpcConsole->close(); + + 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( + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nFeeRequired)); + QMessageBox::StandardButton retval = QMessageBox::question( + this, tr("Confirm transaction fee"), strMessage, + QMessageBox::Yes|QMessageBox::Cancel, QMessageBox::Yes); + *payFee = (retval == QMessageBox::Yes); +} + +void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int end) +{ + if(!walletModel || !clientModel) + return; + TransactionTableModel *ttm = walletModel->getTransactionTableModel(); + qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent) + .data(Qt::EditRole).toULongLong(); + if(!clientModel->inInitialBlockDownload()) + { + // 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(); + QString type = ttm->index(start, TransactionTableModel::Type, parent) + .data().toString(); + QString address = ttm->index(start, TransactionTableModel::ToAddress, parent) + .data().toString(); + QIcon icon = qvariant_cast(ttm->index(start, + TransactionTableModel::ToAddress, parent) + .data(Qt::DecorationRole)); + + notificator->notify(Notificator::Information, + (amount)<0 ? tr("Sent transaction") : + tr("Incoming transaction"), + tr("Date: %1\n" + "Amount: %2\n" + "Type: %3\n" + "Address: %4\n") + .arg(date) + .arg(BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), amount, true)) + .arg(type) + .arg(address), icon); + } +} + +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::gotoMintingPage() +{ + mintingAction->setChecked(true); + centralWidget->setCurrentWidget(mintingPage); + + exportAction->setEnabled(true); + disconnect(exportAction, SIGNAL(triggered()), 0, 0); + connect(exportAction, SIGNAL(triggered()), mintingView, SLOT(exportClicked())); +} + + +void BitcoinGUI::gotoAddressBookPage() +{ + addressBookAction->setChecked(true); + centralWidget->setCurrentWidget(addressBookPage); + + 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(true); + disconnect(exportAction, SIGNAL(triggered()), 0, 0); + connect(exportAction, SIGNAL(triggered()), receiveCoinsPage, SLOT(exportClicked())); +} + +void BitcoinGUI::gotoSendCoinsPage() +{ + sendCoinsAction->setChecked(true); + centralWidget->setCurrentWidget(sendCoinsPage); + + exportAction->setEnabled(false); + disconnect(exportAction, SIGNAL(triggered()), 0, 0); +} + +void BitcoinGUI::gotoSignMessageTab(QString addr) +{ + // call show() in showTab_SM() + signVerifyMessageDialog->showTab_SM(true); + + if(!addr.isEmpty()) + signVerifyMessageDialog->setAddress_SM(addr); +} + +void BitcoinGUI::gotoVerifyMessageTab(QString addr) +{ + // call show() in showTab_VM() + signVerifyMessageDialog->showTab_VM(true); + + if(!addr.isEmpty()) + signVerifyMessageDialog->setAddress_VM(addr); +} + +void BitcoinGUI::gotoSecondAuthPage(QString addr) +{ + secondAuthDialog->show(); + secondAuthDialog->raise(); + secondAuthDialog->activateWindow(); +} + +void BitcoinGUI::gotoMultisigPage() +{ + multisigPage->show(); + multisigPage->raise(); + multisigPage->activateWindow(); +} + +void BitcoinGUI::dragEnterEvent(QDragEnterEvent *event) +{ + // Accept only URIs + if(event->mimeData()->hasUrls()) + event->acceptProposedAction(); +} + +void BitcoinGUI::dropEvent(QDropEvent *event) +{ + if(event->mimeData()->hasUrls()) + { + int nValidUrisFound = 0; + QList uris = event->mimeData()->urls(); + foreach(const QUrl &uri, uris) + { + if (sendCoinsPage->handleURI(uri.toString())) + nValidUrisFound++; + } + + // if valid URIs were found + if (nValidUrisFound) + gotoSendCoinsPage(); + else + notificator->notify(Notificator::Warning, tr("URI handling"), tr("URI can not be parsed! This can be caused by an invalid XP address or malformed URI parameters.")); + } + + event->acceptProposedAction(); +} + +void BitcoinGUI::handleURI(QString strURI) +{ + // URI has to be valid + if (sendCoinsPage->handleURI(strURI)) + { + showNormalIfMinimized(); + gotoSendCoinsPage(); + } + else + notificator->notify(Notificator::Warning, tr("URI handling"), tr("URI can not be parsed! This can be caused by an invalid XP address or malformed URI parameters.")); +} + +void BitcoinGUI::setEncryptionStatus(int status) +{ + switch(status) + { + case WalletModel::Unencrypted: + labelEncryptionIcon->hide(); + encryptWalletAction->setChecked(false); + changePassphraseAction->setEnabled(false); + lockWalletAction->setEnabled(false); + unlockWalletAction->setEnabled(false); + unlockWalletMiningAction->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(true); + + lockWalletAction->setEnabled(true); + lockWalletAction->setChecked(false); + unlockWalletAction->setEnabled(false); + unlockWalletMiningAction->setEnabled(false); + + if (fWalletUnlockMintOnly) + unlockWalletMiningAction->setChecked(true); + else + unlockWalletAction->setChecked(true); + + 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(true); + + lockWalletAction->setChecked(true); + unlockWalletAction->setChecked(false); + unlockWalletMiningAction->setChecked(false); + + lockWalletAction->setEnabled(false); + unlockWalletAction->setEnabled(true); + unlockWalletMiningAction->setEnabled(true); + break; + } +} + +void BitcoinGUI::encryptWallet(bool status) +{ + if(!walletModel) + return; + AskPassphraseDialog dlg(status ? AskPassphraseDialog::Encrypt: + AskPassphraseDialog::Decrypt, this); + dlg.setModel(walletModel); + dlg.exec(); + + setEncryptionStatus(walletModel->getEncryptionStatus()); +} + +void BitcoinGUI::unlockWalletMining(bool status) +{ + if(!walletModel) + return; + + // Unlock wallet when requested by wallet model + if(walletModel->getEncryptionStatus() == WalletModel::Locked) + { + AskPassphraseDialog dlg(AskPassphraseDialog::UnlockMining, this); + dlg.setModel(walletModel); + dlg.exec(); + } +} + +void BitcoinGUI::backupWallet() +{ +#if QT_VERSION < 0x050000 + QString saveDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); +#else + QString saveDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); +#endif + QString filename = QFileDialog::getSaveFileName(this, tr("Backup Wallet"), saveDir, tr("Wallet Data (*.dat)")); + if(!filename.isEmpty()) { + if(!walletModel->backupWallet(filename)) { + QMessageBox::warning(this, tr("Backup Failed"), tr("There was an error trying to save the wallet data to the new location.")); + } + } +} + +void BitcoinGUI::dumpWallet() +{ + if(!walletModel) + return; + + WalletModel::UnlockContext ctx(walletModel->requestUnlock()); + if(!ctx.isValid()) + { + // Unlock wallet failed or was cancelled + return; + } + +#if QT_VERSION < 0x050000 + QString saveDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); +#else + QString saveDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); +#endif + QString filename = QFileDialog::getSaveFileName(this, tr("Dump Wallet"), saveDir, tr("Wallet dump (*.txt)")); + if(!filename.isEmpty()) { + if(!walletModel->dumpWallet(filename)) { + message(tr("Dump failed"), + tr("An error happened while trying to save the keys to your location.\n" + "Keys were not saved.") + ,CClientUIInterface::MSG_ERROR); + } + else + message(tr("Dump successful"), + tr("Keys were saved to this file:\n%2") + .arg(filename) + ,CClientUIInterface::MSG_INFORMATION); + } +} + +void BitcoinGUI::importWallet() +{ + if(!walletModel) + return; + + WalletModel::UnlockContext ctx(walletModel->requestUnlock()); + if(!ctx.isValid()) + { + // Unlock wallet failed or was cancelled + return; + } + +#if QT_VERSION < 0x050000 + QString openDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); +#else + QString openDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); +#endif + QString filename = QFileDialog::getOpenFileName(this, tr("Import Wallet"), openDir, tr("Wallet dump (*.txt)")); + if(!filename.isEmpty()) { + if(!walletModel->importWallet(filename)) { + message(tr("Import Failed"), + tr("An error happened while trying to import the keys.\n" + "Some or all keys from:\n %1,\n were not imported into your wallet.") + .arg(filename) + ,CClientUIInterface::MSG_ERROR); + } + else + message(tr("Import Successful"), + tr("All keys from:\n %1,\n were imported into your wallet.") + .arg(filename) + ,CClientUIInterface::MSG_INFORMATION); + } +} + + +void BitcoinGUI::changePassphrase() +{ + AskPassphraseDialog dlg(AskPassphraseDialog::ChangePass, this); + dlg.setModel(walletModel); + dlg.exec(); +} + +void BitcoinGUI::unlockWallet() +{ + if(!walletModel) + return; + // Unlock wallet when requested by wallet model + if(walletModel->getEncryptionStatus() == WalletModel::Locked) + { + AskPassphraseDialog dlg(AskPassphraseDialog::Unlock, this); + dlg.setModel(walletModel); + dlg.exec(); + } +} + +void BitcoinGUI::lockWallet() +{ + if(!walletModel) + return; + + walletModel->setWalletLocked(true); +} + +void BitcoinGUI::showNormalIfMinimized(bool fToggleHidden) +{ + // activateWindow() (sometimes) helps with keyboard focus on Windows + if (isHidden()) + { + // Make sure the window is not minimized + setWindowState(windowState() & (~Qt::WindowMinimized | Qt::WindowActive)); + // Then show it + show(); + raise(); + activateWindow(); + } + else if (isMinimized()) + { + showNormal(); + raise(); + activateWindow(); + } + else if (GUIUtil::isObscured(this)) + { + raise(); + activateWindow(); + if(fToggleHidden) + { + Sleep(1); + if (GUIUtil::isObscured(this)) + hide(); + } + } + else if(fToggleHidden) + hide(); +} + +void BitcoinGUI::toggleHidden() +{ + showNormalIfMinimized(true); +} + +void BitcoinGUI::error(const QString &title, const QString &message, bool modal) +{ + // Report errors from network/worker thread + if(modal) + { + QMessageBox::critical(this, title, message, QMessageBox::Ok, QMessageBox::Ok); + } else { + notificator->notify(Notificator::Critical, title, message); + } +} diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h new file mode 100644 index 00000000..20eb7ca1 --- /dev/null +++ b/src/qt/bitcoingui.h @@ -0,0 +1,216 @@ +#ifndef BITCOINGUI_H +#define BITCOINGUI_H + +#include +#include + +class TransactionTableModel; +class ClientModel; +class WalletModel; +class TransactionView; +class MintingView; +class OverviewPage; +class AddressBookPage; +class SendCoinsDialog; +class SignVerifyMessageDialog; +class SecondAuthDialog; +class MultisigDialog; +class Notificator; +class RPCConsole; +class AboutDialog; +class OptionsDialog; + +QT_BEGIN_NAMESPACE +class QLabel; +class QLineEdit; +class QTableView; +class QAbstractItemModel; +class QModelIndex; +class QProgressBar; +class QStackedWidget; +class QUrl; +QT_END_NAMESPACE + +/** + Bitcoin GUI main class. This class represents the main window of the Bitcoin UI. It communicates with both the client and + wallet models to give the user an up-to-date view of the current core state. +*/ +class BitcoinGUI : public QMainWindow +{ + Q_OBJECT +public: + explicit BitcoinGUI(QWidget *parent = 0); + ~BitcoinGUI(); + + /** Set the client model. + The client model represents the part of the core that communicates with the P2P network, and is wallet-agnostic. + */ + void setClientModel(ClientModel *clientModel); + /** Set the wallet model. + The wallet model represents a bitcoin wallet, and offers access to the list of transactions, address book and sending + functionality. + */ + void setWalletModel(WalletModel *walletModel); + +protected: + void changeEvent(QEvent *e); + void closeEvent(QCloseEvent *event); + void dragEnterEvent(QDragEnterEvent *event); + void dropEvent(QDropEvent *event); + +private: + ClientModel *clientModel; + WalletModel *walletModel; + + QStackedWidget *centralWidget; + + OverviewPage *overviewPage; + QWidget *transactionsPage; + QWidget *mintingPage; + AddressBookPage *addressBookPage; + AddressBookPage *receiveCoinsPage; + SendCoinsDialog *sendCoinsPage; + SignVerifyMessageDialog *signVerifyMessageDialog; + SecondAuthDialog *secondAuthDialog; + MultisigDialog *multisigPage; + + QLabel *labelEncryptionIcon; + QLabel *labelConnectionsIcon; + QLabel *labelBlocksIcon; + QLabel *labelMiningIcon; + QLabel *progressBarLabel; + QProgressBar *progressBar; + + QMenuBar *appMenuBar; + QAction *overviewAction; + QAction *historyAction; + QAction *mintingAction; + QAction *quitAction; + QAction *sendCoinsAction; + QAction *addressBookAction; + QAction *signMessageAction; + QAction *verifyMessageAction; + QAction *secondAuthAction; + QAction *multisigAction; + QAction *aboutAction; + QAction *receiveCoinsAction; + QAction *optionsAction; + QAction *toggleHideAction; + QAction *exportAction; + QAction *encryptWalletAction; + QAction *lockWalletAction; + QAction *unlockWalletAction; + QAction *unlockWalletMiningAction; + QAction *backupWalletAction; + QAction *dumpWalletAction; + QAction *importWalletAction; + QAction *changePassphraseAction; + QAction *aboutQtAction; + QAction *openRPCConsoleAction; + + QSystemTrayIcon *trayIcon; + Notificator *notificator; + TransactionView *transactionView; + MintingView *mintingView; + RPCConsole *rpcConsole; + AboutDialog *aboutDialog; + OptionsDialog *optionsDialog; + + QMovie *syncIconMovie; + + /** Create the main UI actions. */ + void createActions(); + /** Create the menu bar and sub-menus. */ + void createMenuBar(); + /** Create the toolbars */ + void createToolBars(); + /** Create system tray (notification) icon */ + void createTrayIcon(); + +public slots: + /** Set number of connections shown in the UI */ + void setNumConnections(int count); + /** Set number of blocks shown in the UI */ + void setNumBlocks(int count, int nTotalBlocks); + /** Set stake miner status in the UI */ + void updateMining(); + /** Set the encryption status as shown in the UI. + @param[in] status current encryption status + @see WalletModel::EncryptionStatus + */ + void setEncryptionStatus(int status); + + /** Notify the user of an error in the network or transaction handling code. */ + void error(const QString &title, const QString &message, bool modal); + void message(const QString &title, const QString &message, unsigned int style, const QString &detail=QString()); + + /** Asks the user whether to pay the transaction fee or to cancel the transaction. + It is currently not possible to pass a return value to another thread through + BlockingQueuedConnection, so an indirected pointer is used. + https://bugreports.qt-project.org/browse/QTBUG-10440 + + @param[in] nFeeRequired the required fee + @param[out] payFee true to pay the fee, false to not pay the fee + */ + void askFee(qint64 nFeeRequired, bool *payFee); + void handleURI(QString strURI); + + void gotoMultisigPage(); + +private slots: + /** Switch to overview (home) page */ + void gotoOverviewPage(); + /** Switch to history (transactions) page */ + void gotoHistoryPage(); + /** Switch to minting page */ + void gotoMintingPage(); + /** Switch to address book page */ + void gotoAddressBookPage(); + /** Switch to receive coins page */ + void gotoReceiveCoinsPage(); + /** Switch to send coins page */ + void gotoSendCoinsPage(); + + /** Show Sign/Verify Message dialog and switch to sign message tab */ + void gotoSignMessageTab(QString addr = ""); + /** Show Sign/Verify Message dialog and switch to verify message tab */ + void gotoVerifyMessageTab(QString addr = ""); + + /** Show Second Auth dialog */ + void gotoSecondAuthPage(QString addr = ""); + + /** Show configuration dialog */ + void optionsClicked(); + /** Show about dialog */ + void aboutClicked(); +#ifndef Q_OS_MAC + /** Handle tray icon clicked */ + void trayIconActivated(QSystemTrayIcon::ActivationReason reason); +#endif + /** Show incoming transaction notification for new transactions. + + The new items are those between start and end inclusive, under the given parent item. + */ + void incomingTransaction(const QModelIndex & parent, int start, int end); + /** Encrypt the wallet */ + void encryptWallet(bool status); + /** Backup the wallet */ + void backupWallet(); + /** Change encrypted wallet passphrase */ + + void dumpWallet(); + void importWallet(); + + void changePassphrase(); + /** Ask for passphrase to unlock wallet temporarily */ + void lockWallet(); + void unlockWallet(); + void unlockWalletMining(bool status); + + /** Show window if hidden, unminimize when minimized, rise when obscured or show if hidden and fToggleHidden is true */ + void showNormalIfMinimized(bool fToggleHidden = false); + /** simply calls showNormalIfMinimized(true) for use in SLOT() macro */ + void toggleHidden(); +}; + +#endif diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp new file mode 100644 index 00000000..7be89fba --- /dev/null +++ b/src/qt/bitcoinstrings.cpp @@ -0,0 +1,182 @@ +#include +// Automatically generated by extract_strings.py +#ifdef __GNUC__ +#define UNUSED __attribute__((unused)) +#else +#define UNUSED +#endif +static const char UNUSED *bitcoin_strings[] = { +QT_TRANSLATE_NOOP("bitcoin-core", "" +"%s, you must set a rpcpassword in the configuration file:\n" +" %s\n" +"It is recommended you use the following random password:\n" +"rpcuser=XPrpc\n" +"rpcpassword=%s\n" +"(you do not need to remember this password)\n" +"If the file does not exist, create it with owner-readable-only file " +"permissions.\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:" +"@STRENGTH)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"An error occurred while setting up the RPC port %u for listening on IPv4: %s"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"An error occurred while setting up the RPC port %u for listening on IPv6, " +"falling back to IPv4: %s"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Cannot obtain a lock on data directory %s. XP is probably already " +"running."), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Detach block and address databases. Increases shutdown time (default: 0)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Error initializing database environment %s! To recover, BACKUP THAT " +"DIRECTORY, then remove everything from it except for wallet.dat."), +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", "" +"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: Wallet unlocked for block minting only, unable to create transaction."), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Execute command when the best block changes (%s in cmd is replaced by block " +"hash)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Listen for JSON-RPC connections on (default: 28191 or testnet: 18345)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Number of seconds to keep misbehaving peers from reconnecting (default: " +"86400)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Set maximum size of high-priority/low-fee transactions in bytes (default: " +"27000)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Unable to bind to %s on this computer. XP is probably already running."), +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: Please check that your computer's date and time are correct! If " +"your clock is wrong XP will not work properly."), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Warning: error reading wallet.dat! All keys read correctly, but transaction " +"data or address book entries might be missing or incorrect."), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as " +"wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect " +"you should restore from a backup."), +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", "Accept command line and JSON-RPC commands"), +QT_TRANSLATE_NOOP("bitcoin-core", "Accept connections from outside (default: 1 if no -proxy or -connect)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Add a node to connect to and attempt to keep the connection open"), +QT_TRANSLATE_NOOP("bitcoin-core", "Allow DNS lookups for -addnode, -seednode and -connect"), +QT_TRANSLATE_NOOP("bitcoin-core", "Allow JSON-RPC connections from specified IP address"), +QT_TRANSLATE_NOOP("bitcoin-core", "Attempt to recover private keys from a corrupt wallet.dat"), +QT_TRANSLATE_NOOP("bitcoin-core", "Bind to given address. Use [host]:port notation for IPv6"), +QT_TRANSLATE_NOOP("bitcoin-core", "Block creation options:"), +QT_TRANSLATE_NOOP("bitcoin-core", "Cannot downgrade wallet"), +QT_TRANSLATE_NOOP("bitcoin-core", "Cannot initialize keypool"), +QT_TRANSLATE_NOOP("bitcoin-core", "Cannot resolve -bind address: '%s'"), +QT_TRANSLATE_NOOP("bitcoin-core", "Cannot resolve -externalip address: '%s'"), +QT_TRANSLATE_NOOP("bitcoin-core", "Cannot write default address"), +QT_TRANSLATE_NOOP("bitcoin-core", "Connect only to the specified node(s)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Connect through socks proxy"), +QT_TRANSLATE_NOOP("bitcoin-core", "Connect to a node to retrieve peer addresses, and disconnect"), +QT_TRANSLATE_NOOP("bitcoin-core", "Discover own IP address (default: 1 when listening and no -externalip)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Done loading"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error loading blkindex.dat"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error loading wallet.dat"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error loading wallet.dat: Wallet corrupted"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error loading wallet.dat: Wallet requires newer version of XP"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error: Transaction creation failed "), +QT_TRANSLATE_NOOP("bitcoin-core", "Error: Wallet locked, unable to create transaction "), +QT_TRANSLATE_NOOP("bitcoin-core", "Error: could not start node"), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to listen on any port. Use -listen=0 if you want this."), +QT_TRANSLATE_NOOP("bitcoin-core", "Fee per KB to add to transactions you send"), +QT_TRANSLATE_NOOP("bitcoin-core", "Find peers using DNS lookup (default: 0)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Find peers using internet relay chat (default: 1)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Get help for a command"), +QT_TRANSLATE_NOOP("bitcoin-core", "How many blocks to check at startup (default: 2500, 0 = all)"), +QT_TRANSLATE_NOOP("bitcoin-core", "How thorough the block verification is (0-6, default: 1)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Importing blockchain data file."), +QT_TRANSLATE_NOOP("bitcoin-core", "Importing bootstrap blockchain data file."), +QT_TRANSLATE_NOOP("bitcoin-core", "Imports blocks from external blk000?.dat file"), +QT_TRANSLATE_NOOP("bitcoin-core", "Insufficient funds"), +QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -proxy address: '%s'"), +QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -tor address: '%s'"), +QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -paytxfee=: '%s'"), +QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -reservebalance="), +QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount"), +QT_TRANSLATE_NOOP("bitcoin-core", "List commands"), +QT_TRANSLATE_NOOP("bitcoin-core", "Listen for connections on (default: 28192 or testnet: 17778)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Loading addresses..."), +QT_TRANSLATE_NOOP("bitcoin-core", "Loading block index..."), +QT_TRANSLATE_NOOP("bitcoin-core", "Loading wallet..."), +QT_TRANSLATE_NOOP("bitcoin-core", "Maintain at most connections to peers (default: 125)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Maximum per-connection receive buffer, *1000 bytes (default: 5000)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Maximum per-connection send buffer, *1000 bytes (default: 1000)"), +QT_TRANSLATE_NOOP("bitcoin-core", "XP version"), +QT_TRANSLATE_NOOP("bitcoin-core", "XP"), +QT_TRANSLATE_NOOP("bitcoin-core", "Only connect to nodes in network (IPv4, IPv6 or Tor)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Options:"), +QT_TRANSLATE_NOOP("bitcoin-core", "Output extra debugging information. Implies all other -debug* options"), +QT_TRANSLATE_NOOP("bitcoin-core", "Output extra network debugging information"), +QT_TRANSLATE_NOOP("bitcoin-core", "Password for JSON-RPC connections"), +QT_TRANSLATE_NOOP("bitcoin-core", "Prepend debug output with timestamp"), +QT_TRANSLATE_NOOP("bitcoin-core", "Rescan the block chain for missing wallet transactions"), +QT_TRANSLATE_NOOP("bitcoin-core", "Rescanning..."), +QT_TRANSLATE_NOOP("bitcoin-core", "Run in the background as a daemon and accept commands"), +QT_TRANSLATE_NOOP("bitcoin-core", "SSL options: (see the Bitcoin Wiki for SSL setup instructions)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Select the version of socks proxy to use (4-5, default: 5)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Send command to -server or XPd"), +QT_TRANSLATE_NOOP("bitcoin-core", "Send commands to node running on (default: 127.0.0.1)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Send trace/debug info to console instead of debug.log file"), +QT_TRANSLATE_NOOP("bitcoin-core", "Send trace/debug info to debugger"), +QT_TRANSLATE_NOOP("bitcoin-core", "Sending..."), +QT_TRANSLATE_NOOP("bitcoin-core", "Server certificate file (default: server.cert)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Server private key (default: server.pem)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Set database cache size in megabytes (default: 25)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Set database disk log size in megabytes (default: 100)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Set key pool size to (default: 100)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Set maximum block size in bytes (default: 250000)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Set minimum block size in bytes (default: 0)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Shrink debug.log file on client startup (default: 1 when no -debug)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Specify configuration file (default: XP.conf)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Specify connection timeout in milliseconds (default: 5000)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Specify data directory"), +QT_TRANSLATE_NOOP("bitcoin-core", "Specify pid file (default: XPd.pid)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Specify your own public address"), +QT_TRANSLATE_NOOP("bitcoin-core", "This help message"), +QT_TRANSLATE_NOOP("bitcoin-core", "Threshold for disconnecting misbehaving peers (default: 100)"), +QT_TRANSLATE_NOOP("bitcoin-core", "To use the %s option"), +QT_TRANSLATE_NOOP("bitcoin-core", "Unable to bind to %s on this computer (bind returned error %d, %s)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Unable to sign checkpoint, wrong checkpointkey?\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Unknown -socks proxy version requested: %i"), +QT_TRANSLATE_NOOP("bitcoin-core", "Unknown network specified in -onlynet: '%s'"), +QT_TRANSLATE_NOOP("bitcoin-core", "Upgrade wallet to latest format"), +QT_TRANSLATE_NOOP("bitcoin-core", "Usage:"), +QT_TRANSLATE_NOOP("bitcoin-core", "Use OpenSSL (https) for JSON-RPC connections"), +QT_TRANSLATE_NOOP("bitcoin-core", "Use proxy to reach tor hidden services (default: same as -proxy)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Use the test network"), +QT_TRANSLATE_NOOP("bitcoin-core", "Username for JSON-RPC connections"), +QT_TRANSLATE_NOOP("bitcoin-core", "Verifying database integrity..."), +QT_TRANSLATE_NOOP("bitcoin-core", "Wallet needed to be rewritten: restart XP to complete"), +QT_TRANSLATE_NOOP("bitcoin-core", "Warning: Disk space is low!"), +QT_TRANSLATE_NOOP("bitcoin-core", "Warning: This version is obsolete, upgrade required!"), +QT_TRANSLATE_NOOP("bitcoin-core", "wallet.dat corrupt, salvage failed"), +QT_TRANSLATE_NOOP("bitcoin-core", "Specify wallet file (within data directory)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Use in-memory logging for block index database (default: 1)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Find peers using DNS lookup (default: 1)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Sync checkpoints policy (default: strict)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Require a confirmations for change (default: 0)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Enforce transaction scripts to use canonical PUSH operators (default: 1)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Set the number of script verification threads (1-16, 0=auto, default: 0)"), +QT_TRANSLATE_NOOP("bitcoin-core", "When creating transactions, ignore inputs with value less than this (default: %s)"), +}; diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp new file mode 100644 index 00000000..d50ea30f --- /dev/null +++ b/src/qt/bitcoinunits.cpp @@ -0,0 +1,191 @@ +#include "bitcoinunits.h" + +#include + +BitcoinUnits::BitcoinUnits(QObject *parent): + QAbstractListModel(parent), + unitlist(availableUnits()) +{ +} + +QList BitcoinUnits::availableUnits() +{ + QList unitlist; + unitlist.append(BTC); + unitlist.append(mBTC); + unitlist.append(uBTC); + 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) + { + case BTC: return QString("XP"); + case mBTC: return QString("mXP"); + case uBTC: return QString::fromUtf8("μXP"); + default: return QString("???"); + } +} + +QString BitcoinUnits::description(int unit) +{ + switch(unit) + { + case BTC: return QString(QObject::tr("XPs")); + case mBTC: return QString(QObject::tr("Milli-XPs (1 / 1,000)")); + case uBTC: return QString(QObject::tr("Micro-XPs (1 / 1,000,000)")); + default: return QString("???"); + } +} + +qint64 BitcoinUnits::factor(int unit) +{ + switch(unit) + { + case BTC: return 1000000; + case mBTC: return 1000; + case uBTC: return 1; + default: return 1000000; + } +} + +int BitcoinUnits::amountDigits(int unit) +{ + switch(unit) + { + case BTC: return 12; // 210,000,000 (# digits, without commas) + case mBTC: return 15; // 210,000,000,000 + case uBTC: return 18; // 210,000,000,000,000 + default: return 0; + } +} + +int BitcoinUnits::decimals(int unit) +{ + switch(unit) + { + case BTC: return 6; + case mBTC: return 3; + case uBTC: return 0; + default: return 0; + } +} + +QString BitcoinUnits::format(int unit, qint64 n, bool fPlus, uint8_t nNumberOfZeros) +{ + // 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); + 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 zeros after the decimal point + int nTrim = 0; + for (int i = remainder_str.size()-1; i>=nNumberOfZeros && (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(int unit, qint64 amount, bool plussign, uint8_t nNumberOfZeros) +{ + return format(unit, amount, plussign, nNumberOfZeros) + QString(" ") + name(unit); +} + +bool BitcoinUnits::parse(int unit, const QString &value, qint64 *val_out) +{ + 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) + { + 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 = 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) + { + *val_out = retvalue; + } + 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(); +} + +QString BitcoinUnits::getAmountColumnTitle(int unit) +{ + QString amountTitle = QObject::tr("Amount"); + if (BitcoinUnits::valid(unit)) + { + amountTitle += " ("+BitcoinUnits::name(unit) + ")"; + } + return amountTitle; +} \ No newline at end of file diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h new file mode 100644 index 00000000..5ed0c64c --- /dev/null +++ b/src/qt/bitcoinunits.h @@ -0,0 +1,69 @@ +#ifndef BITCOINUNITS_H +#define BITCOINUNITS_H + +#include +#include + +#include +/** Bitcoin unit definitions. Encapsulates parsing and formatting + and serves as list model for drop-down selection boxes. +*/ +class BitcoinUnits: public QAbstractListModel +{ +public: + explicit BitcoinUnits(QObject *parent); + + /** Bitcoin units. + @note Source: https://en.bitcoin.it/wiki/Units . Please add only sensible ones + */ + enum Unit + { + BTC, + mBTC, + uBTC + }; + + //! @name Static API + //! Unit conversion and formatting + ///@{ + + //! Get list of units, for drop-down box + static QList availableUnits(); + //! Is unit ID valid? + static bool valid(int unit); + //! Short name + static QString name(int unit); + //! Longer description + static QString description(int unit); + //! Number of Satoshis (1e-8) per 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(int unit); + //! Format as string + static QString format(int unit, qint64 amount, bool plussign=false, uint8_t nNumberOfZeros=2); + //! Format as string (with unit) + static QString formatWithUnit(int unit, qint64 amount, bool plussign=false, uint8_t nNumberOfZeros=2); + //! Parse string to coin amount + static bool parse(int unit, const QString &value, qint64 *val_out); + //! Gets title for amount column including current display unit if optionsModel reference available */ + static QString getAmountColumnTitle(int unit); + ///@} + + //! @name AbstractListModel implementation + //! List model for unit drop-down selection box. + ///@{ + enum RoleIndex { + /** Unit identifier */ + UnitRole = Qt::UserRole + }; + 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/clientmodel.cpp b/src/qt/clientmodel.cpp new file mode 100644 index 00000000..ec66cc30 --- /dev/null +++ b/src/qt/clientmodel.cpp @@ -0,0 +1,217 @@ +#include "clientmodel.h" +#include "guiconstants.h" +#include "optionsmodel.h" +#include "addresstablemodel.h" +#include "transactiontablemodel.h" + +#include "alert.h" +#include "main.h" +#include "ui_interface.h" + +#include +#include + +extern double GetPoSKernelPS(); +extern double GetDifficulty(const CBlockIndex* blockindex); + +static const int64_t nClientStartupTime = GetTime(); + +ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) : + QObject(parent), optionsModel(optionsModel), + cachedNumBlocks(0), cachedNumBlocksOfPeers(0), pollTimer(0) +{ + numBlocksAtStartup = -1; + + pollTimer = new QTimer(this); + pollTimer->setInterval(MODEL_UPDATE_DELAY); + pollTimer->start(); + connect(pollTimer, SIGNAL(timeout()), this, SLOT(updateTimer())); + + subscribeToCoreSignals(); +} + +ClientModel::~ClientModel() +{ + unsubscribeFromCoreSignals(); +} + +double ClientModel::getPoSKernelPS() +{ + return GetPoSKernelPS(); +} + +double ClientModel::getDifficulty(bool fProofofStake) +{ + if (fProofofStake) + return GetDifficulty(GetLastBlockIndex(pindexBest,true)); + else + return GetDifficulty(GetLastBlockIndex(pindexBest,false)); +} + +int ClientModel::getNumConnections(uint8_t flags) const +{ + LOCK(cs_vNodes); + if (flags == CONNECTIONS_ALL) // Shortcut if we want total + return (int)(vNodes.size()); + + int nNum = 0; + BOOST_FOREACH(CNode* pnode, vNodes) + if (flags & (pnode->fInbound ? CONNECTIONS_IN : CONNECTIONS_OUT)) + nNum++; + + return nNum; +} + +int ClientModel::getNumBlocks() const +{ + return nBestHeight; +} + +int ClientModel::getNumBlocksAtStartup() +{ + if (numBlocksAtStartup == -1) numBlocksAtStartup = getNumBlocks(); + return numBlocksAtStartup; +} + +quint64 ClientModel::getTotalBytesRecv() const +{ + return CNode::GetTotalBytesRecv(); +} + +quint64 ClientModel::getTotalBytesSent() const +{ + return CNode::GetTotalBytesSent(); +} + +QDateTime ClientModel::getLastBlockDate() const +{ + if (pindexBest) + return QDateTime::fromTime_t(pindexBest->GetBlockTime()); + else + return QDateTime::fromTime_t(1360105017); // Genesis block's time +} + +void ClientModel::updateTimer() +{ + // Some quantities (such as number of blocks) change so fast that we don't want to be notified for each change. + // Periodically check and update with a timer. + int newNumBlocks = getNumBlocks(); + int newNumBlocksOfPeers = getNumBlocksOfPeers(); + + if(cachedNumBlocks != newNumBlocks || cachedNumBlocksOfPeers != newNumBlocksOfPeers) + { + cachedNumBlocks = newNumBlocks; + cachedNumBlocksOfPeers = newNumBlocksOfPeers; + + emit numBlocksChanged(newNumBlocks, newNumBlocksOfPeers); + } + + emit bytesChanged(getTotalBytesRecv(), getTotalBytesSent()); +} + +void ClientModel::updateNumConnections(int numConnections) +{ + emit numConnectionsChanged(numConnections); +} + +void ClientModel::updateAlert(const QString &hash, int status) +{ + // Show error message notification for new alert + if(status == CT_NEW) + { + uint256 hash_256; + hash_256.SetHex(hash.toStdString()); + CAlert alert = CAlert::getAlertByHash(hash_256); + if(!alert.IsNull()) + { + emit error(tr("Network Alert"), QString::fromStdString(alert.strStatusBar), false); + } + } + + // Emit a numBlocksChanged when the status message changes, + // so that the view recomputes and updates the status bar. + emit numBlocksChanged(getNumBlocks(), getNumBlocksOfPeers()); +} + +bool ClientModel::isTestNet() const +{ + return fTestNet; +} + +bool ClientModel::inInitialBlockDownload() const +{ + return IsInitialBlockDownload(); +} + +int ClientModel::getNumBlocksOfPeers() const +{ + return GetNumBlocksOfPeers(); +} + +QString ClientModel::getStatusBarWarnings() const +{ + return QString::fromStdString(GetWarnings("statusbar")); +} + +OptionsModel *ClientModel::getOptionsModel() +{ + return optionsModel; +} + +QString ClientModel::formatFullVersion() const +{ + return QString::fromStdString(FormatFullVersion()); +} + +QString ClientModel::formatBuildDate() const +{ + return QString::fromStdString(CLIENT_DATE); +} + +QString ClientModel::clientName() const +{ + return QString::fromStdString(CLIENT_NAME); +} + +QString ClientModel::formatClientStartupTime() const +{ + return QDateTime::fromTime_t(nClientStartupTime).toString(); +} + +// Handlers for core signals +static void NotifyBlocksChanged(ClientModel *clientmodel) +{ + // This notification is too frequent. Don't trigger a signal. + // Don't remove it, though, as it might be useful later. +} + +static void NotifyNumConnectionsChanged(ClientModel *clientmodel, int newNumConnections) +{ + // Too noisy: OutputDebugStringF("NotifyNumConnectionsChanged %i\n", newNumConnections); + QMetaObject::invokeMethod(clientmodel, "updateNumConnections", Qt::QueuedConnection, + Q_ARG(int, newNumConnections)); +} + +static void NotifyAlertChanged(ClientModel *clientmodel, const uint256 &hash, ChangeType status) +{ + OutputDebugStringF("NotifyAlertChanged %s status=%i\n", hash.GetHex().c_str(), status); + QMetaObject::invokeMethod(clientmodel, "updateAlert", Qt::QueuedConnection, + Q_ARG(QString, QString::fromStdString(hash.GetHex())), + Q_ARG(int, status)); +} + +void ClientModel::subscribeToCoreSignals() +{ + // Connect signals to client + uiInterface.NotifyBlocksChanged.connect(boost::bind(NotifyBlocksChanged, this)); + uiInterface.NotifyNumConnectionsChanged.connect(boost::bind(NotifyNumConnectionsChanged, this, _1)); + uiInterface.NotifyAlertChanged.connect(boost::bind(NotifyAlertChanged, this, _1, _2)); +} + +void ClientModel::unsubscribeFromCoreSignals() +{ + // Disconnect signals from client + uiInterface.NotifyBlocksChanged.disconnect(boost::bind(NotifyBlocksChanged, this)); + uiInterface.NotifyNumConnectionsChanged.disconnect(boost::bind(NotifyNumConnectionsChanged, this, _1)); + uiInterface.NotifyAlertChanged.disconnect(boost::bind(NotifyAlertChanged, this, _1, _2)); +} diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h new file mode 100644 index 00000000..cf3e30e7 --- /dev/null +++ b/src/qt/clientmodel.h @@ -0,0 +1,88 @@ +#ifndef CLIENTMODEL_H +#define CLIENTMODEL_H + +#include + +#include + +class OptionsModel; +class AddressTableModel; +class TransactionTableModel; +class CWallet; + +QT_BEGIN_NAMESPACE +class QDateTime; +class QTimer; +QT_END_NAMESPACE + +enum NumConnections { + CONNECTIONS_NONE = 0, + CONNECTIONS_IN = (1U << 0), + CONNECTIONS_OUT = (1U << 1), + CONNECTIONS_ALL = (CONNECTIONS_IN | CONNECTIONS_OUT), +}; + +/** Model for Bitcoin network client. */ +class ClientModel : public QObject +{ + Q_OBJECT +public: + explicit ClientModel(OptionsModel *optionsModel, QObject *parent = 0); + ~ClientModel(); + + OptionsModel *getOptionsModel(); + + double getPoSKernelPS(); + double getDifficulty(bool fProofofStake); + + //! Return number of connections, default is in- and outbound (total) + int getNumConnections(uint8_t flags = CONNECTIONS_ALL) const; + int getNumBlocks() const; + int getNumBlocksAtStartup(); + + quint64 getTotalBytesRecv() const; + quint64 getTotalBytesSent() const; + + QDateTime getLastBlockDate() 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 + int getNumBlocksOfPeers() const; + //! Return warnings to be displayed in status bar + QString getStatusBarWarnings() const; + + QString formatFullVersion() const; + QString formatBuildDate() const; + QString clientName() const; + QString formatClientStartupTime() const; + +private: + OptionsModel *optionsModel; + + int cachedNumBlocks; + int cachedNumBlocksOfPeers; + + int numBlocksAtStartup; + + QTimer *pollTimer; + + void subscribeToCoreSignals(); + void unsubscribeFromCoreSignals(); +signals: + void numConnectionsChanged(int count); + void numBlocksChanged(int count, int countOfPeers); + void bytesChanged(quint64 totalBytesIn, quint64 totalBytesOut); + + //! Asynchronous error notification + void error(const QString &title, const QString &message, bool modal); + +public slots: + void updateTimer(); + void updateNumConnections(int numConnections); + void updateAlert(const QString &hash, int status); +}; + +#endif // CLIENTMODEL_H diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp new file mode 100644 index 00000000..ae73042b --- /dev/null +++ b/src/qt/coincontroldialog.cpp @@ -0,0 +1,760 @@ +#include "coincontroldialog.h" +#include "ui_coincontroldialog.h" + +#include "init.h" +#include "base58.h" +#include "bitcoinunits.h" +#include "walletmodel.h" +#include "addresstablemodel.h" +#include "optionsmodel.h" +#include "coincontrol.h" +#include "dialogwindowflags.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +QList CoinControlDialog::payAmounts; +CCoinControl* CoinControlDialog::coinControl = new CCoinControl(); + +CoinControlDialog::CoinControlDialog(QWidget *parent) : + QWidget(parent, DIALOGWINDOWHINTS), + ui(new Ui::CoinControlDialog), + model(0) +{ + ui->setupUi(this); + + // context menu actions + QAction *copyAddressAction = new QAction(tr("Copy address"), this); + QAction *copyLabelAction = new QAction(tr("Copy label"), this); + QAction *copyAmountAction = new QAction(tr("Copy amount"), this); + copyTransactionHashAction = new QAction(tr("Copy transaction ID"), this); // we need to enable/disable this + //lockAction = new QAction(tr("Lock unspent"), this); // we need to enable/disable this + //unlockAction = new QAction(tr("Unlock unspent"), this); // we need to enable/disable this + + // context menu + contextMenu = new QMenu(); + contextMenu->addAction(copyAddressAction); + contextMenu->addAction(copyLabelAction); + contextMenu->addAction(copyAmountAction); + contextMenu->addAction(copyTransactionHashAction); + //contextMenu->addSeparator(); + //contextMenu->addAction(lockAction); + //contextMenu->addAction(unlockAction); + + // context menu signals + connect(ui->treeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showMenu(QPoint))); + connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(copyAddress())); + connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel())); + connect(copyAmountAction, SIGNAL(triggered()), this, SLOT(copyAmount())); + connect(copyTransactionHashAction, SIGNAL(triggered()), this, SLOT(copyTransactionHash())); + //connect(lockAction, SIGNAL(triggered()), this, SLOT(lockCoin())); + //connect(unlockAction, SIGNAL(triggered()), this, SLOT(unlockCoin())); + + // clipboard actions + QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this); + QAction *clipboardAmountAction = new QAction(tr("Copy amount"), this); + QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this); + QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this); + QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this); + QAction *clipboardPriorityAction = new QAction(tr("Copy priority"), this); + QAction *clipboardLowOutputAction = new QAction(tr("Copy low output"), this); + QAction *clipboardChangeAction = new QAction(tr("Copy change"), this); + + connect(clipboardQuantityAction, SIGNAL(triggered()), this, SLOT(clipboardQuantity())); + connect(clipboardAmountAction, SIGNAL(triggered()), this, SLOT(clipboardAmount())); + connect(clipboardFeeAction, SIGNAL(triggered()), this, SLOT(clipboardFee())); + connect(clipboardAfterFeeAction, SIGNAL(triggered()), this, SLOT(clipboardAfterFee())); + connect(clipboardBytesAction, SIGNAL(triggered()), this, SLOT(clipboardBytes())); + connect(clipboardPriorityAction, SIGNAL(triggered()), this, SLOT(clipboardPriority())); + connect(clipboardLowOutputAction, SIGNAL(triggered()), this, SLOT(clipboardLowOutput())); + connect(clipboardChangeAction, SIGNAL(triggered()), this, SLOT(clipboardChange())); + + ui->labelCoinControlQuantity->addAction(clipboardQuantityAction); + ui->labelCoinControlAmount->addAction(clipboardAmountAction); + ui->labelCoinControlFee->addAction(clipboardFeeAction); + ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction); + ui->labelCoinControlBytes->addAction(clipboardBytesAction); + ui->labelCoinControlPriority->addAction(clipboardPriorityAction); + ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction); + ui->labelCoinControlChange->addAction(clipboardChangeAction); + + // toggle tree/list mode + connect(ui->radioTreeMode, SIGNAL(toggled(bool)), this, SLOT(radioTreeMode(bool))); + connect(ui->radioListMode, SIGNAL(toggled(bool)), this, SLOT(radioListMode(bool))); + + // click on checkbox + connect(ui->treeWidget, SIGNAL(itemChanged( QTreeWidgetItem*, int)), this, SLOT(viewItemChanged( QTreeWidgetItem*, int))); + + // click on header +#if QT_VERSION < 0x050000 + ui->treeWidget->header()->setClickable(true); +#else + ui->treeWidget->header()->setSectionsClickable(true); +#endif + connect(ui->treeWidget->header(), SIGNAL(sectionClicked(int)), this, SLOT(headerSectionClicked(int))); + + // (un)select all + connect(ui->pushButtonSelectAll, SIGNAL(clicked()), this, SLOT(buttonSelectAllClicked())); + + ui->treeWidget->headerItem()->setText(COLUMN_CHECKBOX, QString()); + + ui->treeWidget->setColumnWidth(COLUMN_CHECKBOX, 84); + ui->treeWidget->setColumnWidth(COLUMN_AMOUNT, 100); + ui->treeWidget->setColumnWidth(COLUMN_LABEL, 170); + ui->treeWidget->setColumnWidth(COLUMN_ADDRESS, 250); + ui->treeWidget->setColumnWidth(COLUMN_DATE, 90); + ui->treeWidget->setColumnWidth(COLUMN_CONFIRMATIONS, 70); + ui->treeWidget->setColumnWidth(COLUMN_PRIORITY, 100); + ui->treeWidget->setColumnWidth(COLUMN_WEIGHT, 100); + ui->treeWidget->setColumnHidden(COLUMN_TXHASH, true); // store transacton hash in this column, but dont show it + ui->treeWidget->setColumnHidden(COLUMN_VOUT_INDEX, true); // store vout index in this column, but dont show it + ui->treeWidget->setColumnHidden(COLUMN_AMOUNT_INT64, true); // store amount int64_t in this column, but dont show it + ui->treeWidget->setColumnHidden(COLUMN_PRIORITY_INT64, true); // store priority int64_t in this column, but dont show it + + // default view is sorted by amount desc + sortView(COLUMN_AMOUNT_INT64, Qt::DescendingOrder); +} + +CoinControlDialog::~CoinControlDialog() +{ + delete ui; +} + +void CoinControlDialog::setModel(WalletModel *model) +{ + this->model = model; + + if(model && model->getOptionsModel() && model->getAddressTableModel()) + { + updateView(); + //updateLabelLocked(); + CoinControlDialog::updateLabels(model, this); + } +} + +// helper function str_pad +QString CoinControlDialog::strPad(QString s, int nPadLength, QString sPadding) +{ + while (s.length() < nPadLength) + s = sPadding + s; + + return s; +} + +// ok button +void CoinControlDialog::on_buttonBox_accepted() +{ + close(); // closes the dialog +} + +// (un)select all +void CoinControlDialog::buttonSelectAllClicked() +{ + Qt::CheckState state = Qt::Checked; + for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++) + { + if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) != Qt::Unchecked) + { + state = Qt::Unchecked; + break; + } + } + ui->treeWidget->setEnabled(false); + for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++) + if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) != state) + ui->treeWidget->topLevelItem(i)->setCheckState(COLUMN_CHECKBOX, state); + ui->treeWidget->setEnabled(true); + CoinControlDialog::updateLabels(model, this); +} + +// context menu +void CoinControlDialog::showMenu(const QPoint &point) +{ + QTreeWidgetItem *item = ui->treeWidget->itemAt(point); + if(item) + { + contextMenuItem = item; + + // disable some items (like Copy Transaction ID, lock, unlock) for tree roots in context menu + if (item->text(COLUMN_TXHASH).length() == 64) // transaction hash is 64 characters (this means its a child node, so its not a parent node in tree mode) + { + copyTransactionHashAction->setEnabled(true); + //if (model->isLockedCoin(uint256(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt())) + //{ + // lockAction->setEnabled(false); + // unlockAction->setEnabled(true); + //} + //else + //{ + // lockAction->setEnabled(true); + // unlockAction->setEnabled(false); + //} + } + else // this means click on parent node in tree mode -> disable all + { + copyTransactionHashAction->setEnabled(false); + //lockAction->setEnabled(false); + //unlockAction->setEnabled(false); + } + + // show context menu + contextMenu->exec(QCursor::pos()); + } +} + +// context menu action: copy amount +void CoinControlDialog::copyAmount() +{ + QApplication::clipboard()->setText(contextMenuItem->text(COLUMN_AMOUNT)); +} + +// context menu action: copy label +void CoinControlDialog::copyLabel() +{ + if (ui->radioTreeMode->isChecked() && contextMenuItem->text(COLUMN_LABEL).length() == 0 && contextMenuItem->parent()) + QApplication::clipboard()->setText(contextMenuItem->parent()->text(COLUMN_LABEL)); + else + QApplication::clipboard()->setText(contextMenuItem->text(COLUMN_LABEL)); +} + +// context menu action: copy address +void CoinControlDialog::copyAddress() +{ + if (ui->radioTreeMode->isChecked() && contextMenuItem->text(COLUMN_ADDRESS).length() == 0 && contextMenuItem->parent()) + QApplication::clipboard()->setText(contextMenuItem->parent()->text(COLUMN_ADDRESS)); + else + QApplication::clipboard()->setText(contextMenuItem->text(COLUMN_ADDRESS)); +} + +// context menu action: copy transaction id +void CoinControlDialog::copyTransactionHash() +{ + QApplication::clipboard()->setText(contextMenuItem->text(COLUMN_TXHASH)); +} + +// context menu action: lock coin +/*void CoinControlDialog::lockCoin() +{ + if (contextMenuItem->checkState(COLUMN_CHECKBOX) == Qt::Checked) + contextMenuItem->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); + + COutPoint outpt(uint256(contextMenuItem->text(COLUMN_TXHASH).toStdString()), contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt()); + model->lockCoin(outpt); + contextMenuItem->setDisabled(true); + contextMenuItem->setIcon(COLUMN_CHECKBOX, QIcon(":/icons/lock_closed")); + updateLabelLocked(); +}*/ + +// context menu action: unlock coin +/*void CoinControlDialog::unlockCoin() +{ + COutPoint outpt(uint256(contextMenuItem->text(COLUMN_TXHASH).toStdString()), contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt()); + model->unlockCoin(outpt); + contextMenuItem->setDisabled(false); + contextMenuItem->setIcon(COLUMN_CHECKBOX, QIcon()); + updateLabelLocked(); +}*/ + +// copy label "Quantity" to clipboard +void CoinControlDialog::clipboardQuantity() +{ + QApplication::clipboard()->setText(ui->labelCoinControlQuantity->text()); +} + +// copy label "Amount" to clipboard +void CoinControlDialog::clipboardAmount() +{ + QApplication::clipboard()->setText(ui->labelCoinControlAmount->text().left(ui->labelCoinControlAmount->text().indexOf(" "))); +} + +// copy label "Fee" to clipboard +void CoinControlDialog::clipboardFee() +{ + QApplication::clipboard()->setText(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" "))); +} + +// copy label "After fee" to clipboard +void CoinControlDialog::clipboardAfterFee() +{ + QApplication::clipboard()->setText(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" "))); +} + +// copy label "Bytes" to clipboard +void CoinControlDialog::clipboardBytes() +{ + QApplication::clipboard()->setText(ui->labelCoinControlBytes->text()); +} + +// copy label "Priority" to clipboard +void CoinControlDialog::clipboardPriority() +{ + QApplication::clipboard()->setText(ui->labelCoinControlPriority->text()); +} + +// copy label "Low output" to clipboard +void CoinControlDialog::clipboardLowOutput() +{ + QApplication::clipboard()->setText(ui->labelCoinControlLowOutput->text()); +} + +// copy label "Change" to clipboard +void CoinControlDialog::clipboardChange() +{ + QApplication::clipboard()->setText(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" "))); +} + +// treeview: sort +void CoinControlDialog::sortView(int column, Qt::SortOrder order) +{ + sortColumn = column; + sortOrder = order; + ui->treeWidget->sortItems(column, order); + ui->treeWidget->header()->setSortIndicator((sortColumn == COLUMN_AMOUNT_INT64 ? COLUMN_AMOUNT : (sortColumn == COLUMN_PRIORITY_INT64 ? COLUMN_PRIORITY : sortColumn)), sortOrder); +} + +// treeview: clicked on header +void CoinControlDialog::headerSectionClicked(int logicalIndex) +{ + if (logicalIndex == COLUMN_CHECKBOX) // click on most left column -> do nothing + { + ui->treeWidget->header()->setSortIndicator((sortColumn == COLUMN_AMOUNT_INT64 ? COLUMN_AMOUNT : (sortColumn == COLUMN_PRIORITY_INT64 ? COLUMN_PRIORITY : sortColumn)), sortOrder); + } + else + { + if (logicalIndex == COLUMN_AMOUNT) // sort by amount + logicalIndex = COLUMN_AMOUNT_INT64; + + if (logicalIndex == COLUMN_PRIORITY) // sort by priority + logicalIndex = COLUMN_PRIORITY_INT64; + + if (sortColumn == logicalIndex) + sortOrder = ((sortOrder == Qt::AscendingOrder) ? Qt::DescendingOrder : Qt::AscendingOrder); + else + { + sortColumn = logicalIndex; + sortOrder = ((sortColumn == COLUMN_AMOUNT_INT64 || sortColumn == COLUMN_PRIORITY_INT64 || sortColumn == COLUMN_DATE || sortColumn == COLUMN_CONFIRMATIONS || sortColumn == COLUMN_WEIGHT) ? Qt::DescendingOrder : Qt::AscendingOrder); // if amount,date,conf,priority then default => desc, else default => asc + } + + sortView(sortColumn, sortOrder); + } +} + +// toggle tree mode +void CoinControlDialog::radioTreeMode(bool checked) +{ + if (checked && model) + updateView(); +} + +// toggle list mode +void CoinControlDialog::radioListMode(bool checked) +{ + if (checked && model) + updateView(); +} + +// checkbox clicked by user +void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column) +{ + if (column == COLUMN_CHECKBOX && item->text(COLUMN_TXHASH).length() == 64) // transaction hash is 64 characters (this means its a child node, so its not a parent node in tree mode) + { + COutPoint outpt(uint256(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt()); + + if (item->checkState(COLUMN_CHECKBOX) == Qt::Unchecked) + coinControl->UnSelect(outpt); + else if (item->isDisabled()) // locked (this happens if "check all" through parent node) + item->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); + else + coinControl->Select(outpt); + + // selection changed -> update labels + if (ui->treeWidget->isEnabled()) // do not update on every click for (un)select all + CoinControlDialog::updateLabels(model, this); + } +} + +// helper function, return human readable label for priority number +QString CoinControlDialog::getPriorityLabel(double dPriority) +{ + if (dPriority > 576000ULL) // at least medium, this number is from AllowFree(), the other thresholds are kinda random + { + if (dPriority > 5760000000ULL) return tr("highest"); + else if (dPriority > 576000000ULL) return tr("high"); + else if (dPriority > 57600000ULL) return tr("medium-high"); + else return tr("medium"); + } + else + { + if (dPriority > 5760ULL) return tr("low-medium"); + else if (dPriority > 58ULL) return tr("low"); + else return tr("lowest"); + } +} + +// shows count of locked unspent outputs +/*void CoinControlDialog::updateLabelLocked() +{ + vector vOutpts; + model->listLockedCoins(vOutpts); + if (vOutpts.size() > 0) + { + ui->labelLocked->setText(tr("(%1 locked)").arg(vOutpts.size())); + ui->labelLocked->setVisible(true); + } + else ui->labelLocked->setVisible(false); +}*/ + +void CoinControlDialog::updateLabels(WalletModel *model, QWidget* dialog) +{ + if (!model) return; + + // nPayAmount + qint64 nPayAmount = 0; + bool fLowOutput = false; + bool fDust = false; + CTransaction txDummy; + foreach(const qint64 &amount, CoinControlDialog::payAmounts) + { + nPayAmount += amount; + + if (amount > 0) + { + if (amount < CENT) + fLowOutput = true; + if (amount < MIN_TX_FEE) + fDust = true; + + CTxOut txout(amount, (CScript)vector(24, 0)); + txDummy.vout.push_back(txout); + } + } + + QString sPriorityLabel = ""; + int64_t nAmount = 0; + int64_t nPayFee = 0; + int64_t nAfterFee = 0; + int64_t nChange = 0; + unsigned int nBytes = 0; + unsigned int nBytesInputs = 0; + double dPriority = 0; + double dPriorityInputs = 0; + unsigned int nQuantity = 0; + + vector vCoinControl; + vector vOutputs; + coinControl->ListSelected(vCoinControl); + model->getOutputs(vCoinControl, vOutputs); + + BOOST_FOREACH(const COutput& out, vOutputs) + { + // Quantity + nQuantity++; + + // Amount + nAmount += out.tx->vout[out.i].nValue; + + // Priority + dPriorityInputs += (double)out.tx->vout[out.i].nValue * (out.nDepth+1); + + // Bytes + CTxDestination address; + if(ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) + { + CPubKey pubkey; + CKeyID *keyid = boost::get< CKeyID >(&address); + if (keyid && model->getPubKey(*keyid, pubkey)) + nBytesInputs += (pubkey.IsCompressed() ? 148 : 180); + else + nBytesInputs += 148; // in all error cases, simply assume 148 here + } + else nBytesInputs += 148; + } + + // calculation + if (nQuantity > 0) + { + // Bytes + nBytes = nBytesInputs + ((CoinControlDialog::payAmounts.size() > 0 ? CoinControlDialog::payAmounts.size() + 1 : 2) * 34) + 10; // always assume +1 output for change here + + // Priority + dPriority = dPriorityInputs / nBytes; + sPriorityLabel = CoinControlDialog::getPriorityLabel(dPriority); + bool fAllowFree = CTransaction::AllowFree(dPriority); + + // Fee + int64_t nFee = nTransactionFee * (1 + (int64_t)nBytes / 1000); + // Min Fee + int64_t nMinFee = txDummy.GetMinFee(1, fAllowFree, GMF_SEND, nBytes); + + nPayFee = max(nFee, nMinFee); + + if (nPayAmount > 0) + { + nChange = nAmount - nPayFee - nPayAmount; + + if (nChange == 0) + nBytes -= 34; + } + + // after fee + nAfterFee = nAmount - nPayFee; + if (nAfterFee < 0) + nAfterFee = 0; + } + + // actually update labels + int nDisplayUnit = BitcoinUnits::BTC; + if (model && model->getOptionsModel()) + nDisplayUnit = model->getOptionsModel()->getDisplayUnit(); + + QLabel *l1 = dialog->findChild("labelCoinControlQuantity"); + QLabel *l2 = dialog->findChild("labelCoinControlAmount"); + QLabel *l3 = dialog->findChild("labelCoinControlFee"); + QLabel *l4 = dialog->findChild("labelCoinControlAfterFee"); + QLabel *l5 = dialog->findChild("labelCoinControlBytes"); + QLabel *l6 = dialog->findChild("labelCoinControlPriority"); + QLabel *l7 = dialog->findChild("labelCoinControlLowOutput"); + QLabel *l8 = dialog->findChild("labelCoinControlChange"); + + // enable/disable "low output" and "change" + dialog->findChild("labelCoinControlLowOutputText")->setEnabled(nPayAmount > 0); + dialog->findChild("labelCoinControlLowOutput") ->setEnabled(nPayAmount > 0); + dialog->findChild("labelCoinControlChangeText") ->setEnabled(nPayAmount > 0); + dialog->findChild("labelCoinControlChange") ->setEnabled(nPayAmount > 0); + + // stats + l1->setText(QString::number(nQuantity)); // Quantity + l2->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nAmount)); // Amount + l3->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nPayFee, false, 3)); // Fee + l4->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nAfterFee)); // After Fee + l5->setText(((nBytes > 0) ? ASYMP_UTF8 : "") + QString::number(nBytes)); // Bytes + l6->setText(sPriorityLabel); // Priority + l7->setText((fLowOutput ? (fDust ? tr("DUST") : tr("yes")) : tr("no"))); // Low Output / Dust + l8->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nChange)); // Change + + // turn labels "red" + l5->setStyleSheet((nBytes >= 1000) ? "color:red;" : ""); // Bytes >= 1000 + l6->setStyleSheet((dPriority <= 576000) ? "color:red;" : ""); // Priority < "medium" + l7->setStyleSheet((fLowOutput) ? "color:red;" : ""); // Low Output = "yes" + l8->setStyleSheet((nChange > 0 && nChange < CENT) ? "color:red;" : ""); // Change < 0.01BTC + + // tool tips + l5->setToolTip(tr("This label turns red, if the transaction size is bigger than 1000 bytes.\n\n This means a fee of at least %1 per kb is required.\n\n Can vary +/- 1 Byte per input.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, MIN_TX_FEE))); + l6->setToolTip(tr("Transactions with higher priority get more likely into a block.\n\nThis label turns red, if the priority is smaller than \"medium\".\n\n This means a fee of at least %1 per kb is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, MIN_TX_FEE))); + l7->setToolTip(tr("This label turns red, if any recipient receives an amount smaller than %1.\n\n This means a fee of at least %2 is required. \n\n Amounts below the minimum fee are shown as DUST.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CENT)).arg(BitcoinUnits::formatWithUnit(nDisplayUnit, MIN_RELAY_TX_FEE))); + l8->setToolTip(tr("This label turns red, if the change is smaller than %1.\n\n This means a fee of at least %2 is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CENT)).arg(BitcoinUnits::formatWithUnit(nDisplayUnit, MIN_TX_FEE))); + dialog->findChild("labelCoinControlBytesText") ->setToolTip(l5->toolTip()); + dialog->findChild("labelCoinControlPriorityText") ->setToolTip(l6->toolTip()); + dialog->findChild("labelCoinControlLowOutputText")->setToolTip(l7->toolTip()); + dialog->findChild("labelCoinControlChangeText") ->setToolTip(l8->toolTip()); + + // Insufficient funds + QLabel *label = dialog->findChild("labelCoinControlInsuffFunds"); + if (label) + label->setVisible(nChange < 0); +} + +void CoinControlDialog::updateView() +{ + bool treeMode = ui->radioTreeMode->isChecked(); + + ui->treeWidget->clear(); + ui->treeWidget->setEnabled(false); // performance, otherwise updateLabels would be called for every checked checkbox + ui->treeWidget->setAlternatingRowColors(!treeMode); + QFlags flgCheckbox=Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; + QFlags flgTristate=Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsTristate; + + int nDisplayUnit = BitcoinUnits::BTC; + if (model && model->getOptionsModel()) + nDisplayUnit = model->getOptionsModel()->getDisplayUnit(); + + map > mapCoins; + model->listCoins(mapCoins); + + BOOST_FOREACH(PAIRTYPE(QString, vector) coins, mapCoins) + { + QTreeWidgetItem *itemWalletAddress = new QTreeWidgetItem(); + QString sWalletAddress = coins.first; + QString sWalletLabel = ""; + if (model->getAddressTableModel()) + sWalletLabel = model->getAddressTableModel()->labelForAddress(sWalletAddress); + if (sWalletLabel.length() == 0) + sWalletLabel = tr("(no label)"); + + if (treeMode) + { + // wallet address + ui->treeWidget->addTopLevelItem(itemWalletAddress); + + itemWalletAddress->setFlags(flgTristate); + itemWalletAddress->setCheckState(COLUMN_CHECKBOX,Qt::Unchecked); + + for (int i = 0; i < ui->treeWidget->columnCount(); i++) + itemWalletAddress->setBackground(i, QColor(248, 247, 246)); + + // label + itemWalletAddress->setText(COLUMN_LABEL, sWalletLabel); + + // address + itemWalletAddress->setText(COLUMN_ADDRESS, sWalletAddress); + } + + int64_t nSum = 0; + double dPrioritySum = 0; + int nChildren = 0; + int nInputSum = 0; + uint64_t nTxWeight = 0, nTxWeightSum = 0; + BOOST_FOREACH(const COutput& out, coins.second) + { + int nInputSize = 148; // 180 if uncompressed public key + nSum += out.tx->vout[out.i].nValue; + model->getStakeWeightFromValue(out.tx->GetTxTime(), out.tx->vout[out.i].nValue, nTxWeight); + nTxWeightSum += nTxWeight; + nChildren++; + + QTreeWidgetItem *itemOutput; + if (treeMode) itemOutput = new QTreeWidgetItem(itemWalletAddress); + else itemOutput = new QTreeWidgetItem(ui->treeWidget); + itemOutput->setFlags(flgCheckbox); + itemOutput->setCheckState(COLUMN_CHECKBOX,Qt::Unchecked); + + // address + CTxDestination outputAddress; + QString sAddress = ""; + if(ExtractDestination(out.tx->vout[out.i].scriptPubKey, outputAddress)) + { + sAddress = CBitcoinAddress(outputAddress).ToString().c_str(); + + // if listMode or change => show bitcoin address. In tree mode, address is not shown again for direct wallet address outputs + if (!treeMode || (!(sAddress == sWalletAddress))) + itemOutput->setText(COLUMN_ADDRESS, sAddress); + + CPubKey pubkey; + CKeyID *keyid = boost::get< CKeyID >(&outputAddress); + if (keyid && model->getPubKey(*keyid, pubkey) && !pubkey.IsCompressed()) + nInputSize = 180; + } + + // label + if (!(sAddress == sWalletAddress)) // change + { + // tooltip from where the change comes from + itemOutput->setToolTip(COLUMN_LABEL, tr("change from %1 (%2)").arg(sWalletLabel).arg(sWalletAddress)); + itemOutput->setText(COLUMN_LABEL, tr("(change)")); + } + else if (!treeMode) + { + QString sLabel = ""; + if (model->getAddressTableModel()) + sLabel = model->getAddressTableModel()->labelForAddress(sAddress); + if (sLabel.length() == 0) + sLabel = tr("(no label)"); + itemOutput->setText(COLUMN_LABEL, sLabel); + } + + // amount + itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->vout[out.i].nValue)); + itemOutput->setText(COLUMN_AMOUNT_INT64, strPad(QString::number(out.tx->vout[out.i].nValue), 15, " ")); // padding so that sorting works correctly + + // date + itemOutput->setText(COLUMN_DATE, QDateTime::fromTime_t(out.tx->GetTxTime()).toUTC().toString("yy-MM-dd hh:mm")); + + // immature PoS reward + if (out.tx->IsCoinStake() && out.tx->GetBlocksToMaturity() > 0 && out.tx->GetDepthInMainChain() > 0) { + itemOutput->setBackground(COLUMN_CONFIRMATIONS, Qt::red); + itemOutput->setDisabled(true); + } + + // confirmations + itemOutput->setText(COLUMN_CONFIRMATIONS, strPad(QString::number(out.nDepth), 8, " ")); + + // priority + double dPriority = ((double)out.tx->vout[out.i].nValue / (nInputSize + 78)) * (out.nDepth+1); // 78 = 2 * 34 + 10 + itemOutput->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPriority)); + itemOutput->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64_t)dPriority), 20, " ")); + dPrioritySum += (double)out.tx->vout[out.i].nValue * (out.nDepth+1); + nInputSum += nInputSize; + + // List Mode Weight + itemOutput->setText(COLUMN_WEIGHT, strPad(QString::number(nTxWeight), 8, " ")); + + // transaction hash + uint256 txhash = out.tx->GetHash(); + itemOutput->setText(COLUMN_TXHASH, txhash.GetHex().c_str()); + + // vout index + itemOutput->setText(COLUMN_VOUT_INDEX, QString::number(out.i)); + + // disable locked coins + /*if (model->isLockedCoin(txhash, out.i)) + { + COutPoint outpt(txhash, out.i); + coinControl->UnSelect(outpt); // just to be sure + itemOutput->setDisabled(true); + itemOutput->setIcon(COLUMN_CHECKBOX, QIcon(":/icons/lock_closed")); + }*/ + + // set checkbox + if (coinControl->IsSelected(txhash, out.i)) + itemOutput->setCheckState(COLUMN_CHECKBOX,Qt::Checked); + } + + // amount + if (treeMode) + { + dPrioritySum = dPrioritySum / (nInputSum + 78); + itemWalletAddress->setText(COLUMN_CHECKBOX, "(" + QString::number(nChildren) + ")"); + itemWalletAddress->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nSum)); + itemWalletAddress->setText(COLUMN_AMOUNT_INT64, strPad(QString::number(nSum), 15, " ")); + itemWalletAddress->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPrioritySum)); + itemWalletAddress->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64_t)dPrioritySum), 20, " ")); + itemWalletAddress->setText(COLUMN_WEIGHT, strPad(QString::number((uint64_t)nTxWeightSum),8," ")); + + } + } + + // expand all partially selected + if (treeMode) + { + for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++) + if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) == Qt::PartiallyChecked) + ui->treeWidget->topLevelItem(i)->setExpanded(true); + } + + // sort view + sortView(sortColumn, sortOrder); + ui->treeWidget->setEnabled(true); +} + +void CoinControlDialog::keyPressEvent(QKeyEvent *event) +{ +#ifdef ANDROID + if(event->key() == Qt::Key_Back) + { + close(); + } +#else + if(event->key() == Qt::Key_Escape) + { + close(); + } +#endif +} + +void CoinControlDialog::closeEvent(QCloseEvent* e) +{ + QWidget::closeEvent(e); + emit beforeClose(); +} diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h new file mode 100644 index 00000000..ba0cf60b --- /dev/null +++ b/src/qt/coincontroldialog.h @@ -0,0 +1,102 @@ +#ifndef COINCONTROLDIALOG_H +#define COINCONTROLDIALOG_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Ui { + class CoinControlDialog; +} +class WalletModel; +class CCoinControl; + +#define ASYMP_UTF8 "\xE2\x89\x88" + +class CoinControlDialog : public QWidget +{ + Q_OBJECT +signals: + void beforeClose(); + +public: + explicit CoinControlDialog(QWidget *parent = 0); + ~CoinControlDialog(); + + void setModel(WalletModel *model); + + // static because also called from sendcoinsdialog + static void updateLabels(WalletModel*, QWidget*); + static QString getPriorityLabel(double); + + static QList payAmounts; + static CCoinControl *coinControl; + +protected: + void closeEvent(QCloseEvent* e); + +private: + Ui::CoinControlDialog *ui; + WalletModel *model; + int sortColumn; + Qt::SortOrder sortOrder; + + QMenu *contextMenu; + QTreeWidgetItem *contextMenuItem; + QAction *copyTransactionHashAction; + //QAction *lockAction; + //QAction *unlockAction; + + QString strPad(QString, int, QString); + void sortView(int, Qt::SortOrder); + void updateView(); + + void keyPressEvent(QKeyEvent *); + + enum + { + COLUMN_CHECKBOX, + COLUMN_AMOUNT, + COLUMN_LABEL, + COLUMN_ADDRESS, + COLUMN_DATE, + COLUMN_CONFIRMATIONS, + COLUMN_WEIGHT, + COLUMN_PRIORITY, + COLUMN_TXHASH, + COLUMN_VOUT_INDEX, + COLUMN_AMOUNT_INT64, + COLUMN_PRIORITY_INT64 + }; + +private slots: + void showMenu(const QPoint &); + void copyAmount(); + void copyLabel(); + void copyAddress(); + void copyTransactionHash(); + //void lockCoin(); + //void unlockCoin(); + void clipboardQuantity(); + void clipboardAmount(); + void clipboardFee(); + void clipboardAfterFee(); + void clipboardBytes(); + void clipboardPriority(); + void clipboardLowOutput(); + void clipboardChange(); + void radioTreeMode(bool); + void radioListMode(bool); + void viewItemChanged(QTreeWidgetItem*, int); + void headerSectionClicked(int); + void on_buttonBox_accepted(); + void buttonSelectAllClicked(); + //void updateLabelLocked(); +}; + +#endif // COINCONTROLDIALOG_H diff --git a/src/qt/coincontroltreewidget.cpp b/src/qt/coincontroltreewidget.cpp new file mode 100644 index 00000000..f1f177e2 --- /dev/null +++ b/src/qt/coincontroltreewidget.cpp @@ -0,0 +1,33 @@ +#include "coincontroltreewidget.h" +#include "coincontroldialog.h" + +CoinControlTreeWidget::CoinControlTreeWidget(QWidget *parent) : + QTreeWidget(parent) +{ + +} + +void CoinControlTreeWidget::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Space) // press spacebar -> select checkbox + { + event->ignore(); + int COLUMN_CHECKBOX = 0; + if(this->currentItem()) + this->currentItem()->setCheckState(COLUMN_CHECKBOX, ((this->currentItem()->checkState(COLUMN_CHECKBOX) == Qt::Checked) ? Qt::Unchecked : Qt::Checked)); + } +#ifdef ANDROID + else if (event->key() == Qt::Key_Back) // press back -> close dialog +#else + else if (event->key() == Qt::Key_Escape) // press esc -> close dialog +#endif + { + event->ignore(); + CoinControlDialog *coinControlDialog = (CoinControlDialog*)this->parentWidget(); + coinControlDialog->close(); + } + else + { + this->QTreeWidget::keyPressEvent(event); + } +} diff --git a/src/qt/coincontroltreewidget.h b/src/qt/coincontroltreewidget.h new file mode 100644 index 00000000..d981f728 --- /dev/null +++ b/src/qt/coincontroltreewidget.h @@ -0,0 +1,17 @@ +#ifndef COINCONTROLTREEWIDGET_H +#define COINCONTROLTREEWIDGET_H + +#include +#include + +class CoinControlTreeWidget : public QTreeWidget { +Q_OBJECT + +public: + explicit CoinControlTreeWidget(QWidget *parent = 0); + +protected: + virtual void keyPressEvent(QKeyEvent *event); +}; + +#endif // COINCONTROLTREEWIDGET_H \ No newline at end of file diff --git a/src/qt/csvmodelwriter.cpp b/src/qt/csvmodelwriter.cpp new file mode 100644 index 00000000..8a50bbab --- /dev/null +++ b/src/qt/csvmodelwriter.cpp @@ -0,0 +1,88 @@ +#include "csvmodelwriter.h" + +#include +#include +#include + +CSVModelWriter::CSVModelWriter(const QString &filename, QObject *parent) : + QObject(parent), + filename(filename), model(0) +{ +} + +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) +{ + QString escaped = value; + escaped.replace('"', "\"\""); + f << "\"" << escaped << "\""; +} + +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 = 0; + if(model) + { + 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..6c9dcbaf --- /dev/null +++ b/src/qt/csvmodelwriter.h @@ -0,0 +1,46 @@ +#ifndef CSVMODELWRITER_H +#define CSVMODELWRITER_H + +#include +#include + +QT_BEGIN_NAMESPACE +class QAbstractItemModel; +QT_END_NAMESPACE + +/** Export a Qt table model to a CSV file. This is useful for analyzing or post-processing the data in + a spreadsheet. + */ +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 export of the model to CSV. + @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/dialogwindowflags.h b/src/qt/dialogwindowflags.h new file mode 100644 index 00000000..a8d55c36 --- /dev/null +++ b/src/qt/dialogwindowflags.h @@ -0,0 +1,9 @@ +#ifndef DIALOGWINDOWFLAGS_H +#define DIALOGWINDOWFLAGS_H +// Dialog Window Flags +#if QT_VERSION < 0x050000 + static const Qt::WindowFlags DIALOGWINDOWHINTS = Qt::WindowSystemMenuHint | Qt::WindowTitleHint; +#else + static const Qt::WindowFlags DIALOGWINDOWHINTS = Qt::WindowCloseButtonHint | Qt::WindowTitleHint; +#endif +#endif \ No newline at end of file diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp new file mode 100644 index 00000000..2653379b --- /dev/null +++ b/src/qt/editaddressdialog.cpp @@ -0,0 +1,137 @@ +#include "editaddressdialog.h" +#include "ui_editaddressdialog.h" +#include "addresstablemodel.h" +#include "dialogwindowflags.h" +#include "guiutil.h" + +#include +#include + +EditAddressDialog::EditAddressDialog(Mode mode, QWidget *parent) : + QDialog(parent, DIALOGWINDOWHINTS), + ui(new Ui::EditAddressDialog), mapper(0), mode(mode), model(0) +{ + ui->setupUi(this); + + GUIUtil::setupAddressWidget(ui->addressEdit, this); + + switch(mode) + { + case NewReceivingAddress: + setWindowTitle(tr("New receiving address")); + ui->addressEdit->setEnabled(false); + break; + case NewSendingAddress: + setWindowTitle(tr("New sending address")); + break; + case EditReceivingAddress: + setWindowTitle(tr("Edit receiving address")); + ui->addressEdit->setEnabled(false); + break; + case EditSendingAddress: + setWindowTitle(tr("Edit sending address")); + break; + } + + mapper = new QDataWidgetMapper(this); + mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit); +} + +EditAddressDialog::~EditAddressDialog() +{ + delete ui; +} + +void EditAddressDialog::setModel(AddressTableModel *model) +{ + this->model = model; + if(!model) + return; + + mapper->setModel(model); + mapper->addMapping(ui->labelEdit, AddressTableModel::Label); + mapper->addMapping(ui->addressEdit, AddressTableModel::Address); +} + +void EditAddressDialog::loadRow(int row) +{ + mapper->setCurrentIndex(row); +} + +bool EditAddressDialog::saveCurrentRow() +{ + if(!model) + return false; + + switch(mode) + { + case NewReceivingAddress: + case NewSendingAddress: + address = model->addRow( + mode == NewSendingAddress ? AddressTableModel::Send : AddressTableModel::Receive, + ui->labelEdit->text(), + ui->addressEdit->text()); + break; + case EditReceivingAddress: + case EditSendingAddress: + if(mapper->submit()) + { + address = ui->addressEdit->text(); + } + break; + } + return !address.isEmpty(); +} + +void EditAddressDialog::accept() +{ + if(!model) + return; + + if(!saveCurrentRow()) + { + switch(model->getEditStatus()) + { + case AddressTableModel::OK: + // Failed with unknown reason. Just reject. + break; + case AddressTableModel::NO_CHANGES: + // No changes were made during edit operation. Just reject. + break; + case AddressTableModel::INVALID_ADDRESS: + QMessageBox::warning(this, windowTitle(), + tr("The entered address \"%1\" is not a valid XP address.").arg(ui->addressEdit->text()), + QMessageBox::Ok, QMessageBox::Ok); + break; + 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::WALLET_UNLOCK_FAILURE: + QMessageBox::critical(this, windowTitle(), + tr("Could not unlock wallet."), + QMessageBox::Ok, QMessageBox::Ok); + break; + case AddressTableModel::KEY_GENERATION_FAILURE: + QMessageBox::critical(this, windowTitle(), + tr("New key generation failed."), + QMessageBox::Ok, QMessageBox::Ok); + break; + + } + return; + } + QDialog::accept(); +} + +QString EditAddressDialog::getAddress() const +{ + return address; +} + +void EditAddressDialog::setAddress(const QString &address) +{ + this->address = address; + ui->addressEdit->setText(address); +} diff --git a/src/qt/editaddressdialog.h b/src/qt/editaddressdialog.h new file mode 100644 index 00000000..0e4183bd --- /dev/null +++ b/src/qt/editaddressdialog.h @@ -0,0 +1,52 @@ +#ifndef EDITADDRESSDIALOG_H +#define EDITADDRESSDIALOG_H + +#include + +QT_BEGIN_NAMESPACE +class QDataWidgetMapper; +QT_END_NAMESPACE + +namespace Ui { + class EditAddressDialog; +} +class AddressTableModel; + +/** Dialog for editing an address and associated information. + */ +class EditAddressDialog : public QDialog +{ + Q_OBJECT + +public: + enum Mode { + NewReceivingAddress, + NewSendingAddress, + EditReceivingAddress, + EditSendingAddress + }; + + explicit EditAddressDialog(Mode mode, QWidget *parent = 0); + ~EditAddressDialog(); + + void setModel(AddressTableModel *model); + void loadRow(int row); + + QString getAddress() const; + void setAddress(const QString &address); + +public slots: + void accept(); + +private: + bool saveCurrentRow(); + + Ui::EditAddressDialog *ui; + QDataWidgetMapper *mapper; + Mode mode; + AddressTableModel *model; + + QString address; +}; + +#endif // EDITADDRESSDIALOG_H diff --git a/src/qt/forms/aboutdialog.ui b/src/qt/forms/aboutdialog.ui new file mode 100644 index 00000000..a5a60b59 --- /dev/null +++ b/src/qt/forms/aboutdialog.ui @@ -0,0 +1,158 @@ + + + AboutDialog + + + + 0 + 0 + 640 + 416 + + + + About XP + + + + + + + 0 + 0 + + + + :/images/about + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + IBeamCursor + + + <b>XP</b> version + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + IBeamCursor + + + 0.3.666-beta + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + IBeamCursor + + + Copyright © 2009-2015 The Bitcoin developers +Copyright © 2011-2012 The PPCoin Developers +Copyright © 2014 The Peerunity Developers +Copyright © 2014 The EmerCoin Developers +Copyright © 2012-2015 The XP developers + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + IBeamCursor + + + <html><head/><body><p><br/>This is experimental software.</p><p>Distributed under the MIT/X11 software license, see the accompanying file COPYING or <br/><a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a>.</p><p>Main icon was designed by VisualPharm.com (<a href="mailto:team@visualpharm.com"><span style=" text-decoration: underline; color:#0000ff;">team@visualpharm.com</span></a>). This product also includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (<a href="http://www.openssl.org/"><span style=" text-decoration: underline; color:#0000ff;">http://www.openssl.org/</span></a>) and cryptographic software written by Eric Young (<a href="mailto:eay@cryptsoft.com"><span style=" text-decoration: underline; color:#0000ff;">eay@cryptsoft.com</span></a>).</p><p><br/></p></body></html> + + + true + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + + + + diff --git a/src/qt/forms/addressbookpage.ui b/src/qt/forms/addressbookpage.ui new file mode 100644 index 00000000..773f9df9 --- /dev/null +++ b/src/qt/forms/addressbookpage.ui @@ -0,0 +1,175 @@ + + + AddressBookPage + + + + 0 + 0 + 760 + 380 + + + + Address Book + + + + + + These are your XP 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::PlainText + + + true + + + + + + + Qt::CustomContextMenu + + + Double-click to edit address or label + + + false + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + true + + + false + + + + + + + + + Create a new address + + + &New Address + + + + :/icons/add:/icons/add + + + + + + + Copy the currently selected address to the system clipboard + + + &Copy Address + + + + :/icons/editcopy:/icons/editcopy + + + + + + + Show &QR Code + + + + :/icons/qrcode:/icons/qrcode + + + + + + + Sign a message to prove you own a XP address + + + Sign &Message + + + + :/icons/edit:/icons/edit + + + + + + + Verify a message to ensure it was signed with a specified XP address + + + &Verify Message + + + + :/icons/transaction_0:/icons/transaction_0 + + + + + + + Delete the currently selected address from the list + + + &Delete + + + + :/icons/remove:/icons/remove + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + QDialogButtonBox::Ok + + + + + + + + + + + + diff --git a/src/qt/forms/askpassphrasedialog.ui b/src/qt/forms/askpassphrasedialog.ui new file mode 100644 index 00000000..25169042 --- /dev/null +++ b/src/qt/forms/askpassphrasedialog.ui @@ -0,0 +1,151 @@ + + + AskPassphraseDialog + + + + 0 + 0 + 598 + 198 + + + + + 0 + 0 + + + + + 550 + 0 + + + + Passphrase Dialog + + + + + + Qt::RichText + + + true + + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Enter passphrase + + + + + + + QLineEdit::Password + + + + + + + New passphrase + + + + + + + QLineEdit::Password + + + + + + + Repeat new passphrase + + + + + + + QLineEdit::Password + + + + + + + + 75 + true + + + + + + + Qt::AlignCenter + + + + + + + + + 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/forms/coincontroldialog.ui b/src/qt/forms/coincontroldialog.ui new file mode 100644 index 00000000..ad89905b --- /dev/null +++ b/src/qt/forms/coincontroldialog.ui @@ -0,0 +1,554 @@ + + + CoinControlDialog + + + + 0 + 0 + 1000 + 500 + + + + Coin Control + + + + + + 0 + + + 10 + + + + + 10 + + + 10 + + + 6 + + + 6 + + + + + font-weight:bold; + + + Quantity: + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + font-weight:bold; + + + Bytes: + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + 10 + + + 10 + + + 6 + + + 6 + + + + + font-weight:bold; + + + Amount: + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0.00 XP + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + font-weight:bold; + + + Priority: + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + 10 + + + 10 + + + 6 + + + 6 + + + + + font-weight:bold; + + + Fee: + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0.00 XP + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + false + + + font-weight:bold; + + + Low Output: + + + + + + + false + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + no + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + 10 + + + 10 + + + 6 + + + 6 + + + + + font-weight:bold; + + + After Fee: + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0.00 XP + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + false + + + font-weight:bold; + + + Change: + + + + + + + false + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0.00 XP + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + + + + 0 + 40 + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + + 10 + 0 + 781 + 41 + + + + + 14 + + + + + + 0 + 0 + + + + (un)select all + + + + + + + + 0 + 0 + + + + Tree mode + + + true + + + + + + + + 0 + 0 + + + + List mode + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::CustomContextMenu + + + false + + + 12 + + + true + + + false + + + + + + + + + Amount + + + + + Label + + + + + Address + + + + + Date + + + + + Confirmations + + + Confirmed + + + + + Weight + + + + + Priority + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + CoinControlTreeWidget + QTreeWidget +
coincontroltreewidget.h
+
+
+ + +
diff --git a/src/qt/forms/editaddressdialog.ui b/src/qt/forms/editaddressdialog.ui new file mode 100644 index 00000000..b4a4c1b1 --- /dev/null +++ b/src/qt/forms/editaddressdialog.ui @@ -0,0 +1,105 @@ + + + EditAddressDialog + + + + 0 + 0 + 457 + 126 + + + + Edit Address + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + &Label + + + labelEdit + + + + + + + The label associated with this address book entry + + + + + + + &Address + + + addressEdit + + + + + + + The address associated with this address book entry. This can only be modified for sending addresses. + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + EditAddressDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + EditAddressDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/qt/forms/intro.ui b/src/qt/forms/intro.ui new file mode 100644 index 00000000..6901ef71 --- /dev/null +++ b/src/qt/forms/intro.ui @@ -0,0 +1,266 @@ + + + Intro + + + + 0 + 0 + 480 + 288 + + + + Welcome + + + + + + QLabel { font-style:italic; } + + + Welcome to XP-qt. + + + true + + + + + + + Qt::Vertical + + + QSizePolicy::Minimum + + + + 20 + 15 + + + + + + + + As this is the first time the program is launched, you can choose where XP-qt will store its data. + + + true + + + + + + + XP-qt will download and store a copy of the XP block chain. At least %1GB of data will be stored in this directory, and it will grow over time. The wallet will also be stored in this directory. + + + true + + + + + + + Use the default data directory + + + + + + + Use a custom data directory: + + + + + + + 0 + + + QLayout::SetDefaultConstraint + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 60 + 20 + + + + + + + + QLayout::SetDefaultConstraint + + + + + + + + + + + 0 + 0 + + + + + 30 + 16777215 + + + + + + + false + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 5 + + + + + + + + + 1 + 0 + + + + + + + true + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 5 + + + + + + + + + 0 + 0 + + + + Qt::RichText + + + true + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + Intro + accept() + + + 20 + 20 + + + 20 + 20 + + + + + buttonBox + rejected() + Intro + reject() + + + 20 + 20 + + + 20 + 20 + + + + + diff --git a/src/qt/forms/multisigaddressentry.ui b/src/qt/forms/multisigaddressentry.ui new file mode 100644 index 00000000..c145f077 --- /dev/null +++ b/src/qt/forms/multisigaddressentry.ui @@ -0,0 +1,170 @@ + + + MultisigAddressEntry + + + + 0 + 0 + 729 + 136 + + + + Form + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + + + Public &key: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + pubkey + + + + + + + 0 + + + + + The public key of an address + + + Enter a public key + + + + + + + Paste public key from clipboard + + + + + + + :/icons/editpaste:/icons/editpaste + + + Alt+P + + + + + + + Remove this public key + + + + + + + :/icons/remove:/icons/remove + + + + + + + + + &Address: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + address + + + + + + + 0 + + + + + true + + + Address associated to the public key + + + Enter one of your addresses to get its public key + + + + + + + Choose address from address book + + + + + + + :/icons/address-book:/icons/address-book + + + Alt+A + + + + + + + + + Label: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + true + + + + + + true + + + + + + + + QValidatedLineEdit + QLineEdit +
qvalidatedlineedit.h
+
+
+ + + + +
diff --git a/src/qt/forms/multisigdialog.ui b/src/qt/forms/multisigdialog.ui new file mode 100644 index 00000000..5af15d1a --- /dev/null +++ b/src/qt/forms/multisigdialog.ui @@ -0,0 +1,793 @@ + + + MultisigDialog + + + + 0 + 0 + 1327 + 595 + + + + Multisig + + + + + + 0 + + + + &Create Address + + + + + + true + + + + + 0 + 0 + 1283 + 345 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + Add a member to the signing pool + + + &Add public key... + + + + :/icons/add:/icons/add + + + + + + + + 0 + 0 + + + + Remove all public key fields + + + Clear all + + + + :/icons/remove:/icons/remove + + + 300 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Required signatures: + + + + + + + + 0 + 0 + + + + + 127 + 0 + + + + + + + 2 + + + true + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + Enter a number + + + + + + + / 1 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Create multisig address + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Multisig address: + + + + + + + true + + + + + + + Copy the multisig address to the system clipboard + + + + + + + :/icons/editcopy:/icons/editcopy + + + false + + + + + + + + + + + Redeem script: + + + + + + + true + + + + + + + Copy the redeem script to the system clipboard + + + + + + + :/icons/editcopy:/icons/editcopy + + + false + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + The redeem script will be required to spend the funds sent to the multisig address + + + Save redeem script + + + + + + + Add the multisig address to your personal addresses + + + Add address to wallet + + + + + + + + + + &Spend Funds + + + + + + + + + + Inputs + + + + + + + 0 + 0 + + + + true + + + + + 0 + 0 + 616 + 271 + + + + + + + + + + Qt::Vertical + + + + 20 + 42 + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Inputs amount: + + + + + + + 123.456 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + XP + + + + + + + + + + + Add input... + + + + :/icons/add:/icons/add + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Outputs + + + + + + + 0 + 0 + + + + true + + + + + 0 + 0 + 615 + 243 + + + + + + + + + + Qt::Vertical + + + + 20 + 42 + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Outputs amount: + + + + + + + 123.456 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + XP + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Fee: + + + + + + + 123.456 + + + + + + + XP + + + + + + + + + + + Add output... + + + + :/icons/add:/icons/add + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + Create transaction + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + 0 + 0 + + + + Enter a raw transaction or create a new one + + + + + + + + + + + + + + :/icons/editcopy:/icons/editcopy + + + false + + + + + + + Paste transaction from clipboard + + + + + + + :/icons/editpaste:/icons/editpaste + + + Alt+P + + + false + + + + + + + + + + + Sign transaction + + + + :/icons/edit:/icons/edit + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + 0 + 0 + + + + true + + + + + + + + + + + + + + :/icons/editcopy:/icons/editcopy + + + false + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Send transaction + + + + :/icons/send:/icons/send + + + + + + + + + + + + + + + + + + diff --git a/src/qt/forms/multisiginputentry.ui b/src/qt/forms/multisiginputentry.ui new file mode 100644 index 00000000..17662fc2 --- /dev/null +++ b/src/qt/forms/multisiginputentry.ui @@ -0,0 +1,160 @@ + + +MultisigInputEntry + + + +0 +0 +729 +136 + + + +Form + + +QFrame::StyledPanel + + +QFrame::Sunken + + + + + +0 + + + + + + + +Enter a transaction id + + + + + + + + + + + + + +:/icons/editpaste:/icons/editpaste + + +Alt+P + + + + + + + + + + + + + +:/icons/remove:/icons/remove + + + + + + + + +Transaction id: + + +Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + +transactionId + + + + + + +Transaction output: + + +Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + +Redeem script: + + +Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + +redeemScript + + + + + + +0 + + + + +true + + + + + +Enter the redeem script of the address in the transaction output + + + + + + + + + + + + + +:/icons/editpaste:/icons/editpaste + + +Alt+A + + + + + + + + + + + + +QValidatedLineEdit +QLineEdit +
qvalidatedlineedit.h
+
+
+ + + + +
\ No newline at end of file diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui new file mode 100644 index 00000000..a29f7b52 --- /dev/null +++ b/src/qt/forms/optionsdialog.ui @@ -0,0 +1,592 @@ + + + OptionsDialog + + + + 0 + 0 + 550 + 380 + + + + Options + + + + + + QTabWidget::North + + + 1 + + + + &Main + + + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. Fee 0.001 recommended. + + + Qt::PlainText + + + true + + + + + + + + + Pay transaction &fee + + + Qt::PlainText + + + transactionFee + + + + + + + + + + per kilobyte + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Automatically start XP after logging in to the system. + + + &Start XP on system login + + + + + + + Detach block and address databases at shutdown. This means they can be moved to another data directory, but it slows down shutdown. The wallet is always detached. + + + &Detach databases at shutdown + + + + + + + Qt::Vertical + + + + 17 + 153 + + + + + + + + + &Network + + + + + + Connect to the XP network through a SOCKS proxy (e.g. when connecting through Tor). + + + &Connect through SOCKS proxy: + + + + + + + + + Proxy &IP: + + + Qt::PlainText + + + proxyIp + + + + + + + + 140 + 16777215 + + + + IP address of the proxy (e.g. 127.0.0.1) + + + + + + + &Port: + + + Qt::PlainText + + + proxyPort + + + + + + + + 55 + 16777215 + + + + Port of the proxy (e.g. 9050) + + + + + + + SOCKS &Version: + + + Qt::PlainText + + + socksVersion + + + + + + + SOCKS version of the proxy (e.g. 5) + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Connect through &Tor: + + + + + + + + + + 60 + 0 + + + + Tor IP: + + + + + + + + 115 + 16777215 + + + + + + + + Port: + + + + + + + + 55 + 16777215 + + + + + + + + Use Tor only + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Tor name: + + + Qt::PlainText + + + torName + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + &Window + + + + + + Show only a tray icon after minimizing the window. + + + &Minimize to the tray instead of the taskbar + + + + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu. + + + M&inimize on close + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + &Display + + + + + + + + User Interface &language: + + + Qt::PlainText + + + lang + + + + + + + The user interface language can be set here. This setting will take effect after restarting XP. + + + + + + + + + + + &Unit to show amounts in: + + + Qt::PlainText + + + unit + + + + + + + Choose the default subdivision unit to show in the interface and when sending coins. + + + + + + + + + Whether to show XP addresses in the transaction list or not. + + + &Display addresses in transaction list + + + + + + + Whether to show coin control features or not. + + + Display coin &control features (experts only!) + + + + + + + + + Third party URLs (e.g. explorer.novaco.in) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |. + + + Third party transaction URLs + + + thirdPartyTxUrls + + + + + + + Third party URLs (e.g. explorer.novaco.in) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |. + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 48 + + + + + + + + + 75 + true + + + + + + + Qt::PlainText + + + true + + + + + + + Qt::Horizontal + + + + 40 + 48 + + + + + + + + &OK + + + + + + + &Cancel + + + false + + + + + + + &Apply + + + false + + + + + + + + + + BitcoinAmountField + QSpinBox +
bitcoinamountfield.h
+
+ + QValueComboBox + QComboBox +
qvaluecombobox.h
+
+ + QValidatedLineEdit + QLineEdit +
qvalidatedlineedit.h
+
+
+ + +
diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui new file mode 100644 index 00000000..d162ce22 --- /dev/null +++ b/src/qt/forms/overviewpage.ui @@ -0,0 +1,434 @@ + + + OverviewPage + + + + 0 + 0 + 573 + 342 + + + + Form + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + 75 + true + + + + Balances + + + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the XP network after a connection is established, but this process has not completed yet. + + + QLabel { color: red; } + + + (out of sync) + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + 12 + + + 12 + + + + + Your current available balance + + + Available: + + + + + + + + 75 + true + + + + IBeamCursor + + + Your current available balance + + + 0 XP + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Total of coins that was staked, and do not yet count toward the current balance + + + Stake: + + + + + + + + 75 + true + + + + IBeamCursor + + + Total of coins that was staked, and do not yet count toward the current balance + + + 0 XP + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + + + Unconfirmed: + + + + + + + + 75 + true + + + + IBeamCursor + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + + + 0 XP + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Mined balance that has not yet matured + + + Immature: + + + + + + + + 75 + true + + + + Mined balance that has not yet matured + + + 0 XP + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Your unspendable balance + + + Unspendable: + + + + + + + + 75 + true + + + + IBeamCursor + + + Your unspendable balance + + + 0 XP + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + 230 + 16777215 + + + + Qt::Horizontal + + + + + + + Your current total balance + + + Total: + + + + + + + + 75 + true + + + + Your current total balance + + + 0 XP + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Total number of transactions in wallet + + + Number of transactions: + + + + + + + Total number of transactions in wallet + + + 0 + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + <b>Recent transactions</b> + + + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the XP network after a connection is established, but this process has not completed yet. + + + QLabel { color: red; } + + + (out of sync) + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + QListView { background: transparent; } + + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + QAbstractItemView::NoSelection + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + diff --git a/src/qt/forms/qrcodedialog.ui b/src/qt/forms/qrcodedialog.ui new file mode 100644 index 00000000..52e9db37 --- /dev/null +++ b/src/qt/forms/qrcodedialog.ui @@ -0,0 +1,212 @@ + + + QRCodeDialog + + + + 0 + 0 + 340 + 530 + + + + QR Code Dialog + + + + + + + 0 + 0 + + + + + 300 + 300 + + + + Qt::PlainText + + + Qt::AlignCenter + + + true + + + + + + + + 0 + 0 + + + + + 0 + 50 + + + + true + + + Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + + true + + + Request Payment + + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Label: + + + Qt::PlainText + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + lnLabel + + + + + + + + + + Message: + + + Qt::PlainText + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + lnMessage + + + + + + + + + + + 0 + 0 + + + + Amount: + + + Qt::PlainText + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + lnReqAmount + + + + + + + false + + + + 80 + 0 + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + &Save As... + + + + + + + + + + + + + BitcoinAmountField + QSpinBox +
bitcoinamountfield.h
+
+
+ + + + chkReqPayment + clicked(bool) + lnReqAmount + setEnabled(bool) + + + 92 + 285 + + + 98 + 311 + + + + +
diff --git a/src/qt/forms/rpcconsole.ui b/src/qt/forms/rpcconsole.ui new file mode 100644 index 00000000..c3e5514b --- /dev/null +++ b/src/qt/forms/rpcconsole.ui @@ -0,0 +1,782 @@ + + + RPCConsole + + + + 0 + 0 + 740 + 450 + + + + XP - Debug window + + + + + + 0 + + + + &Information + + + + + + + 75 + true + + + + Configuration file + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Current number of blocks + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + 75 + true + + + + Command-line options + + + + + + + Open the XP debug log file from the current data directory. This can take a few seconds for large log files. + + + &Open + + + false + + + + + + + + 75 + true + + + + Debug log file + + + + + + + Qt::Horizontal + + + + 404 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Show the XP-Qt help message to get a list with possible XP command-line options. + + + &Show + + + false + + + + + + + Last block time + + + + + + + + 75 + true + + + + Network + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Startup time + + + + + + + On testnet + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Number of connections + + + + + + + + 75 + true + + + + Block chain + + + + + + + false + + + + + + + + + + Estimated total blocks + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Using BerkeleyDB version + + + 10 + + + + + + + Using OpenSSL version + + + 10 + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Build date + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Client version + + + + + + + + 75 + true + + + + XP Core + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Client name + + + + + + + Qt::Vertical + + + + 20 + 20 + + + + + + + + Open the XP configuration file from the current data directory. + + + O&pen + + + false + + + + + + + + &Console + + + + 3 + + + + + + 0 + 100 + + + + true + + + false + + + 2 + + + + + + + 3 + + + + + > + + + + + + + + + + + 24 + 24 + + + + Clear console + + + + + + + :/icons/remove:/icons/remove + + + Ctrl+L + + + false + + + + + + + + + + &Network Traffic + + + + + + + + + 0 + 0 + + + + + + + + + + 1 + + + 288 + + + 12 + + + 6 + + + Qt::Horizontal + + + + + + + + 100 + 0 + + + + Qt::AlignCenter + + + + + + + &Clear + + + false + + + + + + + + + + + + + Totals + + + + + + + + + 0 + 0 + + + + + 10 + 0 + + + + + + + + + 0 + 255 + 0 + + + + + + + + + 0 + 255 + 0 + + + + + + + + + 0 + 255 + 0 + + + + + + + + Qt::Horizontal + + + + + + + Received: + + + + + + + + 50 + 0 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + 0 + 0 + + + + + 10 + 0 + + + + + + + + + 255 + 0 + 0 + + + + + + + + + 255 + 0 + 0 + + + + + + + + + 255 + 0 + 0 + + + + + + + + Qt::Horizontal + + + + + + + Sent: + + + + + + + + 50 + 0 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + Qt::Vertical + + + + 20 + 407 + + + + + + + + + + + + + + + + + + TrafficGraphWidget + QWidget +
trafficgraphwidget.h
+ 1 + + clear() + +
+
+ + + + +
diff --git a/src/qt/forms/secondauthdialog.ui b/src/qt/forms/secondauthdialog.ui new file mode 100644 index 00000000..7d529fc6 --- /dev/null +++ b/src/qt/forms/secondauthdialog.ui @@ -0,0 +1,230 @@ + + + SecondAuthDialog + + + Qt::ApplicationModal + + + + 0 + 0 + 768 + 221 + + + + Second Authentification + + + + + + You can sign hash of transaction exists in the blockchain with your addresses. + + + Qt::PlainText + + + true + + + + + + + + + The address for authentification (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + 34 + + + + + + + Choose an address from the address book + + + + + + + :/icons/address-book:/icons/address-book + + + Alt+A + + + false + + + + + + + + + + + 64 + + + + + + + Paste hash from clipboard + + + + + + + :/icons/editpaste:/icons/editpaste + + + Alt+P + + + false + + + + + + + + + 6 + + + + + + true + + + + true + + + + + + + Copy the current signature to the system clipboard + + + + + + + :/icons/editcopy:/icons/editcopy + + + Alt+C + + + false + + + + + + + + + + + Sign the hash + + + &Sign Data + + + + :/icons/edit:/icons/edit + + + false + + + + + + + Reset all sign message fields + + + Clear &All + + + + :/icons/remove:/icons/remove + + + false + + + + + + + Qt::Horizontal + + + + 10 + 48 + + + + + + + + + 75 + true + + + + + + + true + + + + + + + Qt::Horizontal + + + + 40 + 48 + + + + + + + + + + + QValidatedLineEdit + QLineEdit +
qvalidatedlineedit.h
+
+
+ + + + +
diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui new file mode 100644 index 00000000..1cc35fba --- /dev/null +++ b/src/qt/forms/sendcoinsdialog.ui @@ -0,0 +1,794 @@ + + + SendCoinsDialog + + + + 0 + 0 + 850 + 400 + + + + Send Coins + + + + 8 + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + -1 + + + 0 + + + 0 + + + 0 + + + 6 + + + + + 0 + + + 10 + + + 10 + + + + + 15 + + + + + + 0 + 0 + + + + + 75 + true + + + + font-weight:bold; + + + Coin Control Features + + + + + + + + + 8 + + + 10 + + + + + + + + Inputs... + + + + + + + automatically selected + + + 5 + + + + + + + + 75 + true + + + + color:red;font-weight:bold; + + + Insufficient funds! + + + 5 + + + + + + + Qt::Horizontal + + + + 40 + 1 + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + + 0 + + + + + 20 + + + 0 + + + 10 + + + + + 10 + + + 14 + + + 10 + + + 4 + + + 6 + + + + + font-weight:bold; + + + Quantity: + + + 0 + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0 + + + 0 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + font-weight:bold; + + + Bytes: + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + 10 + + + 14 + + + 6 + + + 4 + + + 6 + + + + + font-weight:bold; + + + Amount: + + + 0 + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0.00 XP + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + font-weight:bold; + + + Priority: + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + medium + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + 10 + + + 14 + + + 6 + + + 4 + + + 6 + + + + + font-weight:bold; + + + Fee: + + + 0 + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0.00 XP + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + font-weight:bold; + + + Low Output: + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + no + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + 10 + + + 14 + + + 6 + + + 4 + + + 6 + + + + + font-weight:bold; + + + After Fee: + + + 0 + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0.00 XP + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + font-weight:bold; + + + Change + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0.00 XP + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + + + + + + 12 + + + QLayout::SetDefaultConstraint + + + 5 + + + 5 + + + + + custom change address + + + + + + + false + + + + 0 + 0 + + + + + + + + false + + + Choose address from address book + + + + + + + :/icons/address-book:/icons/address-book + + + Alt+A + + + + + + + false + + + Paste address from clipboard + + + + + + + :/icons/editpaste:/icons/editpaste + + + Alt+P + + + + + + + + + Qt::Vertical + + + + 800 + 1 + + + + + + + + + + + + + true + + + + + 0 + 0 + 830 + 165 + + + + + 0 + + + + + 6 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + Send to multiple recipients at once + + + Add &Recipient + + + + :/icons/add:/icons/add + + + false + + + + + + + + 0 + 0 + + + + Remove all transaction fields + + + Clear &All + + + + :/icons/remove:/icons/remove + + + 300 + + + false + + + + + + + 3 + + + + + Balance: + + + + + + + IBeamCursor + + + 123.456 XP + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 150 + 0 + + + + Confirm the send action + + + S&end + + + + :/icons/send:/icons/send + + + true + + + + + + + + + + + + diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui new file mode 100644 index 00000000..3d90e497 --- /dev/null +++ b/src/qt/forms/sendcoinsentry.ui @@ -0,0 +1,169 @@ + + + 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. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + 34 + + + + + + + Choose address from address book + + + + + + + :/icons/address-book:/icons/address-book + + + Alt+A + + + + + + + Paste address from clipboard + + + + + + + :/icons/editpaste:/icons/editpaste + + + Alt+P + + + + + + + Remove this recipient + + + + + + + :/icons/remove:/icons/remove + + + + + + + + + + QValidatedLineEdit + QLineEdit +
qvalidatedlineedit.h
+
+ + BitcoinAmountField + QLineEdit +
bitcoinamountfield.h
+ 1 +
+
+ + + + +
diff --git a/src/qt/forms/signverifymessagedialog.ui b/src/qt/forms/signverifymessagedialog.ui new file mode 100644 index 00000000..1d598f2f --- /dev/null +++ b/src/qt/forms/signverifymessagedialog.ui @@ -0,0 +1,386 @@ + + + SignVerifyMessageDialog + + + Qt::ApplicationModal + + + + 0 + 0 + 700 + 380 + + + + Signatures - Sign / Verify a Message + + + + + + 1 + + + + &Sign Message + + + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + + + Qt::PlainText + + + true + + + + + + + 0 + + + + + The address to sign the message with (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + 34 + + + + + + + Choose an address from the address book + + + + + + + :/icons/address-book:/icons/address-book + + + Alt+A + + + false + + + + + + + Paste address from clipboard + + + + + + + :/icons/editpaste:/icons/editpaste + + + Alt+P + + + false + + + + + + + + + Enter the message you want to sign here + + + + + + + 0 + + + + + + true + + + + true + + + + + + + Copy the current signature to the system clipboard + + + + + + + :/icons/editcopy:/icons/editcopy + + + false + + + + + + + + + + + Sign the message to prove you own this XP address + + + &Sign Message + + + + :/icons/edit:/icons/edit + + + false + + + + + + + Reset all sign message fields + + + Clear &All + + + + :/icons/remove:/icons/remove + + + false + + + + + + + Qt::Horizontal + + + + 40 + 48 + + + + + + + + + 75 + true + + + + + + + true + + + + + + + Qt::Horizontal + + + + 40 + 48 + + + + + + + + + + + &Verify Message + + + + + + Enter the signing address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack. + + + Qt::PlainText + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + 0 + + + + + The address the message was signed with (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + 34 + + + + + + + Choose an address from the address book + + + + + + + :/icons/address-book:/icons/address-book + + + Alt+A + + + false + + + + + + + + + + + + + + + + + Verify the message to ensure it was signed with the specified XP address + + + &Verify Message + + + + :/icons/transaction_0:/icons/transaction_0 + + + false + + + + + + + Reset all verify message fields + + + Clear &All + + + + :/icons/remove:/icons/remove + + + false + + + + + + + Qt::Horizontal + + + + 40 + 48 + + + + + + + + + 75 + true + + + + + + + true + + + + + + + Qt::Horizontal + + + + 40 + 48 + + + + + + + + + + + + + + + QValidatedLineEdit + QLineEdit +
qvalidatedlineedit.h
+
+
+ + + + +
diff --git a/src/qt/forms/transactiondescdialog.ui b/src/qt/forms/transactiondescdialog.ui new file mode 100644 index 00000000..dd78c38d --- /dev/null +++ b/src/qt/forms/transactiondescdialog.ui @@ -0,0 +1,74 @@ + + + TransactionDescDialog + + + + 0 + 0 + 620 + 250 + + + + Transaction details + + + + + + This pane shows a detailed description of the transaction + + + true + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + buttonBox + accepted() + TransactionDescDialog + close() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + TransactionDescDialog + close() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h new file mode 100644 index 00000000..61495533 --- /dev/null +++ b/src/qt/guiconstants.h @@ -0,0 +1,39 @@ +#ifndef GUICONSTANTS_H +#define GUICONSTANTS_H + +/* Milliseconds between model updates */ +static const int MODEL_UPDATE_DELAY = 500; + +/* AskPassphraseDialog -- Maximum passphrase length */ +static const int MAX_PASSPHRASE_SIZE = 1024; + +/* BitcoinGUI -- Size of icons in status bar */ +static const int STATUSBAR_ICONSIZE = 16; + +/* 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(255, 0, 0) +/* Transaction list -- bare address (without label) */ +#define COLOR_BAREADDRESS QColor(140, 140, 140) + +/* Tooltips longer than this (in characters) are converted into rich text, + so that they can be word-wrapped. + */ +static const int TOOLTIP_WRAP_THRESHOLD = 80; + +/* Maximum allowed URI length */ +static const int MAX_URI_LENGTH = 255; + +/* QRCodeDialog -- size of exported QR Code image */ +#define EXPORT_IMAGE_SIZE 256 + +/* Colors for minting tab for each coin age group */ +#define COLOR_MINT_YOUNG QColor(180, 180, 250) +#define COLOR_MINT_MATURE QColor(180, 250, 180) +#define COLOR_MINT_OLD QColor(250, 180, 180) + +#endif // GUICONSTANTS_H diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp new file mode 100644 index 00000000..a685ff02 --- /dev/null +++ b/src/qt/guiutil.cpp @@ -0,0 +1,539 @@ +#include "guiutil.h" +#include "bitcoinaddressvalidator.h" +#include "walletmodel.h" +#include "bitcoinunits.h" +#include "util.h" +#include "init.h" + +#include +#include +#include +#include +#include +#if QT_VERSION >= 0x050000 +#include +#else +#include +#endif +#include // For Qt::mightBeRichText +#include +#include +#include +#include +#include +#include + +#ifndef Q_MOC_RUN +#include +#include +#if BOOST_FILESYSTEM_VERSION >= 3 +#include +#endif +#endif + +#ifdef WIN32 +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif +#define _WIN32_WINNT 0x0501 +#ifdef _WIN32_IE +#undef _WIN32_IE +#endif +#define _WIN32_IE 0x0501 +#define WIN32_LEAN_AND_MEAN 1 +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include "shlwapi.h" +#include "shlobj.h" +#include "shellapi.h" +#endif + +#if BOOST_FILESYSTEM_VERSION >= 3 +static boost::filesystem::detail::utf8_codecvt_facet utf8; +#endif + +namespace GUIUtil { + +#if BOOST_FILESYSTEM_VERSION >= 3 +boost::filesystem::path qstringToBoostPath(const QString &path) +{ + return boost::filesystem::path(path.toStdString(), utf8); +} +QString boostPathToQString(const boost::filesystem::path &path) +{ + return QString::fromStdString(path.string(utf8)); +} +#else +#warning Conversion between boost path and QString can use invalid character encoding with boost_filesystem v2 and older +boost::filesystem::path qstringToBoostPath(const QString &path) +{ + return boost::filesystem::path(path.toStdString()); +} +QString boostPathToQString(const boost::filesystem::path &path) +{ + return QString::fromStdString(path.string()); +} +#endif + +QString dateTimeStr(const QDateTime &date) +{ + return date.date().toString(Qt::SystemLocaleShortDate) + QString(" ") + date.toString("hh:mm"); +} + +QString dateTimeStr(qint64 nTime) +{ + return dateTimeStr(QDateTime::fromTime_t((qint32)nTime)); +} + +QFont bitcoinAddressFont() +{ + QFont font("Monospace"); + font.setStyleHint(QFont::TypeWriter); + return font; +} + +void setupAddressWidget(QLineEdit *widget, QWidget *parent) +{ + widget->setMaxLength(BitcoinAddressValidator::MaxAddressLength); + widget->setValidator(new BitcoinAddressValidator(parent)); + widget->setFont(bitcoinAddressFont()); +} + +void setupAmountWidget(QLineEdit *widget, QWidget *parent) +{ + QDoubleValidator *amountValidator = new QDoubleValidator(parent); + amountValidator->setDecimals(8); + amountValidator->setBottom(0.0); + widget->setValidator(amountValidator); + widget->setAlignment(Qt::AlignRight|Qt::AlignVCenter); +} + +bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out) +{ + // XP: check prefix + if(uri.scheme() != QString("XP")) + return false; + + SendCoinsRecipient rv; + rv.address = uri.path(); + rv.amount = 0; +#if QT_VERSION < 0x050000 + QList > items = uri.queryItems(); +#else + QUrlQuery uriQuery(uri); + QList > items = uriQuery.queryItems(); +#endif + for (QList >::iterator i = items.begin(); i != items.end(); i++) + { + bool fShouldReturnFalse = false; + if (i->first.startsWith("req-")) + { + i->first.remove(0, 4); + fShouldReturnFalse = true; + } + + if (i->first == "label") + { + rv.label = i->second; + fShouldReturnFalse = false; + } + else if (i->first == "amount") + { + if(!i->second.isEmpty()) + { + if(!BitcoinUnits::parse(BitcoinUnits::BTC, i->second, &rv.amount)) + { + return false; + } + } + fShouldReturnFalse = false; + } + + if (fShouldReturnFalse) + return false; + } + if(out) + { + *out = rv; + } + return true; +} + +bool parseBitcoinURI(QString uri, SendCoinsRecipient *out) +{ + // Convert XP:// to XP: + // + // Cannot handle this later, because bitcoin:// will cause Qt to see the part after // as host, + // which will lower-case it (and thus invalidate the address). + if(uri.startsWith("XP://")) + { + uri.replace(0, 10, "XP:"); + } + QUrl uriInstance(uri); + return parseBitcoinURI(uriInstance, out); +} + +QString HtmlEscape(const QString& str, bool fMultiLine) +{ +#if QT_VERSION < 0x050000 + QString escaped = Qt::escape(str); +#else + QString escaped = str.toHtmlEscaped(); +#endif + if(fMultiLine) + { + escaped = escaped.replace("\n", "
\n"); + } + return escaped; +} + +QString HtmlEscape(const std::string& str, bool fMultiLine) +{ + return HtmlEscape(QString::fromStdString(str), fMultiLine); +} + +void copyEntryData(QAbstractItemView *view, int column, int role) +{ + if(!view || !view->selectionModel()) + return; + QModelIndexList selection = view->selectionModel()->selectedRows(column); + + if(!selection.isEmpty()) + { + // Copy first item + QApplication::clipboard()->setText(selection.at(0).data(role).toString()); + } +} + +QString getSaveFileName(QWidget *parent, const QString &caption, + const QString &dir, + const QString &filter, + QString *selectedSuffixOut) +{ + QString selectedFilter; + QString myDir; + if(dir.isEmpty()) // Default to user documents location + { +#if QT_VERSION < 0x050000 + myDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); +#else + myDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); +#endif + } + else + { + myDir = dir; + } + QString result = QFileDialog::getSaveFileName(parent, caption, myDir, filter, &selectedFilter); + + /* Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...) */ + QRegExp filter_re(".* \\(\\*\\.(.*)[ \\)]"); + QString selectedSuffix; + if(filter_re.exactMatch(selectedFilter)) + { + selectedSuffix = filter_re.cap(1); + } + + /* Add suffix if needed */ + QFileInfo info(result); + if(!result.isEmpty()) + { + if(info.suffix().isEmpty() && !selectedSuffix.isEmpty()) + { + /* No suffix specified, add selected suffix */ + if(!result.endsWith(".")) + result.append("."); + result.append(selectedSuffix); + } + } + + /* Return selected suffix if asked to */ + if(selectedSuffixOut) + { + *selectedSuffixOut = selectedSuffix; + } + return result; +} + +Qt::ConnectionType blockingGUIThreadConnection() +{ + if(QThread::currentThread() != QCoreApplication::instance()->thread()) + { + return Qt::BlockingQueuedConnection; + } + else + { + return Qt::DirectConnection; + } +} + +bool checkPoint(const QPoint &p, const QWidget *w) +{ + QWidget *atW = qApp->widgetAt(w->mapToGlobal(p)); + if (!atW) return false; + return atW->topLevelWidget() == w; +} + +bool isObscured(QWidget *w) +{ + return !(checkPoint(QPoint(0, 0), w) + && checkPoint(QPoint(w->width() - 1, 0), w) + && checkPoint(QPoint(0, w->height() - 1), w) + && checkPoint(QPoint(w->width() - 1, w->height() - 1), w) + && checkPoint(QPoint(w->width() / 2, w->height() / 2), w)); +} + +void openDebugLogfile() +{ + boost::filesystem::path pathDebug = GetDataDir() / "debug.log"; + + /* Open debug.log with the associated application */ + if (boost::filesystem::exists(pathDebug)) + QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(pathDebug.string()))); +} + +void openConfigfile() +{ + boost::filesystem::path pathConfig = GetConfigFile(); + + /* Open XP.conf with the associated application */ + if (boost::filesystem::exists(pathConfig)) + QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(pathConfig.string()))); +} + +ToolTipToRichTextFilter::ToolTipToRichTextFilter(int size_threshold, QObject *parent) : + QObject(parent), size_threshold(size_threshold) +{ + +} + +bool ToolTipToRichTextFilter::eventFilter(QObject *obj, QEvent *evt) +{ + if(evt->type() == QEvent::ToolTipChange) + { + QWidget *widget = static_cast(obj); + QString tooltip = widget->toolTip(); + if(tooltip.size() > size_threshold && !tooltip.startsWith("") && !Qt::mightBeRichText(tooltip)) + { + // Prefix to make sure Qt detects this as rich text + // Escape the current message as HTML and replace \n by
+ tooltip = "" + HtmlEscape(tooltip, true) + ""; + widget->setToolTip(tooltip); + return true; + } + } + return QObject::eventFilter(obj, evt); +} + +#ifdef WIN32 +boost::filesystem::path static StartupShortcutPath() +{ + return GetSpecialFolderPath(CSIDL_STARTUP) / "XP.lnk"; +} + +bool GetStartOnSystemStartup() +{ + // check for Bitcoin.lnk + return boost::filesystem::exists(StartupShortcutPath()); +} + +bool SetStartOnSystemStartup(bool fAutoStart) +{ + // If the shortcut exists already, remove it for updating + boost::filesystem::remove(StartupShortcutPath()); + + if (fAutoStart) + { + CoInitialize(NULL); + + // Get a pointer to the IShellLink interface. + IShellLink* psl = NULL; + HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, + CLSCTX_INPROC_SERVER, IID_IShellLink, + reinterpret_cast(&psl)); + + if (SUCCEEDED(hres)) + { + // Get the current executable path + TCHAR pszExePath[MAX_PATH]; + GetModuleFileName(NULL, pszExePath, sizeof(pszExePath)); + + TCHAR pszArgs[5] = TEXT("-min"); + + // Set the path to the shortcut target + psl->SetPath(pszExePath); + PathRemoveFileSpec(pszExePath); + psl->SetWorkingDirectory(pszExePath); + psl->SetShowCmd(SW_SHOWMINNOACTIVE); + psl->SetArguments(pszArgs); + + // Query IShellLink for the IPersistFile interface for + // saving the shortcut in persistent storage. + IPersistFile* ppf = NULL; + hres = psl->QueryInterface(IID_IPersistFile, + reinterpret_cast(&ppf)); + if (SUCCEEDED(hres)) + { + WCHAR pwsz[MAX_PATH]; + // Ensure that the string is ANSI. + MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().string().c_str(), -1, pwsz, MAX_PATH); + // Save the link by calling IPersistFile::Save. + hres = ppf->Save(pwsz, TRUE); + ppf->Release(); + psl->Release(); + CoUninitialize(); + return true; + } + psl->Release(); + } + CoUninitialize(); + return false; + } + return true; +} + +#elif defined(LINUX) + +// Follow the Desktop Application Autostart Spec: +// http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html + +boost::filesystem::path static GetAutostartDir() +{ + namespace fs = boost::filesystem; + + char* pszConfigHome = getenv("XDG_CONFIG_HOME"); + if (pszConfigHome) return fs::path(pszConfigHome) / "autostart"; + char* pszHome = getenv("HOME"); + if (pszHome) return fs::path(pszHome) / ".config" / "autostart"; + return fs::path(); +} + +boost::filesystem::path static GetAutostartFilePath() +{ + return GetAutostartDir() / "XP.desktop"; +} + +bool GetStartOnSystemStartup() +{ + boost::filesystem::ifstream optionFile(GetAutostartFilePath()); + if (!optionFile.good()) + return false; + // Scan through file for "Hidden=true": + std::string line; + while (!optionFile.eof()) + { + getline(optionFile, line); + if (line.find("Hidden") != std::string::npos && + line.find("true") != std::string::npos) + return false; + } + optionFile.close(); + + return true; +} + +bool SetStartOnSystemStartup(bool fAutoStart) +{ + if (!fAutoStart) + boost::filesystem::remove(GetAutostartFilePath()); + else + { + char pszExePath[MAX_PATH+1]; + memset(pszExePath, 0, sizeof(pszExePath)); + if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1) + return false; + + boost::filesystem::create_directories(GetAutostartDir()); + + boost::filesystem::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out|std::ios_base::trunc); + if (!optionFile.good()) + return false; + // Write a bitcoin.desktop file to the autostart directory: + optionFile << "[Desktop Entry]\n"; + optionFile << "Type=Application\n"; + optionFile << "Name=XP\n"; + optionFile << "Exec=" << pszExePath << " -min\n"; + optionFile << "Terminal=false\n"; + optionFile << "Hidden=false\n"; + optionFile.close(); + } + return true; +} +#else + +// TODO: OSX startup stuff; see: +// https://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html + +bool GetStartOnSystemStartup() { return false; } +bool SetStartOnSystemStartup(bool fAutoStart) { return false; } + +#endif + +HelpMessageBox::HelpMessageBox(QWidget *parent) : + QMessageBox(parent) +{ + header = tr("XP-Qt") + " " + tr("version") + " " + + QString::fromStdString(FormatFullVersion()) + "\n\n" + + tr("Usage:") + "\n" + + " XP-qt [" + tr("command-line options") + "] " + "\n"; + + coreOptions = QString::fromStdString(HelpMessage()); + + uiOptions = tr("UI options") + ":\n" + + " -lang= " + tr("Set language, for example \"de_DE\" (default: system locale)") + "\n" + + " -min " + tr("Start minimized") + "\n" + + " -splash " + tr("Show splash screen on startup (default: 1)") + "\n"; + + setWindowTitle(tr("XP-Qt")); + setFont(bitcoinAddressFont()); + setTextFormat(Qt::PlainText); + // setMinimumWidth is ignored for QMessageBox so put in non-breaking spaces to make it wider. + setText(header + QString(QChar(0x2003)).repeated(50)); + setDetailedText(coreOptions + "\n" + uiOptions); + addButton("OK", QMessageBox::RejectRole); //кнопка OK будет справа от кнопки "Скрыть подробности" + //addButton("OK", QMessageBox::NoRole); //кнопка OK будет слева от кнопки "Скрыть подробности" + setMouseTracking(true); + setSizeGripEnabled(true); +} + +void HelpMessageBox::printToConsole() +{ + // On other operating systems, the expected action is to print the message to the console. + QString strUsage = header + "\n" + coreOptions + "\n" + uiOptions; + fprintf(stdout, "%s", strUsage.toStdString().c_str()); +} + +void HelpMessageBox::showOrPrint() +{ +#if defined(WIN32) + // On Windows, show a message box, as there is no stderr/stdout in windowed applications + exec(); +#else + // On other operating systems, print help text to console + printToConsole(); +#endif +} + +QString formatDurationStr(int secs) +{ + QStringList strList; + int days = secs / 86400; + int hours = (secs % 86400) / 3600; + int mins = (secs % 3600) / 60; + int seconds = secs % 60; + + if (days) + strList.append(QString(QObject::tr("%1 d")).arg(days)); + if (hours) + strList.append(QString(QObject::tr("%1 h")).arg(hours)); + if (mins) + strList.append(QString(QObject::tr("%1 m")).arg(mins)); + if (seconds || (!days && !hours && !mins)) + strList.append(QString(QObject::tr("%1 s")).arg(seconds)); + + return strList.join(" "); +} + +} // namespace GUIUtil + diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h new file mode 100644 index 00000000..b912a65c --- /dev/null +++ b/src/qt/guiutil.h @@ -0,0 +1,148 @@ +#ifndef GUIUTIL_H +#define GUIUTIL_H + +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE +class QFont; +class QLineEdit; +class QWidget; +class QDateTime; +class QUrl; +class QAbstractItemView; +QT_END_NAMESPACE +class SendCoinsRecipient; + +/** Utility functions used by the Bitcoin Qt UI. + */ +namespace GUIUtil +{ + /* Convert QString to OS specific boost path through UTF-8 */ + boost::filesystem::path qstringToBoostPath(const QString &path); + /* Convert OS specific boost path to QString through UTF-8 */ + QString boostPathToQString(const boost::filesystem::path &path); + + // Create human-readable string from date + QString dateTimeStr(const QDateTime &datetime); + QString dateTimeStr(qint64 nTime); + + // Render Bitcoin addresses in monospace font + QFont bitcoinAddressFont(); + + // Set up widgets for address and amounts + void setupAddressWidget(QLineEdit *widget, QWidget *parent); + void setupAmountWidget(QLineEdit *widget, QWidget *parent); + + // Parse "XP:" URI into recipient object, return true on successful parsing + // See Bitcoin URI definition discussion here: https://bitcointalk.org/index.php?topic=33490.0 + bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out); + bool parseBitcoinURI(QString uri, SendCoinsRecipient *out); + + // HTML escaping for rich text controls + QString HtmlEscape(const QString& str, bool fMultiLine=false); + QString HtmlEscape(const std::string& str, bool fMultiLine=false); + + /** Copy a field of the currently selected entry of a view to the clipboard. Does nothing if nothing + is selected. + @param[in] column Data column to extract from the model + @param[in] role Data role to extract from the model + @see TransactionView::copyLabel, TransactionView::copyAmount, TransactionView::copyAddress + */ + void copyEntryData(QAbstractItemView *view, int column, int role=Qt::EditRole); + + /** Get save filename, mimics QFileDialog::getSaveFileName, except that it appends a default suffix + when no suffix is provided by the user. + + @param[in] parent Parent window (or 0) + @param[in] caption Window caption (or empty, for default) + @param[in] dir Starting directory (or empty, to default to documents directory) + @param[in] filter Filter specification such as "Comma Separated Files (*.csv)" + @param[out] selectedSuffixOut Pointer to return the suffix (file type) that was selected (or 0). + Can be useful when choosing the save file format based on suffix. + */ + QString getSaveFileName(QWidget *parent=0, const QString &caption=QString(), + const QString &dir=QString(), const QString &filter=QString(), + QString *selectedSuffixOut=0); + + /** Get connection type to call object slot in GUI thread with invokeMethod. The call will be blocking. + + @returns If called from the GUI thread, return a Qt::DirectConnection. + If called from another thread, return a Qt::BlockingQueuedConnection. + */ + Qt::ConnectionType blockingGUIThreadConnection(); + + // Determine whether a widget is hidden behind other windows + bool isObscured(QWidget *w); + + // Open debug.log + void openDebugLogfile(); + // Open XP.conf + void openConfigfile(); + /** Qt event filter that intercepts ToolTipChange events, and replaces the tooltip with a rich text + representation if needed. This assures that Qt can word-wrap long tooltip messages. + Tooltips longer than the provided size threshold (in characters) are wrapped. + */ + class ToolTipToRichTextFilter : public QObject + { + Q_OBJECT + + public: + explicit ToolTipToRichTextFilter(int size_threshold, QObject *parent = 0); + + protected: + bool eventFilter(QObject *obj, QEvent *evt); + + private: + int size_threshold; + }; + + bool GetStartOnSystemStartup(); + bool SetStartOnSystemStartup(bool fAutoStart); + + /** Help message for Bitcoin-Qt, shown with --help. */ + class HelpMessageBox : public QMessageBox + { + Q_OBJECT + + public: + HelpMessageBox(QWidget *parent = 0); + + /** Show message box or print help message to standard output, based on operating system. */ + void showOrPrint(); + + /** Print help message to console */ + void printToConsole(); + + private: + QString header; + QString coreOptions; + QString uiOptions; + + virtual bool event(QEvent *e) + { + bool res = QMessageBox::event(e); + if (e->type() == QEvent::MouseMove || e->type() == QEvent::MouseButtonPress) + { + setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); + if (QWidget *textEdit = findChild()) + { + textEdit->setMaximumHeight(QWIDGETSIZE_MAX); + } + } + + return res; + } + }; + /* Convert seconds into a QString with days, hours, mins, secs */ + QString formatDurationStr(int secs); + +} // namespace GUIUtil + +#endif // GUIUTIL_H diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp new file mode 100644 index 00000000..1feac436 --- /dev/null +++ b/src/qt/intro.cpp @@ -0,0 +1,292 @@ +// Copyright (c) 2011-2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "intro.h" +#include "ui_intro.h" + +#include "guiutil.h" + +#include "util.h" + +#include + +#include +#include +#include + +/* Minimum free space (in bytes) needed for data directory */ +static const uint64_t GB_BYTES = 1000000000LL; +static const uint64_t BLOCK_CHAIN_SIZE = 1LL * GB_BYTES; + +/* Check free space asynchronously to prevent hanging the UI thread. + + Up to one request to check a path is in flight to this thread; when the check() + function runs, the current path is requested from the associated Intro object. + The reply is sent back through a signal. + + This ensures that no queue of checking requests is built up while the user is + still entering the path, and that always the most recently entered path is checked as + soon as the thread becomes available. +*/ +class FreespaceChecker : public QObject +{ + Q_OBJECT + +public: + FreespaceChecker(Intro *intro); + + enum Status { + ST_OK, + ST_ERROR + }; + +public slots: + void check(); + +signals: + void reply(int status, const QString &message, quint64 available); + +private: + Intro *intro; +}; + +#include "intro.moc" + +FreespaceChecker::FreespaceChecker(Intro *intro) +{ + this->intro = intro; +} + +void FreespaceChecker::check() +{ + namespace fs = boost::filesystem; + QString dataDirStr = intro->getPathToCheck(); + fs::path dataDir = GUIUtil::qstringToBoostPath(dataDirStr); + uint64_t freeBytesAvailable = 0; + int replyStatus = ST_OK; + QString replyMessage = tr("A new data directory will be created."); + + /* Find first parent that exists, so that fs::space does not fail */ + fs::path parentDir = dataDir; + fs::path parentDirOld = fs::path(); + while(parentDir.has_parent_path() && !fs::exists(parentDir)) + { + parentDir = parentDir.parent_path(); + + /* Check if we make any progress, break if not to prevent an infinite loop here */ + if (parentDirOld == parentDir) + break; + + parentDirOld = parentDir; + } + + try { + freeBytesAvailable = fs::space(parentDir).available; + if(fs::exists(dataDir)) + { + if(fs::is_directory(dataDir)) + { + QString separator = "" + QDir::toNativeSeparators("/") + tr("name") + ""; + replyStatus = ST_OK; + replyMessage = tr("Directory already exists. Add %1 if you intend to create a new directory here.").arg(separator); + } else { + replyStatus = ST_ERROR; + replyMessage = tr("Path already exists, and is not a directory."); + } + } + } catch (const fs::filesystem_error&) + { + /* Parent directory does not exist or is not accessible */ + replyStatus = ST_ERROR; + replyMessage = tr("Cannot create data directory here."); + } + emit reply(replyStatus, replyMessage, freeBytesAvailable); +} + + +Intro::Intro(QWidget *parent) : + QDialog(parent), + ui(new Ui::Intro), + thread(0), + signalled(false) +{ + ui->setupUi(this); + ui->sizeWarningLabel->setText(ui->sizeWarningLabel->text().arg(BLOCK_CHAIN_SIZE/GB_BYTES)); + startThread(); +} + +Intro::~Intro() +{ + delete ui; + /* Ensure thread is finished before it is deleted */ + emit stopThread(); + thread->wait(); +} + +QString Intro::getDataDirectory() +{ + return ui->dataDirectory->text(); +} + +void Intro::setDataDirectory(const QString &dataDir) +{ + ui->dataDirectory->setText(dataDir); + if(dataDir == getDefaultDataDirectory()) + { + ui->dataDirDefault->setChecked(true); + ui->dataDirectory->setEnabled(false); + ui->ellipsisButton->setEnabled(false); + } else { + ui->dataDirCustom->setChecked(true); + ui->dataDirectory->setEnabled(true); + ui->ellipsisButton->setEnabled(true); + } +} + +QString Intro::getDefaultDataDirectory() +{ + return GUIUtil::boostPathToQString(GetDefaultDataDir()); +} + +void Intro::pickDataDirectory() +{ + namespace fs = boost::filesystem; + QSettings settings; + /* If data directory provided on command line, no need to look at settings + or show a picking dialog */ + if(!GetArg("-datadir", "").empty()) + return; + /* 1) Default data directory for operating system */ + QString dataDir = getDefaultDataDirectory(); + /* 2) Allow QSettings to override default dir */ + dataDir = settings.value("strDataDir", dataDir).toString(); + + if(!fs::exists(GUIUtil::qstringToBoostPath(dataDir)) || GetBoolArg("-choosedatadir", false)) + { + /* If current default data directory does not exist, let the user choose one */ + Intro intro; + intro.setDataDirectory(dataDir); + intro.setWindowIcon(QIcon(":icons/bitcoin")); + + while(true) + { + if(!intro.exec()) + { + /* Cancel clicked */ + exit(0); + } + dataDir = intro.getDataDirectory(); + try { + boost::filesystem::create_directory(GUIUtil::qstringToBoostPath(dataDir)); + break; + } catch (const fs::filesystem_error&) { + QMessageBox::critical(0, tr("XP-qt"), + tr("Error: Specified data directory \"%1\" cannot be created.").arg(dataDir)); + /* fall through, back to choosing screen */ + } + } + + settings.setValue("strDataDir", dataDir); + } + /* Only override -datadir if different from the default, to make it possible to + * override -datadir in the bitcoin.conf file in the default data directory + * (to be consistent with bitcoind behavior) + */ + if(dataDir != getDefaultDataDirectory()) + SoftSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting +} + +void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable) +{ + switch(status) + { + case FreespaceChecker::ST_OK: + ui->errorMessage->setText(message); + ui->errorMessage->setStyleSheet(""); + break; + case FreespaceChecker::ST_ERROR: + ui->errorMessage->setText(tr("Error") + ": " + message); + ui->errorMessage->setStyleSheet("QLabel { color: #800000 }"); + break; + } + /* Indicate number of bytes available */ + if(status == FreespaceChecker::ST_ERROR) + { + ui->freeSpace->setText(""); + } else { + QString freeString = tr("%n GB of free space available", "", bytesAvailable/GB_BYTES); + if(bytesAvailable < BLOCK_CHAIN_SIZE) + { + freeString += " " + tr("(of %n GB needed)", "", BLOCK_CHAIN_SIZE/GB_BYTES); + ui->freeSpace->setStyleSheet("QLabel { color: #800000 }"); + } else { + ui->freeSpace->setStyleSheet(""); + } + ui->freeSpace->setText(freeString + "."); + } + /* Don't allow confirm in ERROR state */ + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(status != FreespaceChecker::ST_ERROR); +} + +void Intro::on_dataDirectory_textChanged(const QString &dataDirStr) +{ + /* Disable OK button until check result comes in */ + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + checkPath(dataDirStr); +} + +void Intro::on_ellipsisButton_clicked() +{ + QString dir = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(0, "Choose data directory", ui->dataDirectory->text())); + if(!dir.isEmpty()) + ui->dataDirectory->setText(dir); +} + +void Intro::on_dataDirDefault_clicked() +{ + setDataDirectory(getDefaultDataDirectory()); +} + +void Intro::on_dataDirCustom_clicked() +{ + ui->dataDirectory->setEnabled(true); + ui->ellipsisButton->setEnabled(true); +} + +void Intro::startThread() +{ + thread = new QThread(this); + FreespaceChecker *executor = new FreespaceChecker(this); + executor->moveToThread(thread); + + connect(executor, SIGNAL(reply(int,QString,quint64)), this, SLOT(setStatus(int,QString,quint64))); + connect(this, SIGNAL(requestCheck()), executor, SLOT(check())); + /* make sure executor object is deleted in its own thread */ + connect(this, SIGNAL(stopThread()), executor, SLOT(deleteLater())); + connect(this, SIGNAL(stopThread()), thread, SLOT(quit())); + + thread->start(); +} + +void Intro::checkPath(const QString &dataDir) +{ + mutex.lock(); + pathToCheck = dataDir; + if(!signalled) + { + signalled = true; + emit requestCheck(); + } + mutex.unlock(); +} + +QString Intro::getPathToCheck() +{ + QString retval; + mutex.lock(); + retval = pathToCheck; + signalled = false; /* new request can be queued now */ + mutex.unlock(); + return retval; +} diff --git a/src/qt/intro.h b/src/qt/intro.h new file mode 100644 index 00000000..c9735615 --- /dev/null +++ b/src/qt/intro.h @@ -0,0 +1,73 @@ +// Copyright (c) 2011-2013 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_QT_INTRO_H +#define BITCOIN_QT_INTRO_H + +#include +#include +#include + +class FreespaceChecker; + +namespace Ui { + class Intro; +} + +/** Introduction screen (pre-GUI startup). + Allows the user to choose a data directory, + in which the wallet and block chain will be stored. + */ +class Intro : public QDialog +{ + Q_OBJECT + +public: + explicit Intro(QWidget *parent = 0); + ~Intro(); + + QString getDataDirectory(); + void setDataDirectory(const QString &dataDir); + + /** + * Determine data directory. Let the user choose if the current one doesn't exist. + * + * @note do NOT call global GetDataDir() before calling this function, this + * will cause the wrong path to be cached. + */ + static void pickDataDirectory(); + + /** + * Determine default data directory for operating system. + */ + static QString getDefaultDataDirectory(); + +signals: + void requestCheck(); + void stopThread(); + +public slots: + void setStatus(int status, const QString &message, quint64 bytesAvailable); + +private slots: + void on_dataDirectory_textChanged(const QString &arg1); + void on_ellipsisButton_clicked(); + void on_dataDirDefault_clicked(); + void on_dataDirCustom_clicked(); + +private: + Ui::Intro *ui; + QThread *thread; + QMutex mutex; + bool signalled; + QString pathToCheck; + + void startThread(); + void checkPath(const QString &dataDir); + QString getPathToCheck(); + + friend class FreespaceChecker; +}; + +#endif // BITCOIN_QT_INTRO_H diff --git a/src/qt/locale/bitcoin_en.qm b/src/qt/locale/bitcoin_en.qm new file mode 100644 index 0000000000000000000000000000000000000000..19baaff293c8f0414bb252e1ed7b4a3f114ca74b GIT binary patch literal 109091 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 Khx4b79s6(g=5Ub! literal 0 HcmV?d00001 diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts new file mode 100644 index 00000000..e5086e38 --- /dev/null +++ b/src/qt/locale/bitcoin_en.ts @@ -0,0 +1,4447 @@ + + + + + AboutDialog + + + About XP + About XP + + + + <b>XP</b> version + <b>XP</b> version + + + + Copyright © 2009-2015 The Bitcoin developers +Copyright © 2011-2012 The PPCoin Developers +Copyright © 2014 The Peerunity Developers +Copyright © 2014 The EmerCoin Developers +Copyright © 2012-2015 The XP developers + Copyright © 2009-2015 The Bitcoin developers +Copyright © 2011-2012 The PPCoin Developers +Copyright © 2014 The Peerunity Developers +Copyright © 2014 The EmerCoin Developers +Copyright © 2012-2015 The XP developers + + + + <html><head/><body><p><br/>This is experimental software.</p><p>Distributed under the MIT/X11 software license, see the accompanying file COPYING or <br/><a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a>.</p><p>Main icon was designed by VisualPharm.com (<a href="mailto:team@visualpharm.com"><span style=" text-decoration: underline; color:#0000ff;">team@visualpharm.com</span></a>). This product also includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (<a href="http://www.openssl.org/"><span style=" text-decoration: underline; color:#0000ff;">http://www.openssl.org/</span></a>) and cryptographic software written by Eric Young (<a href="mailto:eay@cryptsoft.com"><span style=" text-decoration: underline; color:#0000ff;">eay@cryptsoft.com</span></a>).</p><p><br/></p></body></html> + + + + <html><head/><body><p><br/>This is experimental software.</p><p>Distributed under the MIT/X11 software license, see the accompanying file COPYING or <br/><a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a>.</p><p>This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (<a href="http://www.openssl.org/"><span style=" text-decoration: underline; color:#0000ff;">http://www.openssl.org/</span></a>) and cryptographic software written by Eric Young (<a href="mailto:eay@cryptsoft.com"><span style=" text-decoration: underline; color:#0000ff;">eay@cryptsoft.com</span></a>).</p></body></html> + <html><head/><body><p><br/>This is experimental software.</p><p>Distributed under the MIT/X11 software license, see the accompanying file COPYING or <br/><a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a>.</p><p>This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (<a href="http://www.openssl.org/"><span style=" text-decoration: underline; color:#0000ff;">http://www.openssl.org/</span></a>) and cryptographic software written by Eric Young (<a href="mailto:eay@cryptsoft.com"><span style=" text-decoration: underline; color:#0000ff;">eay@cryptsoft.com</span></a>).</p></body></html> + + + + AddressBookPage + + + Address Book + Address Book + + + + These are your XP 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. + These are your XP 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 + Double-click to edit address or label + + + + Create a new address + Create a new address + + + + &New Address + &New Address + + + + Copy the currently selected address to the system clipboard + Copy the currently selected address to the system clipboard + + + + &Copy Address + &Copy Address + + + + Show &QR Code + Show &QR Code + + + + Sign a message to prove you own a XP address + Sign a message to prove you own a XP address + + + + Sign &Message + Sign &Message + + + + Verify a message to ensure it was signed with a specified XP address + Verify a message to ensure it was signed with a specified XP address + + + + &Verify Message + &Verify Message + + + + Delete the currently selected address from the list + Delete the currently selected address from the list + + + + &Delete + &Delete + + + + Copy &Label + Copy &Label + + + + &Edit + &Edit + + + + Export Address Book Data + Export Address Book Data + + + + Comma separated file (*.csv) + Comma separated file (*.csv) + + + + Error exporting + Error exporting + + + + Could not write to file %1. + Could not write to file %1. + + + + AddressTableModel + + + Label + Label + + + + Address + Address + + + + (no label) + (no label) + + + + AskPassphraseDialog + + + Passphrase Dialog + Passphrase Dialog + + + + Enter passphrase + Enter passphrase + + + + New passphrase + New passphrase + + + + Repeat new passphrase + Repeat new passphrase + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + + + + Encrypt wallet + Encrypt wallet + + + + This operation needs your wallet passphrase to unlock the wallet. + This operation needs your wallet passphrase to unlock the wallet. + + + + Unlock wallet + Unlock wallet + + + + This operation needs your wallet passphrase to decrypt the wallet. + This operation needs your wallet passphrase to decrypt the wallet. + + + + Decrypt wallet + Decrypt wallet + + + + Change passphrase + Change passphrase + + + + Enter the old and new passphrase to the wallet. + Enter the old and new passphrase to the wallet. + + + + Confirm wallet encryption + Confirm wallet encryption + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR COINS</b>! + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR COINS</b>! + + + + Are you sure you wish to encrypt your wallet? + Are you sure you wish to encrypt your wallet? + + + + + Wallet encrypted + Wallet encrypted + + + + XP will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your coins from being stolen by malware infecting your computer. + XP will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your coins from being stolen by malware infecting your computer. + + + + IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet. + IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet. + + + + + + + Wallet encryption failed + Wallet encryption failed + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + + + + + The supplied passphrases do not match. + The supplied passphrases do not match. + + + + + Wallet unlock failed + Wallet unlock failed + + + + + + + The passphrase entered for the wallet decryption was incorrect. + The passphrase entered for the wallet decryption was incorrect. + + + + Wallet decryption failed + Wallet decryption failed + + + + Wallet decrypted + Wallet decrypted + + + + XP will close now to finish the decryption process. + XP will close now to finish the decryption process. + + + + Wallet passphrase was successfully changed. + Wallet passphrase was successfully changed. + + + + + Warning: The Caps Lock key is on! + Warning: The Caps Lock key is on! + + + + BitcoinGUI + + + A fatal error occurred. XP can no longer continue safely and will quit. + A fatal error occurred. XP can no longer continue safely and will quit. + + + + + XP + XP + + + + Wallet + Wallet + + + + &Overview + &Overview + + + + Show general overview of wallet + Show general overview of wallet + + + + &Send coins + &Send coins + + + + Send coins to a XP address + Send coins to a XP address + + + + &Receive coins + &Receive coins + + + + Show the list of addresses for receiving payments + Show the list of addresses for receiving payments + + + + &Transactions + &Transactions + + + + Browse transaction history + Browse transaction history + + + + &Minting + &Minting + + + + Show your minting capacity + Show your minting capacity + + + + &Address Book + &Address Book + + + + Edit the list of stored addresses and labels + Edit the list of stored addresses and labels + + + + Multisig + Multisig + + + + Open window for working with multisig addresses + Open window for working with multisig addresses + + + + E&xit + E&xit + + + + Quit application + Quit application + + + + &About XP + &About XP + + + + Show information about XP + Show information about XP + + + + + About &Qt + About &Qt + + + + Show information about Qt + Show information about Qt + + + + &Options... + &Options... + + + + Modify configuration options for XP + Modify configuration options for XP + + + + &Show / Hide + &Show / Hide + + + + &Encrypt Wallet... + &Encrypt Wallet... + + + + Encrypt or decrypt wallet + Encrypt or decrypt wallet + + + + &Backup Wallet... + &Backup Wallet... + + + + Backup wallet to another location + Backup wallet to another location + + + + &Dump Wallet... + &Dump Wallet... + + + + Dump keys to a text file + Dump keys to a text file + + + + &Import Wallet... + &Import Wallet... + + + + Import keys into a wallet + Import keys into a wallet + + + + &Change Passphrase... + &Change Passphrase... + + + + Change the passphrase used for wallet encryption + Change the passphrase used for wallet encryption + + + + Sign &message... + Sign &message... + + + + Sign messages with your XP addresses to prove you own them + Sign messages with your XP addresses to prove you own them + + + + &Verify message... + &Verify message... + + + + Verify messages to ensure they were signed with specified XP addresses + Verify messages to ensure they were signed with specified XP addresses + + + + Second &auth... + + + + + Second auth with your XP addresses + + + + + &Lock wallet + &Lock wallet + + + + Lock wallet + Lock wallet + + + + Unlo&ck wallet + Unlo&ck wallet + + + + Unlock wallet + Unlock wallet + + + + Unlo&ck wallet for mining + Unlo&ck wallet for mining + + + + Unlock wallet for mining + Unlock wallet for mining + + + + &Export... + &Export... + + + + Export the data in the current tab to a file + Export the data in the current tab to a file + + + + &Debug window + &Debug window + + + + Open debugging and diagnostic console + Open debugging and diagnostic console + + + + &File + &File + + + + &Settings + &Settings + + + + &Wallet security + &Wallet security + + + + &Help + &Help + + + + Tabs toolbar + Tabs toolbar + + + + Actions toolbar + Actions toolbar + + + + + [testnet] + [testnet] + + + + + XP client + XP client + + + + %n active connection(s) to XP network + + %n active connection to XP network + %n active connections to XP network + + + + + Synchronizing with network... + Synchronizing with network... + + + + ~%n block(s) remaining + + ~%n block remaining + ~%n blocks remaining + + + + + Downloaded %1 of %2 blocks of transaction history (%3% done). + Downloaded %1 of %2 blocks of transaction history (%3% done). + + + + Downloaded %1 blocks of transaction history. + Downloaded %1 blocks of transaction history. + + + + Current PoW difficulty is %1. + + + + + Current PoS difficulty is %1. + + + + + %n second(s) ago + + %n second ago + %n seconds ago + + + + + %n minute(s) ago + + %n minute ago + %n minutes ago + + + + + %n hour(s) ago + + %n hour ago + %n hours ago + + + + + %n day(s) ago + + %n day ago + %n days ago + + + + + Up to date + Up to date + + + + Catching up... + Catching up... + + + + Last received block was generated %1. + Last received block was generated %1. + + + + Wallet is offline + Wallet is offline + + + + Wallet is locked + Wallet is locked + + + + Blockchain download is in progress + Blockchain download is in progress + + + + Stake miner is active<br>%1 inputs being used for mining<br>Network weight is %3 + + + + Stake miner is active<br>Kernel rate is %1 k/s<br>CD rate is %2 CD/s<br>Network weight is %3 + Stake miner is active<br>Kernel rate is %1 k/s<br>CD rate is %2 CD/s<br>Network weight is %3 + + + + No suitable inputs were found + No suitable inputs were found + + + + Error + Error + + + + Warning + Warning + + + + Information + Information + + + + 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? + 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? + + + + Confirm transaction fee + Confirm transaction fee + + + + Sent transaction + Sent transaction + + + + Incoming transaction + Incoming transaction + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + + + + + URI handling + URI handling + + + + + URI can not be parsed! This can be caused by an invalid XP address or malformed URI parameters. + URI can not be parsed! This can be caused by an invalid XP address or malformed URI parameters. + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Wallet is <b>encrypted</b> and currently <b>locked</b> + + + + Backup Wallet + Backup Wallet + + + + Wallet Data (*.dat) + Wallet Data (*.dat) + + + + Backup Failed + Backup Failed + + + + There was an error trying to save the wallet data to the new location. + There was an error trying to save the wallet data to the new location. + + + + Dump Wallet + There was an error trying to save the wallet data to the new location. + + + + + Wallet dump (*.txt) + Wallet dump (*.txt) + + + + Dump failed + Dump failed + + + + An error happened while trying to save the keys to your location. +Keys were not saved. + An error happened while trying to save the keys to your location. +Keys were not saved. + + + + Dump successful + Dump successful + + + + Keys were saved to this file: +%2 + Keys were saved to this file: +%2 + + + + Import Wallet + Import Wallet + + + + Import Failed + Import Failed + + + + An error happened while trying to import the keys. +Some or all keys from: + %1, + were not imported into your wallet. + An error happened while trying to import the keys. +Some or all keys from: + %1, + were not imported into your wallet. + + + + Import Successful + Import Successful + + + + All keys from: + %1, + were imported into your wallet. + All keys from: + %1, + were imported into your wallet. + + + + ClientModel + + + Network Alert + Network Alert + + + + CoinControlDialog + + + Coin Control + Coin Control + + + + Quantity: + Quantity: + + + 0 + 0 + + + + Bytes: + Bytes: + + + + Amount: + Amount: + + + 0.00 XP + 0.00 XP + + + + Priority: + Priority: + + + + Fee: + Fee: + + + + Low Output: + Low Output: + + + + + no + no + + + + After Fee: + After Fee: + + + + Change: + Change: + + + + (un)select all + (un)select all + + + + Tree mode + Tree mode + + + + List mode + List mode + + + + Amount + Amount + + + + Label + Label + + + + Address + Address + + + + Date + Date + + + + Confirmations + Confirmations + + + + Confirmed + Confirmed + + + + Weight + Weight + + + + Priority + Priority + + + + Copy address + Copy address + + + + Copy label + Copy label + + + + + Copy amount + Copy amount + + + + Copy transaction ID + Copy transaction ID + + + + Copy quantity + Copy quantity + + + + Copy fee + Copy fee + + + + Copy after fee + Copy after fee + + + + Copy bytes + Copy bytes + + + + Copy priority + Copy priority + + + + Copy low output + Copy low output + + + + Copy change + Copy change + + + + highest + highest + + + + high + high + + + + medium-high + medium-high + + + + medium + medium + + + + low-medium + low-medium + + + + low + low + + + + lowest + lowest + + + + DUST + DUST + + + + yes + yes + + + + This label turns red, if the transaction size is bigger than 1000 bytes. + + This means a fee of at least %1 per kb is required. + + Can vary +/- 1 Byte per input. + This label turns red, if the transaction size is bigger than 1000 bytes. + + This means a fee of at least %1 per kb is required. + + Can vary +/- 1 Byte per input. + + + + Transactions with higher priority get more likely into a block. + +This label turns red, if the priority is smaller than "medium". + + This means a fee of at least %1 per kb is required. + Transactions with higher priority get more likely into a block. + +This label turns red, if the priority is smaller than "medium". + + This means a fee of at least %1 per kb is required. + + + + This label turns red, if any recipient receives an amount smaller than %1. + + This means a fee of at least %2 is required. + + Amounts below the minimum fee are shown as DUST. + This label turns red, if any recipient receives an amount smaller than %1. + + This means a fee of at least %2 is required. + + Amounts below the minimum fee are shown as DUST. + + + + This label turns red, if the change is smaller than %1. + + This means a fee of at least %2 is required. + This label turns red, if the change is smaller than %1. + + This means a fee of at least %2 is required. + + + + + (no label) + (no label) + + + + change from %1 (%2) + change from %1 (%2) + + + + (change) + (change) + + + + EditAddressDialog + + + Edit Address + Edit Address + + + + &Label + &Label + + + + The label associated with this address book entry + The label associated with this address book entry + + + + &Address + &Address + + + + The address associated with this address book entry. This can only be modified for sending addresses. + The address associated with this address book entry. This can only be modified for sending addresses. + + + + New receiving address + New receiving address + + + + New sending address + New sending address + + + + Edit receiving address + Edit receiving address + + + + Edit sending address + Edit sending address + + + + The entered address "%1" is not a valid XP address. + The entered address "%1" is not a valid XP address. + + + + The entered address "%1" is already in the address book. + The entered address "%1" is already in the address book. + + + + Could not unlock wallet. + Could not unlock wallet. + + + + New key generation failed. + New key generation failed. + + + + FreespaceChecker + + + A new data directory will be created. + + + + + name + + + + + Directory already exists. Add %1 if you intend to create a new directory here. + + + + + Path already exists, and is not a directory. + + + + + Cannot create data directory here. + + + + + GUIUtil::HelpMessageBox + + + + XP-Qt + XP-Qt + + + + version + version + + + + Usage: + Usage: + + + + command-line options + command-line options + + + + UI options + UI options + + + + Set language, for example "de_DE" (default: system locale) + Set language, for example "de_DE" (default: system locale) + + + + Start minimized + Start minimized + + + + Show splash screen on startup (default: 1) + Show splash screen on startup (default: 1) + + + + Intro + + + Welcome + + + + + Welcome to XP-qt. + + + + + As this is the first time the program is launched, you can choose where XP-qt will store its data. + + + + + XP-qt will download and store a copy of the XP block chain. At least %1GB of data will be stored in this directory, and it will grow over time. The wallet will also be stored in this directory. + + + + + Use the default data directory + + + + + Use a custom data directory: + + + + + XP-qt + + + + + Error: Specified data directory "%1" cannot be created. + + + + + Error + Error + + + + %n GB of free space available + + + + + + + + (of %n GB needed) + + + + + + + + MintingTableModel + + + Transaction + Transaction + + + + Address + Address + + + + Balance + Balance + + + + Age + Age + + + + CoinDay + CoinDay + + + + MintProbability + MintProbability + + + + MintReward + MintReward + + + + minutes + minutes + + + + hours + hours + + + + days + days + + + + You have %1 chance to find a POS block if you mint %2 %3 at current difficulty. + You have %1 chance to find a POS block if you mint %2 %3 at current difficulty. + + + + Destination address of the output. + Destination address of the output. + + + + Original transaction id. + Original transaction id. + + + + Age of the transaction in days. + Age of the transaction in days. + + + + Balance of the output. + Balance of the output. + + + + Coin age in the output. + Coin age in the output. + + + + Chance to mint a block within given time interval. + Chance to mint a block within given time interval. + + + + The size of the potential rewards if the block is found at the beginning and the end given time interval. + The size of the potential rewards if the block is found at the beginning and the end given time interval. + + + + MintingView + + + transaction is too young + transaction is too young + + + + transaction is mature + transaction is mature + + + + transaction has reached maximum probability + transaction has reached maximum probability + + + + Display minting probability within : + Display minting probability within : + + + + 10 min + 10 min + + + + 24 hours + 24 hours + + + + 7 days + 7 days + + + + 30 days + 30 days + + + + 60 days + 60 days + + + + 90 days + 90 days + + + + Copy transaction ID of input + Copy transaction ID of input + + + + Copy address of input + Copy address of input + + + + Show/hide 'Address' column + Show/hide 'Address' column + + + + Show/hide 'Transaction' column + Show/hide 'Transaction' column + + + + Export Minting Data + Export Minting Data + + + + Comma separated file (*.csv) + Comma separated file (*.csv) + + + + Address + Address + + + + Transaction + Transaction + + + + Age + Age + + + + CoinDay + CoinDay + + + + Balance + Balance + + + + MintingProbability + MintingProbability + + + + MintingReward + MintingReward + + + + Error exporting + Error exporting + + + + Could not write to file %1. + Could not write to file %1. + + + + MultisigAddressEntry + + + Form + Form + + + + Public &key: + Public &key: + + + + The public key of an address + The public key of an address + + + + Enter a public key + Enter a public key + + + + Paste public key from clipboard + Paste public key from clipboard + + + + Alt+P + Alt+P + + + + Remove this public key + Remove this public key + + + + &Address: + &Address: + + + + Address associated to the public key + Address associated to the public key + + + + Enter one of your addresses to get its public key + Enter one of your addresses to get its public key + + + + Choose address from address book + Choose address from address book + + + + Alt+A + Alt+A + + + + Label: + Label: + + + + MultisigDialog + + + Multisig + Multisig + + + + &Create Address + &Create Address + + + + Add a member to the signing pool + Add a member to the signing pool + + + + &Add public key... + &Add public key... + + + + Remove all public key fields + Remove all public key fields + + + + Clear all + Clear all + + + + Required signatures: + Required signatures: + + + + Enter a number + Enter a number + + + / 1 + / 1 + + + + Create multisig address + Create multisig address + + + + Multisig address: + Multisig address: + + + + Copy the multisig address to the system clipboard + Copy the multisig address to the system clipboard + + + + Redeem script: + Redeem script: + + + + Copy the redeem script to the system clipboard + Copy the redeem script to the system clipboard + + + + The redeem script will be required to spend the funds sent to the multisig address + The redeem script will be required to spend the funds sent to the multisig address + + + + Save redeem script + Save redeem script + + + + Add the multisig address to your personal addresses + Add the multisig address to your personal addresses + + + + Add address to wallet + Add address to wallet + + + + &Spend Funds + &Spend Funds + + + + Inputs + Inputs + + + + Inputs amount: + Inputs amount: + + + 123.456 + 123.456 + + + XP + XP + + + + Add input... + Add input... + + + + Outputs + Outputs + + + + Outputs amount: + Outputs amount: + + + + Fee: + Fee: + + + + Add output... + Add output... + + + + Create transaction + Create transaction + + + + Enter a raw transaction or create a new one + Enter a raw transaction or create a new one + + + + Paste transaction from clipboard + Paste transaction from clipboard + + + + Alt+P + Alt+P + + + + Sign transaction + Sign transaction + + + + Send transaction + Send transaction + + + + + + + Error + Error + + + + Number of addresses involved in the address creation > %1 +Reduce the number + Number of addresses involved in the address creation > %1 +Reduce the number + + + + Number of required signatures is 0 +Number of required signatures must be between 1 and number of keys involved in the creation of address. + Number of required signatures is 0 +Number of required signatures must be between 1 and number of keys involved in the creation of address. + + + + Number of required signatures > Number of keys involved in the creation of address. + Number of required signatures > Number of keys involved in the creation of address. + + + + Redeem script exceeds size limit: %1 > %2 +Reduce the number of addresses involved in the address creation. + Redeem script exceeds size limit: %1 > %2 +Reduce the number of addresses involved in the address creation. + + + + Transaction signature is complete + Transaction signature is complete + + + + Transaction is NOT completely signed + Transaction is NOT completely signed + + + + + Confirm send transaction + Confirm send transaction + + + + The fee of the transaction (%1 XP) is smaller than the expected fee (%2 XP). Do you want to send the transaction anyway? + The fee of the transaction (%1 XP) is smaller than the expected fee (%2 XP). Do you want to send the transaction anyway? + + + + The fee of the transaction (%1 XP) is bigger than the expected fee (%2 XP). Do you want to send the transaction anyway? + The fee of the transaction (%1 XP) is bigger than the expected fee (%2 XP). Do you want to send the transaction anyway? + + + + MultisigInputEntry + + + Form + Form + + + + Enter a transaction id + Enter a transaction id + + + + Alt+P + Alt+P + + + + Transaction id: + Transaction id: + + + + Transaction output: + Transaction output: + + + + Redeem script: + Redeem script: + + + + Enter the redeem script of the address in the transaction output + Enter the redeem script of the address in the transaction output + + + + Alt+A + Alt+A + + + + OptionsDialog + + + Options + Options + + + + &Main + &Main + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. Fee 0.001 recommended. + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. Fee 0.001 recommended. + + + + Pay transaction &fee + Pay transaction &fee + + + + per kilobyte + per kilobyte + + + + Automatically start XP after logging in to the system. + Automatically start XP after logging in to the system. + + + + &Start XP on system login + &Start XP on system login + + + + Detach block and address databases at shutdown. This means they can be moved to another data directory, but it slows down shutdown. The wallet is always detached. + Detach block and address databases at shutdown. This means they can be moved to another data directory, but it slows down shutdown. The wallet is always detached. + + + + &Detach databases at shutdown + &Detach databases at shutdown + + + + &Network + &Network + + + + Connect to the XP network through a SOCKS proxy (e.g. when connecting through Tor). + Connect to the XP network through a SOCKS proxy (e.g. when connecting through Tor). + + + + &Connect through SOCKS proxy: + &Connect through SOCKS proxy: + + + + Proxy &IP: + Proxy &IP: + + + + IP address of the proxy (e.g. 127.0.0.1) + IP address of the proxy (e.g. 127.0.0.1) + + + + &Port: + &Port: + + + + Port of the proxy (e.g. 9050) + Port of the proxy (e.g. 9050) + + + + SOCKS &Version: + SOCKS &Version: + + + + SOCKS version of the proxy (e.g. 5) + SOCKS version of the proxy (e.g. 5) + + + + Connect through &Tor: + Connect through &Tor: + + + + Tor IP: + Tor IP: + + + + Port: + Port: + + + + Use Tor only + Use Tor only + + + + Tor name: + Tor name: + + + + &Window + &Window + + + + Show only a tray icon after minimizing the window. + Show only a tray icon after minimizing the window. + + + + &Minimize to the tray instead of the taskbar + &Minimize to the tray instead of the taskbar + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu. + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu. + + + + M&inimize on close + M&inimize on close + + + + &Display + &Display + + + + User Interface &language: + User Interface &language: + + + + The user interface language can be set here. This setting will take effect after restarting XP. + The user interface language can be set here. This setting will take effect after restarting XP. + + + + &Unit to show amounts in: + &Unit to show amounts in: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Choose the default subdivision unit to show in the interface and when sending coins. + + + + Whether to show XP addresses in the transaction list or not. + Whether to show XP addresses in the transaction list or not. + + + + &Display addresses in transaction list + &Display addresses in transaction list + + + + Whether to show coin control features or not. + Whether to show coin control features or not. + + + + Display coin &control features (experts only!) + Display coin &control features (experts only!) + + + + + Third party URLs (e.g. explorer.novaco.in) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |. + Third party URLs (e.g. explorer.novaco.in) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |. + + + + Third party transaction URLs + Third party transaction URLs + + + + &OK + &OK + + + + &Cancel + &Cancel + + + + &Apply + &Apply + + + + default + default + + + + + + + Warning + Warning + + + + + + + This setting will take effect after restarting XP. + This setting will take effect after restarting XP. + + + + The supplied proxy address is invalid. + The supplied proxy address is invalid. + + + + The supplied tor address is invalid. + The supplied tor address is invalid. + + + + OverviewPage + + + Form + Form + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the XP network after a connection is established, but this process has not completed yet. + The displayed information may be out of date. Your wallet automatically synchronizes with the XP network after a connection is established, but this process has not completed yet. + + + + + Your unspendable balance + Your unspendable balance + + + + Unspendable: + Unspendable: + + + + + Total of coins that was staked, and do not yet count toward the current balance + Total of coins that was staked, and do not yet count toward the current balance + + + + Balances + + + + + + Your current available balance + + + + + Available: + + + + + Stake: + Stake: + + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + + + + Unconfirmed: + Unconfirmed: + + + + + Mined balance that has not yet matured + Mined balance that has not yet matured + + + + Immature: + Immature: + + + + + Your current total balance + + + + + Total: + + + + + + Total number of transactions in wallet + Total number of transactions in wallet + + + + Number of transactions: + Number of transactions: + + + + <b>Recent transactions</b> + <b>Recent transactions</b> + + + + + out of sync + out of sync + + + + QObject + + + XPs + XPs + + + + Milli-XPs (1 / 1,000) + Milli-XPs (1 / 1,000) + + + + Micro-XPs (1 / 1,000,000) + Micro-XPs (1 / 1,000,000) + + + + Amount + Amount + + + + %1 d + %1 d + + + + %1 h + %1 h + + + + %1 m + %1 m + + + + %1 s + %1 s + + + + from %1 to %2 + from %1 to %2 + + + + Error: Specified data directory "%1" does not exist. + + + + + QRCodeDialog + + + QR Code Dialog + QR Code Dialog + + + + Request Payment + Request Payment + + + + Label: + Label: + + + + Message: + Message: + + + + Amount: + Amount: + + + + &Save As... + &Save As... + + + + Error encoding URI into QR Code. + Error encoding URI into QR Code. + + + + The entered amount is invalid, please check. + The entered amount is invalid, please check. + + + + Resulting URI too long, try to reduce the text for label / message. + Resulting URI too long, try to reduce the text for label / message. + + + + Save QR Code + Save QR Code + + + + PNG Images (*.png) + PNG Images (*.png) + + + + RPCConsole + + + XP - Debug window + XP - Debug window + + + + &Information + &Information + + + + Configuration file + Configuration file + + + + + + + + + + + + + + N/A + N/A + + + + Current number of blocks + Current number of blocks + + + + Command-line options + Command-line options + + + + Open the XP debug log file from the current data directory. This can take a few seconds for large log files. + Open the XP debug log file from the current data directory. This can take a few seconds for large log files. + + + + &Open + &Open + + + + Debug log file + Debug log file + + + + Show the XP-Qt help message to get a list with possible XP command-line options. + Show the XP-Qt help message to get a list with possible XP command-line options. + + + + &Show + &Show + + + + Last block time + Last block time + + + + Network + Network + + + + Startup time + Startup time + + + + On testnet + On testnet + + + + Number of connections + Number of connections + + + + Block chain + Block chain + + + + Estimated total blocks + Estimated total blocks + + + + Using BerkeleyDB version + Using BerkeleyDB version + + + + Using OpenSSL version + Using OpenSSL version + + + + Build date + Build date + + + + Client version + Client version + + + + XP Core + XP Core + + + + Client name + Client name + + + + Open the XP configuration file from the current data directory. + Open the XP configuration file from the current data directory. + + + + O&pen + O&pen + + + + &Console + &Console + + + + Clear console + Clear console + + + + &Network Traffic + &Network Traffic + + + + &Clear + &Clear + + + + Totals + Total + + + + Received: + Received: + + + + Sent: + Sent: + + + + Welcome to the XP RPC console. + Welcome to the XP RPC console. + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + + + + Type <b>help</b> for an overview of available commands. + Type <b>help</b> for an overview of available commands. + + + + Inbound: + Inbound: + + + + Outbound: + Outbound: + + + + %1 B + %1 B + + + + %1 KB + %1 KB + + + + %1 MB + %1 MB + + + + %1 GB + %1 GB + + + + SecondAuthDialog + + Signatures - Sign / Verify a Message + Signatures - Sign / Verify a Message + + + The address to sign the message with (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + The address to sign the message with (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + + Choose an address from the address book + Choose an address from the address book + + + + Alt+A + Alt+A + + + Paste address from clipboard + Paste address from clipboard + + + + Second Authentication + + + + + You can sign hash of transaction exists in the blockchain with your addresses. + + + + + The address for authentification (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + + + Paste hash from clipboard + + + + + Alt+P + Alt+P + + + + Copy the current signature to the system clipboard + Copy the current signature to the system clipboard + + + + Alt+C + + + + + Sign the hash + + + + Sign the message to prove you own this XP address + Sign the message to prove you own this XP address + + + + &Sign Data + + + + + Reset all sign message fields + Reset all sign message fields + + + + Clear &All + Clear &All + + + + Enter a XP address (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + Enter a XP address (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + + Click "Sign data" to generate signature + + + + + The entered address is invalid. + The entered address is invalid. + + + + + Please check the address and try again. + Please check the address and try again. + + + + The entered address does not refer to a key. + The entered address does not refer to a key. + + + + Wallet unlock was cancelled. + Wallet unlock was cancelled. + + + + Private key for the entered address is not available. + Private key for the entered address is not available. + + + + No information available about transaction. + + + + + Message signing failed. + Message signing failed. + + + + Message signed. + Message signed. + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Send Coins + + + + Coin Control Features + Coin Control Features + + + + Inputs... + Inputs... + + + + automatically selected + automatically selected + + + + Insufficient funds! + Insufficient funds! + + + + Quantity: + Quantity: + + + 0 + 0 + + + + Bytes: + Bytes: + + + + Amount: + Amount: + + + 0.00 XP + 0.00 XP + + + + Priority: + Priority: + + + + medium + medium + + + + Fee: + Fee: + + + + Low Output: + Low Output: + + + + no + no + + + + After Fee: + After Fee: + + + + Change + Change + + + + custom change address + custom change address + + + + Choose address from address book + Choose address from address book + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Paste address from clipboard + + + + Alt+P + Alt+P + + + + Send to multiple recipients at once + Send to multiple recipients at once + + + + Add &Recipient + Add &Recipient + + + + Remove all transaction fields + Remove all transaction fields + + + + Clear &All + Clear &All + + + + Balance: + Balance: + + + 123.456 XP + 123.456 XP + + + + Confirm the send action + Confirm the send action + + + + S&end + S&end + + + + Enter a XP address (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + Enter a XP address (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + + Copy quantity + Copy quantity + + + + Copy amount + Copy amount + + + + Copy fee + Copy fee + + + + Copy after fee + Copy after fee + + + + Copy bytes + Copy bytes + + + + Copy priority + Copy priority + + + + Copy low output + Copy low output + + + + Copy change + Copy change + + + + + <b>%1</b> to %2 (%3) + <b>%1</b> to %2 (%3) + + + + Confirm send coins + Confirm send coins + + + + Are you sure you want to send %1? + Are you sure you want to send %1? + + + + and + and + + + + The recipient address is not valid, please recheck. + The recipient address is not valid, please recheck. + + + + The amount to pay must be larger than 0. + The amount to pay must be larger than 0. + + + + The amount exceeds your balance. + The amount exceeds your balance. + + + + The total exceeds your balance when the %1 transaction fee is included. + The total exceeds your balance when the %1 transaction fee is included. + + + + Duplicate address found, can only send to each address once per send operation. + Duplicate address found, can only send to each address once per send operation. + + + + Error: Transaction creation failed. + 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. + 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. + + + + SendCoinsEntry + + + Form + Form + + + + A&mount: + A&mount: + + + + Pay &To: + Pay &To: + + + + + 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 + + + + &Label: + &Label: + + + + The address to send the payment to (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + The address to send the payment to (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + + Choose address from address book + Choose address from address book + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Paste address from clipboard + + + + Alt+P + Alt+P + + + + Remove this recipient + Remove this recipient + + + + Enter a XP address (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + Enter a XP address (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + Signatures - Sign / Verify a Message + + + + + &Sign Message + &Sign Message + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + + + + The address to sign the message with (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + The address to sign the message with (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + + + Choose an address from the address book + Choose an address from the address book + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Paste address from clipboard + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Enter the message you want to sign here + + + + Copy the current signature to the system clipboard + Copy the current signature to the system clipboard + + + + Sign the message to prove you own this XP address + Sign the message to prove you own this XP address + + + + Reset all sign message fields + Reset all sign message fields + + + + + Clear &All + Clear &All + + + + + &Verify Message + &Verify Message + + + + Enter the signing address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack. + Enter the signing address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack. + + + + The address the message was signed with (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + The address the message was signed with (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + + Verify the message to ensure it was signed with the specified XP address + Verify the message to ensure it was signed with the specified XP address + + + + Reset all verify message fields + Reset all verify message fields + + + + + Enter a XP address (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + Enter a XP address (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + + Click "Sign Message" to generate signature + Click "Sign Message" to generate signature + + + + Enter XP signature + Enter XP signature + + + + + The entered address is invalid. + The entered address is invalid. + + + + + + + Please check the address and try again. + Please check the address and try again. + + + + + The entered address does not refer to a key. + The entered address does not refer to a key. + + + + Wallet unlock was cancelled. + Wallet unlock was cancelled. + + + + Private key for the entered address is not available. + Private key for the entered address is not available. + + + + Message signing failed. + Message signing failed. + + + + Message signed. + Message signed. + + + + The signature could not be decoded. + The signature could not be decoded. + + + + + Please check the signature and try again. + Please check the signature and try again. + + + + The signature did not match the message digest. + The signature did not match the message digest. + + + + Message verification failed. + Message verification failed. + + + + Message verified. + Message verified. + + + + TrafficGraphWidget + + + KB/s + KB/s + + + + TransactionDesc + + + Open for %n block(s) + + Open for %n block + Open for %n blocks + + + + + Open until %1 + Open until %1 + + + + %1/offline + %1/offline + + + + %1/unconfirmed + %1/unconfirmed + + + + %1 confirmations + %1 confirmations + + + + Status + Status + + + + , has not been successfully broadcast yet + , has not been successfully broadcast yet + + + + , broadcast through %n node(s) + + , broadcast through %n node + , broadcast through %n nodes + + + + + Date + Date + + + + Source + Source + + + + Generated + Generated + + + + + + + From + From + + + + + unknown + unknown + + + + + label + label + + + + + + To + To + + + + + own address + own address + + + + + + + + Credit + Credit + + + + matures in %n more block(s) + + matures in %n more block + matures in %n more blocks + + + + + not accepted + not accepted + + + + + + + Debit + Debit + + + + Transaction fee + Transaction fee + + + + Net amount + Net amount + + + + Message + Message + + + + Comment + Comment + + + + Transaction ID + Transaction ID + + + + Generated coins must mature 520 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, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + Generated coins must mature 520 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, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + + + + Debug information + Debug information + + + + Transaction + Transaction + + + + Inputs + Inputs + + + + Amount + Amount + + + + true + true + + + + false + false + + + + TransactionDescDialog + + + Transaction details + Transaction details + + + + This pane shows a detailed description of the transaction + This pane shows a detailed description of the transaction + + + + TransactionTableModel + + + Date + Date + + + + Type + Type + + + + Address + Address + + + + Open for %n block(s) + + Open for %n block + Open for %n blocks + + + + + Open until %1 + Open until %1 + + + + Offline (%1 confirmations) + Offline (%1 confirmations) + + + + Unconfirmed (%1 of %2 confirmations) + Unconfirmed (%1 of %2 confirmations) + + + + Confirmed (%1 confirmations) + Confirmed (%1 confirmations) + + + + Mined balance will be available when it matures in %n more block(s) + + Mined balance will be available when it matures in %n more block + Mined balance will be available when it matures in %n more blocks + + + + + This block was not received by any other nodes and will probably not be accepted! + This block was not received by any other nodes and will probably not be accepted! + + + + Generated but not accepted + Generated but not accepted + + + + Received with + Received with + + + + Received from + Received from + + + + Sent to + Sent to + + + + Payment to yourself + Payment to yourself + + + + Mined + Mined + + + + (n/a) + (n/a) + + + + Transaction status. Hover over this field to show number of confirmations. + Transaction status. Hover over this field to show number of confirmations. + + + + Date and time that the transaction was received. + Date and time that the transaction was received. + + + + Type of transaction. + Type of transaction. + + + + Destination address of transaction. + Destination address of transaction. + + + + Amount removed from or added to balance. + Amount removed from or added to balance. + + + + TransactionView + + + + All + All + + + + Today + Today + + + + This week + This week + + + + This month + This month + + + + Last month + Last month + + + + This year + This year + + + + Range... + Range... + + + + Received with + Received with + + + + Sent to + Sent to + + + + To yourself + To yourself + + + + Mined + Mined + + + + Other + Other + + + + Enter address or label to search + Enter address or label to search + + + + Min amount + Min amount + + + + Copy address + Copy address + + + + Copy label + Copy label + + + + Copy amount + Copy amount + + + + Copy transaction ID + Copy transaction ID + + + + Edit label + Edit label + + + + Show transaction details + Show transaction details + + + + Clear orphans + Clear orphans + + + + Export Transaction Data + Export Transaction Data + + + + Comma separated file (*.csv) + Comma separated file (*.csv) + + + + Confirmed + Confirmed + + + + Date + Date + + + + Type + Type + + + + Label + Label + + + + Address + Address + + + + Amount + Amount + + + + ID + ID + + + + Error exporting + Error exporting + + + + Could not write to file %1. + Could not write to file %1. + + + + Range: + Range: + + + + to + to + + + + WalletModel + + + Sending... + Sending... + + + + bitcoin-core + + + %s, you must set a rpcpassword in the configuration file: + %s +It is recommended you use the following random password: +rpcuser=XPrpc +rpcpassword=%s +(you do not need to remember this password) +If the file does not exist, create it with owner-readable-only file permissions. + + %s, you must set a rpcpassword in the configuration file: + %s +It is recommended you use the following random password: +rpcuser=XPrpc +rpcpassword=%s +(you do not need to remember this password) +If the file does not exist, create it with owner-readable-only file permissions. + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + An error occurred while setting up the RPC port %u for listening on IPv4: %s + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + + + + Cannot obtain a lock on data directory %s. XP is probably already running. + Cannot obtain a lock on data directory %s. XP is probably already running. + + + + Detach block and address databases. Increases shutdown time (default: 0) + Detach block and address databases. Increases shutdown time (default: 0) + + + + Error initializing database environment %s! To recover, BACKUP THAT DIRECTORY, then remove everything from it except for wallet.dat. + Error initializing database environment %s! To recover, BACKUP THAT DIRECTORY, then remove everything from it except for wallet.dat. + + + + 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: 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: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds + + + + Error: Wallet unlocked for block minting only, unable to create transaction. + Error: Wallet unlocked for block minting only, unable to create transaction. + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + Execute command when the best block changes (%s in cmd is replaced by block hash) + + + + Listen for JSON-RPC connections on <port> (default: 28191 or testnet: 18345) + Listen for JSON-RPC connections on <port> (default: 28191 or testnet: 18345) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + + + + Unable to bind to %s on this computer. XP is probably already running. + Unable to bind to %s on this computer. XP is probably already running. + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong XP will not work properly. + Warning: Please check that your computer's date and time are correct! If your clock is wrong XP will not work properly. + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + + + + 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. + + + + Accept command line and JSON-RPC commands + Accept command line and JSON-RPC commands + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + Accept connections from outside (default: 1 if no -proxy or -connect) + + + + Add a node to connect to and attempt to keep the connection open + Add a node to connect to and attempt to keep the connection open + + + + Allow DNS lookups for -addnode, -seednode and -connect + Allow DNS lookups for -addnode, -seednode and -connect + + + + Allow JSON-RPC connections from specified IP address + Allow JSON-RPC connections from specified IP address + + + + Attempt to recover private keys from a corrupt wallet.dat + Attempt to recover private keys from a corrupt wallet.dat + + + + Bind to given address. Use [host]:port notation for IPv6 + Bind to given address. Use [host]:port notation for IPv6 + + + + Block creation options: + Block creation options: + + + + Cannot downgrade wallet + Cannot downgrade wallet + + + + Cannot initialize keypool + Cannot initialize keypool + + + + Cannot resolve -bind address: '%s' + Cannot resolve -bind address: '%s' + + + + Cannot resolve -externalip address: '%s' + Cannot resolve -externalip address: '%s' + + + + Cannot write default address + Cannot write default address + + + + Connect only to the specified node(s) + Connect only to the specified node(s) + + + + Connect through socks proxy + Connect through socks proxy + + + + Connect to a node to retrieve peer addresses, and disconnect + Connect to a node to retrieve peer addresses, and disconnect + + + + Discover own IP address (default: 1 when listening and no -externalip) + Discover own IP address (default: 1 when listening and no -externalip) + + + + Done loading + Done loading + + + + Error loading blkindex.dat + Error loading blkindex.dat + + + + Error loading wallet.dat + Error loading wallet.dat + + + + Error loading wallet.dat: Wallet corrupted + Error loading wallet.dat: Wallet corrupted + + + + Error loading wallet.dat: Wallet requires newer version of XP + Error loading wallet.dat: Wallet requires newer version of XP + + + + Error + Error + + + + Error: Transaction creation failed + Error: Transaction creation failed + + + + Error: Wallet locked, unable to create transaction + Error: Wallet locked, unable to create transaction + + + + Error: could not start node + Error: could not start node + + + + Failed to listen on any port. Use -listen=0 if you want this. + Failed to listen on any port. Use -listen=0 if you want this. + + + + Fee per KB to add to transactions you send + Fee per KB to add to transactions you send + + + + Find peers using DNS lookup (default: 0) + Find peers using DNS lookup (default: 0) + + + + Find peers using internet relay chat (default: 1) + Find peers using internet relay chat (default: 1) + + + + Get help for a command + Get help for a command + + + + How many blocks to check at startup (default: 2500, 0 = all) + How many blocks to check at startup (default: 2500, 0 = all) + + + + How thorough the block verification is (0-6, default: 1) + How thorough the block verification is (0-6, default: 1) + + + + Importing blockchain data file. + Importing blockchain data file. + + + + Importing bootstrap blockchain data file. + Importing bootstrap blockchain data file. + + + + Imports blocks from external blk000?.dat file + Imports blocks from external blk000?.dat file + + + + Insufficient funds + Insufficient funds + + + + Invalid -proxy address: '%s' + Invalid -proxy address: '%s' + + + + Invalid -tor address: '%s' + Invalid -tor address: '%s' + + + + Invalid amount for -paytxfee=<amount>: '%s' + Invalid amount for -paytxfee=<amount>: '%s' + + + + Invalid amount for -reservebalance=<amount> + Invalid amount for -reservebalance=<amount> + + + + Invalid amount + Invalid amount + + + + List commands + List commands + + + + Listen for connections on <port> (default: 28192 or testnet: 17778) + Listen for connections on <port> (default: 28192 or testnet: 17778) + + + + Loading addresses... + Loading addresses... + + + + Loading block index... + Loading block index... + + + + Loading wallet... + Loading wallet... + + + + Maintain at most <n> connections to peers (default: 125) + Maintain at most <n> connections to peers (default: 125) + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + + + + XP version + XP version + + + + XP + XP + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + + + + Options: + Options: + + + + Output extra debugging information. Implies all other -debug* options + Output extra debugging information. Implies all other -debug* options + + + + Output extra network debugging information + Output extra network debugging information + + + + Password for JSON-RPC connections + Password for JSON-RPC connections + + + + Prepend debug output with timestamp + Prepend debug output with timestamp + + + + Rescan the block chain for missing wallet transactions + Rescan the block chain for missing wallet transactions + + + + Rescanning... + Rescanning... + + + + Run in the background as a daemon and accept commands + Run in the background as a daemon and accept commands + + + + SSL options: (see the Bitcoin Wiki for SSL setup instructions) + SSL options: (see the Bitcoin Wiki for SSL setup instructions) + + + + Select the version of socks proxy to use (4-5, default: 5) + Select the version of socks proxy to use (4-5, default: 5) + + + + Send command to -server or XPd + Send command to -server or XPd + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Send commands to node running on <ip> (default: 127.0.0.1) + + + + Send trace/debug info to console instead of debug.log file + Send trace/debug info to console instead of debug.log file + + + + Send trace/debug info to debugger + Send trace/debug info to debugger + + + + Sending... + Sending... + + + + Server certificate file (default: server.cert) + Server certificate file (default: server.cert) + + + + Server private key (default: server.pem) + Server private key (default: server.pem) + + + + Set database cache size in megabytes (default: 25) + Set database cache size in megabytes (default: 25) + + + + Set database disk log size in megabytes (default: 100) + Set database disk log size in megabytes (default: 100) + + + + Set key pool size to <n> (default: 100) + Set key pool size to <n> (default: 100) + + + + Set maximum block size in bytes (default: 250000) + Set maximum block size in bytes (default: 250000) + + + + Set minimum block size in bytes (default: 0) + Set minimum block size in bytes (default: 0) + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + Shrink debug.log file on client startup (default: 1 when no -debug) + + + + Specify configuration file (default: XP.conf) + Specify configuration file (default: XP.conf) + + + + Specify connection timeout in milliseconds (default: 5000) + Specify connection timeout in milliseconds (default: 5000) + + + + Specify data directory + Specify data directory + + + + Specify pid file (default: XPd.pid) + Specify pid file (default: XPd.pid) + + + + Specify your own public address + Specify your own public address + + + + This help message + This help message + + + + Threshold for disconnecting misbehaving peers (default: 100) + Threshold for disconnecting misbehaving peers (default: 100) + + + + To use the %s option + To use the %s option + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + Unable to bind to %s on this computer (bind returned error %d, %s) + + + + Unable to sign checkpoint, wrong checkpointkey? + + Unable to sign checkpoint, wrong checkpointkey? + + + + + Unknown -socks proxy version requested: %i + Unknown -socks proxy version requested: %i + + + + Unknown network specified in -onlynet: '%s' + Unknown network specified in -onlynet: '%s' + + + + Upgrade wallet to latest format + Upgrade wallet to latest format + + + + Usage: + Usage: + + + + Use OpenSSL (https) for JSON-RPC connections + Use OpenSSL (https) for JSON-RPC connections + + + + Use proxy to reach tor hidden services (default: same as -proxy) + Use proxy to reach tor hidden services (default: same as -proxy) + + + + Use the test network + Use the test network + + + + Username for JSON-RPC connections + Username for JSON-RPC connections + + + + Verifying database integrity... + Verifying database integrity... + + + + Wallet needed to be rewritten: restart XP to complete + Wallet needed to be rewritten: restart XP to complete + + + + Warning: Disk space is low! + Warning: Disk space is low! + + + + Warning: This version is obsolete, upgrade required! + Warning: This version is obsolete, upgrade required! + + + + wallet.dat corrupt, salvage failed + wallet.dat corrupt, salvage failed + + + + Specify wallet file (within data directory) + Specify wallet file (within data directory) + + + + Use in-memory logging for block index database (default: 1) + Use in-memory logging for block index database (default: 1) + + + + Find peers using DNS lookup (default: 1) + Find peers using DNS lookup (default: 0) {1)?} + + + + Sync checkpoints policy (default: strict) + Sync checkpoints policy (default: strict) + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + + + + Require a confirmations for change (default: 0) + Require a confirmations for change (default: 0) + + + + Enforce transaction scripts to use canonical PUSH operators (default: 1) + Enforce transaction scripts to use canonical PUSH operators (default: 1) + + + + Set the number of script verification threads (1-16, 0=auto, default: 0) + Set the number of script verification threads (1-16, 0=auto, default: 0) + + + + When creating transactions, ignore inputs with value less than this (default: %s) + When creating transactions, ignore inputs with value less than this (default: %s) + + + diff --git a/src/qt/locale/bitcoin_ru.qm b/src/qt/locale/bitcoin_ru.qm new file mode 100644 index 0000000000000000000000000000000000000000..d238067e39c59f88495fa531393b36a19c1abb4b GIT binary patch literal 119439 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 H{iXj0)DOOx literal 0 HcmV?d00001 diff --git a/src/qt/locale/bitcoin_ru.ts b/src/qt/locale/bitcoin_ru.ts new file mode 100644 index 00000000..6ca527c5 --- /dev/null +++ b/src/qt/locale/bitcoin_ru.ts @@ -0,0 +1,4453 @@ + + + + + AboutDialog + + + About XP + О XP + + + + <b>XP</b> version + <b>XP</b> версия + + + + Copyright © 2009-2015 The Bitcoin developers +Copyright © 2011-2012 The PPCoin Developers +Copyright © 2014 The Peerunity Developers +Copyright © 2014 The EmerCoin Developers +Copyright © 2012-2015 The XP developers + Все права защищены © 2009-2015 Разработчики Bitcoin +Все права защищены © 2011-2012 Разработчики PPCoin +Все права защищены © 2014 Разработчики Peerunity +Все права защищены © 2014 Разработчики EmerCoin +Все права защищены © 2012-2015 Разработчики XP + + + + <html><head/><body><p><br/>This is experimental software.</p><p>Distributed under the MIT/X11 software license, see the accompanying file COPYING or <br/><a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a>.</p><p>Main icon was designed by VisualPharm.com (<a href="mailto:team@visualpharm.com"><span style=" text-decoration: underline; color:#0000ff;">team@visualpharm.com</span></a>). This product also includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (<a href="http://www.openssl.org/"><span style=" text-decoration: underline; color:#0000ff;">http://www.openssl.org/</span></a>) and cryptographic software written by Eric Young (<a href="mailto:eay@cryptsoft.com"><span style=" text-decoration: underline; color:#0000ff;">eay@cryptsoft.com</span></a>).</p><p><br/></p></body></html> + <html><head/><body><p><br/>Это экспериментальная программа.</p><p>Распространяется на правах лицензии MIT/X11, см. файл license.txt или <br/><a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a>.</p><p>Иконка приложения разработана VisualPharm.com (<a href="mailto:team@visualpharm.com"><span style=" text-decoration: underline; color:#0000ff;">team@visualpharm.com</span></a>). Этот продукт также включает ПО, разработанное OpenSSL Project для использования в OpenSSL Toolkit (<a href="http://www.openssl.org/"><span style=" text-decoration: underline; color:#0000ff;">http://www.openssl.org/</span></a>) и криптографическое ПО, написанное Eric Young (<a href="mailto:eay@cryptsoft.com"><span style=" text-decoration: underline; color:#0000ff;">eay@cryptsoft.com</span></a>).</p></body></html> + + + + AddressBookPage + + + Address Book + Адресная книга + + + + These are your XP 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 Address + &Копировать адрес + + + + Show &QR Code + Показать &QR код + + + + Sign a message to prove you own a XP address + Подписать сообщение, чтобы доказать владение адресом XP + + + + Sign &Message + &Подписать сообщение + + + + Verify a message to ensure it was signed with a specified XP address + Проверить сообщение, чтобы убедиться, что оно было подписано указанным адресом XP + + + + &Verify Message + &Проверить сообщение + + + + Delete the currently selected address from the list + Удалить выбранный адрес из списка + + + + &Delete + &Удалить + + + + Copy &Label + Копировать &метку + + + + &Edit + &Правка + + + + Export Address Book Data + Экспортировать адресную книгу + + + + Comma separated file (*.csv) + Текст, разделённый запятыми (*.csv) + + + + Error exporting + Ошибка экспорта + + + + Could not write to file %1. + Невозможно записать в файл %1. + + + + AddressTableModel + + + Label + Метка + + + + Address + Адрес + + + + (no label) + [нет метки] + + + + AskPassphraseDialog + + + Passphrase Dialog + Диалог ввода пароля + + + + Enter passphrase + Введите пароль + + + + New passphrase + Новый пароль + + + + Repeat new passphrase + Повторите новый пароль + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Введите новый пароль для бумажника. <br/> Пожалуйста, используйте фразы из <b>10 или более случайных символов,</b> или <b>восьми и более слов.</b> + + + + Encrypt wallet + Зашифровать бумажник + + + + This operation needs your wallet passphrase to unlock the wallet. + Для выполнения операции требуется пароль вашего бумажника. + + + + Unlock wallet + Разблокировать бумажник + + + + This operation needs your wallet passphrase to decrypt the wallet. + Для выполнения операции требуется пароль вашего бумажника. + + + + Decrypt wallet + Расшифровать бумажник + + + + Change passphrase + Сменить пароль + + + + Enter the old and new passphrase to the wallet. + Введите старый и новый пароль для бумажника. + + + + Confirm wallet encryption + Подтвердите шифрование бумажника + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR COINS</b>! + Внимание: если вы зашифруете бумажник и потеряете пароль, вы <b>ПОТЕРЯЕТЕ ВСЕ ВАШИ МОНЕТЫ</b>! + + + + Are you sure you wish to encrypt your wallet? + Вы уверены, что хотите зашифровать ваш бумажник? + + + + + Wallet encrypted + Бумажник зашифрован + + + + XP will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your coins from being stolen by malware infecting your computer. + Сейчас программа закроется для завершения процесса шифрования. Помните, что шифрование вашего бумажника не может полностью защитить ваши монеты от кражи с помощью инфицирования вашего компьютера вредоносным ПО. + + + + IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet. + ВАЖНО: все предыдущие резервные копии вашего кошелька должны быть заменены новым зашифрованным файлом. В целях безопасности предыдущие резервные копии нешифрованного кошелька станут бесполезны, как только вы начнёте использовать новый шифрованный кошелёк. + + + + + + + Wallet encryption failed + Не удалось зашифровать бумажник + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Шифрование бумажника не удалось из-за внутренней ошибки. Ваш бумажник не был зашифрован. + + + + + The supplied passphrases do not match. + Введённые пароли не совпадают. + + + + + Wallet unlock failed + Разблокировка бумажника не удалась + + + + + + + The passphrase entered for the wallet decryption was incorrect. + Указанный пароль не подходит. + + + + Wallet decryption failed + Расшифрование бумажника не удалось + + + + Wallet decrypted + Бумажник расшифрован + + + + XP will close now to finish the decryption process. + Сейчас программа закроется для завершения процесса расшифровки. + + + + Wallet passphrase was successfully changed. + Пароль бумажника успешно изменён. + + + + + Warning: The Caps Lock key is on! + Внимание: Caps Lock включен! + + + + BitcoinGUI + + + A fatal error occurred. XP can no longer continue safely and will quit. + Произошла неисправимая ошибка. XP не может безопасно продолжать работу и будет закрыт. + + + + + XP + XP + + + + Wallet + Бумажник + + + + &Overview + О&бзор + + + + Show general overview of wallet + Показать общий обзор действий с бумажником + + + + &Send coins + Отп&равка монет + + + + Send coins to a XP address + Отправить монеты на указанный адрес XP + + + + &Receive coins + &Получение монет + + + + Show the list of addresses for receiving payments + Показать список адресов для получения платежей + + + + &Transactions + &Транзакции + + + + Browse transaction history + Показать историю транзакций + + + + &Minting + &PoS + + + + Show your minting capacity + Показать ваш PoS потенциал + + + + &Address Book + &Адресная книга + + + + Edit the list of stored addresses and labels + Изменить список сохранённых адресов и меток к ним + + + + Multisig + Мультиподпись + + + + Open window for working with multisig addresses + Открыть окно для работы с адресами с мультиподписью + + + + E&xit + В&ыход + + + + Quit application + Закрыть приложение + + + + &About XP + &О XP + + + + Show information about XP + Показать информацию о XP'е + + + + + About &Qt + О &Qt + + + + Show information about Qt + Показать информацию о Qt + + + + &Options... + Оп&ции... + + + + Modify configuration options for XP + Изменить параметры конфигурации XP + + + + &Show / Hide + &Показать / Скрыть + + + + &Encrypt Wallet... + &Зашифровать бумажник + + + + Encrypt or decrypt wallet + Зашифровать или расшифровать бумажник + + + + &Backup Wallet... + &Сделать резервную копию бумажника + + + + Backup wallet to another location + Сделать резервную копию бумажника в другом месте + + + + &Dump Wallet... + &Выгрузка ключей... + + + + Dump keys to a text file + Выгрузить ключи в текстовый файл + + + + &Import Wallet... + &Импорт ключей... + + + + Import keys into a wallet + Импортировать ключи из текстового файла + + + + &Change Passphrase... + &Изменить пароль + + + + Change the passphrase used for wallet encryption + Изменить пароль шифрования бумажника + + + + Sign &message... + &Подписать сообщение + + + + Sign messages with your XP addresses to prove you own them + Подписать сообщения вашим XP адресом, чтобы доказать, что вы им владеете + + + + &Verify message... + &Проверить сообщение... + + + + Verify messages to ensure they were signed with specified XP addresses + Проверить сообщения, чтобы удостовериться, что они подписаны определенным XP адресом + + + + Second &auth... + &Дополнительная авторизация + + + + Second auth with your XP addresses + Дополнительная авторизация по Вашим адресам + + + + &Lock wallet + &Заблокировать бумажник + + + + Lock wallet + Заблокировать бумажник + + + + Unlo&ck wallet + Разб&локировать бумажник + + + + Unlock wallet + Разблокировать бумажник + + + + Unlo&ck wallet for mining + Ра&зблокировать для майнинга + + + + Unlock wallet for mining + Разблокировать для майнинга + + + + &Export... + &Экспорт... + + + + Export the data in the current tab to a file + Экспортировать данные из вкладки в файл + + + + &Debug window + &Окно отладки + + + + Open debugging and diagnostic console + Открыть консоль отладки и диагностики + + + + &File + &Файл + + + + &Settings + &Настройки + + + + &Wallet security + &Безопасность + + + + &Help + &Помощь + + + + Tabs toolbar + Панель вкладок + + + + Actions toolbar + Панель действий + + + + + [testnet] + [тестовая сеть] + + + + + XP client + XP клиент + + + + %n active connection(s) to XP network + + %n активное соединение с сетью + %n активных соединений с сетью + %n активных соединений с сетью + + + + + Synchronizing with network... + Синхронизация с сетью... + + + + ~%n block(s) remaining + + остался ~%n блок + осталось ~%n блоков + осталось ~%n блоков + + + + + Downloaded %1 of %2 blocks of transaction history (%3% done). + Загружено %1 из %2 блоков истории операций (%3% завершено). + + + + Downloaded %1 blocks of transaction history. + Загружено %1 блоков истории транзакций. + + + + Current PoW difficulty is %1. + + + + + Current PoS difficulty is %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. + + + + Wallet is offline + Бумажник не в сети + + + + Wallet is locked + Бумажник заблокирован + + + + Blockchain download is in progress + Загрузка цепочки блоков ещё не завершена + + + + Stake miner is active<br>%1 inputs being used for mining<br>Network weight is %3 + + + + Stake miner is active<br>Kernel rate is %1 k/s<br>CD rate is %2 CD/s<br>Network weight is %3 + Proof-of-Stake майнер активен<br>Попыток генерации %1 в сек<br>Вес попыток %2 монетодень/с<br>Вес сети %3 монетодней + + + + No suitable inputs were found + Нет подходящих транзакций + + + + Error + Ошибка + + + + Warning + Предупреждение + + + + Information + Сообщение + + + + 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, которая отправится тем узлам, которые обработают Вашу транзакцию, и поможет поддержать сеть. Вы хотите добавить комиссию? + + + + Confirm transaction fee + Подтвердите комиссию + + + + Sent transaction + Исходящая транзакция + + + + Incoming transaction + Входящая транзакция + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Дата: %1 +Количество: %2 +Тип: %3 +Адрес: %4 + + + + + + URI handling + Обработка URI + + + + + URI can not be parsed! This can be caused by an invalid XP address or malformed URI parameters. + Не удалось обработать URI! Это может быть связано с неверным адресом XP или неправильными параметрами URI. + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Бумажник <b>зашифрован</b> и в настоящее время <b>разблокирован</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Бумажник <b>зашифрован</b> и в настоящее время <b>заблокирован</b> + + + + Backup Wallet + Сделать резервную копию бумажника + + + + Wallet Data (*.dat) + Данные бумажника (*.dat) + + + + Backup Failed + Резервное копирование не удалось + + + + There was an error trying to save the wallet data to the new location. + При попытке сохранения данных бумажника в новое место произошла ошибка. + + + + Dump Wallet + Произошла ошибка при сохранении копии данных бумажника. + + + + + Wallet dump (*.txt) + Файл с ключами (*.txt) + + + + Dump failed + Ошибка выгрузки + + + + An error happened while trying to save the keys to your location. +Keys were not saved. + Произошла ошибка при попытке выгрузки ключей в указанный файл. +Ключи не сохранены. + + + + Dump successful + Выгрузка завершена + + + + Keys were saved to this file: +%2 + Ключи сохранены в +%2 + + + + Import Wallet + Импорт ключей + + + + Import Failed + Ошибка импорта + + + + An error happened while trying to import the keys. +Some or all keys from: + %1, + were not imported into your wallet. + При попытке импорта ключей произошла ошибка. +Некоторые, либо все ключи из + %1, + не были импортированы. + + + + Import Successful + Импорт завершен + + + + All keys from: + %1, + were imported into your wallet. + Все ключи из файла + %1, + были успешно импортированы. + + + + ClientModel + + + Network Alert + Сетевая Тревога + + + + CoinControlDialog + + + Coin Control + Выбор входов + + + + Quantity: + Количество: + + + 0 + 0 + + + + Bytes: + Размер: + + + + Amount: + Сумма: + + + 0.00 XP + 0.00 XP + + + + Priority: + Приоритет: + + + + Fee: + Комиссия: + + + + Low Output: + Мелкие входы: + + + + + no + нет + + + + After Fee: + С комиссией: + + + + Change: + Сдача: + + + + (un)select all + Выбрать все + + + + Tree mode + Дерево + + + + List mode + Список + + + + Amount + Сумма + + + + Label + Метка + + + + Address + Адрес + + + + Date + Дата + + + + Confirmations + Подтверждения + + + + Confirmed + Подтверждено + + + + Weight + Вес + + + + Priority + Приоритет + + + + Copy address + Копировать адрес + + + + Copy label + Копировать метку + + + + + Copy amount + Копировать сумму + + + + Copy transaction ID + Скопировать ID транзакции + + + + Copy quantity + Копировать количество + + + + Copy fee + Копировать комиссию + + + + Copy after fee + Копировать с комиссией + + + + Copy bytes + Копировать объем + + + + Copy priority + Копировать приоритет + + + + Copy low output + Копировать мелкий выход + + + + Copy change + Копировать сдачу + + + + highest + наивысший + + + + high + высокий + + + + medium-high + выше среднего + + + + medium + средний + + + + low-medium + ниже среднего + + + + low + низкий + + + + lowest + наименьший + + + + DUST + ПЫЛЬ + + + + yes + да + + + + This label turns red, if the transaction size is bigger than 1000 bytes. + + This means a fee of at least %1 per kb is required. + + Can vary +/- 1 Byte per input. + Помечается красным, если размер транзакции превышает 1000 байт. + + Это означает, что требуется комиссия как минимум %1 за 1кб. + + + + Transactions with higher priority get more likely into a block. + +This label turns red, if the priority is smaller than "medium". + + This means a fee of at least %1 per kb is required. + Транзакции с более высоким приоритетом с большей вероятностью попадут в блок. + +Помечается красным, если приоритет ниже "medium". + + Это означает, что будет требоваться комиссия как минимум %2. + + + + This label turns red, if any recipient receives an amount smaller than %1. + + This means a fee of at least %2 is required. + + Amounts below the minimum fee are shown as DUST. + Помечается красным, если какому-либо из получателей будет отправлено менее чем %1. + + Это означает, что будет требоваться комиссия как минимум %2. + + Суммы ниже минимальной комиссии отображаются как DUST. + + + + This label turns red, if the change is smaller than %1. + + This means a fee of at least %2 is required. + Помечается красным, если сдача менее чем %1. + + Это означает, что будет требоваться комиссия как минимум %2. + + + + + (no label) + [нет метки] + + + + change from %1 (%2) + сдача с %1 (%2) + + + + (change) + (сдача) + + + + 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 not a valid XP address. + Введённый адрес "%1" не является правильным XP-адресом. + + + + The entered address "%1" is already in the address book. + Введённый адрес «%1» уже находится в адресной книге. + + + + Could not unlock wallet. + Не удается разблокировать бумажник. + + + + New key generation failed. + Генерация нового ключа не удалась. + + + + FreespaceChecker + + + A new data directory will be created. + + + + + name + + + + + Directory already exists. Add %1 if you intend to create a new directory here. + + + + + Path already exists, and is not a directory. + + + + + Cannot create data directory here. + + + + + GUIUtil::HelpMessageBox + + + + XP-Qt + XP-Qt + + + + version + версия + + + + Usage: + Использование: + + + + command-line options + параметры командной строки + + + + UI options + Опции интерфейса + + + + Set language, for example "de_DE" (default: system locale) + Выберите язык, например "de_DE" (по умолчанию: как в системе) + + + + Start minimized + Запускать свёрнутым + + + + Show splash screen on startup (default: 1) + Показывать сплэш при запуске (по умолчанию: 1) + + + + Intro + + + Welcome + + + + + Welcome to XP-qt. + + + + + As this is the first time the program is launched, you can choose where XP-qt will store its data. + + + + + XP-qt will download and store a copy of the XP block chain. At least %1GB of data will be stored in this directory, and it will grow over time. The wallet will also be stored in this directory. + + + + + Use the default data directory + + + + + Use a custom data directory: + + + + + XP-qt + + + + + Error: Specified data directory "%1" cannot be created. + + + + + Error + Ошибка + + + + %n GB of free space available + + + + + + + + + (of %n GB needed) + + + + + + + + + MintingTableModel + + + Transaction + Транзакция + + + + Address + Адрес + + + + Balance + Баланс + + + + Age + Возраст + + + + CoinDay + МонетоДень + + + + MintProbability + Вероятность PoS + + + + MintReward + Награда PoS + + + + minutes + минут + + + + hours + часов + + + + days + дней + + + + You have %1 chance to find a POS block if you mint %2 %3 at current difficulty. + У вас есть %1 шанс найти PoS блок, если вы будете майнить %2 %3 при текущей сложности. + + + + Destination address of the output. + Адрес, который получил эту транзакцию. + + + + Original transaction id. + Исходный ID транзакции. + + + + Age of the transaction in days. + Возраст транзакции в днях. + + + + Balance of the output. + Баланс выхода. + + + + Coin age in the output. + Вес выхода в монетоднях. + + + + Chance to mint a block within given time interval. + Шанс найти блок в течение выбранного временного интервала. + + + + The size of the potential rewards if the block is found at the beginning and the end given time interval. + Размер потенциальной награды если блок найден в начале и конце выбранного временного интервала. + + + + MintingView + + + transaction is too young + транзакция молода для PoS + + + + transaction is mature + зрелая транзакция + + + + transaction has reached maximum probability + транзакция достигла максимальной вероятности + + + + Display minting probability within : + Показывать вероятность найти блок в течение : + + + + 10 min + 10 минут + + + + 24 hours + 24 часа + + + + 7 days + 7 дней + + + + 30 days + 30 дней + + + + 60 days + 60 дней + + + + 90 days + 90 дней + + + + Copy transaction ID of input + Скопировать ID транзакции входа + + + + Copy address of input + Скопировать адрес входа + + + + Show/hide 'Address' column + Показать/скрыть столбец 'Адрес' + + + + Show/hide 'Transaction' column + Показать/скрыть столбец 'Транзакция' + + + + Export Minting Data + Экспортировать данные таблицы + + + + Comma separated file (*.csv) + Текст, разделённый запятыми (*.csv) + + + + Address + Адрес + + + + Transaction + Транзакция + + + + Age + Возраст + + + + CoinDay + Вес + + + + Balance + Баланс + + + + MintingProbability + Вероятность PoS + + + + MintingReward + Награда PoS + + + + Error exporting + Ошибка экспорта + + + + Could not write to file %1. + Невозможно записать в файл %1. + + + + MultisigAddressEntry + + + Form + Форма + + + + Public &key: + Публичный &ключ: + + + + The public key of an address + Публичный ключ адреса + + + + Enter a public key + Введите публичный ключ + + + + Paste public key from clipboard + Вставить публичный ключ из буфера обмена + + + + Alt+P + Alt+P + + + + Remove this public key + Удалить публичный ключ + + + + &Address: + &Адрес: + + + + Address associated to the public key + Адрес, связанный с публичным ключом + + + + Enter one of your addresses to get its public key + Введите один из своих адресов, чтобы получить его публичный ключ + + + + Choose address from address book + Выбрать адрес из адресной книги + + + + Alt+A + Alt+A + + + + Label: + Метка: + + + + MultisigDialog + + + Multisig + Мультиподпись + + + + &Create Address + &Создание адреса + + + + Add a member to the signing pool + Добавить участника для совместного подписания + + + + &Add public key... + &Добавить публичный ключ... + + + + Remove all public key fields + Очистить все публичные ключи + + + + Clear all + Очистить всё + + + + Required signatures: + Требуется подписей: + + + + Enter a number + Введите количество + + + / 1 + / 1 + + + + Create multisig address + Создать адрес с мультиподписью + + + + Multisig address: + Адрес с мультиподписью: + + + + Copy the multisig address to the system clipboard + Скопировать адрес с мультиподписью в буфер обмена + + + + Redeem script: + Скрипт траты: + + + + Copy the redeem script to the system clipboard + Скопировать cкрипт выплаты в буфер обмена + + + + The redeem script will be required to spend the funds sent to the multisig address + Скрипт траты необходим для отправки монет c адреса с мультиподписью + + + + Save redeem script + Сохранить скрипт траты + + + + Add the multisig address to your personal addresses + Добавить адрес с мультиподписью в список Ваших адресов + + + + Add address to wallet + Добавить адрес в бумажник + + + + &Spend Funds + &Отправка монет + + + + Inputs + Входы + + + + Inputs amount: + Сумма входов: + + + 123.456 + 123.456 + + + XP + XP + + + + Add input... + Добавить вход... + + + + Outputs + Выходы + + + + Outputs amount: + Сумма выходов: + + + + Fee: + Комиссия: + + + + Add output... + Добавить выход... + + + + Create transaction + Создать транзакцию + + + + Enter a raw transaction or create a new one + Введите транзакцию в бинарном формате или создайте новую + + + + Paste transaction from clipboard + Вставить транзакцию из буфера обмена + + + + Alt+P + Alt+P + + + + Sign transaction + Подписать транзакцию + + + + Send transaction + Отправить транзакцию + + + + + + + Error + Ошибка + + + + Number of addresses involved in the address creation > %1 +Reduce the number + Число адресов, используемых при создании адреса с мультиподписью > %1 +Сократите количество + + + + Number of required signatures is 0 +Number of required signatures must be between 1 and number of keys involved in the creation of address. + Число требуемых подписей - 0 +Число требуемых подписей должно быть между 1 и числом ключей, используемых при создании адреса. + + + + Number of required signatures > Number of keys involved in the creation of address. + Число требуемых подписей > числа ключей, используемых при создании адреса. + + + + Redeem script exceeds size limit: %1 > %2 +Reduce the number of addresses involved in the address creation. + Скрипт выплаты превышает максимальный размер: %1 > %2 +Уменьшите число адресов, участвующих в создании адреса с мультиподписью + + + + Transaction signature is complete + Подписание транзакции завершено + + + + Transaction is NOT completely signed + Подписание транзакции завершено не полностью + + + + + Confirm send transaction + Подтвердите отправку транзакции + + + + The fee of the transaction (%1 XP) is smaller than the expected fee (%2 XP). Do you want to send the transaction anyway? + Комиссия (%1 XP) меньше необходимой (%2 XP). Вы хотите отправить транзакцию без изменений? + + + + The fee of the transaction (%1 XP) is bigger than the expected fee (%2 XP). Do you want to send the transaction anyway? + Комиссия (%1 XP) больше необходимой (%2 XP). Вы хотите отправить транзакцию без изменений? + + + + MultisigInputEntry + + + Form + Форма + + + + Enter a transaction id + Введите ID транзакции + + + + Alt+P + Alt+P + + + + Transaction id: + ID транзакции: + + + + Transaction output: + Выходы транзакции: + + + + Redeem script: + Скрипт выплаты: + + + + Enter the redeem script of the address in the transaction output + Введите скрипт выплаты адреса, содержащегося в выходе транзакции + + + + Alt+A + Alt+A + + + + OptionsDialog + + + Options + Опции + + + + &Main + &Главная + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. Fee 0.001 recommended. + Опциональная комиссия за каждый КБ транзакции, которая позволяет быть уверенным, что Ваша транзакция будет обработана быстро. Большинство транзакций занимают 1КБ. Рекомендуется комиссия 0.001. + + + + Pay transaction &fee + Заплатить ко&миссию + + + + per kilobyte + за килобайт + + + + Automatically start XP after logging in to the system. + Автоматически запускать XP после входа в систему + + + + &Start XP on system login + &Запускать XP при входе в систему + + + + Detach block and address databases at shutdown. This means they can be moved to another data directory, but it slows down shutdown. The wallet is always detached. + Отключить базы данных блоков и адресов при выходе. Это означает, что их можно будет переместить в другой каталог данных, но завершение работы будет медленнее. Бумажник всегда отключается. + + + + &Detach databases at shutdown + &Отключать базы данных при выходе + + + + &Network + &Сеть + + + + Connect to the XP network through a SOCKS proxy (e.g. when connecting through Tor). + Подключаться к сети XP через прокси SOCKS (например, при подключении через Tor). + + + + &Connect through SOCKS proxy: + &Подключаться через SOCKS прокси: + + + + 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. 9050) + Порт прокси-сервера (например, 9050) + + + + SOCKS &Version: + &Версия SOCKS: + + + + SOCKS version of the proxy (e.g. 5) + Версия SOCKS-прокси (например, 5) + + + + Connect through &Tor: + Подключаться через &Tor: + + + + Tor IP: + IP tor прокси: + + + + Port: + Порт: + + + + Use Tor only + Подключаться только через Tor + + + + Tor name: + Имя Вашего tor хоста: + + + + &Window + &Окно + + + + Show only a tray icon after minimizing the window. + Показывать только иконку в системном лотке после сворачивания окна. + + + + &Minimize to the tray instead of the taskbar + &Cворачивать в системный лоток вместо панели задач + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu. + Сворачивать вместо закрытия. Если данная опция будет выбрана — приложение закроется только после выбора соответствующего пункта в меню. + + + + M&inimize on close + С&ворачивать при закрытии + + + + &Display + О&тображение + + + + User Interface &language: + &Язык интерфейса: + + + + The user interface language can be set here. This setting will take effect after restarting XP. + Здесь можно выбрать язык интерфейса. Настройки вступят в силу после перезапуска XP. + + + + &Unit to show amounts in: + &Отображать суммы в единицах: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Выберите единицу измерения монет при отображении и отправке. + + + + Whether to show XP addresses in the transaction list or not. + Показывать ли адреса XP в списке транзакций. + + + + &Display addresses in transaction list + &Показывать адреса в списке транзакций + + + + Whether to show coin control features or not. + Выключает и включает отображение панели выбора входов. + + + + Display coin &control features (experts only!) + Управление &входами (только для продвинутых пользователей!) + + + + + Third party URLs (e.g. explorer.novaco.in) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |. + Сторонние URL (например explorer.novaco.in), которые отображаются на вкладке транзакций как пункты контекстного меню. %s в URL заменяется хешем транзакции. URL отделяются друг от друга вертикальной чертой |. + + + + Third party transaction URLs + Сторонние URL транзакций + + + + &OK + О&К + + + + &Cancel + &Отмена + + + + &Apply + &Применить + + + + default + по умолчанию + + + + + + + Warning + Внимание + + + + + + + This setting will take effect after restarting XP. + Эта настройка вступит в силу после перезапуска XP + + + + The supplied proxy address is invalid. + Адрес прокси неверен. + + + + The supplied tor address is invalid. + Tor адрес неверен + + + + OverviewPage + + + Form + Форма + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the XP network after a connection is established, but this process has not completed yet. + Отображаемая информация может быть устаревшей. Ваш бумажник автоматически синхронизируется с сетью XP после подключения, но этот процесс пока не завершён. + + + + + Your unspendable balance + Недоступный баланс + + + + Unspendable: + Недоступно: + + + + + Total of coins that was staked, and do not yet count toward the current balance + Общая сумма всех монет, использованных для Proof-of-Stake, и не учитывающихся на балансе + + + + Balances + Балансы + + + + + Your current available balance + Ваш текущий доступный баланс + + + + Available: + Доступно: + + + + Stake: + Доля: + + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Общая сумма всех транзакций, которые до сих пор не подтверждены, и до сих пор не учитываются в текущем балансе + + + + Unconfirmed: + Не подтверждено: + + + + + Mined balance that has not yet matured + Баланс добытых монет, который ещё не созрел + + + + Immature: + Незрелые: + + + + + Your current total balance + Ваш текущий общий баланс + + + + Total: + Итого: + + + + + Total number of transactions in wallet + Общее количество транзакций в Вашем бумажнике + + + + Number of transactions: + Количество транзакций: + + + + <b>Recent transactions</b> + <b>Последние транзакции</b> + + + + + out of sync + не синхронизировано + + + + QObject + + + XPs + XPs + + + + Milli-XPs (1 / 1,000) + Милли-XPs (1 / 1000) + + + + Micro-XPs (1 / 1,000,000) + Микро-XPs (1 / 1000000) + + + + Amount + Количество + + + + %1 d + %1 д + + + + %1 h + %1 ч + + + + %1 m + %1 мин + + + + %1 s + %1 сек + + + + from %1 to %2 + от %1 до %2 + + + + Error: Specified data directory "%1" does not exist. + + + + + QRCodeDialog + + + QR Code Dialog + Диалог QR-кода + + + + Request Payment + Запросить платёж + + + + Label: + Метка: + + + + Message: + Сообщение: + + + + Amount: + Количество: + + + + &Save As... + &Сохранить как... + + + + Error encoding URI into QR Code. + Ошибка кодирования URI в QR-код + + + + The entered amount is invalid, please check. + Введено неверное количество, проверьте ещё раз. + + + + Resulting URI too long, try to reduce the text for label / message. + Получившийся URI слишком длинный, попробуйте сократить текст метки / сообщения. + + + + Save QR Code + Сохранить QR-код + + + + PNG Images (*.png) + PNG Изображения (*.png) + + + + RPCConsole + + + XP - Debug window + XP - Окно отладки + + + + &Information + &Информация + + + + Configuration file + Конфигурационный файл + + + + + + + + + + + + + + N/A + Н/Д + + + + Current number of blocks + Текущее число блоков + + + + Command-line options + Параметры командной строки + + + + Open the XP debug log file from the current data directory. This can take a few seconds for large log files. + Открыть отладочный лог-файл XP из текущего каталога данных. Это может занять несколько секунд для больших лог-файлов. + + + + &Open + &Открыть + + + + Debug log file + Отладочный лог-файл + + + + Show the XP-Qt help message to get a list with possible XP command-line options. + Показать помощь по XP-Qt, чтобы получить список доступных параметров командной строки. + + + + &Show + &Показать + + + + Last block time + Время последнего блока + + + + Network + Сеть + + + + Startup time + Время запуска + + + + On testnet + В тестовой сети + + + + Number of connections + Число подключений + + + + Block chain + Цепь блоков + + + + Estimated total blocks + Расчётное число блоков + + + + Using BerkeleyDB version + Используется версия BerkeleyDB + + + + Using OpenSSL version + Используется версия OpenSSL + + + + Build date + Дата сборки + + + + Client version + Версия клиента + + + + XP Core + Ядро XP + + + + Client name + Имя клиента + + + + Open the XP configuration file from the current data directory. + Открыть конфигурационный файл XP из текущего каталога данных. + + + + O&pen + О&ткрыть + + + + &Console + Консоль + + + + Clear console + Очистить консоль + + + + &Network Traffic + &Сетевой трафик + + + + &Clear + &Очистить + + + + Totals + Всего + + + + Received: + Получено: + + + + Sent: + Отправлено: + + + + Welcome to the XP RPC console. + Добро пожаловать в RPC-консоль XP. + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + Используйте стрелки вверх и вниз для просмотра истории и <b>Ctrl-L</b> для очистки экрана. + + + + Type <b>help</b> for an overview of available commands. + Напишите <b>help</b> для просмотра доступных команд. + + + + Inbound: + Входящие: + + + + Outbound: + Исходящие: + + + + %1 B + %1 Б + + + + %1 KB + %1 KБ + + + + %1 MB + %1 MБ + + + + %1 GB + %1 ГБ + + + + SecondAuthDialog + + Signatures - Sign / Verify a Message + Подписи - подписать/проверить сообщение + + + The address to sign the message with (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + Адрес, которым вы хотите подписать сообщение (напр. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + + Choose an address from the address book + Выберите адрес из адресной книги + + + + Alt+A + Alt+A + + + + Alt+P + Alt+P + + + + Copy the current signature to the system clipboard + Скопировать текущую подпись в системный буфер обмена + + + + Alt+C + Alt+C + + + + &Sign Data + &Подписать данные + + + + Reset all sign message fields + Сбросить значения всех полей подписывания сообщений + + + + Clear &All + Очистить &всё + + + + Enter a XP address (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + Введите адрес XP (напр. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + + The entered address is invalid. + Введённый адрес неверен + + + + + Please check the address and try again. + Пожалуйста, проверьте адрес и попробуйте ещё раз. + + + + The entered address does not refer to a key. + Введённый адрес не связан с ключом + + + + Wallet unlock was cancelled. + Разблокировка бумажника была отменена. + + + + Private key for the entered address is not available. + Для введённого адреса недоступен закрытый ключ + + + + No information available about transaction. + Информация об указанной транзакции недоступна. + + + + Message signing failed. + Не удалось подписать сообщение + + + + Message signed. + Сообщение подписано + + + + Second Authentification + Дополнительная авторизация + + + + You can sign hash of transaction exists in the blockchain with your addresses. + Вы можете подписывать хэши транзакций, существующих в цепочке блоков, своими адресами. + + + + The address for authentification (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + Адрес, по которому вы проходите авторизацию (напр. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + + Paste hash from clipboard + Вставить хэш из буфера обмена + + + + Sign the hash + Подписать хэш транзакции + + + &Sign data + &Подписать данные + + + + Click "Sign data" to generate signature + Нажмите "Подписать данные" для создания подписи + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Отправка + + + + Coin Control Features + Выбор входов + + + + Inputs... + Входы... + + + + automatically selected + автоматический выбор + + + + Insufficient funds! + Недостаточно средств! + + + + Quantity: + Количество: + + + 0 + 0 + + + + Bytes: + Размер: + + + + Amount: + Сумма: + + + 0.00 XP + 0.00 XP + + + + Priority: + Приоритет: + + + + medium + medium + + + + Fee: + Комиссия: + + + + Low Output: + Мелкие выходы: + + + + no + нет + + + + After Fee: + С комиссией: + + + + Change + Сдача + + + + custom change address + адрес для сдачи + + + + Choose address from address book + Выберите адрес из адресной книги + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Вставить адрес из буфера обмена + + + + Alt+P + Alt+P + + + + Send to multiple recipients at once + Отправить нескольким получателям одновременно + + + + Add &Recipient + &Добавить получателя + + + + Remove all transaction fields + Удалить все поля транзакции + + + + Clear &All + Очистить &всё + + + + Balance: + Баланс: + + + 123.456 XP + 123.456 XP + + + + Confirm the send action + Подтвердить отправку + + + + S&end + &Отправить + + + + Enter a XP address (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + Введите XP-адрес (например 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + + Copy quantity + Копировать количество + + + + Copy amount + Копировать сумму + + + + Copy fee + Копировать комиссию + + + + Copy after fee + Копировать после комиссии + + + + Copy bytes + Копировать объём + + + + Copy priority + Копировать приоритет + + + + Copy low output + Копировать мелкий выход + + + + Copy change + Копировать сдачу + + + + + <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 recipient address is not valid, please recheck. + Адрес получателя неверный, пожалуйста, перепроверьте. + + + + The amount to pay must be larger than 0. + Количество монет для отправки должно быть больше 0. + + + + The amount exceeds your balance. + Количество отправляемых монет превышает Ваш баланс + + + + The total exceeds your balance when the %1 transaction fee is included. + Сумма превысит Ваш баланс, если комиссия в размере %1 будет добавлена к транзакции + + + + Duplicate address found, can only send to each address once per 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. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + Адрес получателя платежа (например 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + + Choose address from address book + Выберите адрес из адресной книги + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Вставить адрес из буфера обмена + + + + Alt+P + Alt+P + + + + Remove this recipient + Удалить этого получателя + + + + Enter a XP address (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + Введите XP-адрес (например 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + Подписи - подписать/проверить сообщение + + + + + &Sign Message + &Подписать сообщение + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + Вы можете подписывать сообщения своими адресами, чтобы доказать владение ими. Будьте осторожны, не подписывайте что-то неопределённое, так как фишинговые атаки могут обманным путём заставить вас подписать нежелательные сообщения. Подписывайте только те сообщения, с которыми вы согласны вплоть до мелочей. + + + + The address to sign the message with (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + Адрес, которым вы хотите подписать сообщение (напр. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + + + Choose an address from the address book + Выберите адрес из адресной книги + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Вставить адрес из буфера обмена + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Введите сообщение для подписи + + + + Copy the current signature to the system clipboard + Скопировать текущую подпись в системный буфер обмена + + + + Sign the message to prove you own this XP address + Подписать сообщение, чтобы доказать владение адресом XP + + + + Reset all sign message fields + Сбросить значения всех полей подписывания сообщений + + + + + Clear &All + Очистить &всё + + + + + &Verify Message + &Проверить сообщение + + + + Enter the signing address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack. + Введите ниже адрес для подписи, сообщение (убедитесь, что переводы строк, пробелы, табы и т.п. в точности скопированы) и подпись, чтобы проверить сообщение. Убедитесь, что не скопировали лишнего в подпись, по сравнению с самим подписываемым сообщением, чтобы не стать жертвой атаки "man-in-the-middle". + + + + The address the message was signed with (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + Адрес, которым было подписано сообщение (напр. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + + Verify the message to ensure it was signed with the specified XP address + Проверить сообщение, чтобы убедиться, что оно было подписано указанным адресом XP + + + + Reset all verify message fields + Сбросить все поля проверки сообщения + + + + + Enter a XP address (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + Введите адрес XP (напр. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + + Click "Sign Message" to generate signature + Нажмите "Подписать сообщение" для создания подписи + + + + Enter XP signature + Введите подпись XP + + + + + The entered address is invalid. + Введённый адрес неверен + + + + + + + Please check the address and try again. + Пожалуйста, проверьте адрес и попробуйте ещё раз. + + + + + The entered address does not refer to a key. + Введённый адрес не связан с ключом + + + + Wallet unlock was cancelled. + Разблокировка бумажника была отменена. + + + + Private key for the entered address is not available. + Для введённого адреса недоступен закрытый ключ + + + + Message signing failed. + Не удалось подписать сообщение + + + + Message signed. + Сообщение подписано + + + + The signature could not be decoded. + Подпись не может быть раскодирована. + + + + + Please check the signature and try again. + Пожалуйста, проверьте подпись и попробуйте ещё раз. + + + + The signature did not match the message digest. + Подпись не соответствует отпечатку сообщения. + + + + Message verification failed. + Проверка сообщения не удалась. + + + + Message verified. + Сообщение проверено. + + + + TrafficGraphWidget + + + KB/s + КБ/сек + + + + TransactionDesc + + + Open for %n block(s) + + Открыто для %n блока + Открыто для %n блоков + Открыто для %n блоков + + + + + Open until %1 + Открыто до %1 + + + + %1/offline + %1/отключен + + + + %1/unconfirmed + %1/не подтверждено + + + + %1 confirmations + %1 подтверждений + + + + Status + Статус + + + + , has not been successfully broadcast yet + , ещё не было успешно разослано + + + + , broadcast through %n node(s) + + , разослано через %n узел + , разослано через %n узла + , разослано через %n узлов + + + + + Date + Дата + + + + Source + Источник + + + + Generated + Сгенерировано + + + + + + + From + От + + + + + unknown + неизвестно + + + + + label + метка + + + + + + To + Для + + + + + own address + свой адрес + + + + + + + + Credit + Кредит + + + + matures in %n more block(s) + + будет доступно через %n блок + будет доступно через %n блока + будет доступно через %n блоков + + + + + not accepted + не принято + + + + + + + Debit + Дебет + + + + Transaction fee + Комиссия + + + + Net amount + Чистая сумма + + + + Message + Сообщение + + + + Comment + Комментарий: + + + + Transaction ID + ID транзакции + + + + Generated coins must mature 520 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, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + Сгенерированные монеты должны подождать 520 блоков, прежде чем они могут быть потрачены. Когда Вы сгенерировали этот блок, он был отправлен в сеть для добавления в цепочку блоков. Если данная процедура не удастся, статус изменится на «не подтверждено», и монеты будут недействительны. Это иногда происходит в случае, если другой узел сгенерирует блок на несколько секунд раньше вас. + + + + Debug information + Отладочная информация + + + + Transaction + Транзакция + + + + Inputs + Входы + + + + Amount + Количество + + + + true + истина + + + + false + ложь + + + + TransactionDescDialog + + + Transaction details + Детали транзакции + + + + This pane shows a detailed description of the transaction + Данный диалог показывает детализированную статистику по выбранной транзакции + + + + TransactionTableModel + + + Date + Дата + + + + Type + Тип + + + + Address + Адрес + + + + Open for %n block(s) + + Открыто для %n блока + Открыто для %n блоков + Открыто для %n блоков + + + + + Open until %1 + Открыто до %1 + + + + Offline (%1 confirmations) + Оффлайн (%1 подтверждений) + + + + Unconfirmed (%1 of %2 confirmations) + Не подтверждено (%1 из %2 подтверждений) + + + + Confirmed (%1 confirmations) + Подтверждено (%1 подтверждений) + + + + Mined balance will be available when it matures in %n more block(s) + + Добытыми монетами можно будет воспользоваться через %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 + Получено от + + + + Sent to + Отправлено + + + + 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 + Другое + + + + Enter address or label to search + Введите адрес или метку для поиска + + + + Min amount + Мин. сумма + + + + Copy address + Копировать адрес + + + + Copy label + Копировать метку + + + + Copy amount + Скопировать сумму + + + + Copy transaction ID + Скопировать ID транзакции + + + + Edit label + Изменить метку + + + + Show transaction details + Показать подробности транзакции + + + + Clear orphans + Удалить орфаны + + + + Export Transaction Data + Экспортировать данные транзакций + + + + Comma separated file (*.csv) + Текст, разделённый запятыми (*.csv) + + + + Confirmed + Подтверждено + + + + Date + Дата + + + + Type + Тип + + + + Label + Метка + + + + Address + Адрес + + + + Amount + Количество + + + + ID + ID + + + + Error exporting + Ошибка экспорта + + + + Could not write to file %1. + Невозможно записать в файл %1. + + + + Range: + Промежуток от: + + + + to + до + + + + WalletModel + + + Sending... + Отправка.... + + + + bitcoin-core + + + %s, you must set a rpcpassword in the configuration file: + %s +It is recommended you use the following random password: +rpcuser=XPrpc +rpcpassword=%s +(you do not need to remember this password) +If the file does not exist, create it with owner-readable-only file permissions. + + %s, вы должны установить опцию rpcpassword в конфигурационном файле: + %s +Рекомендуется использовать следующий случайный пароль: +rpcuser=XPrpc +rpcpassword=%s +(вам не нужно запоминать этот пароль) +Если файл не существует, создайте его и установите права доступа только для владельца. + + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Разрешённые алгоритмы (по умолчанию: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + Произошла ошибка при открытии RPC-порта %u для прослушивания на IPv4: %s + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + Произошла ошибка при открытии на прослушивание IPv6 RPС-порта %u, возвращаемся к IPv4: %s + + + + Cannot obtain a lock on data directory %s. XP is probably already running. + Невозможно установить блокировку на рабочую директорию %s. Возможно, XP уже запущен. + + + + Detach block and address databases. Increases shutdown time (default: 0) + Отключить базы данных блоков и адресов. Увеличивает время завершения работы (по умолчанию: 0) + + + + Error initializing database environment %s! To recover, BACKUP THAT DIRECTORY, then remove everything from it except for wallet.dat. + Ошибка инициализации окружения БД %s! Для восстановления СДЕЛАЙТЕ РЕЗЕРВНУЮ КОПИЮ этой директории, затем удалите из нее все, кроме wallet.dat. + + + + 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, а монеты были потрачены из другой копии, но не были отмечены как потраченные в этой. + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds + Ошибка: эта транзакция требует комиссию в размере как минимум %s из-за её объёма, сложности или использования недавно полученных средств + + + + Error: Wallet unlocked for block minting only, unable to create transaction. + Ошибка: Кошелёк разблокирован только для PoS майнинга, невозможно создать транзакцию. + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + Выполнить команду, когда появляется новый блок (%s в команде заменяется на хэш блока) + + + + Listen for JSON-RPC connections on <port> (default: 28191 or testnet: 18345) + Прослушивать подключения JSON-RPC на <порту> (по умолчанию: 28191 или для testnet: 18345) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Число секунд блокирования неправильно ведущих себя узлов (по умолчанию: 86400) + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + Максимальный размер высокоприоритетных/низкокомиссионных транзакций в байтах (по умолчанию: 27000) + + + + Unable to bind to %s on this computer. XP is probably already running. + Невозможно привязаться к %s на этом компьютере. Возможно, XP уже работает. + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + Внимание: установлено очень большое значение -paytxfee. Это комиссия, которую вы заплатите при проведении транзакции. + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong XP will not work properly. + Внимание: убедитесь, что дата и время на Вашем компьютере выставлены верно. Если Ваши часы идут неправильно, XP будет работать некорректно. + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + Внимание: ошибка чтения wallet.dat! Все ключи восстановлены, но записи в адресной книге и истории транзакций могут быть некорректными. + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + Внимание: wallet.dat был поврежден, данные восстановлены! Оригинальный wallet.dat сохранен как wallet.{timestamp}.bak в %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. + Вы должны установить rpcpassword=<password> в конфигурационном файле: +%s +Если файл не существует, создайте его и установите права доступа только для владельца. + + + + Accept command line and JSON-RPC commands + Принимать командную строку и команды JSON-RPC + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + Принимать подключения извне (по умолчанию: 1, если не используется -proxy или -connect) + + + + Add a node to connect to and attempt to keep the connection open + Добавить узел для подключения и пытаться поддерживать соединение открытым + + + + Allow DNS lookups for -addnode, -seednode and -connect + Разрешить поиск в DNS для -addnode, -seednode и -connect + + + + Allow JSON-RPC connections from specified IP address + Разрешить подключения JSON-RPC с указанного IP + + + + Attempt to recover private keys from a corrupt wallet.dat + Попытка восстановления ключей из поврежденного wallet.dat + + + + Bind to given address. Use [host]:port notation for IPv6 + Привязаться (bind) к указанному адресу. Используйте запись вида [хост]:порт для IPv6 + + + + Block creation options: + Параметры создания блоков: + + + + Cannot downgrade wallet + Не удаётся понизить версию бумажника + + + + Cannot initialize keypool + Не удаётся инициализировать массив ключей + + + + Cannot resolve -bind address: '%s' + Не удаётся разрешить адрес в параметре -bind: '%s' + + + + Cannot resolve -externalip address: '%s' + Не удаётся разрешить адрес в параметре -externalip: '%s' + + + + Cannot write default address + Не удаётся записать адрес по умолчанию + + + + Connect only to the specified node(s) + Подключаться только к указанному узлу(ам) + + + + Connect through socks proxy + Подключаться через socks прокси + + + + Connect to a node to retrieve peer addresses, and disconnect + Подключиться к узлу, чтобы получить список адресов других участников и отключиться + + + + Discover own IP address (default: 1 when listening and no -externalip) + Определить свой IP (по умолчанию: 1 при прослушивании и если не используется -externalip) + + + + Done loading + Загрузка завершена + + + + Error loading blkindex.dat + Ошибка чтения blkindex.dat + + + + Error loading wallet.dat + Ошибка при загрузке wallet.dat + + + + Error loading wallet.dat: Wallet corrupted + Ошибка загрузки wallet.dat: Бумажник поврежден + + + + Error loading wallet.dat: Wallet requires newer version of XP + Ошибка загрузки wallet.dat: бумажник требует более новую версию XP + + + + Error + Ошибка + + + + Error: Transaction creation failed + Ошибка: Создание транзакции не удалось + + + + Error: Wallet locked, unable to create transaction + Ошибка: бумажник заблокирован, невозможно создать транзакцию + + + + Error: could not start node + Ошибка: не удалось запустить узел + + + + Failed to listen on any port. Use -listen=0 if you want this. + Не удалось начать прослушивание на порту. Используйте -listen=0 если вас это устраивает. + + + + Fee per KB to add to transactions you send + Комиссия на килобайт, добавляемая к вашим транзакциям + + + + Find peers using DNS lookup (default: 0) + Искать узлы с помощью DNS (по умолчанию: 1) + + + + Find peers using internet relay chat (default: 1) + Найти участников через IRC (по умолчанию: 1) + + + + Get help for a command + Получить помощь по команде + + + + How many blocks to check at startup (default: 2500, 0 = all) + Сколько блоков проверять при запуске (по умолчанию: 2500, 0 = все) + + + + How thorough the block verification is (0-6, default: 1) + Насколько тщательно проверять блоки (0-6, по умолчанию: 1) + + + + Importing blockchain data file. + Импортируется файл цепи блоков. + + + + Importing bootstrap blockchain data file. + Импортируется bootstrap-файл цепи блоков. + + + + Imports blocks from external blk000?.dat file + Импортировать блоки из внешнего файла blk000?.dat + + + + Insufficient funds + Недостаточно монет + + + + Invalid -proxy address: '%s' + Неверный адрес -proxy: '%s' + + + + Invalid -tor address: '%s' + Неверный адрес -tor: '%s' + + + + Invalid amount for -paytxfee=<amount>: '%s' + Неверное количество в параметре -paytxfee=<кол-во>: '%s' + + + + Invalid amount for -reservebalance=<amount> + Неверная сумма для опции -reservebalance=<amount> + + + + Invalid amount + Неверное количество + + + + List commands + Список команд + + + + + Listen for connections on <port> (default: 28192 or testnet: 17778) + Принимать входящие подключения на <port> (по умолчанию: 28192 или 17778 в тестовой сети) + + + + Loading addresses... + Загрузка адресов... + + + + Loading block index... + Загрузка индекса блоков... + + + + Loading wallet... + Загрузка бумажника... + + + + Maintain at most <n> connections to peers (default: 125) + Поддерживать не более <n> подключений к узлам (по умолчанию: 125) + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + Максимальный размер буфера приёма на соединение, <n>*1000 байт (по умолчанию: 5000) + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + Максимальный размер буфера отправки на соединение, <n>*1000 байт (по умолчанию: 1000) + + + + XP version + Версия + + + + XP + XP + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + Подключаться только к узлам из сети <net> (IPv4, IPv6 или Tor) + + + + Options: + Опции: + + + + Output extra debugging information. Implies all other -debug* options + Выводить больше отладочной информации. Включает все остальные опции -debug* + + + + Output extra network debugging information + Выводить дополнительную сетевую отладочную информацию + + + + Password for JSON-RPC connections + Пароль для подключений JSON-RPC + + + + Prepend debug output with timestamp + Дописывать отметки времени к отладочному выводу + + + + Rescan the block chain for missing wallet transactions + Перепроверить цепь блоков на предмет отсутствующих в бумажнике транзакций + + + + Rescanning... + Сканирование... + + + + Run in the background as a daemon and accept commands + Запускаться в фоне как демон и принимать команды + + + + SSL options: (see the Bitcoin Wiki for SSL setup instructions) + +Параметры SSL: (см. Bitcoin Wiki для инструкций по настройке SSL) + + + + Select the version of socks proxy to use (4-5, default: 5) + Выберите версию SOCKS-прокси (4-5, по умолчанию: 5) + + + + Send command to -server or XPd + Отправить команду на -server или XPd + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Посылать команды узлу, запущенному на <ip> (по умолчанию: 127.0.0.1) + + + + Send trace/debug info to console instead of debug.log file + Выводить информацию трассировки/отладки на консоль вместо файла debug.log + + + + Send trace/debug info to debugger + Отправлять информацию трассировки/отладки в отладчик + + + + Sending... + Отправка... + + + + Server certificate file (default: server.cert) + Файл серверного сертификата (по умолчанию: server.cert) + + + + Server private key (default: server.pem) + Приватный ключ сервера (по умолчанию: server.pem) + + + + Set database cache size in megabytes (default: 25) + Установить размер кэша базы данных в мегабайтах (по умолчанию: 25) + + + + Set database disk log size in megabytes (default: 100) + Установить размер лога базы данных в мегабайтах (по умолчанию: 100) + + + + Set key pool size to <n> (default: 100) + Установить размер запаса ключей в <n> (по умолчанию: 100) + + + + Set maximum block size in bytes (default: 250000) + Максимальный размер блока в байтах (по умолчанию: 250000) + + + + Set minimum block size in bytes (default: 0) + Минимальный размер блока в байтах (по умолчанию: 0) + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + Сжимать файл debug.log при запуске клиента (по умолчанию: 1, если нет -debug) + + + + Specify configuration file (default: XP.conf) + Указать конфигурационный файл (по умолчанию: XP.conf) + + + + Specify connection timeout in milliseconds (default: 5000) + Таймаут соединения в миллисекундах (по умолчанию: 5000) + + + + Specify data directory + Укажите каталог данных + + + + Specify pid file (default: XPd.pid) + Указать pid-файл (по умолчанию: XP.pid) + + + + Specify your own public address + Укажите ваш собственный публичный адрес + + + + This help message + Эта справка + + + + Threshold for disconnecting misbehaving peers (default: 100) + Порог для отключения неправильно ведущих себя узлов (по умолчанию: 100) + + + + To use the %s option + Чтобы использовать опцию %s + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + Невозможно привязаться к %s на этом компьютере (bind вернул ошибку %d, %s) + + + + Unable to sign checkpoint, wrong checkpointkey? + + Невозможно подписать checkpoint, неправильный checkpointkey? + + + + + Unknown -socks proxy version requested: %i + В параметре -socks запрошена неизвестная версия: %i + + + + Unknown network specified in -onlynet: '%s' + В параметре -onlynet указана неизвестная сеть: '%s' + + + + Upgrade wallet to latest format + Обновить бумажник до последнего формата + + + + Usage: + Использование: + + + + Use OpenSSL (https) for JSON-RPC connections + Использовать OpenSSL (https) для подключений JSON-RPC + + + + Use proxy to reach tor hidden services (default: same as -proxy) + Использовать прокси для скрытых сервисов tor(по умолчанию: тот же, что и в -proxy) + + + + Use the test network + Использовать тестовую сеть + + + + Username for JSON-RPC connections + Имя для подключений JSON-RPC + + + + Verifying database integrity... + Проверка целостности базы данных... + + + + Wallet needed to be rewritten: restart XP to complete + Необходимо перезаписать бумажник, перезапустите XP для завершения операции. + + + + Warning: Disk space is low! + Внимание: мало места на диске! + + + + Warning: This version is obsolete, upgrade required! + Внимание: эта версия устарела, требуется обновление! + + + + wallet.dat corrupt, salvage failed + wallet.dat поврежден, восстановление не удалось + + + + Specify wallet file (within data directory) + Указать файл кошелька (в пределах DATA директории) + + + + Use in-memory logging for block index database (default: 1) + Использовать ведение журнала в памяти для индекса базы данных блоков (по умолчанию: 1) + + + + Find peers using DNS lookup (default: 1) + Искать узлы с помощью DNS (по умолчанию: 1) + + + + Sync checkpoints policy (default: strict) + Политика синхронизированных меток (по умолчанию: strict) + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + Выполнить команду, когда получена новая транзакция (%s в команде заменяется на ID транзакции) + + + + Require a confirmations for change (default: 0) + Требовать подтверждения для сдачи (по умолчанию: 0) + + + + Enforce transaction scripts to use canonical PUSH operators (default: 1) + Требовать от скриптов использования стандартных PUSH операторов (по умолчанию: 1) + + + + Set the number of script verification threads (1-16, 0=auto, default: 0) + Установить число потоков проверки скрипта (1-16, 0= автоматически, по умолчанию: 0) + + + + When creating transactions, ignore inputs with value less than this (default: %s) + При создании транзакций игнорировать входы с суммой ниже указанной (по умолчанию: %s) + + + diff --git a/src/qt/locale/bitcoin_uk.qm b/src/qt/locale/bitcoin_uk.qm new file mode 100644 index 0000000000000000000000000000000000000000..f5140ef36a9a0ee666cc0081d720cee77ba019cc GIT binary patch literal 117895 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 literal 0 HcmV?d00001 diff --git a/src/qt/locale/bitcoin_uk.ts b/src/qt/locale/bitcoin_uk.ts new file mode 100644 index 00000000..e8251e03 --- /dev/null +++ b/src/qt/locale/bitcoin_uk.ts @@ -0,0 +1,4476 @@ + + + + + AboutDialog + + + About XP + Про XP + + + + <b>XP</b> version + <b>XP</b> версії + + + + Copyright © 2009-2015 The Bitcoin developers +Copyright © 2011-2012 The PPCoin Developers +Copyright © 2014 The Peerunity Developers +Copyright © 2014 The EmerCoin Developers +Copyright © 2012-2015 The XP developers + © 2009-2015 Розробники Bitcoin +© 2011-2012 Розробники PPCoin +© 2014 Розробники Peerunity +© 2014 Розробники EmerCoin +© 2012-2015 Розробники XP + + + + <html><head/><body><p><br/>This is experimental software.</p><p>Distributed under the MIT/X11 software license, see the accompanying file COPYING or <br/><a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a>.</p><p>Main icon was designed by VisualPharm.com (<a href="mailto:team@visualpharm.com"><span style=" text-decoration: underline; color:#0000ff;">team@visualpharm.com</span></a>). This product also includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (<a href="http://www.openssl.org/"><span style=" text-decoration: underline; color:#0000ff;">http://www.openssl.org/</span></a>) and cryptographic software written by Eric Young (<a href="mailto:eay@cryptsoft.com"><span style=" text-decoration: underline; color:#0000ff;">eay@cryptsoft.com</span></a>).</p><p><br/></p></body></html> + <html><head/><body><p><br/>Це програмне забезпечення є експериментальним.</p><p>Поширюється за ліцензією MIT/X11, додаткова інформація міститься у файлі COPYING та за адресою <br/><a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a>.</p><p>Піктограму програми розробили VisualPharm.com (<a href="mailto:team@visualpharm.com"><span style=" text-decoration: underline; color:#0000ff;">team@visualpharm.com</span></a>). Крім того, цей продукт також включає в себе програмне забезпечення, розроблене в рамках проекту OpenSSL (<a href="http://www.openssl.org/"><span style=" text-decoration: underline; color:#0000ff;">http://www.openssl.org/</span></a>), криптографічне програмне забезпечення, написане Еріком Янгом (<a href="mailto:eay@cryptsoft.com"><span style=" text-decoration: underline; color:#0000ff;">eay@cryptsoft.com</span></a>).</p></body></html> + + + + AddressBookPage + + + Address Book + Адресна книга + + + + These are your XP 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 Address + &Скопіювати адресу + + + + Show &QR Code + Показати QR-&Код + + + + Sign a message to prove you own a XP address + Підпишіть повідомлення щоб довести, що ви є власником цієї адреси + + + + Sign &Message + Підписати &повідомлення + + + + Verify a message to ensure it was signed with a specified XP address + Перевірити повідомлення, щоб переконатися, що воно було підписане вказаним адресою XP + + + + &Verify Message + &Перевірити повідомлення + + + + Delete the currently selected address from the list + Видалити виділену адресу зі списку. Лише адреси з адресної книги можуть бути видалені. + + + + &Delete + &Видалити + + + + Copy &Label + Скопіювати &мітку + + + + &Edit + &Редагувати + + + + Export Address Book Data + Експортувати адресну книгу + + + + Comma separated file (*.csv) + Файли відділені комами (*.csv) + + + + Error exporting + Помилка при експортуванні + + + + Could not write to file %1. + Неможливо записати у файл %1. + + + + AddressTableModel + + + Label + Назва + + + + Address + Адреса + + + + (no label) + (немає назви) + + + + AskPassphraseDialog + + + Passphrase Dialog + Діалог введення пароля + + + + Enter passphrase + Введіть пароль + + + + New passphrase + Новий пароль + + + + Repeat new passphrase + Повторіть пароль + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Введіть новий пароль для гаманця.<br/>Будь ласка, використовуйте паролі що містять <b> як мінімум 10 випадкових символів </b> або <b> як мінімум 8 слів</b>. + + + + Encrypt wallet + Зашифрувати гаманець + + + + This operation needs your wallet passphrase to unlock the wallet. + Ця операція потребує пароль для розблокування гаманця. + + + + Unlock wallet + Розблокувати гаманець + + + + This operation needs your wallet passphrase to decrypt the wallet. + Ця операція потребує пароль для дешифрування гаманця. + + + + Decrypt wallet + Дешифрувати гаманець + + + + Change passphrase + Змінити пароль + + + + Enter the old and new passphrase to the wallet. + Ввести старий та новий паролі для гаманця. + + + + Confirm wallet encryption + Підтвердити шифрування гаманця + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR COINS</b>! + УВАГА: Якщо ви зашифруєте гаманець і забудете пароль, ви <b>ВТРАТИТЕ ВСІ СВОЇ НОВАКОІНИ</b>! +Ви дійсно хочете зашифрувати свій гаманець? + + + + Are you sure you wish to encrypt your wallet? + Ви дійсно хочете зашифрувати свій гаманець? + + + + + Wallet encrypted + Гаманець зашифровано + + + + XP will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your coins from being stolen by malware infecting your computer. + Новакоін-клієнт буде закрито для завершення процесу шифрування. Пам’ятайте, що шифрування гаманця не може повністю захистити ваші новакоіни від кражі, у випадку якщо ваш комп’ютер буде інфіковано шкідливими програмами. + + + + IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet. + ВАЖНО: усі попередні резервні копії вашого гаманця потрібно замінити новим зашифрованим файлом. В цілях безпеки попередні резервні копії гаманця стануть марні, як тільки ви почнете використовувати новий шифрований гаманець. + + + + + + + Wallet encryption failed + Не вдалося зашифрувати гаманець + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Виникла помилка під час шифрування гаманця. Ваш гаманець не було зашифровано. + + + + + The supplied passphrases do not match. + Введені паролі не співпадають. + + + + + Wallet unlock failed + Не вдалося розблокувати гаманець + + + + + + + The passphrase entered for the wallet decryption was incorrect. + Вказаний пароль не підходить. + + + + Wallet decryption failed + Не вдалося розшифрувати гаманець + + + + Wallet decrypted + Гаманець розшифровано + + + + XP will close now to finish the decryption process. + Зараз XP клієнт буде закрито щоб завершити процес розшифровки. + + + + Wallet passphrase was successfully changed. + Ваш пароль було успішно змінено. + + + + + Warning: The Caps Lock key is on! + Увага: Caps Lock включен + + + + BitcoinGUI + + + A fatal error occurred. XP can no longer continue safely and will quit. + Сталася фатальна помилка. Програма зараз закриється, оскільки XP не може продовжувати роботу безпечно. + + + + + XP + XP + + + + Wallet + Гаманець + + + + &Overview + &Огляд + + + + Show general overview of wallet + Показати загальний огляд гаманця + + + + &Send coins + В&ідправити + + + + Send coins to a XP address + Відправити монети на вказану адресу + + + + &Receive coins + О&тримати + + + + Show the list of addresses for receiving payments + Показати список адрес для отримання платежів + + + + &Transactions + &Транзакції + + + + Browse transaction history + Переглянути історію транзакцій + + + + &Minting + &PoS + + + + Show your minting capacity + Показати ваш PoS потенціал + + + + &Address Book + &Адресна книга + + + + Edit the list of stored addresses and labels + Редагувати список збережених адрес та міток + + + + Multisig + Мультипідпис + + + + Open window for working with multisig addresses + Відкрити вікно для роботи з мультипідпис-адресом + + + + E&xit + &Вихід + + + + Quit application + Вийти + + + + &About XP + П&ро XP + + + + Show information about XP + Показати інформацію про XP + + + + + About &Qt + &Про Qt + + + + Show information about Qt + Показати інформацію про Qt + + + + &Options... + &Параметри... + + + + Modify configuration options for XP + Редагувати параметри XP + + + + &Show / Hide + Показати / Приховати + + + + &Encrypt Wallet... + &Шифрування гаманця... + + + + Encrypt or decrypt wallet + Зашифрувати чи розшифрувати гаманець + + + + &Backup Wallet... + &Резервне копіювання гаманця... + + + + Backup wallet to another location + Резервне копіювання гаманця в інше місце + + + + &Dump Wallet... + &Вивантаження гаманця... + + + + Dump keys to a text file + Вивантажити ключі у текстовий файл + + + + &Import Wallet... + &Імпорт гаманця... + + + + Import keys into a wallet + Імпортувати ключі до гаманця + + + + &Change Passphrase... + &Змінити пароль... + + + + Change the passphrase used for wallet encryption + Змінити пароль, який використовується для шифрування гаманця + + + + Sign &message... + Підписати п&овідомлення... + + + + Sign messages with your XP addresses to prove you own them + Підтвердіть, що Ви є власником повідомлення підписавши його Вашою XP-адресою + + + + &Verify message... + П&еревірити повідомлення... + + + + Verify messages to ensure they were signed with specified XP addresses + Перевірте повідомлення для впевненості, що воно підписано вказаною XP-адресою + + + + Second &auth... + + + + + Second auth with your XP addresses + + + + + &Lock wallet + Заблокувати гаманець + + + + Lock wallet + Розблокувати гаманець + + + + Unlo&ck wallet + Розбло&кувати гаманець + + + + Unlock wallet + Розблокувати гаманець + + + + Unlo&ck wallet for mining + Розбло&кувати гаманець для майнігу + + + + Unlock wallet for mining + Розблокувати гаманець для майнігу + + + + &Export... + &Експорт... + + + + Export the data in the current tab to a file + Експортувати дані з поточної вкладки в файл + + + + &Debug window + &Вікно відладки + + + + Open debugging and diagnostic console + Відкрити консоль відладки і діагностики + + + + &File + &Файл + + + + &Settings + &Налаштування + + + + &Wallet security + &Безпека гаманця + + + + &Help + &Довідка + + + + Tabs toolbar + Панель вкладок + + + + Actions toolbar + Панель дій + + + + + [testnet] + [тестова мережа] + + + + + XP client + XP клієнт + + + + %n active connection(s) to XP network + + %n активне з'єднання з мережею XP + %n активних з'єднань з мережею XP + %n активних з'єднань з мережею XP + + + + + Synchronizing with network... + Синхронізація з мережею... + + + + ~%n block(s) remaining + + ~%n блок залишився + блоків залишилося + ~%n блоків залишилося + + + + + Downloaded %1 of %2 blocks of transaction history (%3% done). + Завантажено %1 з %2 блоків історії транзакцій (%3% виконано). + + + + Downloaded %1 blocks of transaction history. + Завантажено %1 блоків історії транзакцій. + + + + Current PoW difficulty is %1. + + + + + Current PoS difficulty is %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. + + + + Wallet is offline + Гаманець офф-лайн + + + + Wallet is locked + Гаманець заблокованний + + + + Blockchain download is in progress + Відбувається завантаження ланцюжка блоків + + + + Stake miner is active<br>%1 inputs being used for mining<br>Network weight is %3 + + + + Stake miner is active<br>Kernel rate is %1 k/s<br>CD rate is %2 CD/s<br>Network weight is %3 + PoS майнер активний<br>Спроб генерації %1 в сек<br>Вага спроб %2 монетодень/с<br>Вага мережі %3 монетоднів + + + + No suitable inputs were found + Було знайдено непридатний вхід + + + + Error + Помилка + + + + Warning + Попередження + + + + Information + Інформація + + + + 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, яка відправиться тим вузлам, які її оброблять, це допоможе підтримати мережу. Ви бажаєте додати комісію? + + + + Confirm transaction fee + Підтвердіть комісію транзакції + + + + Sent transaction + Вихідна транзакція + + + + Incoming transaction + Вхідна транзакція + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Дата: %1 +Сума: %2 +Тип: %3 +Адреса: %4 + + + + + + URI handling + Обробка URI + + + + + URI can not be parsed! This can be caused by an invalid XP address or malformed URI parameters. + Неможливо обробити URI! Це може бути викликано неправильною XP-адресою, чи невірними параметрами URI. + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + <b>Зашифрований</b> гаманець <b>розблоковано</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + <b>Зашифрований</b> гаманець <b>заблоковано</b> + + + + Backup Wallet + Бекап гаманця + + + + Wallet Data (*.dat) + Данi гаманця (*.dat) + + + + Backup Failed + Помилка резервного копіювання + + + + There was an error trying to save the wallet data to the new location. + Виникла помилка при спробі зберегти гаманець на нове місце. + + + + Dump Wallet + Вивантажити гаманець + + + + + Wallet dump (*.txt) + Дамп гаманця (*.txt) + + + + Dump failed + Вивантаження не вдалося + + + + An error happened while trying to save the keys to your location. +Keys were not saved. + Відбулася помилка під час спроби зберегти ключі до місця призначення. +Ключі не були збережені. + + + + Dump successful + Вивантаження успішне + + + + Keys were saved to this file: +%2 + Ключі були збережені у цей фаіл: +%2 + + + + Import Wallet + Імпортувати гаманець + + + + Import Failed + Імпорт не вдався + + + + An error happened while trying to import the keys. +Some or all keys from: + %1, + were not imported into your wallet. + Під час імпорту ключів відбулася помилка. +Деякі чи всі ключі з: +%1, +не були імпортовані до вашого гаманця. + + + + Import Successful + Імпорт успішний + + + + All keys from: + %1, + were imported into your wallet. + Усі ключі з: +%1, +були імпортовані до вашого гаманця. + + + + ClientModel + + + Network Alert + Сповіщення мережі + + + + CoinControlDialog + + + Coin Control + Управління входами + + + + Quantity: + Кількість: + + + 0 + 0 + + + + Bytes: + Байтів: + + + + Amount: + Сума: + + + 0.00 XP + 0.00 XP + + + + Priority: + Пріорітет: + + + + Fee: + Комісія: + + + + Low Output: + Маленькі виходи: + + + + + no + ні + + + + After Fee: + Після комісії: + + + + Change: + Решта: + + + + (un)select all + Вибрати/зняти всі + + + + Tree mode + Деревом + + + + List mode + Списком + + + + Amount + Сума + + + + Label + Мітка + + + + Address + Адреса + + + + Date + Дата + + + + Confirmations + Підтверджень + + + + Confirmed + Підтверджені + + + + Weight + Вес + + + + Priority + Пріоритет + + + + Copy address + Скопіювати адресу + + + + Copy label + Скопіювати мітку + + + + + Copy amount + Скопіювати суму + + + + Copy transaction ID + Скопіювати ID транзакції + + + + Copy quantity + Скопіювати кількість + + + + Copy fee + Скопіювати комісію + + + + Copy after fee + Скопіювати після комісії + + + + Copy bytes + Скопіювати байти + + + + Copy priority + Скопіювати пріорітет + + + + Copy low output + Скопіювати маленькі виходи + + + + Copy change + Скопіювати решту + + + + highest + найвищий + + + + high + високий + + + + medium-high + вище за середній + + + + medium + середній + + + + low-medium + нижче за середній + + + + low + низький + + + + lowest + найнижчий + + + + DUST + ПИЛ + + + + yes + так + + + + This label turns red, if the transaction size is bigger than 1000 bytes. + + This means a fee of at least %1 per kb is required. + + Can vary +/- 1 Byte per input. + Ця позначка буде червоною, якщо розмір транзакції вищий за 1000 байт. + +Це означає, що необхідно внести комісію (щонайменше %1 за КБ). + +Може відрізнятися на +/- 1 байт за вхід. + + + + Transactions with higher priority get more likely into a block. + +This label turns red, if the priority is smaller than "medium". + + This means a fee of at least %1 per kb is required. + Транзакції з вищим пріоритетом мають більше шансів бути включеними до блоку. + +Ця позначка буде червоною, якщо пріоритет транзакції нижчий за «середній». + +Це означає, що необхідно внести комісію (щонайменше %1 за КБ). + + + + This label turns red, if any recipient receives an amount smaller than %1. + + This means a fee of at least %2 is required. + + Amounts below the minimum fee are shown as DUST. + Ця позначка буде червоною, якщо будь хто з отримувачів отримає менше ніж %1. + +Це означає, що необхідно внести комісію (щонайменше %2). + +Суми нижче мінімальної комісії показані як "ПИЛ". + + + + This label turns red, if the change is smaller than %1. + + This means a fee of at least %2 is required. + Ця мітка стає червоною, якщо зміна менше %1. + +Це означає, що необхідно внести комісію (щонайменше %2). + + + + + (no label) + (немає назви) + + + + change from %1 (%2) + здача з %1 (%2) + + + + (change) + (решта) + + + + 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 not a valid XP address. + Введена адреса «%1» не є коректною адресою в мережі XP + + + + The entered address "%1" is already in the address book. + Введена адреса «%1» вже присутня в адресній книзі. + + + + Could not unlock wallet. + Неможливо розблокувати гаманець. + + + + New key generation failed. + Не вдалося згенерувати нові ключі. + + + + FreespaceChecker + + + A new data directory will be created. + + + + + name + + + + + Directory already exists. Add %1 if you intend to create a new directory here. + + + + + Path already exists, and is not a directory. + + + + + Cannot create data directory here. + + + + + GUIUtil::HelpMessageBox + + + + XP-Qt + XP-Qt + + + + version + версії + + + + Usage: + Використання: + + + + command-line options + параметри командного рядка + + + + UI options + Параметри інтерфейсу + + + + Set language, for example "de_DE" (default: system locale) + Встановлення мови, наприклад "de_DE" (типово: системна) + + + + Start minimized + Запускати згорнутим + + + + Show splash screen on startup (default: 1) + Показувати заставку під час запуску (типово: 1) + + + + Intro + + + Welcome + + + + + Welcome to XP-qt. + + + + + As this is the first time the program is launched, you can choose where XP-qt will store its data. + + + + + XP-qt will download and store a copy of the XP block chain. At least %1GB of data will be stored in this directory, and it will grow over time. The wallet will also be stored in this directory. + + + + + Use the default data directory + + + + + Use a custom data directory: + + + + + XP-qt + + + + + Error: Specified data directory "%1" cannot be created. + + + + + Error + Помилка + + + + %n GB of free space available + + + + + + + + + (of %n GB needed) + + + + + + + + + MintingTableModel + + + Transaction + Транзакція + + + + Address + Адреса + + + + Balance + Баланс + + + + Age + Вік + + + + CoinDay + Монетодень + + + + MintProbability + PoS-ймовірність + + + + MintReward + Pos нагорода + + + + minutes + хвилин + + + + hours + годин + + + + days + днів + + + + You have %1 chance to find a POS block if you mint %2 %3 at current difficulty. + У вас є %1 шанс знайти PoS блок, якщо ви будете майнити %2 %3 при поточній складності. + + + + Destination address of the output. + Адреса виходу. + + + + Original transaction id. + ID батьківської транзакції. + + + + Age of the transaction in days. + Вік транзакції у днях. + + + + Balance of the output. + Баланс виходу. + + + + Coin age in the output. + Вік виходу. + + + + Chance to mint a block within given time interval. + Шанс знайти блок в даному інтервалі часу. + + + + The size of the potential rewards if the block is found at the beginning and the end given time interval. + Розмір потенційної винагороди, якщо блок знаходиться на початку і наприкінці заданого інтервалу часу. + + + + MintingView + + + transaction is too young + транзакція занадто молода + + + + transaction is mature + Транзакція дозріла + + + + transaction has reached maximum probability + транзакція досягла максимальної ймовірності + + + + Display minting probability within : + Показувати ймовірність протягом: + + + + 10 min + 10 хвилин + + + + 24 hours + 24 години + + + + 7 days + 7 днів + + + + 30 days + 30 днів + + + + 60 days + 60 днів + + + + 90 days + 90 днів + + + + Copy transaction ID of input + Скопіювати ID транзакції входу + + + + Copy address of input + Скопіювати адресу входу + + + + Show/hide 'Address' column + Показати/приховати колонку "Адреса" + + + + Show/hide 'Transaction' column + Показати/приховати колонку "Транзакція" + + + + Export Minting Data + Експортувати дані PoS + + + + Comma separated file (*.csv) + Значення, розділені комою (*.csv) + + + + Address + Адреса + + + + Transaction + Транзакція + + + + Age + Вік + + + + CoinDay + Монетодень + + + + Balance + Баланс + + + + MintingProbability + PoS-ймовірність + + + + MintingReward + Pos нагорода + + + + Error exporting + Помилка експорту + + + + Could not write to file %1. + Не вдалося записати у файл %1. + + + + MultisigAddressEntry + + + Form + Форма + + + + Public &key: + Відкритий &ключ: + + + + The public key of an address + Відкритий ключ адреси + + + + Enter a public key + Введіть відкритий ключ + + + + Paste public key from clipboard + Вставити відкритий ключ з буфера обміну + + + + Alt+P + Alt+P + + + + Remove this public key + Видалити цей публічний ключ + + + + &Address: + &Адреса: + + + + Address associated to the public key + Адреса, пов'язана з відкритим ключем + + + + Enter one of your addresses to get its public key + Введіть одну з ваших адрес, щоб отримати її відкритий ключ + + + + Choose address from address book + Оберіть адресу з адресної книги + + + + Alt+A + Alt+A + + + + Label: + Мітка: + + + + MultisigDialog + + + Multisig + Мультипідпис + + + + &Create Address + &Створити адресу + + + + Add a member to the signing pool + Додати учасника для підпису + + + + &Add public key... + &Додати відкритий ключ... + + + + Remove all public key fields + Видалити всі публічні ключі + + + + Clear all + Очистити всі + + + + Required signatures: + Необхідно підписів: + + + + Enter a number + Введіть номер + + + / 1 + / 1 + + + + Create multisig address + Створити адресу з мультипідписом + + + + Multisig address: + Адреса з мультипідписом + + + + Copy the multisig address to the system clipboard + Скопіювати адресу з мультипідписом у буфер обміну + + + + Redeem script: + Скрипт виплати: + + + + Copy the redeem script to the system clipboard + Скопіювати скрипт виплати із буфера обміну + + + + The redeem script will be required to spend the funds sent to the multisig address + Скрипт виплати необхідний для відправки монет з адреси із мультіпидписом + + + + Save redeem script + Зберегти скрипт виплати + + + + Add the multisig address to your personal addresses + Додати адресу з мультипідписом до списку ваших адрес + + + + Add address to wallet + Додати адресу у гаманець + + + + &Spend Funds + &Витрачені кошти + + + + Inputs + Входи + + + + Inputs amount: + Сума входів: + + + 123.456 + 123.456 + + + XP + XP + + + + Add input... + Додати вхід... + + + + Outputs + Виходи + + + + Outputs amount: + Сума виходів: + + + + Fee: + Комісія: + + + + Add output... + Додати вихід... + + + + Create transaction + Створити транзакцію + + + + Enter a raw transaction or create a new one + Введіть транзакцію у бінарному форматі або створіть нову + + + + Paste transaction from clipboard + Вставити транзакцію з буфера обміну + + + + Alt+P + Alt+P + + + + Sign transaction + Підписати транзакцію + + + + Send transaction + Надіслати транзакцію + + + + + + + Error + Помилка + + + + Number of addresses involved in the address creation > %1 +Reduce the number + Кількість адрес, що беруть участь у створенні адреси > %1 +Зменшити кількість + + + + Number of required signatures is 0 +Number of required signatures must be between 1 and number of keys involved in the creation of address. + Кількість необхідних підписів 0 +Кількість необхідних підписів має бути між 1 і кількістю ключів, які беруть участь у створенні адреси. + + + + Number of required signatures > Number of keys involved in the creation of address. + Кількість необхідних підписів > Кількість ключів, що беруть участь у створені адреси. + + + + Redeem script exceeds size limit: %1 > %2 +Reduce the number of addresses involved in the address creation. + Скрипт виплати перевищує граничний розмір: %1 > %2 +Зменшіть кількість адрес, що беруть участь у створенні адреси. + + + + Transaction signature is complete + Підписання транзакції завершено + + + + Transaction is NOT completely signed + Транзакція підписана НЕ повністю + + + + + Confirm send transaction + Підтвердіть відправку транзакції + + + + The fee of the transaction (%1 XP) is smaller than the expected fee (%2 XP). Do you want to send the transaction anyway? + Комісія за транзакцію (%1 XP) менше, ніж очікувана комісія (%2 XP). Чи бажаєте ви здійснити транзакцію в будь-якому випадку? + + + + The fee of the transaction (%1 XP) is bigger than the expected fee (%2 XP). Do you want to send the transaction anyway? + Комісія за транзакцію (%1 XP) більша, ніж очікувана комісія (%2 XP). Чи бажаєте ви здійснити транзакцію в будь-якому випадку? + + + + MultisigInputEntry + + + Form + Форма + + + + Enter a transaction id + Введіть ID транзакції + + + + Alt+P + Alt+P + + + + Transaction id: + ID транзакції: + + + + Transaction output: + Вихід транзакції: + + + + Redeem script: + Скрипт виплати: + + + + Enter the redeem script of the address in the transaction output + Введіть скрипт виплати адреси, що міститься на виході транзакції + + + + Alt+A + Alt+A + + + + OptionsDialog + + + Options + Параметри + + + + &Main + &Головні + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. Fee 0.001 recommended. + Необов'язкова комісія транзакції за кБ що сприятеме її швидкому обробленню. Більшість транзакцій 1кБ. Рекомендується коміся 0,001. + + + + Pay transaction &fee + Сплатити комісію &транзакції + + + + per kilobyte + за кілобайт + + + + Automatically start XP after logging in to the system. + Автоматично запускати гаманець при вході до системи. + + + + &Start XP on system login + &Запускати гаманець при вході в систему + + + + Detach block and address databases at shutdown. This means they can be moved to another data directory, but it slows down shutdown. The wallet is always detached. + Відключити бази даних блоків і адрес при виході. Це означає, що їх можна буде перемістити в інший каталог даних, але завершення роботи буде повільніше. Гаманець завжди відключається. + + + + &Detach databases at shutdown + &Відключати бази даних при виході + + + + &Network + &Мережа + + + + Connect to the XP network through a SOCKS proxy (e.g. when connecting through Tor). + Підключення до мережі через SOCKS5 проксі (напр. при підключенні через Tor). + + + + &Connect through SOCKS proxy: + &Підключення через SOCKS проксі: + + + + 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. 9050) + Порт проксі-сервера (наприклад 9050) + + + + SOCKS &Version: + SOCKS &версія: + + + + SOCKS version of the proxy (e.g. 5) + SOCKS версія проксі (наприклад, 5) + + + + Connect through &Tor: + З'єднання через &Тор: + + + + Tor IP: + Тор IP: + + + + Port: + Порт: + + + + Use Tor only + Використовувати тільки Tor + + + + Tor name: + Тор ім'я: + + + + &Window + &Вікно + + + + Show only a tray icon after minimizing the window. + Показувати лише іконку в треї після згортання вікна. + + + + &Minimize to the tray instead of the taskbar + Мінімізувати &у трей + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu. + Згортати замість закриття. Якщо ця опція включена, програма закриється лише після вибору відповідного пункту в меню. + + + + M&inimize on close + Згортати замість закритт&я + + + + &Display + &Відображення + + + + User Interface &language: + Мов&а інтерфейсу користувача: + + + + The user interface language can be set here. This setting will take effect after restarting XP. + Встановлює мову інтерфейсу. Зміни набудуть чинності після перезапуску XP. + + + + &Unit to show amounts in: + В&имірювати суми в: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Виберіть одиницю вимірювання монет, яка буде відображатись в гаманці та при відправленні. + + + + Whether to show XP addresses in the transaction list or not. + Відображати адреси XP у списку транзакцій чи ні. + + + + &Display addresses in transaction list + &Відображати адреси у листі транзакцій + + + + Whether to show coin control features or not. + Показати або сховати керування входами. + + + + Display coin &control features (experts only!) + Управління входами (тільки для експертів!) + + + + + Third party URLs (e.g. explorer.novaco.in) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |. + Сторонні URL (наприклад explorer.novaco.in), які відображаються на вкладці транзакцій як пункти контекстного меню. %s в URL замінюється хешем транзакції. URL відокремлюються один від одного вертикальною рискою |. + + + + Third party transaction URLs + Сторонні URL транзакцій + + + + &OK + &Гаразд + + + + &Cancel + &Скасувати + + + + &Apply + &Застосувати + + + + default + типово + + + + + + + Warning + Попередження + + + + + + + This setting will take effect after restarting XP. + Цей параметр набуде чинності після перезавантаження XP. + + + + The supplied proxy address is invalid. + Надана адреса проксі-сервера недійсна. + + + + The supplied tor address is invalid. + Надана адреса тор недійсна. + + + + OverviewPage + + + Form + Форма + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the XP network after a connection is established, but this process has not completed yet. + Інформація, що відображається може бути застарілою. Ваш гаманець автоматично синхронізується з мережею XP після встановлення з'єднання, але цей процес ще не завершений. + + + + + Your unspendable balance + Недоступний баланс + + + + Unspendable: + Недоступно: + + + + + Total of coins that was staked, and do not yet count toward the current balance + Загальна сума всіх монет, використаних для PoS, які не враховуються в поточному балансі + + + + Balances + Залишки + + + + + Your current available balance + Ваш поточний доступний баланс + + + + Available: + Доступно: + + + + Stake: + Доля: + + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Загальна сума усіх транзакцій, які ще не підтверджені, та до сих пір не враховуються в загальному балансі + + + + Unconfirmed: + Непідтверджені: + + + + + Mined balance that has not yet matured + Баланс видобутих та ще недозрілих монет + + + + Immature: + Незрілі: + + + + + Your current total balance + Ваш поточний загальний баланс + + + + Total: + Всього: + + + + + Total number of transactions in wallet + Загальна кількість транзакцій в гаманці + + + + Number of transactions: + Кількість транзакцій: + + + + <b>Recent transactions</b> + <b>Останні транзакції</b> + + + + + out of sync + не синхронізовано + + + + QObject + + + XPs + XPs + + + + Milli-XPs (1 / 1,000) + Milli-XPs (1 / 1,000) + + + + Micro-XPs (1 / 1,000,000) + Micro-XPs (1 / 1,000,000) + + + + Amount + Сума + + + + %1 d + %1 дн. + + + + %1 h + %1 год. + + + + %1 m + %1 хв. + + + + %1 s + %1 сек. + + + + from %1 to %2 + з %1 до %2 + + + + Error: Specified data directory "%1" does not exist. + + + + + QRCodeDialog + + + QR Code Dialog + Діалог QR-коду + + + + Request Payment + Запросити Платіж + + + + Label: + Мітка: + + + + Message: + Повідомлення: + + + + Amount: + Сума: + + + + &Save As... + &Зберегти як... + + + + Error encoding URI into QR Code. + Помилка при кодуванні URI в QR-код. + + + + The entered amount is invalid, please check. + Введена сума недійсна, будь ласка, перевірте. + + + + Resulting URI too long, try to reduce the text for label / message. + Кінцевий URI занадто довгий, спробуйте зменшити текст для мітки / повідомлення. + + + + Save QR Code + Зберегти QR-код + + + + PNG Images (*.png) + Зображення PNG (*.png) + + + + RPCConsole + + + XP - Debug window + XP - Вікно відладки + + + + &Information + &Інформація + + + + Configuration file + Файл конфігурації + + + + + + + + + + + + + + N/A + Н/Д + + + + Current number of blocks + Поточне число блоків + + + + Command-line options + Параметри командного рядка + + + + Open the XP debug log file from the current data directory. This can take a few seconds for large log files. + Відкрийте файл журналу відладки XP з поточного каталогу даних. Це може зайняти кілька секунд для великих файлів журналів. + + + + &Open + Відкрити + + + + Debug log file + Файл журналу відладки + + + + Show the XP-Qt help message to get a list with possible XP command-line options. + Показати довідку XP-Qt для отримання переліку можливих параметрів командного рядка. + + + + &Show + &Показати + + + + Last block time + Час останнього блоку + + + + Network + Мережа + + + + Startup time + Час запуску + + + + On testnet + Тестова мережа + + + + Number of connections + Кількість підключень + + + + Block chain + Ланцюг блоків + + + + Estimated total blocks + Розрахункова сума блоків + + + + Using BerkeleyDB version + Використовується версія BerkeleyDB + + + + Using OpenSSL version + Використовується OpenSSL версії + + + + Build date + Дата збирання + + + + Client version + Версія клієнту + + + + XP Core + Ядро XP + + + + Client name + Назва клієнту + + + + Open the XP configuration file from the current data directory. + Відкрийте файл конфігурації XP з поточного каталогу даних. + + + + O&pen + Від&крити + + + + &Console + &Консоль + + + + Clear console + Очистити консоль + + + + &Network Traffic + &Мережевий трафік + + + + &Clear + &Очистити + + + + Totals + Всього + + + + Received: + Отримано: + + + + Sent: + Відправлено: + + + + Welcome to the XP RPC console. + Вітаємо у консолі XP RPC. + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + Використовуйте стрілки вгору вниз для навігації по історії, і <b>Ctrl-L</b> для очищення екрана. + + + + Type <b>help</b> for an overview of available commands. + Наберіть <b>help</b> для перегляду доступних команд. + + + + Inbound: + Вхідний: + + + + Outbound: + Вихідний: + + + + %1 B + %1 Б + + + + %1 KB + %1 КБ + + + + %1 MB + %1 МБ + + + + %1 GB + %1 ГБ + + + + SecondAuthDialog + + Signatures - Sign / Verify a Message + Підписи - Підпис / Перевірка повідомлення + + + The address to sign the message with (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + Виберіть адресу, який буде використаний для підписання повідомлення (наприклад 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + + Choose an address from the address book + Оберіть адресу з адресної книги + + + + Alt+A + Alt+A + + + + Second Authentication + + + + + You can sign hash of transaction exists in the blockchain with your addresses. + + + + + The address for authentification (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + + + Paste hash from clipboard + + + + + Alt+P + Alt+P + + + + Copy the current signature to the system clipboard + Скопіювати поточну сигнатуру до системного буферу обміну + + + + Alt+C + + + + + Sign the hash + + + + Sign the message to prove you own this XP address + Підпишіть повідомлення щоб довести, що ви є власником цієї адреси + + + + &Sign Data + + + + + Reset all sign message fields + Скинути всі поля підпису повідомлення + + + + Clear &All + Очистити &все + + + + Enter a XP address (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + Введіть адресу XP (наприклад 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + + Click "Sign data" to generate signature + + + + + The entered address is invalid. + Введена нечинна адреса. + + + + + Please check the address and try again. + Будь ласка, перевірте адресу та спробуйте ще. + + + + The entered address does not refer to a key. + Введена адреса не відноситься до ключа. + + + + Wallet unlock was cancelled. + Розблокування гаманця було скасоване. + + + + Private key for the entered address is not available. + Приватний ключ для введеної адреси недоступний. + + + + No information available about transaction. + + + + + Message signing failed. + Не вдалося підписати повідомлення. + + + + Message signed. + Повідомлення підписано. + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Відправити + + + + Coin Control Features + Керування входами + + + + Inputs... + Входи... + + + + automatically selected + вибираються автоматично + + + + Insufficient funds! + Недостатньо коштів! + + + + Quantity: + Кількість: + + + 0 + 0 + + + + Bytes: + Байтів: + + + + Amount: + Сума: + + + 0.00 XP + 0.00 XP + + + + Priority: + Пріорітет: + + + + medium + середній + + + + Fee: + Комісія: + + + + Low Output: + Маленькі виходи: + + + + no + ні + + + + After Fee: + Після комісії: + + + + Change + Решта: + + + + custom change address + Вказати адресу для решти + + + + Choose address from address book + Оберіть адресу з адресної книги + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Вставити адресу з буфера обміну + + + + Alt+P + Alt+P + + + + Send to multiple recipients at once + Відправити на декілька адрес + + + + Add &Recipient + Дод&ати одержувача + + + + Remove all transaction fields + Видалити всі поля транзакції + + + + Clear &All + Очистити &все + + + + Balance: + Баланс: + + + 123.456 XP + 123.456 XP + + + + Confirm the send action + Підтвердити відправлення + + + + S&end + &Відправити + + + + Enter a XP address (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + Введіть адресу XP (наприклад 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + + Copy quantity + Скопіювати кількість + + + + Copy amount + Скопіювати суму + + + + Copy fee + Скопіювати комісію + + + + Copy after fee + Скопіювати після комісії + + + + Copy bytes + Скопіювати байти + + + + Copy priority + Скопіювати пріорітет + + + + Copy low output + Скопіювати маленькі виходи + + + + Copy change + Скопіювати решту + + + + + <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 recipient address is not valid, please recheck. + Адреса отримувача невірна, будь ласка перепровірте. + + + + The amount to pay must be larger than 0. + Сума монет для відправлення повинна бути більшою 0. + + + + The amount exceeds your balance. + Кількість монет для відправлення перевищує ваш баланс. + + + + The total exceeds your balance when the %1 transaction fee is included. + Сума перевищить ваш баланс, якщо комісія %1 буде додана до вашої транзакції. + + + + Duplicate address found, can only send to each address once per 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. + Помилка: Транзакцію відхилено! Це може статись, якщо декілька монет з вашого гаманця вже використані, наприклад, якщо ви використовуєте одну копію гаманця, а монети були використані з іншої копії, але не позначені як використані в цій. + + + + 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. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + Виберіть адресу для відправлення монет (наприклад 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + + Choose address from address book + Оберіть адресу з адресної книги + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Вставити адресу + + + + Alt+P + Alt+P + + + + Remove this recipient + Видалити цього отримувача + + + + Enter a XP address (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + Введіть адресу XP (наприклад 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + Підписи - Підпис / Перевірка повідомлення + + + + + &Sign Message + &Підписати повідомлення + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + Ви можете підписувати повідомлення зі своїми адресами, щоб довести, що ви є їх власником. Остерігайтеся підписувати будь-що незрозуміле, так як за допомогою фішинг-атаки вас можуть спробувати обдурити для отримання вашого підпису під чужими словами. Підписуйте тільки ті повідомлення, з якими ви повністю згодні. + + + + The address to sign the message with (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + Виберіть адресу, який буде використаний для підписання повідомлення (наприклад 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + + + Choose an address from the address book + Оберіть адресу з адресної книги + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Вставити адресу з буфера обміну + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Введіть повідомлення, яке ви хочете підписати тут + + + + Copy the current signature to the system clipboard + Скопіювати поточну сигнатуру до системного буферу обміну + + + + Sign the message to prove you own this XP address + Підпишіть повідомлення щоб довести, що ви є власником цієї адреси + + + + Reset all sign message fields + Скинути всі поля підпису повідомлення + + + + + Clear &All + Очистити &все + + + + + &Verify Message + Перевірити повідомлення + + + + Enter the signing address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack. + Введіть нижче адресу підпису, повідомлення (впевніться, що ви точно скопіювали символи завершення рядку, табуляцію, пробіли тощо) та підпис для перевірки повідомлення. Впевніться, що в підпис не було додано зайвих символів: це допоможе уникнути атак типу «людина посередині». + + + + The address the message was signed with (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + Введіть адресу (наприклад 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + + Verify the message to ensure it was signed with the specified XP address + Перевірте повідомлення для впевненості, що воно підписано вказаною XP-адресою + + + + Reset all verify message fields + Скинути всі поля перевірки повідомлення + + + + + Enter a XP address (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + Введіть адресу XP (наприклад 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + + + + Click "Sign Message" to generate signature + Натисніть кнопку «Підписати повідомлення», для отримання підпису + + + + Enter XP signature + Введіть підпис XP + + + + + The entered address is invalid. + Введена нечинна адреса. + + + + + + + Please check the address and try again. + Будь ласка, перевірте адресу та спробуйте ще. + + + + + The entered address does not refer to a key. + Введена адреса не відноситься до ключа. + + + + Wallet unlock was cancelled. + Розблокування гаманця було скасоване. + + + + Private key for the entered address is not available. + Приватний ключ для введеної адреси недоступний. + + + + Message signing failed. + Не вдалося підписати повідомлення. + + + + Message signed. + Повідомлення підписано. + + + + The signature could not be decoded. + Підпис не можливо декодувати. + + + + + Please check the signature and try again. + Будь ласка, перевірте підпис та спробуйте ще. + + + + The signature did not match the message digest. + Підпис не збігається з хешем повідомлення. + + + + Message verification failed. + Не вдалося перевірити повідомлення. + + + + Message verified. + Повідомлення перевірено. + + + + TrafficGraphWidget + + + KB/s + КБ/с + + + + TransactionDesc + + + Open for %n block(s) + + Відкрито на %n блок + Відкрито на %n блоки + Відкрито на %n блоків + + + + + Open until %1 + Відкрити до %1 + + + + %1/offline + %1/поза інтернетом + + + + %1/unconfirmed + %1/не підтверджено + + + + %1 confirmations + %1 підтверджень + + + + Status + Статус + + + + , has not been successfully broadcast yet + , ще не було успішно розіслано + + + + , broadcast through %n node(s) + + , розіслано через %n вузол + , розіслано через %n вузли + , розіслано через %n вузлів + + + + + Date + Дата + + + + Source + Джерело + + + + Generated + Згенеровано + + + + + + + From + Відправник + + + + + unknown + невідомий + + + + + label + Мітка + + + + + + To + Отримувач + + + + + own address + Власна адреса + + + + + + + + Credit + Кредит + + + + matures in %n more block(s) + + "дозріє" через %n блок + "дозріє" через %n блоки + "дозріє" через %n блоків + + + + + not accepted + не прийнято + + + + + + + Debit + Дебет + + + + Transaction fee + Комісія за транзакцію + + + + Net amount + Загальна сума + + + + Message + Повідомлення + + + + Comment + Коментар + + + + Transaction ID + ID транзакції + + + + Generated coins must mature 520 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, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + Після генерації монет, потрібно зачекати 520 блоків, перш ніж їх можна буде використати. Коли ви згенерували цей блок, його було відправлено в мережу для того, щоб він був доданий до ланцюжка блоків. Якщо ця процедура не вдасться, статус буде змінено на «не підтверджено» і ви не зможете витратити згенеровані монети. Таке може статись, якщо хтось інший згенерував блок на декілька секунд раніше. + + + + Debug information + Налагоджувальна інформація + + + + Transaction + Транзакція + + + + Inputs + Входи + + + + Amount + Сума + + + + true + true + + + + false + false + + + + TransactionDescDialog + + + Transaction details + Деталі транзакцій + + + + This pane shows a detailed description of the transaction + Даний діалог показує детальну статистику по обранїй транзакції + + + + TransactionTableModel + + + Date + Дата + + + + Type + Тип + + + + Address + Адреса + + + + Open for %n block(s) + + Відкрити для %n блоку + Відкрити для %n блоків + Відкрити для %n блоків + + + + + Open until %1 + Відкрити до %1 + + + + Offline (%1 confirmations) + Поза інтернетом (%1 підтверджень) + + + + Unconfirmed (%1 of %2 confirmations) + Непідтверджено (%1 із %2 підтверджень) + + + + Confirmed (%1 confirmations) + Підтверджено (%1 підтверджень) + + + + Mined balance will be available when it matures in %n more block(s) + + Здобуті монети можуть бути використані через %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 + Отримано від + + + + Sent to + Відправлено + + + + 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 + Інше + + + + Enter address or label to search + Введіть адресу чи мітку для пошуку + + + + Min amount + Мінімальна сума + + + + Copy address + Скопіювати адресу + + + + Copy label + Скопіювати мітку + + + + Copy amount + Скопіювати суму + + + + Copy transaction ID + Скопіювати ID транзакції + + + + Edit label + Редагувати мітку + + + + Show transaction details + Показати деталі транзакції + + + + Clear orphans + Видалити орфани + + + + Export Transaction Data + Експортувати дані транзакцій + + + + Comma separated file (*.csv) + Файли, розділені комою (*.csv) + + + + Confirmed + Підтверджені + + + + Date + Дата + + + + Type + Тип + + + + Label + Мітка + + + + Address + Адреса + + + + Amount + Сума + + + + ID + Ідентифікатор + + + + Error exporting + Помилка експорту + + + + Could not write to file %1. + Неможливо записати у файл %1 + + + + Range: + Діапазон від: + + + + to + до + + + + WalletModel + + + Sending... + Відправка... + + + + bitcoin-core + + + %s, you must set a rpcpassword in the configuration file: + %s +It is recommended you use the following random password: +rpcuser=XPrpc +rpcpassword=%s +(you do not need to remember this password) +If the file does not exist, create it with owner-readable-only file permissions. + + %s, ви повинні встановити rpcpassword в файлі конфігурації: +%s +Рекомендується використати такий випадковий пароль: +rpcuser=XPrpc +rpcpassword=%s +(ви не повинні пам'ятати цей пароль) +Ім’я користувача та пароль ПОВИННІ бути різними. +Якщо файлу не існує, створіть його, обмеживши доступ правом читання для власника. + + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Допустимі шифри (за промовчуванням: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + Сталася помилка при спробі відкрити порт RPC %u для прослуховування на IPv4: %s + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + Сталася помилка при спробі відкрити порт RPC %u для прослуховування на IPv6, повертаємося до IPv4: %s + + + + Cannot obtain a lock on data directory %s. XP is probably already running. + Не вдалося встановити блокування на каталог даних %s. XP, ймовірно, вже запущений. + + + + Detach block and address databases. Increases shutdown time (default: 0) + Відключити бази даних блоків і адрес. Збільшує час завершення роботи (за замовчуванням: 0) + + + + Error initializing database environment %s! To recover, BACKUP THAT DIRECTORY, then remove everything from it except for wallet.dat. + Помилка ініціалізації оточення БД %s! Для відновлення ЗРОБІТЬ РЕЗЕРВНУ КОПІЮ цій директорії, потім видалить з неї все, окрім wallet.dat. + + + + 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: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds + Помилка: ця транзакція потребує комісії у розмірі що найменше %s через її об'єм, складність або використання нещодавно отриманих коштів + + + + Error: Wallet unlocked for block minting only, unable to create transaction. + Помилка: гаманець розблокований тільки для PoS, неможливо створити транзакцію. + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + Виконати команду, коли з'явиться новий блок (%s в команді змінюється на хеш блоку) + + + + Listen for JSON-RPC connections on <port> (default: 28191 or testnet: 18345) + Прослуховувати <port> для JSON-RPC з'єднань (типово: 28191, для тестової мережі: 18345) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Максимальній розмір вхідного буферу на одне з'єднання (за замовчуванням 86400) + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + Встановити максимальний розмір транзакцій з високим пріоритетом та низькою комісією, в байтах (типово: 27000) + + + + Unable to bind to %s on this computer. XP is probably already running. + Неможливо прив'язатися до %s на цьому комп'ютері. Можливо, XP вже запущено. + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + Увага: встановлено занадто велику комісію (-paytxfee). Комісія зніматиметься кожен раз коли ви проводитимете транзакції. + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong XP will not work properly. + Увага: будь ласка, перевірте дату і час на своєму комп&apos;ютері. Якщо ваш годинник йде неправильно, XP може працювати некоректно. + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + Увага: помилка читання wallet.dat! Всі ключі прочитано коректно, але дані транзакцій чи записи адресної книги можуть бути пропущені, або пошкоджені. + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + Увага: файл wallet.dat пошкоджено, дані врятовано! Оригінальний wallet.dat збережено як wallet.{timestamp}.bak до %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. + Ви повинні встановити rpcpassword=<password> в файлі конфігурації: +%s +Якщо файлу не існує, створіть його, обмеживши доступ правом читання для власника. + + + + Accept command line and JSON-RPC commands + Приймати команди із командного рядка та команди JSON-RPC + + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + Приймати підключення ззовні (типово: 1 за відсутності -proxy чи -connect) + + + + Add a node to connect to and attempt to keep the connection open + Додати вузол до підключення і лишити його відкритим + + + + Allow DNS lookups for -addnode, -seednode and -connect + Дозволити пошук в DNS для команд -addnode, -seednode та -connect + + + + Allow JSON-RPC connections from specified IP address + Дозволити JSON-RPC-з’єднання з вказаної IP-адреси + + + + + Attempt to recover private keys from a corrupt wallet.dat + Спроба відновити закриті ключі з пошкодженого wallet.dat + + + + Bind to given address. Use [host]:port notation for IPv6 + Прив'язатися до даної адреси та прослуховувати її. Використовуйте запис виду [хост]:порт для IPv6 + + + + Block creation options: + Опції створення блоку: + + + + Cannot downgrade wallet + Не вдається понизити версію гаманця + + + + Cannot initialize keypool + Не вдається ініціалізувати масив ключів + + + + Cannot resolve -bind address: '%s' + Не вдалося розпізнати адресу для -bind: "%s" + + + + Cannot resolve -externalip address: '%s' + Не вдалося розпізнати адресу для -externalip: "%s" + + + + Cannot write default address + Неможливо записати типову адресу + + + + Connect only to the specified node(s) + Підключитись лише до вказаного вузла (ів) + + + + Connect through socks proxy + Підключення через SOCKS проксі + + + + Connect to a node to retrieve peer addresses, and disconnect + Підключитись до вузла, щоб отримати список адрес інших учасників та від'єднатись + + + + Discover own IP address (default: 1 when listening and no -externalip) + Визначити власну IP-адресу (типово: 1 при прослуховуванні та за відсутності -externalip) + + + + Done loading + Завантаження завершене + + + + Error loading blkindex.dat + Помилка при завантаженні blkindex.dat + + + + Error loading wallet.dat + Помилка при завантаженні wallet.dat + + + + Error loading wallet.dat: Wallet corrupted + Помилка при завантаженні wallet.dat: Гаманець пошкоджено + + + + Error loading wallet.dat: Wallet requires newer version of XP + Помилка при завантаженні wallet.dat: Гаманець потребує новішої версії XP + + + + Error + Помилка + + + + Error: Transaction creation failed + Не вдалося створити транзакцію! + + + + Error: Wallet locked, unable to create transaction + Помилка: Гаманець заблокований, неможливо створити транзакцію! + + + + Error: could not start node + Помилка: не вдалося запустити ноду + + + + Failed to listen on any port. Use -listen=0 if you want this. + Не вдалося слухати на жодному порту. Використовуйте -listen=0, якщо ви хочете цього. + + + + Fee per KB to add to transactions you send + Додати комісію за КБ до транзакцій, які ви відправляєте + + + + Find peers using DNS lookup (default: 0) + Шукати вузли за допомогою DNS (за замовчуванням: 0) + + + + Find peers using internet relay chat (default: 1) + Шукати вузли за допомогою IRC (за замовчуванням: 1) + + + + Get help for a command + Отримати довідку по команді + + + + + How many blocks to check at startup (default: 2500, 0 = all) + Скільки блоків перевіряти під час запуску (типово: 2500, 0 = всі) + + + + How thorough the block verification is (0-6, default: 1) + Рівень ретельності перевірки блоків (0-6, типово: 1) + + + + Importing blockchain data file. + Імпорт з файлу даних ланцюжки блоків. + + + + Importing bootstrap blockchain data file. + Початкове завантаження ланцюжки блоків. + + + + Imports blocks from external blk000?.dat file + Імпорт блоків з зовнішнього файлу blk000?.dat + + + + Insufficient funds + Недостатньо коштів + + + + Invalid -proxy address: '%s' + Помилка в адресі проксі-сервера: "%s" + + + + Invalid -tor address: '%s' + Помилка в адресі -tor: «%s» + + + + Invalid amount for -paytxfee=<amount>: '%s' + Помилка у величині комісії -paytxfee=<amount>: "%s" + + + + Invalid amount for -reservebalance=<amount> + Неприпустима сума для -reservebalance=<amount> + + + + Invalid amount + Некоректна сума + + + + List commands + Список команд + + + + + Listen for connections on <port> (default: 28192 or testnet: 17778) + Чекати на з'єднання на <port> (типово: 28192, для тестової мережі: 17778) + + + + Loading addresses... + Завантаження адрес... + + + + Loading block index... + Завантаження індексу блоків... + + + + Loading wallet... + Завантаження гаманця... + + + + Maintain at most <n> connections to peers (default: 125) + Підтримувати не більше <n> зв'язків з колегами (за замовчуванням: 125) + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + Максимальний розмір вхідного буферу на одне з'єднання, <n>*1000 байтів (типово: 5000) + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + Максимальний розмір вихідного буферу на одне з'єднання, <n>*1000 байтів (типово: 1000) + + + + XP version + XP версії + + + + XP + XP + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + Підключити тільки до вузлів в мережі <net> (IPv4, IPv6 або Tor) + + + + Options: + Параметри: + + + + + Output extra debugging information. Implies all other -debug* options + Виводити додаткову налагоджувальну інформацію. Включає всі -debug* опції. + + + + Output extra network debugging information + Виводити додаткову налагоджувальну інформацію + + + + Password for JSON-RPC connections + Пароль для JSON-RPC-з’єднань + + + + + Prepend debug output with timestamp + Доповнювати налагоджувальний вивід відміткою часу + + + + Rescan the block chain for missing wallet transactions + Пересканувати ланцюжок блоків, в пошуку втрачених переказів + + + + + Rescanning... + Сканування... + + + + Run in the background as a daemon and accept commands + Запустити в фоновому режимі (як демон) та приймати команди + + + + + SSL options: (see the Bitcoin Wiki for SSL setup instructions) + Параметри SSL: (див. інструкцію з налаштування SSL у Bitcoin Wiki) + + + + Select the version of socks proxy to use (4-5, default: 5) + Оберіть версію Socks Proxy для використання (4-5, типово: 5) + + + + Send command to -server or XPd + Відправити команду серверу -server чи демону + + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Відправляти команди на вузол, запущений на <ip> (за промовчуванням: 127.0.0.1) + + + + + Send trace/debug info to console instead of debug.log file + Відсилаті налагоджувальну інформацію на консоль, а не у файл debug.log + + + + Send trace/debug info to debugger + Відсилаті налагоджувальну інформацію до налагоджувача + + + + Sending... + Надсилання... + + + + Server certificate file (default: server.cert) + Сертифікату сервера (за промовчуванням: server.cert) + + + + + Server private key (default: server.pem) + Закритий ключ сервера (за промовчуванням: server.pem) + + + + + Set database cache size in megabytes (default: 25) + Встановіть розмір кеша бази даних в мегабайтах (типово: 25) + + + + Set database disk log size in megabytes (default: 100) + Встановіть розмір бази даних журналу на диску в мегабайтах (типово: 100) + + + + Set key pool size to <n> (default: 100) + Встановити розмір пулу ключів <n> (за промовчуванням: 100) + + + + + Set maximum block size in bytes (default: 250000) + Встановити максимальний розмір блоку у байтах (типово: 250000) + + + + Set minimum block size in bytes (default: 0) + Встановити мінімальний розмір блоку в байтах (типово: 0) + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + Стискати файл debug.log під час старту клієнта (типово: 1 коли відсутній параметр -debug) + + + + Specify configuration file (default: XP.conf) + Вкажіть файл конфігурації (за промовчуванням: XP.conf) + + + + + Specify connection timeout in milliseconds (default: 5000) + Вказати тайм-аут підключення в мілісекундах (типово: 5000) + + + + Specify data directory + Вкажіть робочий каталог + + + + + Specify pid file (default: XPd.pid) + Вкажіть pid-файл (за промовчуванням: XPd.pid) + + + + + Specify your own public address + Вкажіть вашу власну публічну адресу + + + + This help message + Дана довідка + + + + + Threshold for disconnecting misbehaving peers (default: 100) + Поріг відключення неправильно підєднаних пірів (за замовчуванням: 100) + + + + To use the %s option + Щоб використати опцію %s + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + Неможливо прив'язатися до %s на цьому комп'ютері (bind повернув помилку: %d, %s) + + + + Unable to sign checkpoint, wrong checkpointkey? + + Неможливо підписати checkpoint, невірний checkpointkey? + + + + + Unknown -socks proxy version requested: %i + У параметрі -socks запрошена невідома версія: %i + + + + Unknown network specified in -onlynet: '%s' + Невідома мережа вказана в -onlynet: "%s" + + + + Upgrade wallet to latest format + Модернізувати гаманець до найновішого формату + + + + Usage: + Вкористання: + + + + Use OpenSSL (https) for JSON-RPC connections + Використовувати OpenSSL (https) для JSON-RPC-з’єднань + + + + + Use proxy to reach tor hidden services (default: same as -proxy) + Використовувати окремий проксі для з'єднання з учасниками через приховані сервіси Tor (типово: так само, як -proxy) + + + + Use the test network + Використовувати тестову мережу + + + + + Username for JSON-RPC connections + Ім’я користувача для JSON-RPC-з’єднань + + + + + Verifying database integrity... + Перевірка цілісності бази даних... + + + + Wallet needed to be rewritten: restart XP to complete + Потрібно перезаписати гаманець: перезапустіть XP для завершення + + + + Warning: Disk space is low! + Увага: Мало вільного місця на диску! + + + + Warning: This version is obsolete, upgrade required! + Увага: Поточна версія застаріла, необхідне оновлення! + + + + wallet.dat corrupt, salvage failed + wallet.dat пошкоджено, відновлення не вдалося + + + + Specify wallet file (within data directory) + Вкажіть файл гаманця (в межах каталогу даних) + + + + Use in-memory logging for block index database (default: 1) + Використовувати ведення журналу у пам'яті для індексу бази даних блоків (за замовчуванням: 1) + + + + Find peers using DNS lookup (default: 1) + Шукати вузли за допомогою DNS (за замовчуванням: 0) {1)?} + + + + Sync checkpoints policy (default: strict) + Політика синхронізованих міток (за замовчуванням: strict) + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + Виконати команду, коли транзакція гаманця зміниться (замість %s в команді буде підставлено ідентифікатор транзакції) + + + + Require a confirmations for change (default: 0) + Вимагати підтвердження для решти (типово: 0) + + + + Enforce transaction scripts to use canonical PUSH operators (default: 1) + Вимагати від скриптів використання стандартних PUSH операторів (за замовчуванням: 1) + + + + Set the number of script verification threads (1-16, 0=auto, default: 0) + Встановити кількість потоків скрипту перевірки (1-16, 0=автоматично, типово: 0) + + + + When creating transactions, ignore inputs with value less than this (default: %s) + При створенні транзакцій, ігнорувати входи з сумою меншу за цю (за замовчуванням: %s) + + + diff --git a/src/qt/locale/translations.pro b/src/qt/locale/translations.pro new file mode 100644 index 00000000..ada7a2f8 --- /dev/null +++ b/src/qt/locale/translations.pro @@ -0,0 +1,17 @@ +CODECFORTR = UTF-8 + +# for lrelease/lupdate +# also add new translations to src/qt/bitcoin.qrc under translations/ +TRANSLATIONS = $$files(bitcoin_*.ts) + +isEmpty(QM_DIR):QM_DIR = $$PWD/src/qt/locale +# automatically build translations, so they can be included in resource file +TSQM.name = lrelease ${QMAKE_FILE_IN} +TSQM.input = TRANSLATIONS +TSQM.output = $$QM_DIR/${QMAKE_FILE_BASE}.qm +TSQM.commands = $$QMAKE_LRELEASE ${QMAKE_FILE_IN} -qm ${QMAKE_FILE_OUT} +TSQM.CONFIG = no_link +QMAKE_EXTRA_COMPILERS += TSQM + +windows:DEFINES += WIN32 +windows:RC_FILE = $$PWD/src/qt/res/bitcoin-qt.rc diff --git a/src/qt/macdockiconhandler.h b/src/qt/macdockiconhandler.h new file mode 100644 index 00000000..8cf6576e --- /dev/null +++ b/src/qt/macdockiconhandler.h @@ -0,0 +1,54 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_QT_MACDOCKICONHANDLER_H +#define BITCOIN_QT_MACDOCKICONHANDLER_H + +#include +#include + +QT_BEGIN_NAMESPACE +class QIcon; +class QMenu; +class QIcon; +class QWidget; +QT_END_NAMESPACE + +#ifdef __OBJC__ +@class DockIconClickEventHandler; +#else +class DockIconClickEventHandler; +#endif + +/** Macintosh-specific dock icon handler. + */ +class MacDockIconHandler : public QObject +{ + Q_OBJECT + +public: + ~MacDockIconHandler(); + + QMenu *dockMenu(); + void setIcon(const QIcon &icon); + void setMainWindow(QMainWindow *window); + static MacDockIconHandler *instance(); + + void handleDockIconClickEvent(); + +signals: + void dockIconClicked(); + +public slots: + +private: + MacDockIconHandler(); + + DockIconClickEventHandler *m_dockIconClickEventHandler; + QWidget *m_dummyWidget; + QMenu *m_dockMenu; + QMainWindow *mainWindow; +}; + +#endif // BITCOIN_QT_MACDOCKICONHANDLER_H diff --git a/src/qt/macdockiconhandler.mm b/src/qt/macdockiconhandler.mm new file mode 100644 index 00000000..32701137 --- /dev/null +++ b/src/qt/macdockiconhandler.mm @@ -0,0 +1,139 @@ +// Copyright (c) 2011-2013 The Bitcoin Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "macdockiconhandler.h" + +#include +#include +#include +#include + +extern void qt_mac_set_dock_menu(QMenu*); + +#undef slots +#include + +#if QT_VERSION < 0x050000 +extern void qt_mac_set_dock_menu(QMenu *); +#endif + +@interface DockIconClickEventHandler : NSObject +{ + MacDockIconHandler* dockIconHandler; +} + +@end + +@implementation DockIconClickEventHandler + +- (id)initWithDockIconHandler:(MacDockIconHandler *)aDockIconHandler +{ + self = [super init]; + if (self) { + dockIconHandler = aDockIconHandler; + + [[NSAppleEventManager sharedAppleEventManager] + setEventHandler:self + andSelector:@selector(handleDockClickEvent:withReplyEvent:) + forEventClass:kCoreEventClass + andEventID:kAEReopenApplication]; + } + return self; +} + +- (void)handleDockClickEvent:(NSAppleEventDescriptor*)event withReplyEvent:(NSAppleEventDescriptor*)replyEvent +{ + Q_UNUSED(event) + Q_UNUSED(replyEvent) + + if (dockIconHandler) { + dockIconHandler->handleDockIconClickEvent(); + } +} + +@end + +MacDockIconHandler::MacDockIconHandler() : QObject() +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + this->m_dockIconClickEventHandler = [[DockIconClickEventHandler alloc] initWithDockIconHandler:this]; + this->m_dummyWidget = new QWidget(); + this->m_dockMenu = new QMenu(this->m_dummyWidget); + this->setMainWindow(NULL); +#if QT_VERSION < 0x050000 + qt_mac_set_dock_menu(this->m_dockMenu); +#elif QT_VERSION >= 0x050200 + this->m_dockMenu->setAsDockMenu(); +#endif + [pool release]; +} + +void MacDockIconHandler::setMainWindow(QMainWindow *window) { + this->mainWindow = window; +} + +MacDockIconHandler::~MacDockIconHandler() +{ + [this->m_dockIconClickEventHandler release]; + delete this->m_dummyWidget; + this->setMainWindow(NULL); +} + +QMenu *MacDockIconHandler::dockMenu() +{ + return this->m_dockMenu; +} + +void MacDockIconHandler::setIcon(const QIcon &icon) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSImage *image = nil; + if (icon.isNull()) + image = [[NSImage imageNamed:@"NSApplicationIcon"] retain]; + else { + // generate NSImage from QIcon and use this as dock icon. + QSize size = icon.actualSize(QSize(128, 128)); + QPixmap pixmap = icon.pixmap(size); + + // Write image into a R/W buffer from raw pixmap, then save the image. + QBuffer notificationBuffer; + if (!pixmap.isNull() && notificationBuffer.open(QIODevice::ReadWrite)) { + QImageWriter writer(¬ificationBuffer, "PNG"); + if (writer.write(pixmap.toImage())) { + NSData* macImgData = [NSData dataWithBytes:notificationBuffer.buffer().data() + length:notificationBuffer.buffer().size()]; + image = [[NSImage alloc] initWithData:macImgData]; + } + } + + if(!image) { + // if testnet image could not be created, load std. app icon + image = [[NSImage imageNamed:@"NSApplicationIcon"] retain]; + } + } + + [NSApp setApplicationIconImage:image]; + [image release]; + [pool release]; +} + +MacDockIconHandler *MacDockIconHandler::instance() +{ + static MacDockIconHandler *s_instance = NULL; + if (!s_instance) + s_instance = new MacDockIconHandler(); + return s_instance; +} + +void MacDockIconHandler::handleDockIconClickEvent() +{ + if (this->mainWindow) + { + this->mainWindow->activateWindow(); + this->mainWindow->show(); + } + + emit this->dockIconClicked(); +} diff --git a/src/qt/macnotificationhandler.h b/src/qt/macnotificationhandler.h new file mode 100644 index 00000000..f7a4cb7f --- /dev/null +++ b/src/qt/macnotificationhandler.h @@ -0,0 +1,30 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_QT_MACNOTIFICATIONHANDLER_H +#define BITCOIN_QT_MACNOTIFICATIONHANDLER_H + +#include + +/** Macintosh-specific notification handler (supports UserNotificationCenter and Growl). + */ +class MacNotificationHandler : public QObject +{ + Q_OBJECT + +public: + /** shows a 10.8+ UserNotification in the UserNotificationCenter + */ + void showNotification(const QString &title, const QString &text); + + /** executes AppleScript */ + void sendAppleScript(const QString &script); + + /** check if OS can handle UserNotifications */ + bool hasUserNotificationCenterSupport(void); + static MacNotificationHandler *instance(); +}; + + +#endif // BITCOIN_QT_MACNOTIFICATIONHANDLER_H diff --git a/src/qt/macnotificationhandler.mm b/src/qt/macnotificationhandler.mm new file mode 100644 index 00000000..aa50a0d9 --- /dev/null +++ b/src/qt/macnotificationhandler.mm @@ -0,0 +1,91 @@ +// Copyright (c) 2011-2013 The Bitcoin Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "macnotificationhandler.h" + +#undef slots +#import +#include + +// Add an obj-c category (extension) to return the expected bundle identifier +@implementation NSBundle(returnCorrectIdentifier) +- (NSString *)__bundleIdentifier +{ + if (self == [NSBundle mainBundle]) { + return @"org.bitcoinfoundation.Bitcoin-Qt"; + } else { + return [self __bundleIdentifier]; + } +} +@end + +void MacNotificationHandler::showNotification(const QString &title, const QString &text) +{ + // check if users OS has support for NSUserNotification + if(this->hasUserNotificationCenterSupport()) { + // okay, seems like 10.8+ + QByteArray utf8 = title.toUtf8(); + char* cString = (char *)utf8.constData(); + NSString *titleMac = [[NSString alloc] initWithUTF8String:cString]; + + utf8 = text.toUtf8(); + cString = (char *)utf8.constData(); + NSString *textMac = [[NSString alloc] initWithUTF8String:cString]; + + // do everything weak linked (because we will keep <10.8 compatibility) + id userNotification = [[NSClassFromString(@"NSUserNotification") alloc] init]; + [userNotification performSelector:@selector(setTitle:) withObject:titleMac]; + [userNotification performSelector:@selector(setInformativeText:) withObject:textMac]; + + id notificationCenterInstance = [NSClassFromString(@"NSUserNotificationCenter") performSelector:@selector(defaultUserNotificationCenter)]; + [notificationCenterInstance performSelector:@selector(deliverNotification:) withObject:userNotification]; + + [titleMac release]; + [textMac release]; + [userNotification release]; + } +} + +// sendAppleScript just take a QString and executes it as apple script +void MacNotificationHandler::sendAppleScript(const QString &script) +{ + QByteArray utf8 = script.toUtf8(); + char* cString = (char *)utf8.constData(); + NSString *scriptApple = [[NSString alloc] initWithUTF8String:cString]; + + NSAppleScript *as = [[NSAppleScript alloc] initWithSource:scriptApple]; + NSDictionary *err = nil; + [as executeAndReturnError:&err]; + [as release]; + [scriptApple release]; +} + +bool MacNotificationHandler::hasUserNotificationCenterSupport(void) +{ + Class possibleClass = NSClassFromString(@"NSUserNotificationCenter"); + + // check if users OS has support for NSUserNotification + if(possibleClass!=nil) { + return true; + } + return false; +} + + +MacNotificationHandler *MacNotificationHandler::instance() +{ + static MacNotificationHandler *s_instance = NULL; + if (!s_instance) { + s_instance = new MacNotificationHandler(); + + Class aPossibleClass = objc_getClass("NSBundle"); + if (aPossibleClass) { + // change NSBundle -bundleIdentifier method to return a correct bundle identifier + // a bundle identifier is required to use OSXs User Notification Center + method_exchangeImplementations(class_getInstanceMethod(aPossibleClass, @selector(bundleIdentifier)), + class_getInstanceMethod(aPossibleClass, @selector(__bundleIdentifier))); + } + } + return s_instance; +} diff --git a/src/qt/mintingfilterproxy.cpp b/src/qt/mintingfilterproxy.cpp new file mode 100644 index 00000000..664cbe70 --- /dev/null +++ b/src/qt/mintingfilterproxy.cpp @@ -0,0 +1,7 @@ +#include "mintingfilterproxy.h" + +MintingFilterProxy::MintingFilterProxy(QObject * parent) : + QSortFilterProxyModel(parent) +{ + +} diff --git a/src/qt/mintingfilterproxy.h b/src/qt/mintingfilterproxy.h new file mode 100644 index 00000000..26c4abf5 --- /dev/null +++ b/src/qt/mintingfilterproxy.h @@ -0,0 +1,13 @@ +#ifndef MINTINGFILTERPROXY_H +#define MINTINGFILTERPROXY_H + +#include + +class MintingFilterProxy : public QSortFilterProxyModel +{ + Q_OBJECT +public: + explicit MintingFilterProxy(QObject *parent = 0); +}; + +#endif // MINTINGFILTERPROXY_H diff --git a/src/qt/mintingtablemodel.cpp b/src/qt/mintingtablemodel.cpp new file mode 100644 index 00000000..23df6cca --- /dev/null +++ b/src/qt/mintingtablemodel.cpp @@ -0,0 +1,503 @@ +#include "mintingtablemodel.h" +#include "mintingfilterproxy.h" +#include "transactiontablemodel.h" +#include "guiutil.h" +#include "kernelrecord.h" +#include "guiconstants.h" +#include "transactiondesc.h" +#include "walletmodel.h" +#include "optionsmodel.h" +#include "addresstablemodel.h" +#include "bitcoinunits.h" +#include "util.h" +#include "kernel.h" + +#include "wallet.h" + +#include +#include +#include +#include +#include +#include +#include + +extern double GetDifficulty(const CBlockIndex* blockindex); + +static int column_alignments[] = { + Qt::AlignLeft|Qt::AlignVCenter, + Qt::AlignLeft|Qt::AlignVCenter, + Qt::AlignLeft|Qt::AlignVCenter, + Qt::AlignLeft|Qt::AlignVCenter, + Qt::AlignLeft|Qt::AlignVCenter, + Qt::AlignLeft|Qt::AlignVCenter, + Qt::AlignLeft|Qt::AlignVCenter +}; + +struct TxLessThan +{ + bool operator()(const KernelRecord &a, const KernelRecord &b) const + { + return a.hash < b.hash; + } + bool operator()(const KernelRecord &a, const uint256 &b) const + { + return a.hash < b; + } + bool operator()(const uint256 &a, const KernelRecord &b) const + { + return a < b.hash; + } +}; + +class MintingTablePriv +{ +public: + MintingTablePriv(CWallet *wallet, MintingTableModel *parent): + wallet(wallet), + parent(parent) + { + } + CWallet *wallet; + MintingTableModel *parent; + + QList cachedWallet; + + void refreshWallet() + { +#ifdef WALLET_UPDATE_DEBUG + qDebug() << "refreshWallet"; +#endif + cachedWallet.clear(); + { + LOCK(wallet->cs_wallet); + for(std::map::iterator it = wallet->mapWallet.begin(); it != wallet->mapWallet.end(); ++it) + { + std::vector txList = KernelRecord::decomposeOutput(wallet, it->second); + BOOST_FOREACH(KernelRecord& kr, txList) { + if(!kr.spent) { + cachedWallet.append(kr); + } + } + + } + } + } + + /* 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. +#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). + QList updated_sorted = updated; + qSort(updated_sorted); + + { + LOCK(wallet->cs_wallet); + 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 = 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()); + QList::iterator upper = qUpperBound( + cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan()); + int lowerIndex = (lower - cachedWallet.begin()); + int upperIndex = (upper - cachedWallet.begin()); + + // Determine if transaction is in model already + bool inModel = false; + if(lower != upper) + { + inModel = true; + } + +#ifdef WALLET_UPDATE_DEBUG + qDebug() << " " << QString::fromStdString(hash.ToString()) << inWallet << " " << inModel + << lowerIndex << "-" << upperIndex; +#endif + + if(inWallet && !inModel) + { + // Added -- insert at the right position + std::vector toInsert = + KernelRecord::decomposeOutput(wallet, mi->second); + if(!toInsert.empty()) /* only if something to insert */ + { + int insert_idx = lowerIndex; + BOOST_FOREACH(const KernelRecord &rec, toInsert) + { + if(!rec.spent) + { + parent->beginInsertRows(QModelIndex(), insert_idx, insert_idx); + cachedWallet.insert(insert_idx, rec); + parent->endInsertRows(); + insert_idx += 1; + } + } + } + } + else if(!inWallet && inModel) + { + // Removed -- remove entire transaction from table + parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1); + cachedWallet.erase(lower, upper); + parent->endRemoveRows(); + } + else if(inWallet && inModel) + { + // Updated -- remove spent coins from table + std::vector toCheck = KernelRecord::decomposeOutput(wallet, mi->second); + BOOST_FOREACH(const KernelRecord &rec, toCheck) + { + if(rec.spent) + { + for(int i = 0; i < cachedWallet.size(); i++) + { + KernelRecord cachedRec = cachedWallet.at(i); + if((rec.hash == cachedRec.hash) + && (rec.nTime == cachedRec.nTime) + && (rec.nValue == cachedRec.nValue)) + { + parent->beginRemoveRows(QModelIndex(), i, i); + cachedWallet.removeAt(i); + parent->endRemoveRows(); + break; + } + } + } + } + } + } + } + } + + int size() + { + return cachedWallet.size(); + } + + KernelRecord *index(int idx) + { + if(idx >= 0 && idx < cachedWallet.size()) + { + KernelRecord *rec = &cachedWallet[idx]; + return rec; + } + else + { + return 0; + } + } + + QString describe(KernelRecord *rec) + { + { + LOCK(wallet->cs_wallet); + std::map::iterator mi = wallet->mapWallet.find(rec->hash); + if(mi != wallet->mapWallet.end()) + { + return TransactionDesc::toHTML(wallet, mi->second); + } + } + return QString(""); + } + +}; + + +MintingTableModel::MintingTableModel(CWallet *wallet, WalletModel *parent): + QAbstractTableModel(parent), + wallet(wallet), + walletModel(parent), + mintingInterval(10), + priv(new MintingTablePriv(wallet, this)) +{ + columns << tr("Transaction") << tr("Address") << tr("Balance") << tr("Age") << tr("CoinDay") << tr("MintProbability") << tr("MintReward"); + priv->refreshWallet(); + + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(update())); + timer->start(MODEL_UPDATE_DELAY); +} + +MintingTableModel::~MintingTableModel() +{ + delete priv; +} + +void MintingTableModel::update() +{ + QList updated; + + // Check if there are changes to wallet map + { + TRY_LOCK(wallet->cs_wallet, lockWallet); + if (lockWallet && !wallet->vMintingWalletUpdated.empty()) + { + BOOST_FOREACH(uint256 hash, wallet->vMintingWalletUpdated) + { + updated.append(hash); + + // Also check the inputs to remove spent outputs from the table if necessary + CWalletTx wtx; + if(wallet->GetTransaction(hash, wtx)) + { + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + { + updated.append(txin.prevout.hash); + } + } + } + wallet->vMintingWalletUpdated.clear(); + } + } + + if(!updated.empty()) + { + priv->updateWallet(updated); + mintingProxyModel->invalidate(); // Force deletion of empty rows + } +} + +void MintingTableModel::setMintingProxyModel(MintingFilterProxy *mintingProxy) +{ + mintingProxyModel = mintingProxy; +} + +int MintingTableModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return priv->size(); +} + +int MintingTableModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return columns.length(); +} + +QVariant MintingTableModel::data(const QModelIndex &index, int role) const +{ + if(!index.isValid()) + return QVariant(); + KernelRecord *rec = static_cast(index.internalPointer()); + + switch(role) + { + case Qt::DisplayRole: + switch(index.column()) + { + case Address: + return formatTxAddress(rec, false); + case TxHash: + return formatTxHash(rec); + case Age: + return formatTxAge(rec); + case Balance: + return formatTxBalance(rec); + case CoinDay: + return formatTxCoinDay(rec); + case MintProbability: + return formatDayToMint(rec); + case MintReward: + return formatTxPoSReward(rec); + } + break; + case Qt::TextAlignmentRole: + return column_alignments[index.column()]; + break; + case Qt::ToolTipRole: + switch(index.column()) + { + case MintProbability: + int interval = this->mintingInterval; + QString unit = tr("minutes"); + + int hours = interval / 60; + int days = hours / 24; + + if(hours > 1) { + interval = hours; + unit = tr("hours"); + } + if(days > 1) { + interval = days; + unit = tr("days"); + } + + QString str = QString(tr("You have %1 chance to find a POS block if you mint %2 %3 at current difficulty.")); + return str.arg(index.data().toString().toUtf8().constData()).arg(interval).arg(unit); + } + break; + case Qt::EditRole: + switch(index.column()) + { + case Address: + return formatTxAddress(rec, false); + case TxHash: + return formatTxHash(rec); + case Age: + return static_cast(rec->getAge()); + case CoinDay: + return static_cast(rec->getCoinDay()); + case Balance: + return static_cast(rec->nValue); + case MintProbability: + return getDayToMint(rec); + case MintReward: + return formatTxPoSReward(rec); + } + break; + case Qt::BackgroundColorRole: + int minAge = nStakeMinAge / 60 / 60 / 24; + int maxAge = nStakeMaxAge / 60 / 60 / 24; + if(rec->getAge() < minAge) + { + return COLOR_MINT_YOUNG; + } + else if (rec->getAge() >= minAge && rec->getAge() < (maxAge + minAge)) + { + return COLOR_MINT_MATURE; + } + else + { + return COLOR_MINT_OLD; + } + break; + + } + return QVariant(); +} + +void MintingTableModel::setMintingInterval(int interval) +{ + mintingInterval = interval; +} + +QString MintingTableModel::lookupAddress(const std::string &address, bool tooltip) const +{ + QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(address)); + QString description; + if(!label.isEmpty()) + { + description += label + QString(" "); + } + if(label.isEmpty() || walletModel->getOptionsModel()->getDisplayAddresses() || tooltip) + { + description += QString("(") + QString::fromStdString(address) + QString(")"); + } + return description; +} + +QString MintingTableModel::formatTxPoSReward(KernelRecord *wtx) const +{ + QString posReward; + int nBits = GetLastBlockIndex(pindexBest, true)->nBits; + posReward += QString(QObject::tr("from %1 to %2")).arg(BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), wtx->getPoSReward(nBits, 0)), + BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), wtx->getPoSReward(nBits, mintingInterval))); + return posReward; +} + +double MintingTableModel::getDayToMint(KernelRecord *wtx) const +{ + const CBlockIndex *p = GetLastBlockIndex(pindexBest, true); + double difficulty = GetDifficulty(p); + + double prob = wtx->getProbToMintWithinNMinutes(difficulty, mintingInterval); + prob = prob * 100; + return prob; +} + +QString MintingTableModel::formatDayToMint(KernelRecord *wtx) const +{ + double prob = getDayToMint(wtx); + return QString::number(prob, 'f', 3) + "%"; +} + +QString MintingTableModel::formatTxAddress(const KernelRecord *wtx, bool tooltip) const +{ + return QString::fromStdString(wtx->address); +} + +QString MintingTableModel::formatTxHash(const KernelRecord *wtx) const +{ + return QString::fromStdString(wtx->hash.ToString()); +} + +QString MintingTableModel::formatTxCoinDay(const KernelRecord *wtx) const +{ + return QString::number(wtx->getCoinDay()); +} + +QString MintingTableModel::formatTxAge(const KernelRecord *wtx) const +{ + int64_t nAge = wtx->getAge(); + return QString::number(nAge); +} + +QString MintingTableModel::formatTxBalance(const KernelRecord *wtx) const +{ + return BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), wtx->nValue); +} + +QVariant MintingTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if(orientation == Qt::Horizontal) + { + if(role == Qt::DisplayRole) + { + return columns[section]; + } + else if (role == Qt::TextAlignmentRole) + { + return column_alignments[section]; + } else if (role == Qt::ToolTipRole) + { + switch(section) + { + case Address: + return tr("Destination address of the output."); + case TxHash: + return tr("Original transaction id."); + case Age: + return tr("Age of the transaction in days."); + case Balance: + return tr("Balance of the output."); + case CoinDay: + return tr("Coin age in the output."); + case MintProbability: + return tr("Chance to mint a block within given time interval."); + case MintReward: + return tr("The size of the potential rewards if the block is found at the beginning and the end given time interval."); + } + } + } + return QVariant(); +} + +QModelIndex MintingTableModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_UNUSED(parent); + KernelRecord *data = priv->index(row); + if(data) + { + return createIndex(row, column, priv->index(row)); + } + else + { + return QModelIndex(); + } +} diff --git a/src/qt/mintingtablemodel.h b/src/qt/mintingtablemodel.h new file mode 100644 index 00000000..86b64650 --- /dev/null +++ b/src/qt/mintingtablemodel.h @@ -0,0 +1,64 @@ +#ifndef MINTINGTABLEMODEL_H +#define MINTINGTABLEMODEL_H + + +#include +#include + +class CWallet; +class MintingTablePriv; +class MintingFilterProxy; +class KernelRecord; +class WalletModel; + +class MintingTableModel : public QAbstractTableModel +{ + Q_OBJECT +public: + explicit MintingTableModel(CWallet * wallet, WalletModel *parent = 0); + ~MintingTableModel(); + + enum ColumnIndex { + TxHash = 0, + Address = 1, + Balance = 2, + Age = 3, + CoinDay = 4, + MintProbability = 5, + MintReward = 6 + }; + + void setMintingProxyModel(MintingFilterProxy *mintingProxy); + 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; + QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const; + + void setMintingInterval(int interval); + +private: + CWallet* wallet; + WalletModel *walletModel; + QStringList columns; + int mintingInterval; + MintingTablePriv *priv; + MintingFilterProxy *mintingProxyModel; + + QString lookupAddress(const std::string &address, bool tooltip) const; + + double getDayToMint(KernelRecord *wtx) const; + QString formatDayToMint(KernelRecord *wtx) const; + QString formatTxAddress(const KernelRecord *wtx, bool tooltip) const; + QString formatTxHash(const KernelRecord *wtx) const; + QString formatTxAge(const KernelRecord *wtx) const; + QString formatTxBalance(const KernelRecord *wtx) const; + QString formatTxCoinDay(const KernelRecord *wtx) const; + QString formatTxPoSReward(KernelRecord *wtx) const; +private slots: + void update(); + + friend class MintingTablePriv; +}; + +#endif // MINTINGTABLEMODEL_H diff --git a/src/qt/mintingview.cpp b/src/qt/mintingview.cpp new file mode 100644 index 00000000..1fdef92a --- /dev/null +++ b/src/qt/mintingview.cpp @@ -0,0 +1,250 @@ +#include "mintingview.h" +#include "mintingfilterproxy.h" +#include "transactionrecord.h" +#include "mintingtablemodel.h" +#include "walletmodel.h" +#include "guiconstants.h" +#include "guiutil.h" +#include "csvmodelwriter.h" + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MintingView::MintingView(QWidget *parent) : + QWidget(parent), model(0), mintingView(0) +{ + QHBoxLayout *hlayout = new QHBoxLayout(); + hlayout->setContentsMargins(0,0,0,0); + + QString legendBoxStyle = "background-color: rgb(%1,%2,%3); border: 1px solid black;"; + + QLabel *youngColor = new QLabel(" "); + youngColor->setMaximumHeight(15); + youngColor->setMaximumWidth(10); + youngColor->setStyleSheet(legendBoxStyle.arg(COLOR_MINT_YOUNG.red()).arg(COLOR_MINT_YOUNG.green()).arg(COLOR_MINT_YOUNG.blue())); + QLabel *youngLegend = new QLabel(tr("transaction is too young")); + youngLegend->setContentsMargins(5,0,15,0); + + QLabel *matureColor = new QLabel(" "); + matureColor->setMaximumHeight(15); + matureColor->setMaximumWidth(10); + matureColor->setStyleSheet(legendBoxStyle.arg(COLOR_MINT_MATURE.red()).arg(COLOR_MINT_MATURE.green()).arg(COLOR_MINT_MATURE.blue())); + QLabel *matureLegend = new QLabel(tr("transaction is mature")); + matureLegend->setContentsMargins(5,0,15,0); + + QLabel *oldColor = new QLabel(" "); + oldColor->setMaximumHeight(15); + oldColor->setMaximumWidth(10); + oldColor->setStyleSheet(legendBoxStyle.arg(COLOR_MINT_OLD.red()).arg(COLOR_MINT_OLD.green()).arg(COLOR_MINT_OLD.blue())); + QLabel *oldLegend = new QLabel(tr("transaction has reached maximum probability")); + oldLegend->setContentsMargins(5,0,15,0); + + QHBoxLayout *legendLayout = new QHBoxLayout(); + legendLayout->setContentsMargins(10,10,0,0); + legendLayout->addWidget(youngColor); + legendLayout->addWidget(youngLegend); + legendLayout->addWidget(matureColor); + legendLayout->addWidget(matureLegend); + legendLayout->addWidget(oldColor); + legendLayout->addWidget(oldLegend); + legendLayout->insertStretch(-1); + + QLabel *mintingLabel = new QLabel(tr("Display minting probability within : ")); + mintingCombo = new QComboBox(); + mintingCombo->addItem(tr("10 min"), Minting10min); + mintingCombo->addItem(tr("24 hours"), Minting1day); + mintingCombo->addItem(tr("7 days"), Minting7days); + mintingCombo->addItem(tr("30 days"), Minting30days); + mintingCombo->addItem(tr("60 days"), Minting60days); + mintingCombo->addItem(tr("90 days"), Minting90days); + mintingCombo->setFixedWidth(120); + + + hlayout->insertStretch(0); + hlayout->addWidget(mintingLabel); + hlayout->addWidget(mintingCombo); + + QVBoxLayout *vlayout = new QVBoxLayout(this); + vlayout->setContentsMargins(0,0,0,0); + vlayout->setSpacing(0); + + QTableView *view = new QTableView(this); + vlayout->addLayout(hlayout); + vlayout->addWidget(view); + vlayout->addLayout(legendLayout); + + vlayout->setSpacing(0); + int width = view->verticalScrollBar()->sizeHint().width(); + // Cover scroll bar width with spacing +#ifdef Q_WS_MAC + hlayout->addSpacing(width+2); +#else + hlayout->addSpacing(width); +#endif + // Always show scroll bar + view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + view->setTabKeyNavigation(false); + view->setContextMenuPolicy(Qt::CustomContextMenu); + + mintingView = view; + + connect(mintingCombo, SIGNAL(activated(int)), this, SLOT(chooseMintingInterval(int))); + + // Actions + QAction *copyTxIDAction = new QAction(tr("Copy transaction ID of input"), this); + QAction *copyAddressAction = new QAction(tr("Copy address of input"), this); + QAction *showHideAddressAction = new QAction(tr("Show/hide 'Address' column"), this); + QAction *showHideTxIDAction = new QAction(tr("Show/hide 'Transaction' column"), this); + + contextMenu = new QMenu(); + contextMenu->addAction(copyAddressAction); + contextMenu->addAction(copyTxIDAction); + contextMenu->addAction(showHideAddressAction); + contextMenu->addAction(showHideTxIDAction); + + connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(copyAddress())); + connect(copyTxIDAction, SIGNAL(triggered()), this, SLOT(copyTxID())); + connect(showHideAddressAction, SIGNAL(triggered()), this, SLOT(showHideAddress())); + connect(showHideTxIDAction, SIGNAL(triggered()), this, SLOT(showHideTxID())); + + connect(view, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint))); +} + + +void MintingView::setModel(WalletModel *model) +{ + this->model = model; + if(model) + { + mintingProxyModel = new MintingFilterProxy(this); + mintingProxyModel->setSourceModel(model->getMintingTableModel()); + mintingProxyModel->setDynamicSortFilter(true); + mintingProxyModel->setSortRole(Qt::EditRole); + model->getMintingTableModel()->setMintingProxyModel(mintingProxyModel); + + mintingView->setModel(mintingProxyModel); + mintingView->setAlternatingRowColors(true); + mintingView->setSelectionBehavior(QAbstractItemView::SelectRows); + mintingView->setSelectionMode(QAbstractItemView::ExtendedSelection); + mintingView->setSortingEnabled(true); + mintingView->sortByColumn(MintingTableModel::CoinDay, Qt::DescendingOrder); + mintingView->verticalHeader()->hide(); + + mintingView->horizontalHeader()->resizeSection( + MintingTableModel::Age, 60); + mintingView->horizontalHeader()->resizeSection( + MintingTableModel::Balance, 80); + mintingView->horizontalHeader()->resizeSection( + MintingTableModel::CoinDay,60); + mintingView->horizontalHeader()->resizeSection( + MintingTableModel::MintProbability, 105); +#if QT_VERSION < 0x050000 + mintingView->horizontalHeader()->setResizeMode( + MintingTableModel::MintReward, QHeaderView::Stretch); +#else + mintingView->horizontalHeader()->setSectionResizeMode( + MintingTableModel::MintReward, QHeaderView::Stretch); +#endif + mintingView->horizontalHeader()->resizeSection( + MintingTableModel::Address, 245); + mintingView->horizontalHeader()->resizeSection( + MintingTableModel::TxHash, 75); + } +} + +void MintingView::chooseMintingInterval(int idx) +{ + int interval = 10; + switch(mintingCombo->itemData(idx).toInt()) + { + case Minting10min: + interval = 10; + break; + case Minting1day: + interval = 60*24; + break; + case Minting7days: + interval = 60*24*7; + break; + case Minting30days: + interval = 60*24*30; + break; + case Minting60days: + interval = 60*24*60; + break; + case Minting90days: + interval = 60*24*90; + break; + } + model->getMintingTableModel()->setMintingInterval(interval); + mintingProxyModel->invalidate(); +} + +void MintingView::exportClicked() +{ + // CSV is currently the only supported format + QString filename = GUIUtil::getSaveFileName( + this, + tr("Export Minting Data"), QString(), + tr("Comma separated file (*.csv)")); + + if (filename.isNull()) return; + + CSVModelWriter writer(filename); + + // name, column, role + writer.setModel(mintingProxyModel); + writer.addColumn(tr("Address"),MintingTableModel::Address,0); + writer.addColumn(tr("Transaction"),MintingTableModel::TxHash,0); + writer.addColumn(tr("Age"), MintingTableModel::Age,0); + writer.addColumn(tr("CoinDay"), MintingTableModel::CoinDay,0); + writer.addColumn(tr("Balance"), MintingTableModel::Balance,0); + writer.addColumn(tr("MintingProbability"), MintingTableModel::MintProbability,0); + writer.addColumn(tr("MintingReward"), MintingTableModel::MintReward,0); + + if(!writer.write()) + { + QMessageBox::critical(this, tr("Error exporting"), tr("Could not write to file %1.").arg(filename), + QMessageBox::Abort, QMessageBox::Abort); + } +} + +void MintingView::copyTxID() +{ + GUIUtil::copyEntryData(mintingView, MintingTableModel::TxHash, 0); +} + +void MintingView::copyAddress() +{ + GUIUtil::copyEntryData(mintingView, MintingTableModel::Address, 0 ); +} + +void MintingView::showHideAddress() +{ + mintingView->horizontalHeader()->setSectionHidden(MintingTableModel::Address, + !(mintingView->horizontalHeader()->isSectionHidden(MintingTableModel::Address))); +} + +void MintingView::showHideTxID() +{ + mintingView->horizontalHeader()->setSectionHidden(MintingTableModel::TxHash, + !(mintingView->horizontalHeader()->isSectionHidden(MintingTableModel::TxHash))); +} + +void MintingView::contextualMenu(const QPoint &point) +{ + QModelIndex index = mintingView->indexAt(point); + if(index.isValid()) + { + contextMenu->exec(QCursor::pos()); + } +} \ No newline at end of file diff --git a/src/qt/mintingview.h b/src/qt/mintingview.h new file mode 100644 index 00000000..f3180112 --- /dev/null +++ b/src/qt/mintingview.h @@ -0,0 +1,55 @@ +#ifndef MINTINGVIEW_H +#define MINTINGVIEW_H + +#include +#include +#include "mintingfilterproxy.h" + +class WalletModel; + + +QT_BEGIN_NAMESPACE +class QTableView; +class QMenu; +QT_END_NAMESPACE + +class MintingView : public QWidget +{ + Q_OBJECT +public: + explicit MintingView(QWidget *parent = 0); + void setModel(WalletModel *model); + + enum MintingEnum + { + Minting10min, + Minting1day, + Minting7days, + Minting30days, + Minting60days, + Minting90days + }; + +private: + WalletModel *model; + QTableView *mintingView; + + QComboBox *mintingCombo; + + MintingFilterProxy *mintingProxyModel; + + QMenu *contextMenu; + +signals: + +public slots: + void exportClicked(); + void chooseMintingInterval(int idx); + void copyTxID(); + void copyAddress(); + void showHideAddress(); + void showHideTxID(); + void contextualMenu(const QPoint &point); +}; + +#endif // MINTINGVIEW_H diff --git a/src/qt/monitoreddatamapper.cpp b/src/qt/monitoreddatamapper.cpp new file mode 100644 index 00000000..88948d07 --- /dev/null +++ b/src/qt/monitoreddatamapper.cpp @@ -0,0 +1,36 @@ +#include "monitoreddatamapper.h" + +#include +#include +#include + +MonitoredDataMapper::MonitoredDataMapper(QObject *parent) : + QDataWidgetMapper(parent) +{ +} + + +void MonitoredDataMapper::addMapping(QWidget *widget, int section) +{ + QDataWidgetMapper::addMapping(widget, section); + addChangeMonitor(widget); +} + +void MonitoredDataMapper::addMapping(QWidget *widget, int section, const QByteArray &propertyName) +{ + QDataWidgetMapper::addMapping(widget, section, propertyName); + addChangeMonitor(widget); +} + +void MonitoredDataMapper::addChangeMonitor(QWidget *widget) +{ + // Watch user property of widget for changes, and connect + // the signal to our viewModified signal. + QMetaProperty prop = widget->metaObject()->userProperty(); + int signal = prop.notifySignalIndex(); + int method = this->metaObject()->indexOfMethod("viewModified()"); + if(signal != -1 && method != -1) + { + QMetaObject::connect(widget, signal, this, method); + } +} diff --git a/src/qt/monitoreddatamapper.h b/src/qt/monitoreddatamapper.h new file mode 100644 index 00000000..33a874e7 --- /dev/null +++ b/src/qt/monitoreddatamapper.h @@ -0,0 +1,31 @@ +#ifndef MONITOREDDATAMAPPER_H +#define MONITOREDDATAMAPPER_H + +#include + +QT_BEGIN_NAMESPACE +class QWidget; +QT_END_NAMESPACE + +/** Data to Widget mapper that watches for edits and notifies listeners when a field is edited. + This can be used, for example, to enable a commit/apply button in a configuration dialog. + */ +class MonitoredDataMapper : public QDataWidgetMapper +{ + Q_OBJECT +public: + explicit MonitoredDataMapper(QObject *parent=0); + + void addMapping(QWidget *widget, int section); + void addMapping(QWidget *widget, int section, const QByteArray &propertyName); +private: + void addChangeMonitor(QWidget *widget); + +signals: + void viewModified(); + +}; + + + +#endif // MONITOREDDATAMAPPER_H diff --git a/src/qt/multisigaddressentry.cpp b/src/qt/multisigaddressentry.cpp new file mode 100644 index 00000000..166b9edd --- /dev/null +++ b/src/qt/multisigaddressentry.cpp @@ -0,0 +1,120 @@ +#include +#include +#include +#include + +#include "addressbookpage.h" +#include "addresstablemodel.h" +#include "base58.h" +#include "guiutil.h" +#include "key.h" +#include "multisigaddressentry.h" +#include "ui_multisigaddressentry.h" +#include "walletmodel.h" + + +MultisigAddressEntry::MultisigAddressEntry(QWidget *parent) : QFrame(parent), ui(new Ui::MultisigAddressEntry), model(0) +{ + ui->setupUi(this); + GUIUtil::setupAddressWidget(ui->address, this); +} + +MultisigAddressEntry::~MultisigAddressEntry() +{ + delete ui; +} + +void MultisigAddressEntry::setModel(WalletModel *model) +{ + this->model = model; + clear(); +} + +void MultisigAddressEntry::clear() +{ + ui->pubkey->clear(); + ui->address->clear(); + ui->label->clear(); + ui->pubkey->setFocus(); +} + +bool MultisigAddressEntry::validate() +{ + return !ui->pubkey->text().isEmpty(); +} + +QString MultisigAddressEntry::getPubkey() +{ + return ui->pubkey->text(); +} + +void MultisigAddressEntry::setRemoveEnabled(bool enabled) +{ + ui->deleteButton->setEnabled(enabled); +} + +void MultisigAddressEntry::on_pasteButton_clicked() +{ + ui->address->setText(QApplication::clipboard()->text()); +} + +void MultisigAddressEntry::on_deleteButton_clicked() +{ + emit removeEntry(this); +} + +void MultisigAddressEntry::on_addressBookButton_clicked() +{ + if(!model) + return; + + AddressBookPage dlg(AddressBookPage::ForSending, AddressBookPage::ReceivingTab, this); + dlg.setModel(model->getAddressTableModel()); + if(dlg.exec()) + { + ui->address->setText(dlg.getReturnValue()); + } +} + +void MultisigAddressEntry::on_pubkey_textChanged(const QString &pubkey) +{ + // Compute address from public key + std::vector vchPubKey(ParseHex(pubkey.toStdString().c_str())); + CPubKey pkey(vchPubKey); + CKeyID keyID = pkey.GetID(); + CBitcoinAddress address(keyID); + ui->address->setText(address.ToString().c_str()); + + if(!model) + return; + + // Get label of address + QString associatedLabel = model->getAddressTableModel()->labelForAddress(address.ToString().c_str()); + if(!associatedLabel.isEmpty()) + ui->label->setText(associatedLabel); + else + ui->label->setText(QString()); +} + +void MultisigAddressEntry::on_address_textChanged(const QString &address) +{ + if(!model) + return; + + // Get public key of address + CBitcoinAddress addr(address.toStdString().c_str()); + CKeyID keyID; + if(addr.GetKeyID(keyID)) + { + CPubKey vchPubKey; + model->getPubKey(keyID, vchPubKey); + std::string pubkey = HexStr(vchPubKey.Raw()); + if(!pubkey.empty()) + ui->pubkey->setText(pubkey.c_str()); + } + + // Get label of address + QString associatedLabel = model->getAddressTableModel()->labelForAddress(address); + if(!associatedLabel.isEmpty()) + ui->label->setText(associatedLabel); +} diff --git a/src/qt/multisigaddressentry.h b/src/qt/multisigaddressentry.h new file mode 100644 index 00000000..85f5d16e --- /dev/null +++ b/src/qt/multisigaddressentry.h @@ -0,0 +1,44 @@ +#ifndef MULTISIGADDRESSENTRY_H +#define MULTISIGADDRESSENTRY_H + +#include + + +class WalletModel; + +namespace Ui +{ + class MultisigAddressEntry; +} + +class MultisigAddressEntry : public QFrame +{ + Q_OBJECT; + + public: + explicit MultisigAddressEntry(QWidget *parent = 0); + ~MultisigAddressEntry(); + void setModel(WalletModel *model); + bool validate(); + QString getPubkey(); + + public slots: + void setRemoveEnabled(bool enabled); + void clear(); + + signals: + void removeEntry(MultisigAddressEntry *entry); + + private: + Ui::MultisigAddressEntry *ui; + WalletModel *model; + + private slots: + void on_pubkey_textChanged(const QString &pubkey); + void on_pasteButton_clicked(); + void on_deleteButton_clicked(); + void on_address_textChanged(const QString &address); + void on_addressBookButton_clicked(); +}; + +#endif // MULTISIGADDRESSENTRY_H \ No newline at end of file diff --git a/src/qt/multisigdialog.cpp b/src/qt/multisigdialog.cpp new file mode 100644 index 00000000..3eb71674 --- /dev/null +++ b/src/qt/multisigdialog.cpp @@ -0,0 +1,660 @@ +#include +#include +#include +#include +#include +#include + +#include "addresstablemodel.h" +#include "base58.h" +#include "key.h" +#include "main.h" +#include "multisigaddressentry.h" +#include "multisiginputentry.h" +#include "multisigdialog.h" +#include "ui_multisigdialog.h" +#include "script.h" +#include "sendcoinsentry.h" +#include "util.h" +#include "wallet.h" +#include "walletmodel.h" + +#ifdef USE_LEVELDB +#include "txdb-leveldb.h" +#else +#include "txdb-bdb.h" +#endif + +MultisigDialog::MultisigDialog(QWidget *parent) : QWidget(parent), ui(new Ui::MultisigDialog), model(0) +{ + ui->setupUi(this); + +#ifdef Q_WS_MAC // Icons on push buttons are very uncommon on Mac + ui->addPubKeyButton->setIcon(QIcon()); + ui->clearButton->setIcon(QIcon()); + ui->addInputButton->setIcon(QIcon()); + ui->addOutputButton->setIcon(QIcon()); + ui->signTransactionButton->setIcon(QIcon()); + ui->sendTransactionButton->setIcon(QIcon()); +#endif + + addPubKey(); + addPubKey(); + + connect(ui->addPubKeyButton, SIGNAL(clicked()), this, SLOT(addPubKey())); + connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear())); + + addInput(); + addOutput(); + + connect(ui->addInputButton, SIGNAL(clicked()), this, SLOT(addInput())); + connect(ui->addOutputButton, SIGNAL(clicked()), this, SLOT(addOutput())); + + ui->signTransactionButton->setEnabled(false); + ui->sendTransactionButton->setEnabled(false); +} + +void MultisigDialog::showEvent(QShowEvent *event) +{ + QWidget::showEvent(event); + + if (!model) + return; + + updateAmounts(); +} + +void MultisigDialog::hideEvent(QHideEvent *event) +{ + QWidget::hideEvent(event); + + if (!model) + return; + + clear(); +} + +MultisigDialog::~MultisigDialog() +{ + delete ui; +} + +void MultisigDialog::setModel(WalletModel *model) +{ + this->model = model; + + for(int i = 0; i < ui->pubkeyEntries->count(); i++) + { + MultisigAddressEntry *entry = qobject_cast(ui->pubkeyEntries->itemAt(i)->widget()); + if(entry) + entry->setModel(model); + } + + + for(int i = 0; i < ui->inputs->count(); i++) + { + MultisigInputEntry *entry = qobject_cast(ui->inputs->itemAt(i)->widget()); + if(entry) + entry->setModel(model); + } + + + for(int i = 0; i < ui->outputs->count(); i++) + { + SendCoinsEntry *entry = qobject_cast(ui->outputs->itemAt(i)->widget()); + if(entry) + entry->setModel(model); + } +} + +void MultisigDialog::updateRemoveEnabled() +{ + bool enabled = (ui->pubkeyEntries->count() > 2); + + for(int i = 0; i < ui->pubkeyEntries->count(); i++) + { + MultisigAddressEntry *entry = qobject_cast(ui->pubkeyEntries->itemAt(i)->widget()); + if(entry) + entry->setRemoveEnabled(enabled); + } + + QString maxSigsStr; + maxSigsStr.setNum(ui->pubkeyEntries->count()); + ui->maxSignaturesLabel->setText(QString("/ ") + maxSigsStr); + + + enabled = (ui->inputs->count() > 1); + for(int i = 0; i < ui->inputs->count(); i++) + { + MultisigInputEntry *entry = qobject_cast(ui->inputs->itemAt(i)->widget()); + if(entry) + entry->setRemoveEnabled(enabled); + } + + + enabled = (ui->outputs->count() > 1); + for(int i = 0; i < ui->outputs->count(); i++) + { + SendCoinsEntry *entry = qobject_cast(ui->outputs->itemAt(i)->widget()); + if(entry) + entry->setRemoveEnabled(enabled); + } +} + +void MultisigDialog::on_createAddressButton_clicked() +{ + ui->multisigAddress->clear(); + ui->redeemScript->clear(); + + if(!model) + return; + + std::vector pubkeys; + pubkeys.resize(ui->pubkeyEntries->count()); + unsigned int required = ui->requiredSignatures->text().toUInt(); + + for(int i = 0; i < ui->pubkeyEntries->count(); i++) + { + MultisigAddressEntry *entry = qobject_cast(ui->pubkeyEntries->itemAt(i)->widget()); + if(!entry->validate()) + return; + QString str = entry->getPubkey(); + CPubKey vchPubKey(ParseHex(str.toStdString().c_str())); + if(!vchPubKey.IsValid()) + return; + pubkeys[i].SetPubKey(vchPubKey); + } + + if(pubkeys.size() > 16) + { + QMessageBox::warning(this, tr("Error"), tr("Number of addresses involved in the address creation > %1\nReduce the number").arg(16), QMessageBox::Ok); + return; + } + + if(required == 0) + { + QMessageBox::warning(this, tr("Error"), tr("Number of required signatures is 0\nNumber of required signatures must be between 1 and number of keys involved in the creation of address."), QMessageBox::Ok); + return; + } + + if(required > pubkeys.size()) + { + QMessageBox::warning(this, tr("Error"), tr("Number of required signatures > Number of keys involved in the creation of address."), QMessageBox::Ok); + return; + } + + CScript script; + script.SetMultisig(required, pubkeys); + if (script.size() > MAX_SCRIPT_ELEMENT_SIZE) + { + QMessageBox::warning(this, tr("Error"), tr("Redeem script exceeds size limit: %1 > %2\nReduce the number of addresses involved in the address creation.").arg(script.size()).arg(MAX_SCRIPT_ELEMENT_SIZE), QMessageBox::Ok); + return; + } + CScriptID scriptID = script.GetID(); + CBitcoinAddress address(scriptID); + + ui->multisigAddress->setText(address.ToString().c_str()); + ui->redeemScript->setText(HexStr(script.begin(), script.end()).c_str()); +} + +void MultisigDialog::on_copyMultisigAddressButton_clicked() +{ + QApplication::clipboard()->setText(ui->multisigAddress->text()); +} + +void MultisigDialog::on_copyRedeemScriptButton_clicked() +{ + QApplication::clipboard()->setText(ui->redeemScript->text()); +} + +void MultisigDialog::on_saveRedeemScriptButton_clicked() +{ + if(!model) + return; + + CWallet *wallet = model->getWallet(); + std::string redeemScript = ui->redeemScript->text().toStdString(); + std::vector scriptData(ParseHex(redeemScript)); + CScript script(scriptData.begin(), scriptData.end()); + CScriptID scriptID = script.GetID(); + + LOCK(wallet->cs_wallet); + if(!wallet->HaveCScript(scriptID)) + wallet->AddCScript(script); +} + +void MultisigDialog::on_saveMultisigAddressButton_clicked() +{ + if(!model) + return; + + CWallet *wallet = model->getWallet(); + std::string redeemScript = ui->redeemScript->text().toStdString(); + std::string address = ui->multisigAddress->text().toStdString(); + std::string label("multisig"); + + if(!model->validateAddress(QString(address.c_str()))) + return; + + std::vector scriptData(ParseHex(redeemScript)); + CScript script(scriptData.begin(), scriptData.end()); + CScriptID scriptID = script.GetID(); + + LOCK(wallet->cs_wallet); + if(!wallet->HaveCScript(scriptID)) + wallet->AddCScript(script); + if(!wallet->mapAddressBook.count(CBitcoinAddress(address).Get())) + wallet->SetAddressBookName(CBitcoinAddress(address).Get(), label); +} + +void MultisigDialog::clear() +{ + while(ui->pubkeyEntries->count()) + delete ui->pubkeyEntries->takeAt(0)->widget(); + + addPubKey(); + addPubKey(); + updateRemoveEnabled(); +} + +MultisigAddressEntry * MultisigDialog::addPubKey() +{ + MultisigAddressEntry *entry = new MultisigAddressEntry(this); + + entry->setModel(model); + ui->pubkeyEntries->addWidget(entry); + connect(entry, SIGNAL(removeEntry(MultisigAddressEntry *)), this, SLOT(removeEntry(MultisigAddressEntry *))); + updateRemoveEnabled(); + entry->clear(); + ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint()); + QScrollBar *bar = ui->scrollArea->verticalScrollBar(); + if(bar) + bar->setSliderPosition(bar->maximum()); + + return entry; +} + +void MultisigDialog::removeEntry(MultisigAddressEntry *entry) +{ + delete entry; + updateRemoveEnabled(); +} + +void MultisigDialog::on_createTransactionButton_clicked() +{ + CTransaction transaction; + + // Get inputs + for(int i = 0; i < ui->inputs->count(); i++) + { + MultisigInputEntry *entry = qobject_cast(ui->inputs->itemAt(i)->widget()); + if(entry) + { + if(entry->validate()) + { + CTxIn input = entry->getInput(); + transaction.vin.push_back(input); + } + else + return; + } + } + + // Get outputs + for(int i = 0; i < ui->outputs->count(); i++) + { + SendCoinsEntry *entry = qobject_cast(ui->outputs->itemAt(i)->widget()); + + if(entry) + { + if(entry->validate()) + { + SendCoinsRecipient recipient = entry->getValue(); + CBitcoinAddress address(recipient.address.toStdString()); + CScript scriptPubKey; + scriptPubKey.SetDestination(address.Get()); + int64_t amount = recipient.amount; + CTxOut output(amount, scriptPubKey); + transaction.vout.push_back(output); + } + else + return; + } + } + + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss << transaction; + ui->transaction->setText(HexStr(ss.begin(), ss.end()).c_str()); +} + +void MultisigDialog::on_transaction_textChanged() +{ + while(ui->inputs->count()) + delete ui->inputs->takeAt(0)->widget(); + while(ui->outputs->count()) + delete ui->outputs->takeAt(0)->widget(); + + if(ui->transaction->text().size() > 0) + ui->signTransactionButton->setEnabled(true); + else + ui->signTransactionButton->setEnabled(false); + + // Decode the raw transaction + std::vector txData(ParseHex(ui->transaction->text().toStdString())); + CDataStream ss(txData, SER_NETWORK, PROTOCOL_VERSION); + CTransaction tx; + try + { + ss >> tx; + } + catch(std::exception &e) + { + (void)e; + return; + } + + // Fill input list + int index = -1; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + uint256 prevoutHash = txin.prevout.hash; + addInput(); + index++; + MultisigInputEntry *entry = qobject_cast(ui->inputs->itemAt(index)->widget()); + if(entry) + { + entry->setTransactionId(QString(prevoutHash.GetHex().c_str())); + entry->setTransactionOutputIndex(txin.prevout.n); + } + } + + // Fill output list + index = -1; + BOOST_FOREACH(const CTxOut& txout, tx.vout) + { + CScript scriptPubKey = txout.scriptPubKey; + CTxDestination addr; + ExtractDestination(scriptPubKey, addr); + CBitcoinAddress address(addr); + SendCoinsRecipient recipient; + recipient.address = QString(address.ToString().c_str()); + recipient.amount = txout.nValue; + addOutput(); + index++; + SendCoinsEntry *entry = qobject_cast(ui->outputs->itemAt(index)->widget()); + if(entry) + { + entry->setValue(recipient); + } + } + + updateRemoveEnabled(); +} + +void MultisigDialog::on_copyTransactionButton_clicked() +{ + QApplication::clipboard()->setText(ui->transaction->text()); +} + +void MultisigDialog::on_pasteTransactionButton_clicked() +{ + ui->transaction->setText(QApplication::clipboard()->text()); +} + +void MultisigDialog::on_signTransactionButton_clicked() +{ + ui->signedTransaction->clear(); + + if(!model) + return; + + CWallet *wallet = model->getWallet(); + + // Decode the raw transaction + std::vector txData(ParseHex(ui->transaction->text().toStdString())); + CDataStream ss(txData, SER_NETWORK, PROTOCOL_VERSION); + CTransaction tx; + try + { + ss >> tx; + } + catch(std::exception &e) + { + (void)e; + return; + } + CTransaction mergedTx(tx); + + // Fetch previous transactions (inputs) + std::map mapPrevOut; + for(unsigned int i = 0; i < mergedTx.vin.size(); i++) + { + CTransaction tempTx; + MapPrevTx mapPrevTx; + CTxDB txdb("r"); + std::map unused; + bool fInvalid; + + tempTx.vin.push_back(mergedTx.vin[i]); + tempTx.FetchInputs(txdb, unused, false, false, mapPrevTx, fInvalid); + + BOOST_FOREACH(const CTxIn& txin, tempTx.vin) + { + const uint256& prevHash = txin.prevout.hash; + if(mapPrevTx.count(prevHash) && mapPrevTx[prevHash].second.vout.size() > txin.prevout.n) + mapPrevOut[txin.prevout] = mapPrevTx[prevHash].second.vout[txin.prevout.n].scriptPubKey; + } + } + + // Add the redeem scripts to the wallet keystore + for(int i = 0; i < ui->inputs->count(); i++) + { + MultisigInputEntry *entry = qobject_cast(ui->inputs->itemAt(i)->widget()); + if(entry) + { + QString redeemScriptStr = entry->getRedeemScript(); + if(redeemScriptStr.size() > 0) + { + std::vector scriptData(ParseHex(redeemScriptStr.toStdString())); + CScript redeemScript(scriptData.begin(), scriptData.end()); + wallet->AddCScript(redeemScript); + } + } + } + + WalletModel::UnlockContext ctx(model->requestUnlock()); + if(!ctx.isValid()) + return; + + // Sign what we can + bool fComplete = true; + for(unsigned int i = 0; i < mergedTx.vin.size(); i++) + { + CTxIn& txin = mergedTx.vin[i]; + if(mapPrevOut.count(txin.prevout) == 0) + { + fComplete = false; + continue; + } + const CScript& prevPubKey = mapPrevOut[txin.prevout]; + + txin.scriptSig.clear(); + SignSignature(*wallet, prevPubKey, mergedTx, i, SIGHASH_ALL); + txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, tx.vin[i].scriptSig); + if(!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, true, 0)) + { + fComplete = false; + } + } + + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); + ssTx << mergedTx; + ui->signedTransaction->setText(HexStr(ssTx.begin(), ssTx.end()).c_str()); + + if(fComplete) + { + ui->statusLabel->setText(tr("Transaction signature is complete")); + ui->sendTransactionButton->setEnabled(true); + } + else + { + ui->statusLabel->setText(tr("Transaction is NOT completely signed")); + ui->sendTransactionButton->setEnabled(false); + } +} + +void MultisigDialog::on_copySignedTransactionButton_clicked() +{ + QApplication::clipboard()->setText(ui->signedTransaction->text()); +} + +void MultisigDialog::on_sendTransactionButton_clicked() +{ + int64_t transactionSize = ui->signedTransaction->text().size() / 2; + if(transactionSize == 0) + return; + + // Check the fee + int64_t fee = (int64_t ) (ui->fee->text().toDouble() * COIN); + int64_t minFee = MIN_TX_FEE * (1 + (int64_t) transactionSize / 1000); + if(fee < minFee) + { + QMessageBox::StandardButton ret = QMessageBox::question(this, tr("Confirm send transaction"), tr("The fee of the transaction (%1 XP) is smaller than the expected fee (%2 XP). Do you want to send the transaction anyway?").arg((double) fee / COIN).arg((double) minFee / COIN), QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); + if(ret != QMessageBox::Yes) + return; + } + else if(fee > minFee) + { + QMessageBox::StandardButton ret = QMessageBox::question(this, tr("Confirm send transaction"), tr("The fee of the transaction (%1 XP) is bigger than the expected fee (%2 XP). Do you want to send the transaction anyway?").arg((double) fee / COIN).arg((double) minFee / COIN), QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); + if(ret != QMessageBox::Yes) + return; + } + + // Decode the raw transaction + std::vector txData(ParseHex(ui->signedTransaction->text().toStdString())); + CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); + CTransaction tx; + try + { + ssData >> tx; + } + catch(std::exception &e) + { + (void)e; + return; + } + uint256 txHash = tx.GetHash(); + + // Check if the transaction is already in the blockchain + CTransaction existingTx; + uint256 blockHash = 0; + if(GetTransaction(txHash, existingTx, blockHash)) + { + if(blockHash != 0) + return; + } + + // Send the transaction to the local node + CTxDB txdb("r"); + if(!tx.AcceptToMemoryPool(txdb, false)) + return; + SyncWithWallets(tx, NULL, true); + //(CInv(MSG_TX, txHash), tx); + RelayTransaction(tx, txHash); +} + +MultisigInputEntry * MultisigDialog::addInput() +{ + MultisigInputEntry *entry = new MultisigInputEntry(this); + + entry->setModel(model); + ui->inputs->addWidget(entry); + connect(entry, SIGNAL(removeEntry(MultisigInputEntry *)), this, SLOT(removeEntry(MultisigInputEntry *))); + connect(entry, SIGNAL(updateAmount()), this, SLOT(updateAmounts())); + updateRemoveEnabled(); + entry->clear(); + ui->scrollAreaWidgetContents_2->resize(ui->scrollAreaWidgetContents_2->sizeHint()); + QScrollBar *bar = ui->scrollArea_2->verticalScrollBar(); + if(bar) + bar->setSliderPosition(bar->maximum()); + + return entry; +} + +void MultisigDialog::removeEntry(MultisigInputEntry *entry) +{ + delete entry; + updateRemoveEnabled(); +} + +SendCoinsEntry * MultisigDialog::addOutput() +{ + SendCoinsEntry *entry = new SendCoinsEntry(this); + + entry->setModel(model); + ui->outputs->addWidget(entry); + connect(entry, SIGNAL(removeEntry(SendCoinsEntry *)), this, SLOT(removeEntry(SendCoinsEntry *))); + connect(entry, SIGNAL(payAmountChanged()), this, SLOT(updateAmounts())); + updateRemoveEnabled(); + entry->clear(); + ui->scrollAreaWidgetContents_3->resize(ui->scrollAreaWidgetContents_3->sizeHint()); + QScrollBar *bar = ui->scrollArea_3->verticalScrollBar(); + if(bar) + bar->setSliderPosition(bar->maximum()); + + return entry; +} + +void MultisigDialog::removeEntry(SendCoinsEntry *entry) +{ + delete entry; + updateRemoveEnabled(); +} + +void MultisigDialog::updateAmounts() +{ + // Update inputs amount + int64_t inputsAmount = 0; + for(int i = 0; i < ui->inputs->count(); i++) + { + MultisigInputEntry *entry = qobject_cast(ui->inputs->itemAt(i)->widget()); + if(entry) + inputsAmount += entry->getAmount(); + } + QString inputsAmountStr; + inputsAmountStr.sprintf("%.6f", (double) inputsAmount / COIN); + ui->inputsAmount->setText(inputsAmountStr); + + // Update outputs amount + int64_t outputsAmount = 0; + for(int i = 0; i < ui->outputs->count(); i++) + { + SendCoinsEntry *entry = qobject_cast(ui->outputs->itemAt(i)->widget()); + if(entry) + outputsAmount += entry->getValue().amount; + } + QString outputsAmountStr; + outputsAmountStr.sprintf("%.6f", (double) outputsAmount / COIN); + ui->outputsAmount->setText(outputsAmountStr); + + // Update Fee amount + int64_t fee = inputsAmount - outputsAmount; + QString feeStr; + feeStr.sprintf("%.6f", (double) fee / COIN); + ui->fee->setText(feeStr); +} + +void MultisigDialog::keyPressEvent(QKeyEvent *event) +{ +#ifdef ANDROID + if(windowType() != Qt::Widget && event->key() == Qt::Key_Back) + { + close(); + } +#else + if(windowType() != Qt::Widget && event->key() == Qt::Key_Escape) + { + close(); + } +#endif +} \ No newline at end of file diff --git a/src/qt/multisigdialog.h b/src/qt/multisigdialog.h new file mode 100644 index 00000000..1c444c2e --- /dev/null +++ b/src/qt/multisigdialog.h @@ -0,0 +1,61 @@ +#ifndef MULTISIGDIALOG_H +#define MULTISIGDIALOG_H + +#include + +#include "multisigaddressentry.h" +#include "multisiginputentry.h" +#include "sendcoinsentry.h" +#include "walletmodel.h" + + +namespace Ui +{ + class MultisigDialog; +} + +class MultisigDialog : public QWidget +{ + Q_OBJECT; + protected: + void keyPressEvent(QKeyEvent *); + + public: + explicit MultisigDialog(QWidget *parent); + MultisigDialog(); + ~MultisigDialog(); + void setModel(WalletModel *model); + + public slots: + MultisigAddressEntry * addPubKey(); + void clear(); + void updateRemoveEnabled(); + MultisigInputEntry * addInput(); + SendCoinsEntry * addOutput(); + + private: + Ui::MultisigDialog *ui; + WalletModel *model; + + private slots: + void showEvent(QShowEvent *event); + void hideEvent(QHideEvent *event); + void on_createAddressButton_clicked(); + void on_copyMultisigAddressButton_clicked(); + void on_copyRedeemScriptButton_clicked(); + void on_saveRedeemScriptButton_clicked(); + void on_saveMultisigAddressButton_clicked(); + void removeEntry(MultisigAddressEntry *entry); + void on_createTransactionButton_clicked(); + void on_transaction_textChanged(); + void on_copyTransactionButton_clicked(); + void on_pasteTransactionButton_clicked(); + void on_signTransactionButton_clicked(); + void on_copySignedTransactionButton_clicked(); + void on_sendTransactionButton_clicked(); + void removeEntry(MultisigInputEntry *entry); + void removeEntry(SendCoinsEntry *entry); + void updateAmounts(); +}; + +#endif // MULTISIGDIALOG_H \ No newline at end of file diff --git a/src/qt/multisiginputentry.cpp b/src/qt/multisiginputentry.cpp new file mode 100644 index 00000000..54710d07 --- /dev/null +++ b/src/qt/multisiginputentry.cpp @@ -0,0 +1,174 @@ +#include +#include +#include +#include + +#include "base58.h" +#include "multisiginputentry.h" +#include "ui_multisiginputentry.h" +#include "main.h" +#include "script.h" +#include "util.h" +#include "wallet.h" +#include "walletmodel.h" + + +MultisigInputEntry::MultisigInputEntry(QWidget *parent) : QFrame(parent), ui(new Ui::MultisigInputEntry), model(0) +{ + ui->setupUi(this); +} + +MultisigInputEntry::~MultisigInputEntry() +{ + delete ui; +} + +void MultisigInputEntry::setModel(WalletModel *model) +{ + this->model = model; + clear(); +} + +void MultisigInputEntry::clear() +{ + ui->transactionId->clear(); + ui->transactionOutput->clear(); + ui->redeemScript->clear(); +} + +bool MultisigInputEntry::validate() +{ + return (ui->transactionOutput->count() > 0); +} + +CTxIn MultisigInputEntry::getInput() +{ + unsigned int nOutput = ui->transactionOutput->currentIndex(); + CTxIn input(COutPoint(txHash, nOutput)); + + return input; +} + +int64_t MultisigInputEntry::getAmount() +{ + int64_t amount = 0; + unsigned int nOutput = ui->transactionOutput->currentIndex(); + CTransaction tx; + uint256 blockHash = 0; + + if(GetTransaction(txHash, tx, blockHash)) + { + if(nOutput < tx.vout.size()) + { + const CTxOut& txOut = tx.vout[nOutput]; + amount = txOut.nValue; + } + } + + return amount; +} + +QString MultisigInputEntry::getRedeemScript() +{ + return ui->redeemScript->text(); +} + +void MultisigInputEntry::setTransactionId(QString transactionId) +{ + ui->transactionId->setText(transactionId); +} + +void MultisigInputEntry::setTransactionOutputIndex(int index) +{ + ui->transactionOutput->setCurrentIndex(index); +} + +void MultisigInputEntry::setRemoveEnabled(bool enabled) +{ + ui->deleteButton->setEnabled(enabled); +} + +void MultisigInputEntry::on_pasteTransactionIdButton_clicked() +{ + ui->transactionId->setText(QApplication::clipboard()->text()); +} + +void MultisigInputEntry::on_deleteButton_clicked() +{ + emit removeEntry(this); +} + +void MultisigInputEntry::on_pasteRedeemScriptButton_clicked() +{ + ui->redeemScript->setText(QApplication::clipboard()->text()); +} + +void MultisigInputEntry::on_transactionId_textChanged(const QString &transactionId) +{ + ui->transactionOutput->clear(); + if(transactionId.isEmpty()) + return; + + // Make list of transaction outputs + txHash.SetHex(transactionId.toStdString().c_str()); + CTransaction tx; + uint256 blockHash = 0; + if(!GetTransaction(txHash, tx, blockHash)) + return; + for(unsigned int i = 0; i < tx.vout.size(); i++) + { + QString idStr; + idStr.setNum(i); + const CTxOut& txOut = tx.vout[i]; + int64_t amount = txOut.nValue; + QString amountStr; + amountStr.sprintf("%.6f", (double) amount / COIN); + CScript script = txOut.scriptPubKey; + CTxDestination addr; + if(ExtractDestination(script, addr)) + { + CBitcoinAddress address(addr); + QString addressStr(address.ToString().c_str()); + ui->transactionOutput->addItem(idStr + QString(" - ") + addressStr + QString(" - ") + amountStr + QString(" XP")); + } + else + ui->transactionOutput->addItem(idStr + QString(" - ") + amountStr + QString(" XP")); + } +} + +void MultisigInputEntry::on_transactionOutput_currentIndexChanged(int index) +{ + if(ui->transactionOutput->itemText(index).isEmpty()) + return; + + CTransaction tx; + uint256 blockHash = 0; + if(!GetTransaction(txHash, tx, blockHash)) + return; + const CTxOut& txOut = tx.vout[index]; + CScript script = txOut.scriptPubKey; + + if(script.IsPayToScriptHash()) + { + ui->redeemScript->setEnabled(true); + + if(model) + { + // Try to find the redeem script + CTxDestination dest; + if(ExtractDestination(script, dest)) + { + CScriptID scriptID = boost::get(dest); + CScript redeemScript; + if(model->getWallet()->GetCScript(scriptID, redeemScript)) + ui->redeemScript->setText(HexStr(redeemScript.begin(), redeemScript.end()).c_str()); + } + } + } + else + { + ui->redeemScript->setEnabled(false); + } + + emit updateAmount(); +} \ No newline at end of file diff --git a/src/qt/multisiginputentry.h b/src/qt/multisiginputentry.h new file mode 100644 index 00000000..1a181af7 --- /dev/null +++ b/src/qt/multisiginputentry.h @@ -0,0 +1,53 @@ +#ifndef MULTISIGINPUTENTRY_H +#define MULTISIGINPUTENTRY_H + +#include + +#include "uint256.h" + + +class CTxIn; +class WalletModel; + +namespace Ui +{ + class MultisigInputEntry; +} + +class MultisigInputEntry : public QFrame +{ + Q_OBJECT; + + public: + explicit MultisigInputEntry(QWidget *parent = 0); + ~MultisigInputEntry(); + void setModel(WalletModel *model); + bool validate(); + CTxIn getInput(); + int64_t getAmount(); + QString getRedeemScript(); + void setTransactionId(QString transactionId); + void setTransactionOutputIndex(int index); + + public slots: + void setRemoveEnabled(bool enabled); + void clear(); + + signals: + void removeEntry(MultisigInputEntry *entry); + void updateAmount(); + + private: + Ui::MultisigInputEntry *ui; + WalletModel *model; + uint256 txHash; + + private slots: + void on_transactionId_textChanged(const QString &transactionId); + void on_pasteTransactionIdButton_clicked(); + void on_deleteButton_clicked(); + void on_transactionOutput_currentIndexChanged(int index); + void on_pasteRedeemScriptButton_clicked(); +}; + +#endif // MULTISIGINPUTENTRY_H \ No newline at end of file diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp new file mode 100644 index 00000000..3d588cd3 --- /dev/null +++ b/src/qt/notificator.cpp @@ -0,0 +1,326 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "notificator.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef USE_DBUS +#include +#include +#endif +// Include ApplicationServices.h after QtDbus to avoid redefinition of check(). +// This affects at least OSX 10.6. See /usr/include/AssertMacros.h for details. +// Note: This could also be worked around using: +// #define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0 +#ifdef Q_OS_MAC +#include +#include "macnotificationhandler.h" +#endif + + +#ifdef USE_DBUS +// https://wiki.ubuntu.com/NotificationDevelopmentGuidelines recommends at least 128 +const int FREEDESKTOP_NOTIFICATION_ICON_SIZE = 128; +#endif + +Notificator::Notificator(const QString &programName, QSystemTrayIcon *trayicon, QWidget *parent) : + QObject(parent), + parent(parent), + programName(programName), + mode(None), + trayIcon(trayicon) +#ifdef USE_DBUS + ,interface(0) +#endif +{ + if(trayicon && trayicon->supportsMessages()) + { + mode = QSystemTray; + } +#ifdef USE_DBUS + interface = new QDBusInterface("org.freedesktop.Notifications", + "/org/freedesktop/Notifications", "org.freedesktop.Notifications"); + if(interface->isValid()) + { + mode = Freedesktop; + } +#endif +#ifdef Q_OS_MAC + // check if users OS has support for NSUserNotification + if( MacNotificationHandler::instance()->hasUserNotificationCenterSupport()) { + mode = UserNotificationCenter; + } + else { + // Check if Growl is installed (based on Qt's tray icon implementation) + CFURLRef cfurl; + OSStatus status = LSGetApplicationForInfo(kLSUnknownType, kLSUnknownCreator, CFSTR("growlTicket"), kLSRolesAll, 0, &cfurl); + if (status != kLSApplicationNotFoundErr) { + CFBundleRef bundle = CFBundleCreate(0, cfurl); + if (CFStringCompare(CFBundleGetIdentifier(bundle), CFSTR("com.Growl.GrowlHelperApp"), kCFCompareCaseInsensitive | kCFCompareBackwards) == kCFCompareEqualTo) { + if (CFStringHasSuffix(CFURLGetString(cfurl), CFSTR("/Growl.app/"))) + mode = Growl13; + else + mode = Growl12; + } + CFRelease(cfurl); + CFRelease(bundle); + } + } +#endif +} + +Notificator::~Notificator() +{ +#ifdef USE_DBUS + delete interface; +#endif +} + +#ifdef USE_DBUS + +// Loosely based on http://www.qtcentre.org/archive/index.php/t-25879.html +class FreedesktopImage +{ +public: + FreedesktopImage() {} + FreedesktopImage(const QImage &img); + + static int metaType(); + + // Image to variant that can be marshalled over DBus + static QVariant toVariant(const QImage &img); + +private: + int width, height, stride; + bool hasAlpha; + int channels; + int bitsPerSample; + QByteArray image; + + friend QDBusArgument &operator<<(QDBusArgument &a, const FreedesktopImage &i); + friend const QDBusArgument &operator>>(const QDBusArgument &a, FreedesktopImage &i); +}; + +Q_DECLARE_METATYPE(FreedesktopImage); + +// Image configuration settings +const int CHANNELS = 4; +const int BYTES_PER_PIXEL = 4; +const int BITS_PER_SAMPLE = 8; + +FreedesktopImage::FreedesktopImage(const QImage &img): + width(img.width()), + height(img.height()), + stride(img.width() * BYTES_PER_PIXEL), + hasAlpha(true), + channels(CHANNELS), + bitsPerSample(BITS_PER_SAMPLE) +{ + // Convert 00xAARRGGBB to RGBA bytewise (endian-independent) format + QImage tmp = img.convertToFormat(QImage::Format_ARGB32); + const uint32_t *data = reinterpret_cast(tmp.bits()); + + unsigned int num_pixels = width * height; + image.resize(num_pixels * BYTES_PER_PIXEL); + + for(unsigned int ptr = 0; ptr < num_pixels; ++ptr) + { + image[ptr*BYTES_PER_PIXEL+0] = data[ptr] >> 16; // R + image[ptr*BYTES_PER_PIXEL+1] = data[ptr] >> 8; // G + image[ptr*BYTES_PER_PIXEL+2] = data[ptr]; // B + image[ptr*BYTES_PER_PIXEL+3] = data[ptr] >> 24; // A + } +} + +QDBusArgument &operator<<(QDBusArgument &a, const FreedesktopImage &i) +{ + a.beginStructure(); + a << i.width << i.height << i.stride << i.hasAlpha << i.bitsPerSample << i.channels << i.image; + a.endStructure(); + return a; +} + +const QDBusArgument &operator>>(const QDBusArgument &a, FreedesktopImage &i) +{ + a.beginStructure(); + a >> i.width >> i.height >> i.stride >> i.hasAlpha >> i.bitsPerSample >> i.channels >> i.image; + a.endStructure(); + return a; +} + +int FreedesktopImage::metaType() +{ + return qDBusRegisterMetaType(); +} + +QVariant FreedesktopImage::toVariant(const QImage &img) +{ + FreedesktopImage fimg(img); + return QVariant(FreedesktopImage::metaType(), &fimg); +} + +void Notificator::notifyDBus(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout) +{ + Q_UNUSED(cls); + // Arguments for DBus call: + QList args; + + // Program Name: + args.append(programName); + + // Unique ID of this notification type: + args.append(0U); + + // Application Icon, empty string + args.append(QString()); + + // Summary + args.append(title); + + // Body + args.append(text); + + // Actions (none, actions are deprecated) + QStringList actions; + args.append(actions); + + // Hints + QVariantMap hints; + + // If no icon specified, set icon based on class + QIcon tmpicon; + if(icon.isNull()) + { + QStyle::StandardPixmap sicon = QStyle::SP_MessageBoxQuestion; + switch(cls) + { + case Information: sicon = QStyle::SP_MessageBoxInformation; break; + case Warning: sicon = QStyle::SP_MessageBoxWarning; break; + case Critical: sicon = QStyle::SP_MessageBoxCritical; break; + default: break; + } + tmpicon = QApplication::style()->standardIcon(sicon); + } + else + { + tmpicon = icon; + } + hints["icon_data"] = FreedesktopImage::toVariant(tmpicon.pixmap(FREEDESKTOP_NOTIFICATION_ICON_SIZE).toImage()); + args.append(hints); + + // Timeout (in msec) + args.append(millisTimeout); + + // "Fire and forget" + interface->callWithArgumentList(QDBus::NoBlock, "Notify", args); +} +#endif + +void Notificator::notifySystray(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout) +{ + Q_UNUSED(icon); + QSystemTrayIcon::MessageIcon sicon = QSystemTrayIcon::NoIcon; + switch(cls) // Set icon based on class + { + case Information: sicon = QSystemTrayIcon::Information; break; + case Warning: sicon = QSystemTrayIcon::Warning; break; + case Critical: sicon = QSystemTrayIcon::Critical; break; + } + trayIcon->showMessage(title, text, sicon, millisTimeout); +} + +// Based on Qt's tray icon implementation +#ifdef Q_OS_MAC +void Notificator::notifyGrowl(Class cls, const QString &title, const QString &text, const QIcon &icon) +{ + const QString script( + "tell application \"%5\"\n" + " set the allNotificationsList to {\"Notification\"}\n" // -- Make a list of all the notification types (all) + " set the enabledNotificationsList to {\"Notification\"}\n" // -- Make a list of the notifications (enabled) + " register as application \"%1\" all notifications allNotificationsList default notifications enabledNotificationsList\n" // -- Register our script with Growl + " notify with name \"Notification\" title \"%2\" description \"%3\" application name \"%1\"%4\n" // -- Send a Notification + "end tell" + ); + + QString notificationApp(QApplication::applicationName()); + if (notificationApp.isEmpty()) + notificationApp = "Application"; + + QPixmap notificationIconPixmap; + if (icon.isNull()) { // If no icon specified, set icon based on class + QStyle::StandardPixmap sicon = QStyle::SP_MessageBoxQuestion; + switch (cls) + { + case Information: sicon = QStyle::SP_MessageBoxInformation; break; + case Warning: sicon = QStyle::SP_MessageBoxWarning; break; + case Critical: sicon = QStyle::SP_MessageBoxCritical; break; + } + notificationIconPixmap = QApplication::style()->standardPixmap(sicon); + } + else { + QSize size = icon.actualSize(QSize(48, 48)); + notificationIconPixmap = icon.pixmap(size); + } + + QString notificationIcon; + QTemporaryFile notificationIconFile; + if (!notificationIconPixmap.isNull() && notificationIconFile.open()) { + QImageWriter writer(¬ificationIconFile, "PNG"); + if (writer.write(notificationIconPixmap.toImage())) + notificationIcon = QString(" image from location \"file://%1\"").arg(notificationIconFile.fileName()); + } + + QString quotedTitle(title), quotedText(text); + quotedTitle.replace("\\", "\\\\").replace("\"", "\\"); + quotedText.replace("\\", "\\\\").replace("\"", "\\"); + QString growlApp(this->mode == Notificator::Growl13 ? "Growl" : "GrowlHelperApp"); + MacNotificationHandler::instance()->sendAppleScript(script.arg(notificationApp, quotedTitle, quotedText, notificationIcon, growlApp)); +} + +void Notificator::notifyMacUserNotificationCenter(Class cls, const QString &title, const QString &text, const QIcon &icon) { + // icon is not supported by the user notification center yet. OSX will use the app icon. + MacNotificationHandler::instance()->showNotification(title, text); +} + +#endif + +void Notificator::notify(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout) +{ + switch(mode) + { +#ifdef USE_DBUS + case Freedesktop: + notifyDBus(cls, title, text, icon, millisTimeout); + break; +#endif + case QSystemTray: + notifySystray(cls, title, text, icon, millisTimeout); + break; +#ifdef Q_OS_MAC + case UserNotificationCenter: + notifyMacUserNotificationCenter(cls, title, text, icon); + break; + case Growl12: + case Growl13: + notifyGrowl(cls, title, text, icon); + break; +#endif + default: + if(cls == Critical) + { + // Fall back to old fashioned pop-up dialog if critical and no other notification available + QMessageBox::critical(parent, title, text, QMessageBox::Ok, QMessageBox::Ok); + } + break; + } +} diff --git a/src/qt/notificator.h b/src/qt/notificator.h new file mode 100644 index 00000000..61c27e7f --- /dev/null +++ b/src/qt/notificator.h @@ -0,0 +1,80 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_QT_NOTIFICATOR_H +#define BITCOIN_QT_NOTIFICATOR_H + +#if defined(HAVE_CONFIG_H) +#include "config/bitcoin-config.h" +#endif + +#include +#include + +QT_BEGIN_NAMESPACE +class QSystemTrayIcon; + +#ifdef USE_DBUS +class QDBusInterface; +#endif +QT_END_NAMESPACE + +/** Cross-platform desktop notification client. */ +class Notificator: public QObject +{ + Q_OBJECT + +public: + /** Create a new notificator. + @note Ownership of trayIcon is not transferred to this object. + */ + Notificator(const QString &programName, QSystemTrayIcon *trayIcon, QWidget *parent); + ~Notificator(); + + // Message class + enum Class + { + Information, /**< Informational message */ + Warning, /**< Notify user of potential problem */ + Critical /**< An error occurred */ + }; + +public slots: + /** Show notification message. + @param[in] cls general message class + @param[in] title title shown with message + @param[in] text message content + @param[in] icon optional icon to show with message + @param[in] millisTimeout notification timeout in milliseconds (defaults to 10 seconds) + @note Platform implementations are free to ignore any of the provided fields except for \a text. + */ + void notify(Class cls, const QString &title, const QString &text, + const QIcon &icon = QIcon(), int millisTimeout = 10000); + +private: + QWidget *parent; + enum Mode { + None, /**< Ignore informational notifications, and show a modal pop-up dialog for Critical notifications. */ + Freedesktop, /**< Use DBus org.freedesktop.Notifications */ + QSystemTray, /**< Use QSystemTray::showMessage */ + Growl12, /**< Use the Growl 1.2 notification system (Mac only) */ + Growl13, /**< Use the Growl 1.3 notification system (Mac only) */ + UserNotificationCenter /**< Use the 10.8+ User Notification Center (Mac only) */ + }; + QString programName; + Mode mode; + QSystemTrayIcon *trayIcon; +#ifdef USE_DBUS + QDBusInterface *interface; + + void notifyDBus(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout); +#endif + void notifySystray(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout); +#ifdef Q_OS_MAC + void notifyGrowl(Class cls, const QString &title, const QString &text, const QIcon &icon); + void notifyMacUserNotificationCenter(Class cls, const QString &title, const QString &text, const QIcon &icon); +#endif +}; + +#endif // BITCOIN_QT_NOTIFICATOR_H diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp new file mode 100644 index 00000000..713e9279 --- /dev/null +++ b/src/qt/optionsdialog.cpp @@ -0,0 +1,348 @@ +#include "optionsdialog.h" +#include "ui_optionsdialog.h" + +#include "bitcoinunits.h" +#include "monitoreddatamapper.h" +#include "netbase.h" +#include "optionsmodel.h" +#include "dialogwindowflags.h" + +#include +#include +#include +#include +#include +#include +#include + +OptionsDialog::OptionsDialog(QWidget *parent) : + QWidget(parent, DIALOGWINDOWHINTS), + ui(new Ui::OptionsDialog), + model(0), + mapper(0), + fRestartWarningDisplayed_Proxy(false), + fRestartWarningDisplayed_Tor(false), + fRestartWarningDisplayed_Lang(false), + fRestartWarningDisplayed_URL(false), + fProxyIpValid(true), + fTorIpValid(true) +{ + ui->setupUi(this); + + /* Network elements init */ + ui->proxyIp->setEnabled(false); + ui->proxyPort->setEnabled(false); + ui->proxyPort->setValidator(new QIntValidator(1, 65535, this)); + + ui->torIp->setEnabled(false); + ui->torPort->setEnabled(false); + ui->torPort->setValidator(new QIntValidator(1, 65535, this)); + ui->TorOnly->setEnabled(false); + ui->torName->setEnabled(false); + + ui->socksVersion->setEnabled(false); + ui->socksVersion->addItem("5", 5); + ui->socksVersion->addItem("4", 4); + ui->socksVersion->setCurrentIndex(0); + + connect(ui->connectSocks, SIGNAL(toggled(bool)), ui->proxyIp, SLOT(setEnabled(bool))); + connect(ui->connectSocks, SIGNAL(toggled(bool)), ui->proxyPort, SLOT(setEnabled(bool))); + connect(ui->connectSocks, SIGNAL(toggled(bool)), ui->socksVersion, SLOT(setEnabled(bool))); + connect(ui->connectSocks, SIGNAL(clicked(bool)), this, SLOT(showRestartWarning_Proxy())); + + connect(ui->connectTor, SIGNAL(clicked(bool)), this, SLOT(showRestartWarning_Tor())); + connect(ui->connectTor, SIGNAL(toggled(bool)), ui->torIp, SLOT(setEnabled(bool))); + connect(ui->connectTor, SIGNAL(toggled(bool)), ui->torPort, SLOT(setEnabled(bool))); + connect(ui->connectTor, SIGNAL(toggled(bool)), ui->TorOnly, SLOT(setEnabled(bool))); + connect(ui->connectTor, SIGNAL(toggled(bool)), ui->torName, SLOT(setEnabled(bool))); + connect(ui->TorOnly, SIGNAL(toggled(bool)), ui->connectSocks, SLOT(setDisabled(bool))); + + ui->proxyIp->installEventFilter(this); + ui->torIp->installEventFilter(this); + + /* Window elements init */ +#ifdef Q_OS_MAC + ui->tabWindow->setVisible(false); +#endif + + /* Display elements init */ + QDir translations(":translations"); + ui->lang->addItem(QString("(") + tr("default") + QString(")"), QVariant("")); + foreach(const QString &langStr, translations.entryList()) + { + QLocale locale(langStr); + + /** check if the locale name consists of 2 parts (language_country) */ + if(langStr.contains("_")) + { +#if QT_VERSION >= 0x040800 + /** display language strings as "native language - native country (locale name)", e.g. "Deutsch - Deutschland (de)" */ + ui->lang->addItem(locale.nativeLanguageName() + QString(" - ") + locale.nativeCountryName() + QString(" (") + langStr + QString(")"), QVariant(langStr)); +#else + /** display language strings as "language - country (locale name)", e.g. "German - Germany (de)" */ + ui->lang->addItem(QLocale::languageToString(locale.language()) + QString(" - ") + QLocale::countryToString(locale.country()) + QString(" (") + langStr + QString(")"), QVariant(langStr)); +#endif + } + else + { +#if QT_VERSION >= 0x040800 + /** display language strings as "native language (locale name)", e.g. "Deutsch (de)" */ + ui->lang->addItem(locale.nativeLanguageName() + QString(" (") + langStr + QString(")"), QVariant(langStr)); +#else + /** display language strings as "language (locale name)", e.g. "German (de)" */ + ui->lang->addItem(QLocale::languageToString(locale.language()) + QString(" (") + langStr + QString(")"), QVariant(langStr)); +#endif + } + } + +#if QT_VERSION >= 0x040700 + ui->thirdPartyTxUrls->setPlaceholderText("https://example.com/tx/%s"); +#endif + + + ui->unit->setModel(new BitcoinUnits(this)); + + /* Widget-to-option mapper */ + mapper = new MonitoredDataMapper(this); + mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit); + mapper->setOrientation(Qt::Vertical); + + /* enable apply button when data modified */ + connect(mapper, SIGNAL(viewModified()), this, SLOT(enableApplyButton())); + /* disable apply button when new data loaded */ + connect(mapper, SIGNAL(currentIndexChanged(int)), this, SLOT(disableApplyButton())); + /* setup/change UI elements when proxy IP is invalid/valid */ + connect(this, SIGNAL(proxyIpValid(QValidatedLineEdit *, bool)), this, SLOT(handleProxyIpValid(QValidatedLineEdit *, bool))); + /* setup/change UI elements when Tor IP is invalid/valid */ + connect(this, SIGNAL(torIpValid(QValidatedLineEdit *, bool)), this, SLOT(handleTorIpValid(QValidatedLineEdit *, bool))); +} + +OptionsDialog::~OptionsDialog() +{ + delete ui; +} + +void OptionsDialog::setModel(OptionsModel *model) +{ + this->model = model; + + if(model) + { + connect(model, SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); + + mapper->setModel(model); + setMapper(); + mapper->toFirst(); + } + + /* update the display unit, to not use the default ("BTC") */ + updateDisplayUnit(); + + /* warn only when language selection changes by user action (placed here so init via mapper doesn't trigger this) */ + connect(ui->lang, SIGNAL(valueChanged()), this, SLOT(showRestartWarning_Lang())); + connect(ui->thirdPartyTxUrls, SIGNAL(textChanged(const QString &)), this, SLOT(showRestartWarning_URL())); + + /* disable apply button after settings are loaded as there is nothing to save */ + disableApplyButton(); +} + +void OptionsDialog::setMapper() +{ + /* Main */ + mapper->addMapping(ui->transactionFee, OptionsModel::Fee); + mapper->addMapping(ui->bitcoinAtStartup, OptionsModel::StartAtStartup); + mapper->addMapping(ui->detachDatabases, OptionsModel::DetachDatabases); + + /* Network */ + mapper->addMapping(ui->connectSocks, OptionsModel::ProxyUse); + mapper->addMapping(ui->proxyIp, OptionsModel::ProxyIP); + mapper->addMapping(ui->proxyPort, OptionsModel::ProxyPort); + mapper->addMapping(ui->socksVersion, OptionsModel::ProxySocksVersion); + + mapper->addMapping(ui->connectTor, OptionsModel::TorUse); + mapper->addMapping(ui->torIp, OptionsModel::TorIP); + mapper->addMapping(ui->torPort, OptionsModel::TorPort); + mapper->addMapping(ui->TorOnly, OptionsModel::TorOnly); + mapper->addMapping(ui->torName, OptionsModel::TorName); + + + /* Window */ +#ifndef Q_OS_MAC + mapper->addMapping(ui->minimizeToTray, OptionsModel::MinimizeToTray); + mapper->addMapping(ui->minimizeOnClose, OptionsModel::MinimizeOnClose); +#endif + + /* Display */ + mapper->addMapping(ui->lang, OptionsModel::Language); + mapper->addMapping(ui->unit, OptionsModel::DisplayUnit); + mapper->addMapping(ui->displayAddresses, OptionsModel::DisplayAddresses); + mapper->addMapping(ui->coinControlFeatures, OptionsModel::CoinControlFeatures); + mapper->addMapping(ui->thirdPartyTxUrls, OptionsModel::ThirdPartyTxUrls); +} + +void OptionsDialog::enableApplyButton() +{ + ui->applyButton->setEnabled(true); +} + +void OptionsDialog::disableApplyButton() +{ + ui->applyButton->setEnabled(false); +} + +void OptionsDialog::enableSaveButtons() +{ + /* prevent enabling of the save buttons when data modified, if there is an invalid proxy address present */ + if(fProxyIpValid && fTorIpValid) + setSaveButtonState(true); +} + +void OptionsDialog::disableSaveButtons() +{ + setSaveButtonState(false); +} + +void OptionsDialog::setSaveButtonState(bool fState) +{ + ui->applyButton->setEnabled(fState); + ui->okButton->setEnabled(fState); +} + +void OptionsDialog::on_okButton_clicked() +{ + mapper->submit(); +// accept(); + close(); +} + +void OptionsDialog::on_cancelButton_clicked() +{ +// reject(); + close(); +} + +void OptionsDialog::on_applyButton_clicked() +{ + mapper->submit(); + disableApplyButton(); +} + +void OptionsDialog::showRestartWarning_Proxy() +{ + if(!fRestartWarningDisplayed_Proxy) + { + QMessageBox::warning(this, tr("Warning"), tr("This setting will take effect after restarting XP."), QMessageBox::Ok); + fRestartWarningDisplayed_Proxy = true; + } +} + +void OptionsDialog::showRestartWarning_Tor() +{ + if(!fRestartWarningDisplayed_Proxy) + { + QMessageBox::warning(this, tr("Warning"), tr("This setting will take effect after restarting XP."), QMessageBox::Ok); + fRestartWarningDisplayed_Tor = true; + } +} + +void OptionsDialog::showRestartWarning_Lang() +{ + if(!fRestartWarningDisplayed_Lang) + { + QMessageBox::warning(this, tr("Warning"), tr("This setting will take effect after restarting XP."), QMessageBox::Ok); + fRestartWarningDisplayed_Lang = true; + } +} + +void OptionsDialog::showRestartWarning_URL() +{ + if(!fRestartWarningDisplayed_URL) + { + QMessageBox::warning(this, tr("Warning"), tr("This setting will take effect after restarting XP."), QMessageBox::Ok); + fRestartWarningDisplayed_URL = true; + } +} + + +void OptionsDialog::updateDisplayUnit() +{ + if(model) + { + /* Update transactionFee with the current unit */ + ui->transactionFee->setDisplayUnit(model->getDisplayUnit()); + } +} + +void OptionsDialog::handleProxyIpValid(QValidatedLineEdit *object, bool fState) +{ + // this is used in a check before re-enabling the save buttons + fProxyIpValid = fState; + + if(fProxyIpValid) + { + enableSaveButtons(); + ui->statusLabel->clear(); + } + else + { + disableSaveButtons(); + object->setValid(fProxyIpValid); + ui->statusLabel->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel->setText(tr("The supplied proxy address is invalid.")); + } +} + +void OptionsDialog::handleTorIpValid(QValidatedLineEdit *object, bool fState) +{ + // this is used in a check before re-enabling the save buttons + fTorIpValid = fState; + + if(fTorIpValid) + { + enableSaveButtons(); + ui->statusLabel->clear(); + } + else + { + disableSaveButtons(); + object->setValid(fTorIpValid); + ui->statusLabel->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel->setText(tr("The supplied tor address is invalid.")); + } +} + +bool OptionsDialog::eventFilter(QObject *object, QEvent *event) +{ + if(event->type() == QEvent::FocusOut) + { + if(object == ui->proxyIp) + { + CService addr; + /* Check proxyIp for a valid IPv4/IPv6 address and emit the proxyIpValid signal */ + emit proxyIpValid(ui->proxyIp, LookupNumeric(ui->proxyIp->text().toStdString().c_str(), addr)); + } + + if(object == ui->torIp) + { + CService addr; + /* Check proxyIp for a valid IPv4/IPv6 address and emit the torIpValid signal */ + emit torIpValid(ui->torIp, LookupNumeric(ui->torIp->text().toStdString().c_str(), addr)); + } + } + return QWidget::eventFilter(object, event); +} + +void OptionsDialog::keyPressEvent(QKeyEvent *event) +{ +#ifdef ANDROID + if(windowType() != Qt::Widget && event->key() == Qt::Key_Back) + { + close(); + } +#else + if(windowType() != Qt::Widget && event->key() == Qt::Key_Escape) + { + close(); + } +#endif +} \ No newline at end of file diff --git a/src/qt/optionsdialog.h b/src/qt/optionsdialog.h new file mode 100644 index 00000000..cec9331e --- /dev/null +++ b/src/qt/optionsdialog.h @@ -0,0 +1,68 @@ +#ifndef OPTIONSDIALOG_H +#define OPTIONSDIALOG_H + +#include + +namespace Ui { +class OptionsDialog; +} +class OptionsModel; +class MonitoredDataMapper; +class QValidatedLineEdit; + +/** Preferences dialog. */ +class OptionsDialog : public QWidget +{ + Q_OBJECT + +public: + explicit OptionsDialog(QWidget *parent = 0); + ~OptionsDialog(); + + void setModel(OptionsModel *model); + void setMapper(); + +protected: + bool eventFilter(QObject *object, QEvent *event); + void keyPressEvent(QKeyEvent *); + +private slots: + /* enable only apply button */ + void enableApplyButton(); + /* disable only apply button */ + void disableApplyButton(); + /* enable apply button and OK button */ + void enableSaveButtons(); + /* disable apply button and OK button */ + void disableSaveButtons(); + /* set apply button and OK button state (enabled / disabled) */ + void setSaveButtonState(bool fState); + void on_okButton_clicked(); + void on_cancelButton_clicked(); + void on_applyButton_clicked(); + + void showRestartWarning_Proxy(); + void showRestartWarning_Tor(); + void showRestartWarning_Lang(); + void showRestartWarning_URL(); + void updateDisplayUnit(); + void handleProxyIpValid(QValidatedLineEdit *object, bool fState); + void handleTorIpValid(QValidatedLineEdit *object, bool fState); + +signals: + void proxyIpValid(QValidatedLineEdit *object, bool fValid); + void torIpValid(QValidatedLineEdit *object, bool fValid); + +private: + Ui::OptionsDialog *ui; + OptionsModel *model; + MonitoredDataMapper *mapper; + bool fRestartWarningDisplayed_Proxy; + bool fRestartWarningDisplayed_Tor; + bool fRestartWarningDisplayed_Lang; + bool fRestartWarningDisplayed_URL; + bool fProxyIpValid; + bool fTorIpValid; +}; + +#endif // OPTIONSDIALOG_H diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp new file mode 100644 index 00000000..ce1b6ba3 --- /dev/null +++ b/src/qt/optionsmodel.cpp @@ -0,0 +1,345 @@ +#include "optionsmodel.h" +#include "bitcoinunits.h" +#include + +#include "init.h" +#include "walletdb.h" +#include "guiutil.h" + +OptionsModel::OptionsModel(QObject *parent) : + QAbstractListModel(parent) +{ + Init(); +} + +bool static ApplyProxySettings() +{ + QSettings settings; + CService addrProxy(settings.value("addrProxy", "127.0.0.1:9050").toString().toStdString()); + int nSocksVersion(settings.value("nSocksVersion", 5).toInt()); + if (!settings.value("fUseProxy", false).toBool()) { + addrProxy = CService(); + nSocksVersion = 0; + return false; + } + if (nSocksVersion && !addrProxy.IsValid()) + return false; + + if (!IsLimited(NET_IPV4)) + SetProxy(NET_IPV4, addrProxy, nSocksVersion); + if (nSocksVersion > 4) { +#ifdef USE_IPV6 + if (!IsLimited(NET_IPV6)) + SetProxy(NET_IPV6, addrProxy, nSocksVersion); +#endif + } + + SetNameProxy(addrProxy, nSocksVersion); + + return true; +} + +bool static ApplyTorSettings() +{ + QSettings settings; + CService addrTor(settings.value("addrTor", "127.0.0.1:9050").toString().toStdString()); + if (!settings.value("fUseTor", false).toBool()) { + addrTor = CService(); + return false; + } + if (!addrTor.IsValid()) + return false; + + SetProxy(NET_TOR, addrTor, 5); + SetReachable(NET_TOR); + + return true; +} + +void OptionsModel::Init() +{ + QSettings settings; + + // These are Qt-only settings: + nDisplayUnit = settings.value("nDisplayUnit", BitcoinUnits::BTC).toInt(); + bDisplayAddresses = settings.value("bDisplayAddresses", false).toBool(); + if (!settings.contains("strThirdPartyTxUrls")) { + if(fTestNet) + settings.setValue("strThirdPartyTxUrls", ""); + else + settings.setValue("strThirdPartyTxUrls", ""); + } + strThirdPartyTxUrls = settings.value("strThirdPartyTxUrls", "").toString(); + fMinimizeToTray = settings.value("fMinimizeToTray", false).toBool(); + fMinimizeOnClose = settings.value("fMinimizeOnClose", false).toBool(); + fCoinControlFeatures = settings.value("fCoinControlFeatures", false).toBool(); + nTransactionFee = settings.value("nTransactionFee").toLongLong(); + language = settings.value("language", "").toString(); + + // These are shared with core Bitcoin; we want + // command-line options to override the GUI settings: + if ( !(settings.value("fTorOnly").toBool() && settings.contains("addrTor")) ) { + if (settings.contains("addrProxy") && settings.value("fUseProxy").toBool()) + SoftSetArg("-proxy", settings.value("addrProxy").toString().toStdString()); + if (settings.contains("nSocksVersion") && settings.value("fUseProxy").toBool()) + SoftSetArg("-socks", settings.value("nSocksVersion").toString().toStdString()); + } + + if (settings.contains("addrTor") && settings.value("fUseTor").toBool()) { + SoftSetArg("-tor", settings.value("addrTor").toString().toStdString()); + if (settings.value("fTorOnly").toBool()) + SoftSetArg("-onlynet", "tor"); + + if (settings.value("TorName").toString().length() == 22) { + std::string strTorName = settings.value("TorName").toString().toStdString(); + + CService addrTorName(strTorName, GetListenPort()); + if (addrTorName.IsValid()) + SoftSetArg("-torname", strTorName); + } + } + + if (settings.contains("detachDB")) + SoftSetBoolArg("-detachdb", settings.value("detachDB").toBool()); + if (!language.isEmpty()) + SoftSetArg("-lang", language.toStdString()); +} + +int OptionsModel::rowCount(const QModelIndex & parent) const +{ + return OptionIDRowCount; +} + +QVariant OptionsModel::data(const QModelIndex & index, int role) const +{ + if(role == Qt::EditRole) + { + QSettings settings; + switch(index.row()) + { + case StartAtStartup: + return QVariant(GUIUtil::GetStartOnSystemStartup()); + case MinimizeToTray: + return QVariant(fMinimizeToTray); + case MinimizeOnClose: + return QVariant(fMinimizeOnClose); + case ProxyUse: + return settings.value("fUseProxy", false); + case ProxyIP: { + proxyType proxy; + if (GetProxy(NET_IPV4, proxy)) + return QVariant(QString::fromStdString(proxy.first.ToStringIP())); + else + return QVariant(QString::fromStdString("127.0.0.1")); + } + case ProxyPort: { + proxyType proxy; + if (GetProxy(NET_IPV4, proxy)) + return QVariant(proxy.first.GetPort()); + else + return QVariant(nSocksDefault); + } + case ProxySocksVersion: + return settings.value("nSocksVersion", 5); + case TorUse: + return settings.value("fUseTor", false); + case TorIP: { + proxyType proxy; + if (GetProxy(NET_TOR, proxy)) + return QVariant(QString::fromStdString(proxy.first.ToStringIP())); + else + return QVariant(QString::fromStdString("127.0.0.1")); + } + case TorPort: { + proxyType proxy; + if (GetProxy(NET_TOR, proxy)) + return QVariant(proxy.first.GetPort()); + else + return QVariant(nSocksDefault); + } + case TorOnly: + return settings.value("fTorOnly", false); + case TorName: + return settings.value("TorName", ""); + case Fee: + return QVariant(static_cast(nTransactionFee)); + case DisplayUnit: + return QVariant(nDisplayUnit); + case DisplayAddresses: + return QVariant(bDisplayAddresses); + case ThirdPartyTxUrls: + return QVariant(strThirdPartyTxUrls); + case DetachDatabases: + return QVariant(bitdb.GetDetach()); + case Language: + return settings.value("language", ""); + case CoinControlFeatures: + return QVariant(fCoinControlFeatures); + default: + return QVariant(); + } + } + return QVariant(); +} + +bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, int role) +{ + bool successful = true; /* set to false on parse error */ + if(role == Qt::EditRole) + { + QSettings settings; + switch(index.row()) + { + case StartAtStartup: + successful = GUIUtil::SetStartOnSystemStartup(value.toBool()); + break; + case MinimizeToTray: + fMinimizeToTray = value.toBool(); + settings.setValue("fMinimizeToTray", fMinimizeToTray); + break; + case MinimizeOnClose: + fMinimizeOnClose = value.toBool(); + settings.setValue("fMinimizeOnClose", fMinimizeOnClose); + break; + case ProxyUse: + settings.setValue("fUseProxy", value.toBool()); + ApplyProxySettings(); + break; + case ProxyIP: { + proxyType proxy; + proxy.first = CService("127.0.0.1", nSocksDefault); + GetProxy(NET_IPV4, proxy); + + CNetAddr addr(value.toString().toStdString()); + proxy.first.SetIP(addr); + settings.setValue("addrProxy", proxy.first.ToStringIPPort().c_str()); + successful = ApplyProxySettings(); + } + break; + case ProxyPort: { + proxyType proxy; + proxy.first = CService("127.0.0.1", nSocksDefault); + GetProxy(NET_IPV4, proxy); + + proxy.first.SetPort(value.toInt()); + settings.setValue("addrProxy", proxy.first.ToStringIPPort().c_str()); + successful = ApplyProxySettings(); + } + break; + case ProxySocksVersion: { + proxyType proxy; + proxy.second = 5; + GetProxy(NET_IPV4, proxy); + + proxy.second = value.toInt(); + settings.setValue("nSocksVersion", proxy.second); + successful = ApplyProxySettings(); + } + break; + case TorUse: { + settings.setValue("fUseTor", value.toBool()); + ApplyTorSettings(); + } + break; + case TorIP: { + proxyType proxy; + proxy.first = CService("127.0.0.1", nSocksDefault); + GetProxy(NET_TOR, proxy); + + CNetAddr addr(value.toString().toStdString()); + proxy.first.SetIP(addr); + settings.setValue("addrTor", proxy.first.ToStringIPPort().c_str()); + successful = ApplyTorSettings(); + } + break; + case TorPort: { + proxyType proxy; + proxy.first = CService("127.0.0.1", nSocksDefault); + GetProxy(NET_TOR, proxy); + + proxy.first.SetPort((uint16_t)value.toUInt()); + settings.setValue("addrTor", proxy.first.ToStringIPPort().c_str()); + successful = ApplyTorSettings(); + } + break; + case TorOnly: { + settings.setValue("fTorOnly", value.toBool()); + ApplyTorSettings(); + } + case TorName: { + settings.setValue("TorName", value.toString()); + } + break; + case Fee: + nTransactionFee = value.toLongLong(); + settings.setValue("nTransactionFee", static_cast(nTransactionFee)); + emit transactionFeeChanged(nTransactionFee); + break; + case DisplayUnit: + nDisplayUnit = value.toInt(); + settings.setValue("nDisplayUnit", nDisplayUnit); + emit displayUnitChanged(nDisplayUnit); + break; + case DisplayAddresses: + bDisplayAddresses = value.toBool(); + settings.setValue("bDisplayAddresses", bDisplayAddresses); + break; + case DetachDatabases: { + bool fDetachDB = value.toBool(); + bitdb.SetDetach(fDetachDB); + settings.setValue("detachDB", fDetachDB); + } + break; + case ThirdPartyTxUrls: + if (strThirdPartyTxUrls != value.toString()) { + strThirdPartyTxUrls = value.toString(); + settings.setValue("strThirdPartyTxUrls", strThirdPartyTxUrls); + } + break; + case Language: + settings.setValue("language", value); + break; + case CoinControlFeatures: { + fCoinControlFeatures = value.toBool(); + settings.setValue("fCoinControlFeatures", fCoinControlFeatures); + emit coinControlFeaturesChanged(fCoinControlFeatures); + } + break; + default: + break; + } + } + emit dataChanged(index, index); + + return successful; +} + +qint64 OptionsModel::getTransactionFee() +{ + return nTransactionFee; +} + +bool OptionsModel::getCoinControlFeatures() +{ + return fCoinControlFeatures; +} + +bool OptionsModel::getMinimizeToTray() +{ + return fMinimizeToTray; +} + +bool OptionsModel::getMinimizeOnClose() +{ + return fMinimizeOnClose; +} + +int OptionsModel::getDisplayUnit() +{ + return nDisplayUnit; +} + +bool OptionsModel::getDisplayAddresses() +{ + return bDisplayAddresses; +} diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h new file mode 100644 index 00000000..e3d2cae4 --- /dev/null +++ b/src/qt/optionsmodel.h @@ -0,0 +1,73 @@ +#ifndef OPTIONSMODEL_H +#define OPTIONSMODEL_H + +#include + +/** Interface from Qt to configuration data structure for Bitcoin client. + To Qt, the options are presented as a list with the different options + laid out vertically. + This can be changed to a tree once the settings become sufficiently + complex. + */ +class OptionsModel : public QAbstractListModel +{ + Q_OBJECT + +public: + explicit OptionsModel(QObject *parent = 0); + + enum OptionID { + StartAtStartup, // bool + MinimizeToTray, // bool + MinimizeOnClose, // bool + ProxyUse, // bool + ProxyIP, // QString + ProxyPort, // int + ProxySocksVersion, // int + TorUse, // bool + TorIP, // QString + TorPort, // int + TorOnly, // bool + TorName, // QString + Fee, // qint64 + DisplayUnit, // BitcoinUnits::Unit + DisplayAddresses, // bool + ThirdPartyTxUrls, // QString + DetachDatabases, // bool + Language, // QString + CoinControlFeatures, // bool + OptionIDRowCount, + }; + + void Init(); + + int rowCount(const QModelIndex & parent = QModelIndex()) const; + QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; + bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole); + + /* Explicit getters */ + qint64 getTransactionFee(); + bool getMinimizeToTray(); + bool getMinimizeOnClose(); + int getDisplayUnit(); + bool getDisplayAddresses(); + bool getCoinControlFeatures(); + QString getThirdPartyTxUrls() { return strThirdPartyTxUrls; } + QString getLanguage() { return language; } + +private: + int nDisplayUnit; + bool bDisplayAddresses; + bool fMinimizeToTray; + bool fMinimizeOnClose; + bool fCoinControlFeatures; + QString language; + QString strThirdPartyTxUrls; + +signals: + void displayUnitChanged(int unit); + void transactionFeeChanged(qint64); + void coinControlFeaturesChanged(bool); +}; + +#endif // OPTIONSMODEL_H diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp new file mode 100644 index 00000000..5e991f55 --- /dev/null +++ b/src/qt/overviewpage.cpp @@ -0,0 +1,234 @@ +#include "overviewpage.h" +#include "ui_overviewpage.h" + +#include "walletmodel.h" +#include "bitcoinunits.h" +#include "optionsmodel.h" +#include "transactiontablemodel.h" +#include "transactionfilterproxy.h" +#include "guiutil.h" +#include "guiconstants.h" + +#include +#include + +#define DECORATION_SIZE 64 +#define NUM_ITEMS 3 + +class TxViewDelegate : public QAbstractItemDelegate +{ + Q_OBJECT +public: + TxViewDelegate(): QAbstractItemDelegate(), unit(BitcoinUnits::BTC) + { + + } + + inline void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index ) const + { + 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 QT_VERSION < 0x050000 + if(qVariantCanConvert(value)) +#else + if(value.canConvert(QMetaType::QColor)) +#endif + { + foreground = qvariant_cast(value); + } + + painter->setPen(foreground); + painter->drawText(addressRect, Qt::AlignLeft|Qt::AlignVCenter, address); + + if(amount < 0) + { + foreground = COLOR_NEGATIVE; + } + else if(!confirmed) + { + foreground = COLOR_UNCONFIRMED; + } + 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(); + } + + inline QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const + { + return QSize(DECORATION_SIZE, DECORATION_SIZE); + } + + int unit; + +}; +#include "overviewpage.moc" + +OverviewPage::OverviewPage(QWidget *parent) : + QWidget(parent), + ui(new Ui::OverviewPage), + currentBalanceTotal(-1), + currentBalanceWatchOnly(0), + currentStake(0), + currentUnconfirmedBalance(-1), + currentImmatureBalance(-1), + txdelegate(new TxViewDelegate()), + filter(0) +{ + ui->setupUi(this); + + QFont balance = QApplication::font(); + balance.setPointSize(balance.pointSize() * 1.5); + balance.setBold(true); + ui->label_5->setFont(balance); + + // Recent transactions + ui->listTransactions->setItemDelegate(txdelegate); + ui->listTransactions->setIconSize(QSize(DECORATION_SIZE, DECORATION_SIZE)); + ui->listTransactions->setMinimumHeight(NUM_ITEMS * (DECORATION_SIZE + 2)); + ui->listTransactions->setAttribute(Qt::WA_MacShowFocusRect, false); + + connect(ui->listTransactions, SIGNAL(clicked(QModelIndex)), this, SLOT(handleTransactionClicked(QModelIndex))); + + // init "out of sync" warning labels + ui->labelWalletStatus->setText("(" + tr("out of sync") + ")"); + ui->labelTransactionsStatus->setText("(" + tr("out of sync") + ")"); + + // start with displaying the "out of sync" warnings + showOutOfSyncWarning(true); +} + +void OverviewPage::handleTransactionClicked(const QModelIndex &index) +{ + if(filter) + emit transactionClicked(filter->mapToSource(index)); +} + +OverviewPage::~OverviewPage() +{ + delete ui; +} + +void OverviewPage::setBalance(qint64 total, qint64 watchOnly, qint64 stake, qint64 unconfirmedBalance, qint64 immatureBalance) +{ + int unit = model->getOptionsModel()->getDisplayUnit(); + currentBalanceTotal = total; + currentBalanceWatchOnly = watchOnly; + currentStake = stake; + currentUnconfirmedBalance = unconfirmedBalance; + currentImmatureBalance = immatureBalance; + ui->labelAvailable->setText(BitcoinUnits::formatWithUnit(unit, total)); + ui->labelBalanceWatchOnly->setText(BitcoinUnits::formatWithUnit(unit, watchOnly)); + ui->labelStake->setText(BitcoinUnits::formatWithUnit(unit, stake)); + ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, unconfirmedBalance)); + ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, immatureBalance)); + ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, total+stake+unconfirmedBalance+immatureBalance)); + + // only show immature (newly mined) balance if it's non-zero, so as not to complicate things + // for the non-mining users + bool showImmature = immatureBalance != 0; + ui->labelImmature->setVisible(showImmature); + ui->labelImmatureText->setVisible(showImmature); + + // only show watch-only balance if it's non-zero, so as not to complicate things + // for users + bool showWatchOnly = watchOnly != 0; + ui->labelBalanceWatchOnly->setVisible(showWatchOnly); + ui->labelBalanceWatchOnlyText->setVisible(showWatchOnly); + +} + +// show/hide watch-only labels +void OverviewPage::updateWatchOnlyLabels(bool showWatchOnly) +{ + ui->labelBalanceWatchOnly->setVisible(showWatchOnly); + ui->labelBalanceWatchOnlyText->setVisible(showWatchOnly); +} + +void OverviewPage::setNumTransactions(int count) +{ + ui->labelNumTransactions->setText(QLocale::system().toString(count)); +} + +void OverviewPage::setModel(WalletModel *model) +{ + this->model = model; + if(model && model->getOptionsModel()) + { + // Set up transaction list + filter = new TransactionFilterProxy(); + filter->setSourceModel(model->getTransactionTableModel()); + filter->setLimit(NUM_ITEMS); + filter->setDynamicSortFilter(true); +// filter->setSortRole(Qt::EditRole); + filter->setSortRole(TransactionTableModel::DateRole); + 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->getBalanceWatchOnly(), model->getStake(), model->getUnconfirmedBalance(), model->getImmatureBalance()); + connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64, qint64, qint64))); + + setNumTransactions(model->getNumTransactions()); + connect(model, SIGNAL(numTransactionsChanged(int)), this, SLOT(setNumTransactions(int))); + + connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); + + updateWatchOnlyLabels(model->haveWatchOnly()); + connect(model, SIGNAL(notifyWatchonlyChanged(bool)), this, SLOT(updateWatchOnlyLabels(bool))); + } + + // update the display unit, to not use the default ("BTC") + updateDisplayUnit(); +} + +void OverviewPage::updateDisplayUnit() +{ + if(model && model->getOptionsModel()) + { + if(currentBalanceTotal != -1) + setBalance(currentBalanceTotal, currentBalanceWatchOnly, model->getStake(), currentUnconfirmedBalance, currentImmatureBalance); + + // Update txdelegate->unit with the current unit + txdelegate->unit = model->getOptionsModel()->getDisplayUnit(); + + ui->listTransactions->update(); + } +} + +void OverviewPage::showOutOfSyncWarning(bool fShow) +{ + ui->labelWalletStatus->setVisible(fShow); + ui->labelTransactionsStatus->setVisible(fShow); +} diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h new file mode 100644 index 00000000..fe3c19e4 --- /dev/null +++ b/src/qt/overviewpage.h @@ -0,0 +1,54 @@ +#ifndef OVERVIEWPAGE_H +#define OVERVIEWPAGE_H + +#include + +QT_BEGIN_NAMESPACE +class QModelIndex; +QT_END_NAMESPACE + +namespace Ui { + class OverviewPage; +} +class WalletModel; +class TxViewDelegate; +class TransactionFilterProxy; + +/** Overview ("home") page widget */ +class OverviewPage : public QWidget +{ + Q_OBJECT + +public: + explicit OverviewPage(QWidget *parent = 0); + ~OverviewPage(); + + void setModel(WalletModel *model); + void showOutOfSyncWarning(bool fShow); + +public slots: + void setBalance(qint64 total, qint64 watchOnly, qint64 stake, qint64 unconfirmedBalance, qint64 immatureBalance); + void setNumTransactions(int count); + +signals: + void transactionClicked(const QModelIndex &index); + +private: + Ui::OverviewPage *ui; + WalletModel *model; + qint64 currentBalanceTotal; + qint64 currentBalanceWatchOnly; + qint64 currentStake; + qint64 currentUnconfirmedBalance; + qint64 currentImmatureBalance; + + TxViewDelegate *txdelegate; + TransactionFilterProxy *filter; + +private slots: + void updateDisplayUnit(); + void handleTransactionClicked(const QModelIndex &index); + void updateWatchOnlyLabels(bool showWatchOnly); +}; + +#endif // OVERVIEWPAGE_H diff --git a/src/qt/qrcodedialog.cpp b/src/qt/qrcodedialog.cpp new file mode 100644 index 00000000..19a58781 --- /dev/null +++ b/src/qt/qrcodedialog.cpp @@ -0,0 +1,172 @@ +#include "qrcodedialog.h" +#include "ui_qrcodedialog.h" + +#include "bitcoinunits.h" +#include "dialogwindowflags.h" +#include "guiconstants.h" +#include "guiutil.h" +#include "optionsmodel.h" + +#include +#include + +#include + +QRCodeDialog::QRCodeDialog(const QString &addr, const QString &label, bool enableReq, QWidget *parent) : + QDialog(parent, DIALOGWINDOWHINTS), + ui(new Ui::QRCodeDialog), + model(0), + address(addr) +{ + ui->setupUi(this); + + setWindowTitle(QString("%1").arg(address)); + + ui->chkReqPayment->setVisible(enableReq); + ui->lblAmount->setVisible(enableReq); + ui->lnReqAmount->setVisible(enableReq); + + ui->lnLabel->setText(label); + + ui->btnSaveAs->setEnabled(false); + + genCode(); +} + +QRCodeDialog::~QRCodeDialog() +{ + delete ui; +} + +void QRCodeDialog::setModel(OptionsModel *model) +{ + this->model = model; + + if (model) + connect(model, SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); + + // update the display unit, to not use the default ("BTC") + updateDisplayUnit(); +} + +void QRCodeDialog::genCode() +{ + QString uri = getURI(); + + if (uri != "") + { + ui->lblQRCode->setText(""); + + QRcode *code = QRcode_encodeString(uri.toUtf8().constData(), 0, QR_ECLEVEL_L, QR_MODE_8, 1); + if (!code) + { + ui->lblQRCode->setText(tr("Error encoding URI into QR Code.")); + return; + } + myImage = QImage(code->width + 8, code->width + 8, QImage::Format_RGB32); + myImage.fill(0xffffff); + unsigned char *p = code->data; + for (int y = 0; y < code->width; y++) + { + for (int x = 0; x < code->width; x++) + { + myImage.setPixel(x + 4, y + 4, ((*p & 1) ? 0x0 : 0xffffff)); + p++; + } + } + QRcode_free(code); + + ui->lblQRCode->setPixmap(QPixmap::fromImage(myImage).scaled(300, 300)); + + ui->outUri->setPlainText(uri); + } +} + +QString QRCodeDialog::getURI() +{ + QString ret = QString("XP:%1").arg(address); + int paramCount = 0; + + ui->outUri->clear(); + + if (ui->chkReqPayment->isChecked()) + { + if (ui->lnReqAmount->validate()) + { + // even if we allow a non BTC unit input in lnReqAmount, we generate the URI with BTC as unit (as defined in BIP21) + ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnits::BTC, ui->lnReqAmount->value())); + paramCount++; + } + else + { + ui->btnSaveAs->setEnabled(false); + ui->lblQRCode->setText(tr("The entered amount is invalid, please check.")); + return QString(""); + } + } + + if (!ui->lnLabel->text().isEmpty()) + { + QString lbl(QUrl::toPercentEncoding(ui->lnLabel->text())); + ret += QString("%1label=%2").arg(paramCount == 0 ? "?" : "&").arg(lbl); + paramCount++; + } + + if (!ui->lnMessage->text().isEmpty()) + { + QString msg(QUrl::toPercentEncoding(ui->lnMessage->text())); + ret += QString("%1message=%2").arg(paramCount == 0 ? "?" : "&").arg(msg); + paramCount++; + } + + // limit URI length to prevent a DoS against the QR-Code dialog + if (ret.length() > MAX_URI_LENGTH) + { + ui->btnSaveAs->setEnabled(false); + ui->lblQRCode->setText(tr("Resulting URI too long, try to reduce the text for label / message.")); + return QString(""); + } + + ui->btnSaveAs->setEnabled(true); + return ret; +} + +void QRCodeDialog::on_lnReqAmount_textChanged() +{ + genCode(); +} + +void QRCodeDialog::on_lnLabel_textChanged() +{ + genCode(); +} + +void QRCodeDialog::on_lnMessage_textChanged() +{ + genCode(); +} + +void QRCodeDialog::on_btnSaveAs_clicked() +{ + QString fn = GUIUtil::getSaveFileName(this, tr("Save QR Code"), QString(), tr("PNG Images (*.png)")); + if (!fn.isEmpty()) + myImage.scaled(EXPORT_IMAGE_SIZE, EXPORT_IMAGE_SIZE).save(fn); +} + +void QRCodeDialog::on_chkReqPayment_toggled(bool fChecked) +{ + if (!fChecked) + // if chkReqPayment is not active, don't display lnReqAmount as invalid + ui->lnReqAmount->setValid(true); + + genCode(); +} + +void QRCodeDialog::updateDisplayUnit() +{ + if (model) + { + // Update lnReqAmount with the current unit + ui->lnReqAmount->setDisplayUnit(model->getDisplayUnit()); + } +} diff --git a/src/qt/qrcodedialog.h b/src/qt/qrcodedialog.h new file mode 100644 index 00000000..c55c34bc --- /dev/null +++ b/src/qt/qrcodedialog.h @@ -0,0 +1,41 @@ +#ifndef QRCODEDIALOG_H +#define QRCODEDIALOG_H + +#include +#include + +namespace Ui { + class QRCodeDialog; +} +class OptionsModel; + +class QRCodeDialog : public QDialog +{ + Q_OBJECT + +public: + explicit QRCodeDialog(const QString &addr, const QString &label, bool enableReq, QWidget *parent = 0); + ~QRCodeDialog(); + + void setModel(OptionsModel *model); + +private slots: + void on_lnReqAmount_textChanged(); + void on_lnLabel_textChanged(); + void on_lnMessage_textChanged(); + void on_btnSaveAs_clicked(); + void on_chkReqPayment_toggled(bool fChecked); + + void updateDisplayUnit(); + +private: + Ui::QRCodeDialog *ui; + OptionsModel *model; + QString address; + QImage myImage; + + void genCode(); + QString getURI(); +}; + +#endif // QRCODEDIALOG_H diff --git a/src/qt/qtipcserver.cpp b/src/qt/qtipcserver.cpp new file mode 100644 index 00000000..74ddf28b --- /dev/null +++ b/src/qt/qtipcserver.cpp @@ -0,0 +1,164 @@ +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#if defined(WIN32) && BOOST_VERSION <= 104900 +#define BOOST_INTERPROCESS_HAS_WINDOWS_KERNEL_BOOTTIME +#define BOOST_INTERPROCESS_HAS_KERNEL_BOOTTIME +#endif + +#include "qtipcserver.h" +#include "guiconstants.h" +#include "ui_interface.h" +#include "util.h" + +#include +#include +#include +#include + +#ifndef _MSC_VER +#if defined(WIN32) && (!defined(BOOST_INTERPROCESS_HAS_WINDOWS_KERNEL_BOOTTIME) || !defined(BOOST_INTERPROCESS_HAS_KERNEL_BOOTTIME)) && BOOST_VERSION <= 104900 +#warning Compiling without BOOST_INTERPROCESS_HAS_WINDOWS_KERNEL_BOOTTIME and BOOST_INTERPROCESS_HAS_KERNEL_BOOTTIME uncommented in boost/interprocess/detail/tmp_dir_helpers.hpp or using a boost version before 1.49 may have unintended results see svn.boost.org/trac/boost/ticket/5392 +#endif +#endif + +using namespace boost; +using namespace boost::interprocess; +using namespace boost::posix_time; + +#if defined MAC_OSX || defined __FreeBSD__ +// URI handling not implemented on OSX yet + +void ipcScanRelay(int argc, char *argv[]) { } +void ipcInit(int argc, char *argv[]) { } + +#else + +static void ipcThread2(void* pArg); + +static bool ipcScanCmd(int argc, char *argv[], bool fRelay) +{ + // Check for URI in argv + bool fSent = false; + for (int i = 1; i < argc; i++) + { + if (boost::algorithm::istarts_with(argv[i], "XP:")) + { + const char *strURI = argv[i]; + try { + boost::interprocess::message_queue mq(boost::interprocess::open_only, BITCOINURI_QUEUE_NAME); + if (mq.try_send(strURI, strlen(strURI), 0)) + fSent = true; + else if (fRelay) + break; + } + catch (boost::interprocess::interprocess_exception &ex) { + // don't log the "file not found" exception, because that's normal for + // the first start of the first instance + if (ex.get_error_code() != boost::interprocess::not_found_error || !fRelay) + { + printf("main() - boost interprocess exception #%d: %s\n", ex.get_error_code(), ex.what()); + break; + } + } + } + } + return fSent; +} + +void ipcScanRelay(int argc, char *argv[]) +{ + if (ipcScanCmd(argc, argv, true)) + exit(0); +} + +static void ipcThread(void* pArg) +{ + // Make this thread recognisable as the GUI-IPC thread + RenameThread("XP-gui-ipc"); + + try + { + ipcThread2(pArg); + } + catch (std::exception& e) { + PrintExceptionContinue(&e, "ipcThread()"); + } catch (...) { + PrintExceptionContinue(NULL, "ipcThread()"); + } + printf("ipcThread exited\n"); +} + +static void ipcThread2(void* pArg) +{ + printf("ipcThread started\n"); + + message_queue* mq = (message_queue*)pArg; + char buffer[MAX_URI_LENGTH + 1] = ""; + size_t nSize = 0; + unsigned int nPriority = 0; + + while (true) + { + ptime d = boost::posix_time::microsec_clock::universal_time() + millisec(100); + if (mq->timed_receive(&buffer, sizeof(buffer), nSize, nPriority, d)) + { + uiInterface.ThreadSafeHandleURI(std::string(buffer, nSize)); + Sleep(1000); + } + + if (fShutdown) + break; + } + + // Remove message queue + message_queue::remove(BITCOINURI_QUEUE_NAME); + // Cleanup allocated memory + delete mq; +} + +void ipcInit(int argc, char *argv[]) +{ + message_queue* mq = NULL; + char buffer[MAX_URI_LENGTH + 1] = ""; + size_t nSize = 0; + unsigned int nPriority = 0; + + try { + mq = new message_queue(open_or_create, BITCOINURI_QUEUE_NAME, 2, MAX_URI_LENGTH); + + // Make sure we don't lose any bitcoin: URIs + for (int i = 0; i < 2; i++) + { + ptime d = boost::posix_time::microsec_clock::universal_time() + millisec(1); + if (mq->timed_receive(&buffer, sizeof(buffer), nSize, nPriority, d)) + { + uiInterface.ThreadSafeHandleURI(std::string(buffer, nSize)); + } + else + break; + } + + // Make sure only one bitcoin instance is listening + message_queue::remove(BITCOINURI_QUEUE_NAME); + delete mq; + + mq = new message_queue(open_or_create, BITCOINURI_QUEUE_NAME, 2, MAX_URI_LENGTH); + } + catch (interprocess_exception &ex) { + printf("ipcInit() - boost interprocess exception #%d: %s\n", ex.get_error_code(), ex.what()); + return; + } + + if (!NewThread(ipcThread, mq)) + { + delete mq; + return; + } + + ipcScanCmd(argc, argv, false); +} + +#endif diff --git a/src/qt/qtipcserver.h b/src/qt/qtipcserver.h new file mode 100644 index 00000000..a1fde7d8 --- /dev/null +++ b/src/qt/qtipcserver.h @@ -0,0 +1,10 @@ +#ifndef QTIPCSERVER_H +#define QTIPCSERVER_H + +// Define Bitcoin-Qt message queue name +#define BITCOINURI_QUEUE_NAME "XPURI" + +void ipcScanRelay(int argc, char *argv[]); +void ipcInit(int argc, char *argv[]); + +#endif // QTIPCSERVER_H diff --git a/src/qt/qvalidatedlineedit.cpp b/src/qt/qvalidatedlineedit.cpp new file mode 100644 index 00000000..8ca230c9 --- /dev/null +++ b/src/qt/qvalidatedlineedit.cpp @@ -0,0 +1,45 @@ +#include "qvalidatedlineedit.h" + +#include "guiconstants.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(STYLE_INVALID); + } + this->valid = valid; +} + +void QValidatedLineEdit::focusInEvent(QFocusEvent *evt) +{ + // Clear invalid flag on focus + setValid(true); + QLineEdit::focusInEvent(evt); +} + +void QValidatedLineEdit::markValid() +{ + setValid(true); +} + +void QValidatedLineEdit::clear() +{ + setValid(true); + QLineEdit::clear(); +} diff --git a/src/qt/qvalidatedlineedit.h b/src/qt/qvalidatedlineedit.h new file mode 100644 index 00000000..66e26be9 --- /dev/null +++ b/src/qt/qvalidatedlineedit.h @@ -0,0 +1,29 @@ +#ifndef QVALIDATEDLINEEDIT_H +#define QVALIDATEDLINEEDIT_H + +#include + +/** Line edit that can be marked as "invalid" to show input validation feedback. 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); + void clear(); + +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/qvaluecombobox.cpp b/src/qt/qvaluecombobox.cpp new file mode 100644 index 00000000..d7ce3d01 --- /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))); +} + +QVariant QValueComboBox::value() const +{ + return itemData(currentIndex(), role); +} + +void QValueComboBox::setValue(const QVariant &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..1a47bb65 --- /dev/null +++ b/src/qt/qvaluecombobox.h @@ -0,0 +1,33 @@ +#ifndef QVALUECOMBOBOX_H +#define QVALUECOMBOBOX_H + +#include +#include + +/* QComboBox that can be used with QDataWidgetMapper to select ordinal values from a model. */ +class QValueComboBox : public QComboBox +{ + Q_OBJECT + Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged USER true) +public: + explicit QValueComboBox(QWidget *parent = 0); + + QVariant value() const; + void setValue(const QVariant &value); + + /** Specify model role to use as ordinal value (defaults to Qt::UserRole) */ + 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/res/bitcoin-qt.rc b/src/qt/res/bitcoin-qt.rc new file mode 100644 index 00000000..a0064c40 --- /dev/null +++ b/src/qt/res/bitcoin-qt.rc @@ -0,0 +1,37 @@ +IDI_ICON1 ICON DISCARDABLE "icons/bitcoin.ico" + +#include // needed for VERSIONINFO +#include "../../clientversion.h" // holds the needed client version information + +#define VER_PRODUCTVERSION CLIENT_VERSION_MAJOR,CLIENT_VERSION_MINOR,CLIENT_VERSION_REVISION,CLIENT_VERSION_BUILD +#define VER_PRODUCTVERSION_STR STRINGIZE(CLIENT_VERSION_MAJOR) "." STRINGIZE(CLIENT_VERSION_MINOR) "." STRINGIZE(CLIENT_VERSION_REVISION) "." STRINGIZE(CLIENT_VERSION_BUILD) +#define VER_FILEVERSION VER_PRODUCTVERSION +#define VER_FILEVERSION_STR VER_PRODUCTVERSION_STR + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VER_FILEVERSION +PRODUCTVERSION VER_PRODUCTVERSION +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_APP +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" // U.S. English - multilingual (hex) + BEGIN + VALUE "CompanyName", "XP" + VALUE "FileDescription", "XP-Qt (OSS GUI client for XP)" + VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "InternalName", "XP-qt" + VALUE "LegalCopyright", "2009-2012 The Bitcoin developers, 2012-2014 The XP & PPCoin developers" + VALUE "LegalTrademarks1", "Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php." + VALUE "OriginalFilename", "XP-qt.exe" + VALUE "ProductName", "XP-Qt" + VALUE "ProductVersion", VER_PRODUCTVERSION_STR + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1252 // language neutral - multilingual (decimal) + END +END 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 diff --git a/src/qt/res/icons/address-book.png b/src/qt/res/icons/address-book.png new file mode 100644 index 0000000000000000000000000000000000000000..d41dbe6539f64829c004d0f7fc9bf0ba1aa4d7df GIT binary patch literal 1916 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 jxaJ& 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 I4_B@K*@E8Ei~s-t literal 0 HcmV?d00001 diff --git a/src/qt/res/icons/bitcoin.ico b/src/qt/res/icons/bitcoin.ico new file mode 100644 index 0000000000000000000000000000000000000000..d40e169b7efb8c765dec01ad0b2cf3621923a473 GIT binary patch literal 135961 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 O0000pOR literal 0 HcmV?d00001 diff --git a/src/qt/res/icons/bitcoin.png b/src/qt/res/icons/bitcoin.png new file mode 100644 index 0000000000000000000000000000000000000000..a1ae438ea97a9dde189c37a83464572dc20dd47f GIT binary patch literal 35605 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�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 FV1nY$+f)Dm literal 0 HcmV?d00001 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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 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+0000)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/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..c1232f5682289555dc5b221c34fa27e3b6820ff0 GIT binary patch literal 673 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 RSZ9Jjnrb?#mC7j0{{UqMhiCu* literal 0 HcmV?d00001 diff --git a/src/qt/res/icons/dump.png b/src/qt/res/icons/dump.png new file mode 100644 index 0000000000000000000000000000000000000000..cb4151166146ececc79989ad6ad193eeef49afd7 GIT binary patch literal 1058 zcmV+-1l{|IP)Qup;`P2tEj+_+Z6~1tm~hEP{qAh*3fN z=7ZEei4`j%h*k=H5b>#vCAOhy6SHiy+1a%;%ZAi_~8tU3Xb#=I-2c&Ue1= zn{n_TKK>hZfg=bH6rJ_Hv+j-lOd#T>7*Gbm!Rz`<;J;jxL0@6#jv>q~Xq??W-ai8W zl|Wj}HfCQMM`6gr{-br2rziSD;P0aE*XOXN1c&Bg9R0M3@a?P2fqyWNSQ32n>=+)N$YD9uSW1DfR}_x_6d@=JJl}zk zR2vpnDlnWQ;n*<{PKSqp_Hy!90{^w9$WV+=o*BcvqXU=^G`^{51O)KJ!4!c#PY@8S ztqH=aia3S@j$%M{1RcVjFKhhr@xmVo{MSQq@=0{;sj>W>zKi*W#&;EiCc$I-BGo=H z>0xW$Lsjbx%!0Qy$vAIxfR1Bux@z$4dBM_=D+v5Up$z5|Irik3bNjH1vjji6W)L%c z+gvRI1&P6c;bs>k)g*P$VgxA#Bm}o}O)G0{u2UD4;}^OF{yS|c77~2+_$apI9LzU0 zPEm!TV&d>+DkMlqr4b23+SB6NWNRShv&0xA)}qva+p|2BKfeUWf4?cWZZQS@9{0R)dFiy-P))sn3hbt^_&{el+xvrT#EZWnKE z8|DUs6SSp4P*`7e7c?WV^Ik5j_cj_8+h-)PHd!$dNx8PfDrRAPd9G*CTTQv+HiMbF z3b;tKFNFql2dylLRHm#&B+@3E$GvE*HFlo%ehIBpHnNV3xRIju{gp{(?ca2Z!Sp=^ zR3kx&c}UO&&OohIWQ`LCDW6BQAWf0|uTz_73g9S~R9vL1Ekq|5|5T>0)?~|#f;~HY zR3c!7BwBhlS%0anZMt5!6PBc{ebr5@CCf(Jd?bih1Ei-)f759DYO-lcjHq-14WkFi3+48>~c6%#w%|wb__l?5w z*tt!G#nTNeF0~N8dvl+T>%FUz+n})h{t*=X0i5}vhNVRwcC%aic;URrqmzDbf24?d zCBeB9ZPX4=_4&$tuEa}{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/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 diff --git a/src/qt/res/icons/export.png b/src/qt/res/icons/export.png new file mode 100644 index 0000000000000000000000000000000000000000..1df9c2398d3b579d329792fef85b3cfc5a3b8340 GIT binary patch literal 2148 zcmV-q2%GnbP)c$192;$bmO>dVP_?XM?aGR@KQ?JorEZ<7 zX%f@4uBx_n6BLy)=+;sNFz82wOd!F?mgN5>OK#9jevTuNo=e_QTJsC(Q`tG(W7GTA3hbQTVmpN4Gww4 z)mzQtT5GfD?skgMSVVjyEgn%-h=?pid=cl`RI%^%WAVM-5bpXCe+iTt0mCG01Aupw zlVi>xbesoE7ON_JG&BuyyQ~HIESOcz{F&8En^sC%mIYOSqJa<~fT#kCs|<`4h0Gk0b4vUZ8lmkVe)1yM7P%#6_9)kAQS-f03<0h%5{Un2^HWT;WcDh^@d!m>MYV(| zSk8a0q%-~L>bz}f?9l*eG6P5i28a37+ZBe+i(t;+(dk|q{_DQW`IH1}I$Tb`$19`6 z;FxScj_`#1ioF z2*sdw2BenN@$budEZkBtbz=&Sx5lMtHCA@bOMN*FG851V29(5x(E$&~`+LIB-wdVg)dBRLgQ$hNnIp8f zIq*e5l9D}F^of_XBs7_Q@;(xQC+mwic2#RXgbMVRx)^5z4xe}hby$S|MWD<=B zpbU8VXLtK3jM~AN&YD@h9RI?Fq8pXtp(cQLi=+t>Yd8e;k6Ss?QjapffM+WV`wd_s zX&|woPX>S}9KCi84Q~_38Pv@h#M#k_!Hc82~yHq8nzytYE@z8qSzrKVG8!{Kv z!Ul224Coh9V!dW>kuFDi?I6oc$&XN(-pSd^0gR@J+1~9wIjE8WOyL|Ly$VKX?P%661baGGWLQ^|ljmnnRx2Nz@bbcze4K7UnNQ3koFy(@j4VncM4Pt(X48@}l!u=2#gy;x_BM?*xxdn&c zl>@JKi`p?E+Qus|26O?{x&b3Nx-Kh?V_mJQvlp#{wA~=PDKD`A5-7oV3`3|J|f|?2FH71}*ARmH#2?}N;pMop}hB72t{zDuGi6Vp; z*sOoQ8Ke~T8Duq#8UFwMgOTL`0&qnJNdSN#2-Ga6|36rhED|7bcOl0=`Q_o}VI6Wd+pB9(8B07A<3e@GgD6;jMt%3Xi}!bpU$-~=T?Mn*J8FhW8CB>ESUY2b$ZgQf3(&}<4$ zsEpt!fTa-@EDit&AdED?0L=v~u)K!_fKoFo3o=0yBsdX5qXsPYAL2@o17HOtHsb&S z2qO)^asd-C!oGg_!SL(fFJSQf162$RpnBmytXKwSe300`e-I3k`wR3cSS={k1NqFr zqL`JHg#lD(#W#n3o}6;g1={6~ZsObx_Q;0g(xRsQ^E zX7~wAI7}?qLjWLvFdP61LQtGN`~=htOlhCL154E3h};3A|3cFWm=7%lL4^`1de~VJ zYW_j%4PH)22%`gl0K%FGK?No=Q0B$`-wfRE-ZRLE0v+)eQQ09^zlbWBfdLrW4EJ9! zF&w|j&cH9k%phsN$-oFqOMm|0PJ{pfgpmedK?-s`-*<+iXMo23f)+r~90996(E&gJv7qHLuutGY4CMU;`ux{F7KXPUe=__6 zN^r6=GjMRQVa*+^z@P(#o}eHD13w=VymEp?9k!wvAb^f;_k9AEaK9M7f5*h%zWoB{1U_CS25xR9c>4jCNU?#W^@rU8Z4=mvcDhGh_{J;=k=VoMh4NcI0 zp^23dixz+YV!;&!;2IAY1;2qI@bf3o$3G!e;O{>W{?DIGSRDXzARiAC11m5J{(WPF zG*qB1Dr_MD5J1@51OFhY1C(&S{rJW3<^wZ>x7jxaJ%vvUYLb5$-o9rB=XrGS8`xj~ zrvW}-9l#091>Zm=A|t$>z?KF80tnRsOpGj`77ftTz}7H?{_^<;!`pWZ4B83|3|^aL$)Sv_DA>hJ$QzZp2bKLZx6pmLv)U@r$4 z*bG1Zu`~SRkpLzaU=I2VZNjlKFh9Qj;^E~JkIVo6FWrf(2OxkL2fA+v5I_UoHv|Zv j0e1jE01dbU00ImEejxC)$7P#?00000NkvXXu0mjfTPNqK literal 0 HcmV?d00001 diff --git a/src/qt/res/icons/history.png b/src/qt/res/icons/history.png new file mode 100644 index 0000000000000000000000000000000000000000..10ac0e159228d08a8b4425dec4559f19583db936 GIT binary patch literal 1432 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 V&>E0bEnWZs002ovPDHLkV1iUoI3EB2 literal 0 HcmV?d00001 diff --git a/src/qt/res/icons/lock_closed.png b/src/qt/res/icons/lock_closed.png new file mode 100644 index 0000000000000000000000000000000000000000..c566510c408b5ece254fdc528a2326a50f038ba3 GIT binary patch literal 1679 zcmV;A25|X_P)qkbu`{)JGFvyd=g4VR z#$Z`CW{WLbUTF}}QfRFZ1i@{$-n!khOKX6Kx<`|r7D!w1Wjt<2eRDq z*WSFKuWyOvy58pP+jk!%{=NU%0i`7C?(Tef!Ggu@!^6L0|NifPEf&u|9EsQmY4-rk zd5hhnqh|+q?)>KCSS-4#ukVJ~(9rPfBO`yTr{7#$CK^0qTo}9S0D-SsxbXV>YPA4E zL&s#XSbUPs{-7b&;L|YSIPu|1rTplD0|zI3U!lK$=~~;iH*&LH9ZcBG1=y+v2@6^q z>&?Q6jp=k-g3cSc+}NOPTVDYhidl&2GGeGeSt$@1qS5G2qocVG&z>DaXJ?1VWHOty zMo7+SzAQBtFc9oQlDiOg3`B-RB5|W>seC?fYGIHI3raYM=_>lmgIH1Bf%)DMfJceQ zra9^hYDwuhF{IP!TZFJI0uagK+Np1w3y7;pvV@5Rm7rc3Y$DNt>lS?uqo)zG#|%w3tuW}RZOq*(Fve!;a@s|NnW`1yC9{yg{5WgVE)nSJA~ z$2avknG_HNpj=29KyYSh)<2*_qB<`Jv?54?CNR(knDQu9LWnGf=)H-OAp?dT@9*jb zaCrf#Y`i1WmH@KL09gf81(u>wmjPLueuj1d5OjfH=72!slm&PZfB~0wyaF=aNYBZn zn!k#r4zyba3y4rn3kXM9F=QdRDgq<>aQyHXJgKn!h9tziZa@*aN7SrgQJ|KDl1er& zLdF1910<0IhL8{n3BN}T^3->D?$r^zy*u3e1N~LJ`+OORxpOJUZmf140708syJ;JS zA&r13KpERhG!o1R65@{H+po`H3z26`%@98Q5!kwo1ePYkyr2=rK@HYQ8-@;q0=$ck zfWW=!?EEYlsNtI*f>NxD(EC`>Gf=bO8=R ztZ4`CUdaGSM1Bbf$}><2*92M=paMg9DRg}?<|DnL9Z%d2r0W76T?6#=fx-`6(=(v} z<+per&e3kR=88q z_-+Z0prsn%q1Ezq_-4}*V6yJ3rrwJIT@GdqX%MnB2;fAen%6)`Vj0EqqRQoUu%=gB zLSt|aXv=J*6$c1H08R{$0#_uA0AHA{YA%2=CYU3DB)@_JG~i9&wnb9`)k>x47P1{t ze!UTVMS=;9pqxUbG*oLUU_6(9@r$<(y%3AX&5cO?!;;WwJ;+t=d%yXkXU5;0^-A`C Z#$Rq#3pK@&%5(q#002ovPDHLkV1gZn3Dp1q 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..c98ca8663bf94b299530410074a7607c59d9c41c GIT binary patch literal 1644 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 literal 0 HcmV?d00001 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 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 JR;^-<{y(0T(|P~^ literal 0 HcmV?d00001 diff --git a/src/qt/res/icons/qrcode.png b/src/qt/res/icons/qrcode.png new file mode 100644 index 0000000000000000000000000000000000000000..a8d97174b39197ce4f457640f948212edf109451 GIT binary patch literal 237 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0P3?wHke>@jRF%}28J29*~C-V}>VGHmHasB`Q zKad%E=yDxMI?3DJg`tC0)&t1lEbxddW?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 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..53ad1d1565e74c0f4895b7fbf525dbd58bb663d2 GIT binary patch literal 1437 zcmV;O1!DS%P)h#JsQc@Ozh*@G;=JEp}Bos3GVxeZGX-HzRW}%^EYN8p9 zghenj0~bj8M`^8<&hJf0O*1vWXmd_ByUYmkbj-_~6Z*&BJr9TXa^ClO?(_S- z@A+M(wZ{J$2I0Pd|23>z2kenAktGI0V-vU>1i&Exn!vKGX&J3B*mMB3xJ~H&vpNXq zI?`h7z@oh2%innn!GK9Rv?l=CySxDy11!_PvZ}_s`n?w?Az;>a$ARo2G>e99Oeq~m z1PnJrcZ4P|3)S0BqMj=azll!4*j)%<&`N1sOFGC8+Sqko<4RJ45up?koM1s=WFgQu z&j*JqcTVSuW8HP2^L$05n~xhfa*4rG9NbYzCI!NVz>vu{VoYbB4D?I@aYHEu8w{qF zFiC(`4lFHT5|lRFdSK95$a^3_-?+QBbI7y=h+H^=Kx0!t3!4OygwEtW79eiyIYk75 zOCoh>;F5bVC6w+^H!?eUb^*DNYAB?(Zu!wkII@ID-5e+d5|GJ##(v@6d75BLNGaVE z386r9iU}hC1UtB48OH%MXAz()Bfb1D+5qXedC~}>PJ^X%g%|7iT{7x<7&d1=WF1<=$ku;r)I61K4_ zy8wgz=A`86x0lsb?D*QOpKilHKkQwW*J0S$(mQ4Rk^-%Uj^&c&3 zzCDIuZVskAoQpZ*qqr7NyFz$|{7MopKh+m=Cl?_~wI2H68V63rcJ)uDh z_{{n5uGo!@+s`5r4dK->Q7o7ob93KLJ7UJf{wSX|0I`A)4xPD**B9?Z(h*Bx8Jq7- zN8~fAMOyZrRZ;gT|8TVN!uVp8jV-`{-j-XGrNbiV+b4uQf3{)9qTkWdnp#U$zXTe% zHv%-lZ>~wCq;K|;ou6DyJc?H)4Zt%aE$_ItOzdcA!R)2G(cBcTq`WVHrq3M&(^_}x zin9hIaP=GPx#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>3pDMXV*lVvtf2(-?_T5=q)L+C+oIfS}RVMdK^tBQ-WO zji^|W67W$=Ynl>UTWYimVz3mo7`ruFNz^E&m6!O~?sf0Z%sJ=RDSKf9{IQE};~(uw zzB%9d=9f92%=w+1j1m#P%%Oq*Z2)_8puV=YVSs@ExqyP90$T9$2sJmB#NLa@t9T$t zA;1fVpZ!oq7|bNQafDw$fJhx%k%x}9mgI^#7oI-PySAM(jS7a|>_4`4?Sj4prx3a|>G4xkXk!=7Fbg;_j* z3Y-6MDKloBbQvGjpCibW&vgobuaNq0;DVqK00Q6yL>&EYvMWW12ucN>ctbOx3YSlH zoO5hJJ79 zO9!*&<`_`%hyf76<(_(fPGmLCgAGCyz%;xIP#QWM(C`BEWtz*U1!kQD0PENH0PeNp z_yU4PXflCB2ckmeKeO^!r3z^*j^GrzQV4)R5J*$Fbed+tR0Fom?>4^6Q|ouuu<@Fa z0SNTgjVV;jL{_t6)2F=t&x{rYbtm8g3uZI>#MW*9 zV9C|b1KfpH+eQGOOcHCxwU#NDB9hpkm4ZS_L9y^bpQesH^TdRMebS3iF2^iAznSlz z8&j^9d1c35=FfkY5BJtqQ;Js-gkb^1P+Refzqf99p+fG4no0!)L2y~1trBy~k`9_< z7)WzI{#QoFX+?H%mD!U^2N&P?{4N$P`XdMS_Wy*YZ+~VcVCDLo^aK^q;XmYFcVLT_ET<)#;T^0A(eDA{W%+YQL@ z*YU`Q07w&n4?-yP^)s6o+g@SE-#(`Ewr#w=efM7(6E8yO#tSeS00EAESqMV4*@B5! zxo#)RSL|@T2L^slYqE^MzDE5Q1W?jX-|pHoRO4GwyoaYTMgu^A0Xa60^xOM;)0bDT>luSp p_fbuLiIbcle4g?DpVKcL;NROra-e_cBVYgk002ovPDHLkV1h7LxF-Mr literal 0 HcmV?d00001 diff --git a/src/qt/res/icons/synced.png b/src/qt/res/icons/synced.png new file mode 100644 index 0000000000000000000000000000000000000000..4d7e0e88210c542421e80e98b683826e7e8512c0 GIT binary patch literal 781 zcmV+o1M>WdP)_DXvzP7PgXv^ODb>s4cdYyIpAM9Y9}y@HADW zD5GbxT0sR? zpi|?~%Na;V^B#X0NkA9pAf~}9oo=bGsm{0eOl1|^4hJsuzrvlnGo7lzTJXc?nBvQv znvWPqj#bx4s*`VcI{LB6<8?M3ulJ|cc#09@VYEMgiLP5S_n=z`p_)-s(qSoa2-O60 z`EtI@%}3XzWoyOtzH*{vMic8w3T1CnF6PY{bPo@qyK{DsERqIUrnAhFAX^F)!v-au z0Ol(42rD8Ri)3McQNEk_(l>$$h6O8xM{h^a)$uhb=EyEZB|+I-7EKLW+yQMV3ATs} zETv!zWK)}ngtISpm9Qv62z+{no{s3OF)8h)nlUUG$0|fXtc3E_P)%21<4T-N zCH4dY3z#P37!18fZ-*Yy#}W=Oh5-HmWtNJSXk5Mi`Gx(%v$1JB?qqs&$kogkdhr)n zM&4FX!9WR#(j_f@cFa*48+D!nGcO6n1ZM_>WB{utkY5(8xz67KP^}>|X1vnl00000 LNkvXXu0mjf9!^u| literal 0 HcmV?d00001 diff --git a/src/qt/res/icons/transaction0.png b/src/qt/res/icons/transaction0.png new file mode 100644 index 0000000000000000000000000000000000000000..4578666ee4640bb6c1473a52196faf336c2ee637 GIT binary patch literal 569 zcmV-90>=G`P)6TKe5D=lb*hwL) z(Ba*RA{D_+>{eX+XB5OiaFBxFVCfog$RHH8EulkO92{)@%;^v<#=bG+z}cSXckg-b zxk^N^Dy5VLJOK6p0e%C2WXqto0ZJ(a7_nIFWhfMS<@I`xCX>mm-|v5{)oO2m?;>)! z3*gD;^Pf_w)Kh@bXe1twM*%dO&0pnm`D6vTGK$Ex1`dnG;^%ZaeHI7=62NgZ8hu-- zR4&`?wj`6ucfkHXy#>Gq90G3O9-x3n*=+Wr)oO`pnk_(I>+Rd&t>znUjAR22g2A9M zpU)W#1|NXwrs2k3o&GqN%Uv`Y4asCOec8f%*N46tqMY?fPeRr@}l?^HzSbUN+XZ1!dI{nxb%pd1c|zE~_+E|))m z|IA>qNE_+f!{Km3*Y(73IDFsjcF#m)ahDs|-EQ}Dzu*7D>2#jlq}>I$Zy3gDJRX1I za=8+Kc2~fBJRX1R^?H-(bov!oT8VaLpa30s2#m1!$^d@>M~5aO#PL7+00000NkvXX Hu0mjfRpJ7b 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 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 IM6N<$f*YKb@Bjb+ 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..0f5fea3a8456c73a27c2f0f3d01b3bc96aee389d GIT binary patch literal 2152 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 Hu0mjf+T;As literal 0 HcmV?d00001 diff --git a/src/qt/res/images/about.png b/src/qt/res/images/about.png new file mode 100644 index 0000000000000000000000000000000000000000..c9ab9511efb6da23110dea9a1630c8d59336242a GIT binary patch literal 3488 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 literal 0 HcmV?d00001 diff --git a/src/qt/res/movies/update_spinner.mng b/src/qt/res/movies/update_spinner.mng new file mode 100644 index 0000000000000000000000000000000000000000..7df3baac6f9045553ebc88e3eb6aaf9fafa37e29 GIT binary patch literal 27817 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 X_3U)dpy>Z91dJB^2GVJs?Y{p4I{`$4 literal 0 HcmV?d00001 diff --git a/src/qt/res/src/bitcoin.svg b/src/qt/res/src/bitcoin.svg new file mode 100644 index 00000000..96f10178 --- /dev/null +++ b/src/qt/res/src/bitcoin.svg @@ -0,0 +1,115 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/src/clock1.svg b/src/qt/res/src/clock1.svg new file mode 100644 index 00000000..793dc7f9 --- /dev/null +++ b/src/qt/res/src/clock1.svg @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/src/clock2.svg b/src/qt/res/src/clock2.svg new file mode 100644 index 00000000..6a78adf7 --- /dev/null +++ b/src/qt/res/src/clock2.svg @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/src/clock3.svg b/src/qt/res/src/clock3.svg new file mode 100644 index 00000000..09ccc254 --- /dev/null +++ b/src/qt/res/src/clock3.svg @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/src/clock4.svg b/src/qt/res/src/clock4.svg new file mode 100644 index 00000000..7d9dc37a --- /dev/null +++ b/src/qt/res/src/clock4.svg @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/src/clock5.svg b/src/qt/res/src/clock5.svg new file mode 100644 index 00000000..9fd58d9d --- /dev/null +++ b/src/qt/res/src/clock5.svg @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/src/clock_green.svg b/src/qt/res/src/clock_green.svg new file mode 100644 index 00000000..e31f0e79 --- /dev/null +++ b/src/qt/res/src/clock_green.svg @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/src/inout.svg b/src/qt/res/src/inout.svg new file mode 100644 index 00000000..bfab8ef6 --- /dev/null +++ b/src/qt/res/src/inout.svg @@ -0,0 +1,122 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/src/questionmark.svg b/src/qt/res/src/questionmark.svg new file mode 100644 index 00000000..c03c159a --- /dev/null +++ b/src/qt/res/src/questionmark.svg @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + ? + ? + + + ? + + diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp new file mode 100644 index 00000000..e9ec9be7 --- /dev/null +++ b/src/qt/rpcconsole.cpp @@ -0,0 +1,525 @@ +#include "rpcconsole.h" +#include "ui_rpcconsole.h" + +#include "clientmodel.h" +#include "bitcoinrpc.h" +#include "guiutil.h" +#include "dialogwindowflags.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// TODO: make it possible to filter out categories (esp debug messages when implemented) +// TODO: receive errors and debug messages through ClientModel + +const int CONSOLE_HISTORY = 50; + +const QSize ICON_SIZE(24, 24); + +const int INITIAL_TRAFFIC_GRAPH_MINS = 30; + +const struct { + const char *url; + const char *source; +} ICON_MAPPING[] = { + {"cmd-request", ":/icons/tx_input"}, + {"cmd-reply", ":/icons/tx_output"}, + {"cmd-error", ":/icons/tx_output"}, + {"misc", ":/icons/tx_inout"}, + {NULL, NULL} +}; + +/* Object for executing console RPC commands in a separate thread. +*/ +class RPCExecutor: public QObject +{ + Q_OBJECT +public slots: + void start(); + void request(const QString &command); +signals: + void reply(int category, const QString &command); +}; + +#include "rpcconsole.moc" + +void RPCExecutor::start() +{ + // Nothing to do +} + +/** + * Split shell command line into a list of arguments. Aims to emulate \c bash and friends. + * + * - Arguments are delimited with whitespace + * - Extra whitespace at the beginning and end and between arguments will be ignored + * - Text can be "double" or 'single' quoted + * - The backslash \c \ is used as escape character + * - Outside quotes, any character can be escaped + * - Within double quotes, only escape \c " and backslashes before a \c " or another backslash + * - Within single quotes, no escaping is possible and no special interpretation takes place + * + * @param[out] args Parsed arguments will be appended to this list + * @param[in] strCommand Command line to split + */ +bool parseCommandLine(std::vector &args, const std::string &strCommand) +{ + enum CmdParseState + { + STATE_EATING_SPACES, + STATE_ARGUMENT, + STATE_SINGLEQUOTED, + STATE_DOUBLEQUOTED, + STATE_ESCAPE_OUTER, + STATE_ESCAPE_DOUBLEQUOTED + } state = STATE_EATING_SPACES; + std::string curarg; + foreach(char ch, strCommand) + { + switch(state) + { + case STATE_ARGUMENT: // In or after argument + case STATE_EATING_SPACES: // Handle runs of whitespace + switch(ch) + { + case '"': state = STATE_DOUBLEQUOTED; break; + case '\'': state = STATE_SINGLEQUOTED; break; + case '\\': state = STATE_ESCAPE_OUTER; break; + case ' ': case '\n': case '\t': + if(state == STATE_ARGUMENT) // Space ends argument + { + args.push_back(curarg); + curarg.clear(); + } + state = STATE_EATING_SPACES; + break; + default: curarg += ch; state = STATE_ARGUMENT; + } + break; + case STATE_SINGLEQUOTED: // Single-quoted string + switch(ch) + { + case '\'': state = STATE_ARGUMENT; break; + default: curarg += ch; + } + break; + case STATE_DOUBLEQUOTED: // Double-quoted string + switch(ch) + { + case '"': state = STATE_ARGUMENT; break; + case '\\': state = STATE_ESCAPE_DOUBLEQUOTED; break; + default: curarg += ch; + } + break; + case STATE_ESCAPE_OUTER: // '\' outside quotes + curarg += ch; state = STATE_ARGUMENT; + break; + case STATE_ESCAPE_DOUBLEQUOTED: // '\' in double-quoted text + if(ch != '"' && ch != '\\') curarg += '\\'; // keep '\' for everything but the quote and '\' itself + curarg += ch; state = STATE_DOUBLEQUOTED; + break; + } + } + switch(state) // final state + { + case STATE_EATING_SPACES: + return true; + case STATE_ARGUMENT: + args.push_back(curarg); + return true; + default: // ERROR to end in one of the other states + return false; + } +} + +void RPCExecutor::request(const QString &command) +{ + std::vector args; + if(!parseCommandLine(args, command.toStdString())) + { + emit reply(RPCConsole::CMD_ERROR, QString("Parse error: unbalanced ' or \"")); + return; + } + if(args.empty()) + return; // Nothing to do + try + { + std::string strPrint; + // Convert argument list to JSON objects in method-dependent way, + // and pass it along with the method name to the dispatcher. + json_spirit::Value result = tableRPC.execute( + args[0], + RPCConvertValues(args[0], std::vector(args.begin() + 1, args.end()))); + + // Format result reply + if (result.type() == json_spirit::null_type) + strPrint = ""; + else if (result.type() == json_spirit::str_type) + strPrint = result.get_str(); + else + strPrint = write_string(result, true); + + emit reply(RPCConsole::CMD_REPLY, QString::fromStdString(strPrint)); + } + catch (json_spirit::Object& objError) + { + try // Nice formatting for standard-format error + { + int code = find_value(objError, "code").get_int(); + std::string message = find_value(objError, "message").get_str(); + emit reply(RPCConsole::CMD_ERROR, QString::fromStdString(message) + " (code " + QString::number(code) + ")"); + } + catch(std::runtime_error &) // raised when converting to invalid type, i.e. missing code or message + { // Show raw JSON object + emit reply(RPCConsole::CMD_ERROR, QString::fromStdString(write_string(json_spirit::Value(objError), false))); + } + } + catch (std::exception& e) + { + emit reply(RPCConsole::CMD_ERROR, QString("Error: ") + QString::fromStdString(e.what())); + } +} + +RPCConsole::RPCConsole(QWidget *parent) : + QWidget(parent), + ui(new Ui::RPCConsole), + historyPtr(0) +{ + ui->setupUi(this); + +#ifndef Q_OS_MAC + ui->openDebugLogfileButton->setIcon(QIcon(":/icons/export")); + ui->openConfigurationfileButton->setIcon(QIcon(":/icons/export")); + ui->showCLOptionsButton->setIcon(QIcon(":/icons/options")); +#endif + + // Install event filter for up and down arrow + ui->lineEdit->installEventFilter(this); + ui->messagesWidget->installEventFilter(this); + + connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear())); + connect(ui->btnClearTrafficGraph, SIGNAL(clicked()), ui->trafficGraph, SLOT(clear())); + + // set library version labels + ui->openSSLVersion->setText(SSLeay_version(SSLEAY_VERSION)); + ui->berkeleyDBVersion->setText(DbEnv::version(0, 0, 0)); + + startExecutor(); + setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS); + + clear(); +} + +RPCConsole::~RPCConsole() +{ + emit stopExecutor(); + delete ui; +} + +bool RPCConsole::eventFilter(QObject* obj, QEvent *event) +{ + if(event->type() == QEvent::KeyPress) // Special key handling + { + QKeyEvent *keyevt = static_cast(event); + int key = keyevt->key(); + Qt::KeyboardModifiers mod = keyevt->modifiers(); + switch(key) + { + case Qt::Key_Up: if(obj == ui->lineEdit) { browseHistory(-1); return true; } break; + case Qt::Key_Down: if(obj == ui->lineEdit) { browseHistory(1); return true; } break; + case Qt::Key_PageUp: /* pass paging keys to messages widget */ + case Qt::Key_PageDown: + if(obj == ui->lineEdit) + { + QApplication::postEvent(ui->messagesWidget, new QKeyEvent(*keyevt)); + return true; + } + break; + default: + // Typing in messages widget brings focus to line edit, and redirects key there + // Exclude most combinations and keys that emit no text, except paste shortcuts + if(obj == ui->messagesWidget && ( + (!mod && !keyevt->text().isEmpty() && key != Qt::Key_Tab) || + ((mod & Qt::ControlModifier) && key == Qt::Key_V) || + ((mod & Qt::ShiftModifier) && key == Qt::Key_Insert))) + { + ui->lineEdit->setFocus(); + QApplication::postEvent(ui->lineEdit, new QKeyEvent(*keyevt)); + return true; + } + } + } + return QWidget::eventFilter(obj, event); +} + +void RPCConsole::setClientModel(ClientModel *model) +{ + this->clientModel = model; + ui->trafficGraph->setClientModel(model); + if(model) + { + // Subscribe to information, replies, messages, errors + connect(model, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int))); + connect(model, SIGNAL(numBlocksChanged(int,int)), this, SLOT(setNumBlocks(int,int))); + + updateTrafficStats(model->getTotalBytesRecv(), model->getTotalBytesSent()); + connect(model, SIGNAL(bytesChanged(quint64,quint64)), this, SLOT(updateTrafficStats(quint64, quint64))); + // Provide initial values + ui->clientVersion->setText(model->formatFullVersion()); + ui->clientName->setText(model->clientName()); + ui->buildDate->setText(model->formatBuildDate()); + ui->startupTime->setText(model->formatClientStartupTime()); + + setNumConnections(model->getNumConnections()); + ui->isTestNet->setChecked(model->isTestNet()); + + setNumBlocks(model->getNumBlocks(), model->getNumBlocksOfPeers()); + } +} + +static QString categoryClass(int category) +{ + switch(category) + { + case RPCConsole::CMD_REQUEST: return "cmd-request"; break; + case RPCConsole::CMD_REPLY: return "cmd-reply"; break; + case RPCConsole::CMD_ERROR: return "cmd-error"; break; + default: return "misc"; + } +} + +void RPCConsole::clear() +{ + ui->messagesWidget->clear(); + ui->lineEdit->clear(); + ui->lineEdit->setFocus(); + + // Add smoothly scaled icon images. + // (when using width/height on an img, Qt uses nearest instead of linear interpolation) + for(int i=0; ICON_MAPPING[i].url; ++i) + { + ui->messagesWidget->document()->addResource( + QTextDocument::ImageResource, + QUrl(ICON_MAPPING[i].url), + QImage(ICON_MAPPING[i].source).scaled(ICON_SIZE, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + } + + // Set default style sheet + ui->messagesWidget->document()->setDefaultStyleSheet( + "table { }" + "td.time { color: #808080; padding-top: 3px; } " + "td.message { font-family: Monospace; } " + "td.cmd-request { color: #006060; } " + "td.cmd-error { color: red; } " + "b { color: #006060; } " + ); + + message(CMD_REPLY, (tr("Welcome to the XP RPC console.") + "
" + + tr("Use up and down arrows to navigate history, and Ctrl-L to clear screen.") + "
" + + tr("Type help for an overview of available commands.")), true); +} + +void RPCConsole::message(int category, const QString &message, bool html) +{ + QTime time = QTime::currentTime(); + QString timeString = time.toString(); + QString out; + out += ""; + out += ""; + out += "
" + timeString + ""; + if(html) + out += message; + else + out += GUIUtil::HtmlEscape(message, true); + out += "
"; + ui->messagesWidget->append(out); +} + +void RPCConsole::setNumConnections(int count) +{ + if (!clientModel) + return; + + QString connections = QString::number(count) + " ("; + connections += tr("Inbound:") + " " + QString::number(clientModel->getNumConnections(CONNECTIONS_IN)) + " / "; + connections += tr("Outbound:") + " " + QString::number(clientModel->getNumConnections(CONNECTIONS_OUT)) + ")"; + + ui->numberOfConnections->setText(connections); +} + +void RPCConsole::setNumBlocks(int count, int countOfPeers) +{ + ui->numberOfBlocks->setText(QString::number(count)); + ui->totalBlocks->setText(QString::number(countOfPeers)); + if(clientModel) + { + // If there is no current number available display N/A instead of 0, which can't ever be true + ui->totalBlocks->setText(clientModel->getNumBlocksOfPeers() == 0 ? tr("N/A") : QString::number(clientModel->getNumBlocksOfPeers())); + ui->lastBlockTime->setText(clientModel->getLastBlockDate().toString()); + } +} + +void RPCConsole::on_lineEdit_returnPressed() +{ + QString cmd = ui->lineEdit->text(); + ui->lineEdit->clear(); + + if(!cmd.isEmpty()) + { + message(CMD_REQUEST, cmd); + emit cmdRequest(cmd); + // Remove command, if already in history + history.removeOne(cmd); + // Append command to history + history.append(cmd); + // Enforce maximum history size + while(history.size() > CONSOLE_HISTORY) + history.removeFirst(); + // Set pointer to end of history + historyPtr = history.size(); + // Scroll console view to end + scrollToEnd(); + } +} + +void RPCConsole::browseHistory(int offset) +{ + historyPtr += offset; + if(historyPtr < 0) + historyPtr = 0; + if(historyPtr > history.size()) + historyPtr = history.size(); + QString cmd; + if(historyPtr < history.size()) + cmd = history.at(historyPtr); + ui->lineEdit->setText(cmd); +} + +void RPCConsole::startExecutor() +{ + QThread* thread = new QThread; + RPCExecutor *executor = new RPCExecutor(); + executor->moveToThread(thread); + + // Notify executor when thread started (in executor thread) + connect(thread, SIGNAL(started()), executor, SLOT(start())); + // Replies from executor object must go to this object + connect(executor, SIGNAL(reply(int,QString)), this, SLOT(message(int,QString))); + // Requests from this object must go to executor + connect(this, SIGNAL(cmdRequest(QString)), executor, SLOT(request(QString))); + // On stopExecutor signal + // - queue executor for deletion (in execution thread) + // - quit the Qt event loop in the execution thread + connect(this, SIGNAL(stopExecutor()), executor, SLOT(deleteLater())); + connect(this, SIGNAL(stopExecutor()), thread, SLOT(quit())); + // Queue the thread for deletion (in this thread) when it is finished + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); + + // Default implementation of QThread::run() simply spins up an event loop in the thread, + // which is what we want. + thread->start(); +} + +void RPCConsole::on_tabWidget_currentChanged(int index) +{ + if(ui->tabWidget->widget(index) == ui->tab_console) + { + ui->lineEdit->setFocus(); + } +} + +void RPCConsole::on_openDebugLogfileButton_clicked() +{ + GUIUtil::openDebugLogfile(); +} + +void RPCConsole::on_openConfigurationfileButton_clicked() +{ + GUIUtil::openConfigfile(); +} + +void RPCConsole::scrollToEnd() +{ + QScrollBar *scrollbar = ui->messagesWidget->verticalScrollBar(); + scrollbar->setValue(scrollbar->maximum()); +} + +void RPCConsole::on_showCLOptionsButton_clicked() +{ + GUIUtil::HelpMessageBox help; + help.exec(); +} +void RPCConsole::on_sldGraphRange_valueChanged(int value) +{ + const int multiplier = 5; // each position on the slider represents 5 min + int mins = value * multiplier; + setTrafficGraphRange(mins); +} + +QString RPCConsole::FormatBytes(quint64 bytes) +{ + if(bytes < 1024) + return QString(tr("%1 B")).arg(bytes); + if(bytes < 1024 * 1024) + return QString(tr("%1 KB")).arg(bytes / 1024); + if(bytes < 1024 * 1024 * 1024) + return QString(tr("%1 MB")).arg(bytes / 1024 / 1024); + + return QString(tr("%1 GB")).arg(bytes / 1024 / 1024 / 1024); +} + +void RPCConsole::setTrafficGraphRange(int mins) +{ + ui->trafficGraph->setGraphRangeMins(mins); + ui->lblGraphRange->setText(GUIUtil::formatDurationStr(mins * 60)); +} + +void RPCConsole::updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut) +{ + ui->lblBytesIn->setText(FormatBytes(totalBytesIn)); + ui->lblBytesOut->setText(FormatBytes(totalBytesOut)); +} + +void RPCConsole::resizeEvent(QResizeEvent *event) +{ + QWidget::resizeEvent(event); +} + +void RPCConsole::showEvent(QShowEvent *event) +{ + QWidget::showEvent(event); + + if (!clientModel) + return; +} + +void RPCConsole::hideEvent(QHideEvent *event) +{ + QWidget::hideEvent(event); + + if (!clientModel) + return; +} + +void RPCConsole::keyPressEvent(QKeyEvent *event) +{ +#ifdef ANDROID + if(windowType() != Qt::Widget && event->key() == Qt::Key_Back) + { + close(); + } +#else + if(windowType() != Qt::Widget && event->key() == Qt::Key_Escape) + { + close(); + } +#endif +} diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h new file mode 100644 index 00000000..bc2fbd8c --- /dev/null +++ b/src/qt/rpcconsole.h @@ -0,0 +1,87 @@ +#ifndef RPCCONSOLE_H +#define RPCCONSOLE_H + +#include + +namespace Ui { + class RPCConsole; +} +class ClientModel; + +/** Local Bitcoin RPC console. */ +class RPCConsole: public QWidget +{ + Q_OBJECT + +public: + explicit RPCConsole(QWidget *parent = 0); + ~RPCConsole(); + + void setClientModel(ClientModel *model); + + enum MessageClass { + MC_ERROR, + MC_DEBUG, + CMD_REQUEST, + CMD_REPLY, + CMD_ERROR + }; + +protected: + virtual bool eventFilter(QObject* obj, QEvent *event); + void keyPressEvent(QKeyEvent *); + +private slots: + void on_lineEdit_returnPressed(); + void on_tabWidget_currentChanged(int index); + /** open the debug.log from the current datadir */ + void on_openDebugLogfileButton_clicked(); + /** open the XP.conf from the current datadir */ + void on_openConfigurationfileButton_clicked(); + /** change the time range of the network traffic graph */ + void on_sldGraphRange_valueChanged(int value); + /** update traffic statistics */ + void updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut); + void resizeEvent(QResizeEvent *event); + void showEvent(QShowEvent *event); + void hideEvent(QHideEvent *event); + /** display messagebox with program parameters (same as bitcoin-qt --help) */ + void on_showCLOptionsButton_clicked(); + +public slots: + void clear(); + void message(int category, const QString &message, bool html = false); + /** Set number of connections shown in the UI */ + void setNumConnections(int count); + /** Set number of blocks shown in the UI */ + void setNumBlocks(int count, int countOfPeers); + /** Go forward or back in history */ + void browseHistory(int offset); + /** Scroll console view to end */ + void scrollToEnd(); +signals: + // For RPC command executor + void stopExecutor(); + void cmdRequest(const QString &command); + +private: + static QString FormatBytes(quint64 bytes); + void setTrafficGraphRange(int mins); + /** show detailed information on ui about selected node */ + + enum ColumnWidths + { + ADDRESS_COLUMN_WIDTH = 200, + SUBVERSION_COLUMN_WIDTH = 100, + PING_COLUMN_WIDTH = 80 + }; + + Ui::RPCConsole *ui; + ClientModel *clientModel; + QStringList history; + int historyPtr; + + void startExecutor(); +}; + +#endif // RPCCONSOLE_H diff --git a/src/qt/secondauthdialog.cpp b/src/qt/secondauthdialog.cpp new file mode 100644 index 00000000..a3d5a9e0 --- /dev/null +++ b/src/qt/secondauthdialog.cpp @@ -0,0 +1,170 @@ +#include "secondauthdialog.h" +#include "ui_secondauthdialog.h" + +#include "addressbookpage.h" +#include "base58.h" +#include "guiutil.h" +#include "dialogwindowflags.h" +#include "init.h" +#include "main.h" +#include "optionsmodel.h" +#include "walletmodel.h" +#include "wallet.h" + +#include +#include + +#include +#include + +SecondAuthDialog::SecondAuthDialog(QWidget *parent) : + QWidget(parent, DIALOGWINDOWHINTS), + ui(new Ui::SecondAuthDialog), + model(0) +{ + ui->setupUi(this); + +#if (QT_VERSION >= 0x040700) + /* Do not move this to the XML file, Qt before 4.7 will choke on it */ + ui->addressIn->setPlaceholderText(tr("Enter a XP address (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5)")); + ui->signatureOut->setPlaceholderText(tr("Click \"Sign data\" to generate signature")); +#endif + + GUIUtil::setupAddressWidget(ui->addressIn, this); + + ui->addressIn->installEventFilter(this); + ui->messageIn->installEventFilter(this); + ui->signatureOut->installEventFilter(this); + + ui->signatureOut->setFont(GUIUtil::bitcoinAddressFont()); +} + +SecondAuthDialog::~SecondAuthDialog() +{ + delete ui; +} + +void SecondAuthDialog::setModel(WalletModel *model) +{ + this->model = model; +} + +void SecondAuthDialog::on_addressBookButton_clicked() +{ + if (model && model->getAddressTableModel()) + { + AddressBookPage dlg(AddressBookPage::ForSending, AddressBookPage::ReceivingTab, this); + dlg.setModel(model->getAddressTableModel()); + if (dlg.exec()) + { + ui->addressIn->setText(dlg.getReturnValue()); + } + } +} + +void SecondAuthDialog::on_pasteButton_clicked() +{ + ui->messageIn->setText(QApplication::clipboard()->text()); +} + +void SecondAuthDialog::on_signMessageButton_clicked() +{ + /* Clear old signature to ensure users don't get confused on error with an old signature displayed */ + ui->signatureOut->clear(); + + CBitcoinAddress addr(ui->addressIn->text().toStdString()); + if (!addr.IsValid()) + { + ui->addressIn->setValid(false); + ui->statusLabel->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again.")); + return; + } + + CKeyID keyID; + if (!addr.GetKeyID(keyID)) + { + ui->addressIn->setValid(false); + ui->statusLabel->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel->setText(tr("The entered address does not refer to a key.") + QString(" ") + tr("Please check the address and try again.")); + return; + } + + WalletModel::UnlockContext ctx(model->requestUnlock()); + if (!ctx.isValid()) + { + ui->statusLabel->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel->setText(tr("Wallet unlock was cancelled.")); + return; + } + + CKey key; + if (!pwalletMain->GetKey(keyID, key)) + { + ui->statusLabel->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel->setText(tr("Private key for the entered address is not available.")); + return; + } + + uint256 hash; + hash.SetHex(ui->messageIn->text().toStdString()); + CTransaction tx; + uint256 hashBlock = 0; + if (!GetTransaction(hash, tx, hashBlock) || !hashBlock) { + ui->statusLabel->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel->setText(tr("No information available about transaction.")); + return; + } + + CDataStream ss(SER_GETHASH, 0); + ss << strMessageMagic; + ss << ui->messageIn->text().toStdString(); + + std::vector vchSig; + if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig)) + { + ui->statusLabel->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel->setText(QString("
") + tr("Message signing failed.") + QString("")); + return; + } + + ui->statusLabel->setStyleSheet("QLabel { color: green; }"); + ui->statusLabel->setText(QString("") + tr("Message signed.") + QString("")); + + ui->signatureOut->setText(QString::fromStdString(EncodeBase64(&vchSig[0], vchSig.size()))); +} + +void SecondAuthDialog::on_copySignatureButton_clicked() +{ + QApplication::clipboard()->setText(ui->signatureOut->text()); +} + +void SecondAuthDialog::on_clearButton_clicked() +{ + ui->addressIn->clear(); + ui->messageIn->clear(); + ui->signatureOut->clear(); + ui->statusLabel->clear(); + + ui->addressIn->setFocus(); +} + +bool SecondAuthDialog::eventFilter(QObject *object, QEvent *event) +{ + return QWidget::eventFilter(object, event); +} + +void SecondAuthDialog::keyPressEvent(QKeyEvent *event) +{ +#ifdef ANDROID + if(event->key() == Qt::Key_Back) + { + close(); + } +#else + if(event->key() == Qt::Key_Escape) + { + close(); + } +#endif +} diff --git a/src/qt/secondauthdialog.h b/src/qt/secondauthdialog.h new file mode 100644 index 00000000..1fa329cc --- /dev/null +++ b/src/qt/secondauthdialog.h @@ -0,0 +1,42 @@ +#ifndef SECONDAUTHDIALOG_H +#define SECONDAUTHDIALOG_H + +#include + +namespace Ui { + class SecondAuthDialog; +} +class WalletModel; + +QT_BEGIN_NAMESPACE +QT_END_NAMESPACE + +class SecondAuthDialog : public QWidget +{ + Q_OBJECT + +public: + explicit SecondAuthDialog(QWidget *parent = 0); + ~SecondAuthDialog(); + + void setModel(WalletModel *model); + void setAddress(QString address); + +protected: + bool eventFilter(QObject *object, QEvent *event); + void keyPressEvent(QKeyEvent *); + +private: + Ui::SecondAuthDialog *ui; + WalletModel *model; + +private slots: + /* sign */ + void on_addressBookButton_clicked(); + void on_pasteButton_clicked(); + void on_signMessageButton_clicked(); + void on_copySignatureButton_clicked(); + void on_clearButton_clicked(); +}; + +#endif // SECONDAUTHDIALOG_H diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp new file mode 100644 index 00000000..81c633aa --- /dev/null +++ b/src/qt/sendcoinsdialog.cpp @@ -0,0 +1,524 @@ +#include "sendcoinsdialog.h" +#include "ui_sendcoinsdialog.h" + +#include "init.h" +#include "base58.h" +#include "walletmodel.h" +#include "addresstablemodel.h" +#include "addressbookpage.h" + +#include "bitcoinunits.h" +#include "addressbookpage.h" +#include "optionsmodel.h" +#include "sendcoinsentry.h" +#include "guiutil.h" +#include "dialogwindowflags.h" +#include "askpassphrasedialog.h" + +#include "coincontrol.h" +#include "coincontroldialog.h" + +#include +#include +#include +#include +#include + +SendCoinsDialog::SendCoinsDialog(QWidget *parent) : + QDialog(parent, DIALOGWINDOWHINTS), + ui(new Ui::SendCoinsDialog), + model(0), + coinControl(0) +{ + ui->setupUi(this); + +#ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac + ui->addButton->setIcon(QIcon()); + ui->clearButton->setIcon(QIcon()); + ui->sendButton->setIcon(QIcon()); +#endif + +#if QT_VERSION >= 0x040700 + /* Do not move this to the XML file, Qt before 4.7 will choke on it */ + ui->lineEditCoinControlChange->setPlaceholderText(tr("Enter a XP address (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5)")); +#endif + + addEntry(); + + connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addEntry())); + connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear())); + + // Coin Control + ui->lineEditCoinControlChange->setFont(GUIUtil::bitcoinAddressFont()); + connect(ui->pushButtonCoinControl, SIGNAL(clicked()), this, SLOT(coinControlButtonClicked())); + connect(ui->checkBoxCoinControlChange, SIGNAL(stateChanged(int)), this, SLOT(coinControlChangeChecked(int))); + + // Coin Control: clipboard actions + QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this); + QAction *clipboardAmountAction = new QAction(tr("Copy amount"), this); + QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this); + QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this); + QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this); + QAction *clipboardPriorityAction = new QAction(tr("Copy priority"), this); + QAction *clipboardLowOutputAction = new QAction(tr("Copy low output"), this); + QAction *clipboardChangeAction = new QAction(tr("Copy change"), this); + connect(clipboardQuantityAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardQuantity())); + connect(clipboardAmountAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardAmount())); + connect(clipboardFeeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardFee())); + connect(clipboardAfterFeeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardAfterFee())); + connect(clipboardBytesAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardBytes())); + connect(clipboardPriorityAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardPriority())); + connect(clipboardLowOutputAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardLowOutput())); + connect(clipboardChangeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardChange())); + ui->labelCoinControlQuantity->addAction(clipboardQuantityAction); + ui->labelCoinControlAmount->addAction(clipboardAmountAction); + ui->labelCoinControlFee->addAction(clipboardFeeAction); + ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction); + ui->labelCoinControlBytes->addAction(clipboardBytesAction); + ui->labelCoinControlPriority->addAction(clipboardPriorityAction); + ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction); + ui->labelCoinControlChange->addAction(clipboardChangeAction); + + fNewRecipientAllowed = true; + + coinControl = new CoinControlDialog(0); + connect(coinControl, SIGNAL(beforeClose()), this, SLOT(coinControlUpdateLabels())); +} + +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); + } + } + if(model && model->getOptionsModel()) + { + setBalance(model->getBalance(), model->getBalanceWatchOnly(), model->getStake(), model->getUnconfirmedBalance(), model->getImmatureBalance()); + connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64, qint64, qint64))); + connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); + + // Coin Control + connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(coinControlUpdateLabels())); + connect(model->getOptionsModel(), SIGNAL(coinControlFeaturesChanged(bool)), this, SLOT(coinControlFeatureChanged(bool))); + connect(model->getOptionsModel(), SIGNAL(transactionFeeChanged(qint64)), this, SLOT(coinControlUpdateLabels())); + ui->frameCoinControl->setVisible(model->getOptionsModel()->getCoinControlFeatures()); + coinControlUpdateLabels(); + } +} + +SendCoinsDialog::~SendCoinsDialog() +{ + delete coinControl; + delete ui; +} + +void SendCoinsDialog::on_sendButton_clicked() +{ + QList recipients; + bool valid = true; + + if(!model) + return; + + if (ui->lineEditCoinControlChange->isEnabled()) + { + if(!ui->lineEditCoinControlChange->hasAcceptableInput() || + (model && !model->validateAddress(ui->lineEditCoinControlChange->text()))) + { + CoinControlDialog::coinControl->destChange = CNoDestination(); + ui->lineEditCoinControlChange->setValid(false); + valid = false; + } + else + CoinControlDialog::coinControl->destChange = CBitcoinAddress(ui->lineEditCoinControlChange->text().toStdString()).Get(); + } + + 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 || recipients.isEmpty()) + { + return; + } + + // Format confirmation message + QStringList formatted; + foreach(const SendCoinsRecipient &rcp, recipients) + { +#if QT_VERSION < 0x050000 + formatted.append(tr("%1 to %2 (%3)").arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, rcp.amount), Qt::escape(rcp.label), rcp.address)); +#else + formatted.append(tr("%1 to %2 (%3)").arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, rcp.amount), rcp.label.toHtmlEscaped(), rcp.address)); +#endif + } + + fNewRecipientAllowed = false; + + QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"), + tr("Are you sure you want to send %1?").arg(formatted.join(tr(" and "))), + QMessageBox::Yes|QMessageBox::Cancel, + QMessageBox::Cancel); + + if(retval != QMessageBox::Yes) + { + fNewRecipientAllowed = true; + return; + } + + WalletModel::UnlockContext ctx(model->requestUnlock()); + if(!ctx.isValid()) + { + // Unlock wallet was cancelled + fNewRecipientAllowed = true; + return; + } + + WalletModel::SendCoinsReturn sendstatus; + + if (!model->getOptionsModel() || !model->getOptionsModel()->getCoinControlFeatures()) + sendstatus = model->sendCoins(recipients); + else + sendstatus = model->sendCoins(recipients, CoinControlDialog::coinControl); + + switch(sendstatus.status) + { + case WalletModel::InvalidAddress: + QMessageBox::warning(this, tr("Send Coins"), + tr("The recipient address is not valid, please recheck."), + QMessageBox::Ok, QMessageBox::Ok); + break; + case WalletModel::InvalidAmount: + QMessageBox::warning(this, tr("Send Coins"), + tr("The amount to pay must be larger than 0."), + QMessageBox::Ok, QMessageBox::Ok); + break; + case WalletModel::AmountExceedsBalance: + QMessageBox::warning(this, tr("Send Coins"), + tr("The amount exceeds your balance."), + QMessageBox::Ok, QMessageBox::Ok); + break; + case WalletModel::AmountWithFeeExceedsBalance: + QMessageBox::warning(this, tr("Send Coins"), + tr("The total exceeds your balance when the %1 transaction fee is included."). + arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, 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 per 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; + 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); + break; + case WalletModel::Aborted: // User aborted, nothing to do + break; + case WalletModel::OK: + accept(); + CoinControlDialog::coinControl->UnSelectAll(); + coinControlUpdateLabels(); + break; + } + fNewRecipientAllowed = true; +} + +void SendCoinsDialog::clear() +{ + // Remove entries until only one left + while(ui->entries->count()) + { + delete ui->entries->takeAt(0)->widget(); + } + addEntry(); + + updateRemoveEnabled(); + + ui->sendButton->setDefault(true); +} + +void SendCoinsDialog::reject() +{ + clear(); +} + +void SendCoinsDialog::accept() +{ + clear(); +} + +SendCoinsEntry *SendCoinsDialog::addEntry() +{ + SendCoinsEntry *entry = new SendCoinsEntry(this); + entry->setModel(model); + ui->entries->addWidget(entry); + connect(entry, SIGNAL(removeEntry(SendCoinsEntry*)), this, SLOT(removeEntry(SendCoinsEntry*))); + connect(entry, SIGNAL(payAmountChanged()), this, SLOT(coinControlUpdateLabels())); + + updateRemoveEnabled(); + + // Focus the field, so that entry can start immediately + entry->clear(); + entry->setFocus(); + ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint()); + QCoreApplication::instance()->processEvents(); + QScrollBar* bar = ui->scrollArea->verticalScrollBar(); + if(bar) + bar->setSliderPosition(bar->maximum()); + return entry; +} + +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); + coinControlUpdateLabels(); +} + +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; +} + +void SendCoinsDialog::pasteEntry(const SendCoinsRecipient &rv) +{ + if(!fNewRecipientAllowed) + return; + + 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); +} + +bool SendCoinsDialog::handleURI(const QString &uri) +{ + SendCoinsRecipient rv; + // URI has to be valid + if (GUIUtil::parseBitcoinURI(uri, &rv)) + { + CBitcoinAddress address(rv.address.toStdString()); + if (!address.IsValid()) + return false; + pasteEntry(rv); + return true; + } + + return false; +} + +void SendCoinsDialog::setBalance(qint64 total, qint64 watchOnly, qint64 stake, qint64 unconfirmedBalance, qint64 immatureBalance) +{ + Q_UNUSED(stake); + Q_UNUSED(unconfirmedBalance); + Q_UNUSED(immatureBalance); + if(!model || !model->getOptionsModel()) + return; + + int unit = model->getOptionsModel()->getDisplayUnit(); + ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, total - watchOnly)); +} + +void SendCoinsDialog::updateDisplayUnit() +{ + if(model && model->getOptionsModel()) + { + // Update labelBalance with the current balance and the current unit + ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->getBalance() - model->getBalanceWatchOnly())); + } +} + +// Coin Control: copy label "Quantity" to clipboard +void SendCoinsDialog::coinControlClipboardQuantity() +{ + QApplication::clipboard()->setText(ui->labelCoinControlQuantity->text()); +} + +// Coin Control: copy label "Amount" to clipboard +void SendCoinsDialog::coinControlClipboardAmount() +{ + QApplication::clipboard()->setText(ui->labelCoinControlAmount->text().left(ui->labelCoinControlAmount->text().indexOf(" "))); +} + +// Coin Control: copy label "Fee" to clipboard +void SendCoinsDialog::coinControlClipboardFee() +{ + QApplication::clipboard()->setText(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" "))); +} + +// Coin Control: copy label "After fee" to clipboard +void SendCoinsDialog::coinControlClipboardAfterFee() +{ + QApplication::clipboard()->setText(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" "))); +} + +// Coin Control: copy label "Bytes" to clipboard +void SendCoinsDialog::coinControlClipboardBytes() +{ + QApplication::clipboard()->setText(ui->labelCoinControlBytes->text()); +} + +// Coin Control: copy label "Priority" to clipboard +void SendCoinsDialog::coinControlClipboardPriority() +{ + QApplication::clipboard()->setText(ui->labelCoinControlPriority->text()); +} + +// Coin Control: copy label "Low output" to clipboard +void SendCoinsDialog::coinControlClipboardLowOutput() +{ + QApplication::clipboard()->setText(ui->labelCoinControlLowOutput->text()); +} + +// Coin Control: copy label "Change" to clipboard +void SendCoinsDialog::coinControlClipboardChange() +{ + QApplication::clipboard()->setText(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" "))); +} + +// Coin Control: settings menu - coin control enabled/disabled by user +void SendCoinsDialog::coinControlFeatureChanged(bool checked) +{ + ui->frameCoinControl->setVisible(checked); + + if (!checked && model) // coin control features disabled + CoinControlDialog::coinControl->SetNull(); +} + +// Coin Control: button inputs -> show actual coin control dialog +void SendCoinsDialog::coinControlButtonClicked() +{ + coinControl->setModel(model); + coinControl->setWindowModality(Qt::ApplicationModal); + coinControl->show(); +} + +// Coin Control: checkbox custom change address +void SendCoinsDialog::coinControlChangeChecked(int state) +{ + if (model) + { + if (state == Qt::Checked) + CoinControlDialog::coinControl->destChange = CBitcoinAddress(ui->lineEditCoinControlChange->text().toStdString()).Get(); + else + CoinControlDialog::coinControl->destChange = CNoDestination(); + } + + ui->lineEditCoinControlChange->setEnabled((state == Qt::Checked)); +// ui->labelCoinControlChangeLabel->setEnabled((state == Qt::Checked)); + ui->addressBookButton->setEnabled((state == Qt::Checked)); + ui->pasteButton->setEnabled((state == Qt::Checked)); +} + +void SendCoinsDialog::on_pasteButton_clicked() +{ + // Paste text from clipboard into recipient field + ui->lineEditCoinControlChange->setText(QApplication::clipboard()->text()); +} + +void SendCoinsDialog::on_addressBookButton_clicked() +{ + if(!model) + return; + AddressBookPage dlg(AddressBookPage::ForSending, AddressBookPage::ReceivingTab, this); + dlg.setModel(model->getAddressTableModel()); + if(dlg.exec()) + { + ui->lineEditCoinControlChange->setText(dlg.getReturnValue()); + } +} + +// Coin Control: update labels +void SendCoinsDialog::coinControlUpdateLabels() +{ + if (!model || !model->getOptionsModel() || !model->getOptionsModel()->getCoinControlFeatures()) + return; + + // set pay amounts + CoinControlDialog::payAmounts.clear(); + for(int i = 0; i < ui->entries->count(); ++i) + { + SendCoinsEntry *entry = qobject_cast(ui->entries->itemAt(i)->widget()); + if(entry) + CoinControlDialog::payAmounts.append(entry->getValue().amount); + } + + if (CoinControlDialog::coinControl->HasSelected()) + { + // actual coin control calculation + CoinControlDialog::updateLabels(model, this); + + // show coin control stats + ui->labelCoinControlAutomaticallySelected->hide(); + ui->widgetCoinControl->show(); + } + else + { + // hide coin control stats + ui->labelCoinControlAutomaticallySelected->show(); + ui->widgetCoinControl->hide(); + ui->labelCoinControlInsuffFunds->hide(); + } +} diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h new file mode 100644 index 00000000..73e42e4a --- /dev/null +++ b/src/qt/sendcoinsdialog.h @@ -0,0 +1,72 @@ +#ifndef SENDCOINSDIALOG_H +#define SENDCOINSDIALOG_H + +#include +#include + +namespace Ui { + class SendCoinsDialog; +} +class WalletModel; +class SendCoinsEntry; +class SendCoinsRecipient; +class CoinControlDialog; + +QT_BEGIN_NAMESPACE +class QUrl; +QT_END_NAMESPACE + +/** Dialog for sending bitcoins */ +class SendCoinsDialog : public QDialog +{ + Q_OBJECT + +public: + explicit SendCoinsDialog(QWidget *parent = 0); + ~SendCoinsDialog(); + + void setModel(WalletModel *model); + + /** Set up the tab chain manually, as Qt messes up the tab chain by default in some cases (issue https://bugreports.qt-project.org/browse/QTBUG-10907). + */ + QWidget *setupTabChain(QWidget *prev); + + void pasteEntry(const SendCoinsRecipient &rv); + bool handleURI(const QString &uri); + +public slots: + void clear(); + void reject(); + void accept(); + SendCoinsEntry *addEntry(); + void updateRemoveEnabled(); + void setBalance(qint64 total, qint64 watchOnly, qint64 stake, qint64 unconfirmedBalance, qint64 immatureBalance); + + void on_addressBookButton_clicked(); + void on_pasteButton_clicked(); + +private: + Ui::SendCoinsDialog *ui; + WalletModel *model; + bool fNewRecipientAllowed; + CoinControlDialog *coinControl; + +private slots: + void on_sendButton_clicked(); + void removeEntry(SendCoinsEntry* entry); + void updateDisplayUnit(); + void coinControlFeatureChanged(bool); + void coinControlButtonClicked(); + void coinControlChangeChecked(int); + void coinControlUpdateLabels(); + void coinControlClipboardQuantity(); + void coinControlClipboardAmount(); + void coinControlClipboardFee(); + void coinControlClipboardAfterFee(); + void coinControlClipboardBytes(); + void coinControlClipboardPriority(); + void coinControlClipboardLowOutput(); + void coinControlClipboardChange(); +}; + +#endif // SENDCOINSDIALOG_H diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp new file mode 100644 index 00000000..2a32250e --- /dev/null +++ b/src/qt/sendcoinsentry.cpp @@ -0,0 +1,172 @@ +#include "sendcoinsentry.h" +#include "ui_sendcoinsentry.h" +#include "guiutil.h" +#include "bitcoinunits.h" +#include "addressbookpage.h" +#include "walletmodel.h" +#include "optionsmodel.h" +#include "addresstablemodel.h" + +#include +#include + +SendCoinsEntry::SendCoinsEntry(QWidget *parent) : + QFrame(parent), + ui(new Ui::SendCoinsEntry), + model(0) +{ + ui->setupUi(this); + +#ifdef Q_OS_MAC + ui->payToLayout->setSpacing(4); +#endif +#if QT_VERSION >= 0x040700 + /* Do not move this to the XML file, Qt before 4.7 will choke on it */ + ui->addAsLabel->setPlaceholderText(tr("Enter a label for this address to add it to your address book")); + ui->payTo->setPlaceholderText(tr("Enter a XP address (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5)")); +#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() +{ + if(!model) + return; + 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) +{ + if(!model) + return; + // Fill in label from address book + ui->addAsLabel->setText(model->getAddressTableModel()->labelForAddress(address)); +} + +void SendCoinsEntry::setModel(WalletModel *model) +{ + this->model = model; + + if(model && model->getOptionsModel()) + connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); + + connect(ui->payAmount, SIGNAL(textChanged()), this, SIGNAL(payAmountChanged())); + + clear(); +} + +void SendCoinsEntry::setRemoveEnabled(bool enabled) +{ + ui->deleteButton->setEnabled(enabled); +} + +void SendCoinsEntry::clear() +{ + ui->payTo->clear(); + ui->addAsLabel->clear(); + ui->payAmount->clear(); + ui->payTo->setFocus(); + // update the display unit, to not use the default ("BTC") + updateDisplayUnit(); +} + +void SendCoinsEntry::on_deleteButton_clicked() +{ + emit removeEntry(this); +} + +bool SendCoinsEntry::validate() +{ + // Check input validity + bool retval = true; + + if(!ui->payAmount->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()))) + { + ui->payTo->setValid(false); + retval = false; + } + + return retval; +} + +SendCoinsRecipient SendCoinsEntry::getValue() +{ + SendCoinsRecipient rv; + + rv.address = ui->payTo->text(); + rv.label = ui->addAsLabel->text(); + rv.amount = ui->payAmount->value(); + + 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); +} + +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(); +} + +void SendCoinsEntry::setFocus() +{ + ui->payTo->setFocus(); +} + +void SendCoinsEntry::updateDisplayUnit() +{ + if(model && model->getOptionsModel()) + { + // Update payAmount with the current unit + ui->payAmount->setDisplayUnit(model->getOptionsModel()->getDisplayUnit()); + } +} diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h new file mode 100644 index 00000000..3271e0d8 --- /dev/null +++ b/src/qt/sendcoinsentry.h @@ -0,0 +1,56 @@ +#ifndef SENDCOINSENTRY_H +#define SENDCOINSENTRY_H + +#include + +namespace Ui { + class SendCoinsEntry; +} +class WalletModel; +class SendCoinsRecipient; + +/** A single entry in the dialog for sending bitcoins. */ +class SendCoinsEntry : public QFrame +{ + Q_OBJECT + +public: + explicit SendCoinsEntry(QWidget *parent = 0); + ~SendCoinsEntry(); + + void setModel(WalletModel *model); + bool validate(); + SendCoinsRecipient getValue(); + + /** Return whether the entry is still empty and unedited */ + bool isClear(); + + void setValue(const SendCoinsRecipient &value); + + /** Set up the tab chain manually, as Qt messes up the tab chain by default in some cases (issue https://bugreports.qt-project.org/browse/QTBUG-10907). + */ + QWidget *setupTabChain(QWidget *prev); + + void setFocus(); + +public slots: + void setRemoveEnabled(bool enabled); + void clear(); + +signals: + void removeEntry(SendCoinsEntry *entry); + void payAmountChanged(); + +private slots: + void on_deleteButton_clicked(); + void on_payTo_textChanged(const QString &address); + void on_addressBookButton_clicked(); + void on_pasteButton_clicked(); + void updateDisplayUnit(); + +private: + Ui::SendCoinsEntry *ui; + WalletModel *model; +}; + +#endif // SENDCOINSENTRY_H diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp new file mode 100644 index 00000000..041df2fc --- /dev/null +++ b/src/qt/signverifymessagedialog.cpp @@ -0,0 +1,291 @@ +#include "signverifymessagedialog.h" +#include "ui_signverifymessagedialog.h" + +#include "addressbookpage.h" +#include "base58.h" +#include "guiutil.h" +#include "dialogwindowflags.h" +#include "init.h" +#include "main.h" +#include "optionsmodel.h" +#include "walletmodel.h" +#include "wallet.h" + +#include +#include + +#include +#include + +SignVerifyMessageDialog::SignVerifyMessageDialog(QWidget *parent) : + QWidget(parent, DIALOGWINDOWHINTS), + ui(new Ui::SignVerifyMessageDialog), + model(0) +{ + ui->setupUi(this); + +#if (QT_VERSION >= 0x040700) + /* Do not move this to the XML file, Qt before 4.7 will choke on it */ + ui->addressIn_SM->setPlaceholderText(tr("Enter a XP address (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5)")); + ui->signatureOut_SM->setPlaceholderText(tr("Click \"Sign Message\" to generate signature")); + + ui->addressIn_VM->setPlaceholderText(tr("Enter a XP address (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5)")); + ui->signatureIn_VM->setPlaceholderText(tr("Enter XP signature")); +#endif + + GUIUtil::setupAddressWidget(ui->addressIn_SM, this); + GUIUtil::setupAddressWidget(ui->addressIn_VM, this); + + ui->addressIn_SM->installEventFilter(this); + ui->messageIn_SM->installEventFilter(this); + ui->signatureOut_SM->installEventFilter(this); + ui->addressIn_VM->installEventFilter(this); + ui->messageIn_VM->installEventFilter(this); + ui->signatureIn_VM->installEventFilter(this); + + ui->signatureOut_SM->setFont(GUIUtil::bitcoinAddressFont()); + ui->signatureIn_VM->setFont(GUIUtil::bitcoinAddressFont()); +} + +SignVerifyMessageDialog::~SignVerifyMessageDialog() +{ + delete ui; +} + +void SignVerifyMessageDialog::setModel(WalletModel *model) +{ + this->model = model; +} + +void SignVerifyMessageDialog::setAddress_SM(QString address) +{ + ui->addressIn_SM->setText(address); + ui->messageIn_SM->setFocus(); +} + +void SignVerifyMessageDialog::setAddress_VM(QString address) +{ + ui->addressIn_VM->setText(address); + ui->messageIn_VM->setFocus(); +} + +void SignVerifyMessageDialog::showTab_SM(bool fShow) +{ + ui->tabWidget->setCurrentIndex(0); + + if (fShow) + this->show(); +} + +void SignVerifyMessageDialog::showTab_VM(bool fShow) +{ + ui->tabWidget->setCurrentIndex(1); + if (fShow) + this->show(); +} + +void SignVerifyMessageDialog::on_addressBookButton_SM_clicked() +{ + if (model && model->getAddressTableModel()) + { + AddressBookPage dlg(AddressBookPage::ForSending, AddressBookPage::ReceivingTab, this); + dlg.setModel(model->getAddressTableModel()); + if (dlg.exec()) + { + setAddress_SM(dlg.getReturnValue()); + } + } +} + +void SignVerifyMessageDialog::on_pasteButton_SM_clicked() +{ + setAddress_SM(QApplication::clipboard()->text()); +} + +void SignVerifyMessageDialog::on_signMessageButton_SM_clicked() +{ + /* Clear old signature to ensure users don't get confused on error with an old signature displayed */ + ui->signatureOut_SM->clear(); + + CBitcoinAddress addr(ui->addressIn_SM->text().toStdString()); + if (!addr.IsValid()) + { + ui->addressIn_SM->setValid(false); + ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel_SM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again.")); + return; + } + CKeyID keyID; + if (!addr.GetKeyID(keyID)) + { + ui->addressIn_SM->setValid(false); + ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel_SM->setText(tr("The entered address does not refer to a key.") + QString(" ") + tr("Please check the address and try again.")); + return; + } + + WalletModel::UnlockContext ctx(model->requestUnlock()); + if (!ctx.isValid()) + { + ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel_SM->setText(tr("Wallet unlock was cancelled.")); + return; + } + + CKey key; + if (!pwalletMain->GetKey(keyID, key)) + { + ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel_SM->setText(tr("Private key for the entered address is not available.")); + return; + } + + CDataStream ss(SER_GETHASH, 0); + ss << strMessageMagic; + ss << ui->messageIn_SM->document()->toPlainText().toStdString(); + + std::vector vchSig; + if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig)) + { + ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel_SM->setText(QString("") + tr("Message signing failed.") + QString("")); + return; + } + + ui->statusLabel_SM->setStyleSheet("QLabel { color: green; }"); + ui->statusLabel_SM->setText(QString("") + tr("Message signed.") + QString("")); + + ui->signatureOut_SM->setText(QString::fromStdString(EncodeBase64(&vchSig[0], vchSig.size()))); +} + +void SignVerifyMessageDialog::on_copySignatureButton_SM_clicked() +{ + QApplication::clipboard()->setText(ui->signatureOut_SM->text()); +} + +void SignVerifyMessageDialog::on_clearButton_SM_clicked() +{ + ui->addressIn_SM->clear(); + ui->messageIn_SM->clear(); + ui->signatureOut_SM->clear(); + ui->statusLabel_SM->clear(); + + ui->addressIn_SM->setFocus(); +} + +void SignVerifyMessageDialog::on_addressBookButton_VM_clicked() +{ + if (model && model->getAddressTableModel()) + { + AddressBookPage dlg(AddressBookPage::ForSending, AddressBookPage::SendingTab, this); + dlg.setModel(model->getAddressTableModel()); + if (dlg.exec()) + { + setAddress_VM(dlg.getReturnValue()); + } + } +} + +void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked() +{ + CBitcoinAddress addr(ui->addressIn_VM->text().toStdString()); + if (!addr.IsValid()) + { + ui->addressIn_VM->setValid(false); + ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel_VM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again.")); + return; + } + CKeyID keyID; + if (!addr.GetKeyID(keyID)) + { + ui->addressIn_VM->setValid(false); + ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel_VM->setText(tr("The entered address does not refer to a key.") + QString(" ") + tr("Please check the address and try again.")); + return; + } + + bool fInvalid = false; + std::vector vchSig = DecodeBase64(ui->signatureIn_VM->text().toStdString().c_str(), &fInvalid); + + if (fInvalid) + { + ui->signatureIn_VM->setValid(false); + ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel_VM->setText(tr("The signature could not be decoded.") + QString(" ") + tr("Please check the signature and try again.")); + return; + } + + CDataStream ss(SER_GETHASH, 0); + ss << strMessageMagic; + ss << ui->messageIn_VM->document()->toPlainText().toStdString(); + + CKey key; + if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig)) + { + ui->signatureIn_VM->setValid(false); + ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel_VM->setText(tr("The signature did not match the message digest.") + QString(" ") + tr("Please check the signature and try again.")); + return; + } + + if (!(CBitcoinAddress(key.GetPubKey().GetID()) == addr)) + { + ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel_VM->setText(QString("") + tr("Message verification failed.") + QString("")); + return; + } + + ui->statusLabel_VM->setStyleSheet("QLabel { color: green; }"); + ui->statusLabel_VM->setText(QString("") + tr("Message verified.") + QString("")); +} + +void SignVerifyMessageDialog::on_clearButton_VM_clicked() +{ + ui->addressIn_VM->clear(); + ui->signatureIn_VM->clear(); + ui->messageIn_VM->clear(); + ui->statusLabel_VM->clear(); + + ui->addressIn_VM->setFocus(); +} + +bool SignVerifyMessageDialog::eventFilter(QObject *object, QEvent *event) +{ + if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::FocusIn) + { + if (ui->tabWidget->currentIndex() == 0) + { + /* Clear status message on focus change */ + ui->statusLabel_SM->clear(); + + /* Select generated signature */ + if (object == ui->signatureOut_SM) + { + ui->signatureOut_SM->selectAll(); + return true; + } + } + else if (ui->tabWidget->currentIndex() == 1) + { + /* Clear status message on focus change */ + ui->statusLabel_VM->clear(); + } + } + return QWidget::eventFilter(object, event); +} + +void SignVerifyMessageDialog::keyPressEvent(QKeyEvent *event) +{ +#ifdef ANDROID + if(event->key() == Qt::Key_Back) + { + close(); + } +#else + if(event->key() == Qt::Key_Escape) + { + close(); + } +#endif +} \ No newline at end of file diff --git a/src/qt/signverifymessagedialog.h b/src/qt/signverifymessagedialog.h new file mode 100644 index 00000000..e177d000 --- /dev/null +++ b/src/qt/signverifymessagedialog.h @@ -0,0 +1,50 @@ +#ifndef SIGNVERIFYMESSAGEDIALOG_H +#define SIGNVERIFYMESSAGEDIALOG_H + +#include + +namespace Ui { + class SignVerifyMessageDialog; +} +class WalletModel; + +QT_BEGIN_NAMESPACE +QT_END_NAMESPACE + +class SignVerifyMessageDialog : public QWidget +{ + Q_OBJECT + +public: + explicit SignVerifyMessageDialog(QWidget *parent = 0); + ~SignVerifyMessageDialog(); + + void setModel(WalletModel *model); + void setAddress_SM(QString address); + void setAddress_VM(QString address); + + void showTab_SM(bool fShow); + void showTab_VM(bool fShow); + +protected: + bool eventFilter(QObject *object, QEvent *event); + void keyPressEvent(QKeyEvent *); + +private: + Ui::SignVerifyMessageDialog *ui; + WalletModel *model; + +private slots: + /* sign message */ + void on_addressBookButton_SM_clicked(); + void on_pasteButton_SM_clicked(); + void on_signMessageButton_SM_clicked(); + void on_copySignatureButton_SM_clicked(); + void on_clearButton_SM_clicked(); + /* verify message */ + void on_addressBookButton_VM_clicked(); + void on_verifyMessageButton_VM_clicked(); + void on_clearButton_VM_clicked(); +}; + +#endif // SIGNVERIFYMESSAGEDIALOG_H diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp new file mode 100644 index 00000000..3c7794b3 --- /dev/null +++ b/src/qt/test/test_main.cpp @@ -0,0 +1,16 @@ +#include +#include + +#include "uritests.h" + +// This is all you need to run all the tests +int main(int argc, char *argv[]) +{ + bool fInvalid = false; + + URITests test1; + if (QTest::qExec(&test1) != 0) + fInvalid = true; + + return fInvalid; +} diff --git a/src/qt/test/uritests.cpp b/src/qt/test/uritests.cpp new file mode 100644 index 00000000..4662f5ed --- /dev/null +++ b/src/qt/test/uritests.cpp @@ -0,0 +1,71 @@ +#include "uritests.h" +#include "../guiutil.h" +#include "../walletmodel.h" + +#include + +/* +struct SendCoinsRecipient +{ + QString address; + QString label; + qint64 amount; +}; +*/ + +void URITests::uriTests() +{ + SendCoinsRecipient rv; + QUrl uri; + uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?req-dontexist=")); + QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv)); + + uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?dontexist=")); + QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(rv.address == QString("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W")); + QVERIFY(rv.label == QString()); + QVERIFY(rv.amount == 0); + + uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?label=Wikipedia Example Address")); + QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(rv.address == QString("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W")); + QVERIFY(rv.label == QString("Wikipedia Example Address")); + QVERIFY(rv.amount == 0); + + uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=0.001")); + QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(rv.address == QString("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W")); + QVERIFY(rv.label == QString()); + QVERIFY(rv.amount == 100000); + + uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=1.001")); + QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(rv.address == QString("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W")); + QVERIFY(rv.label == QString()); + QVERIFY(rv.amount == 100100000); + + uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=100&label=Wikipedia Example")); + QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(rv.address == QString("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W")); + QVERIFY(rv.amount == 10000000000LL); + QVERIFY(rv.label == QString("Wikipedia Example")); + + uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?message=Wikipedia Example Address")); + QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(rv.address == QString("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W")); + QVERIFY(rv.label == QString()); + + QVERIFY(GUIUtil::parseBitcoinURI("bitcoin://175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?message=Wikipedia Example Address", &rv)); + QVERIFY(rv.address == QString("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W")); + QVERIFY(rv.label == QString()); + + // We currently don't implement the message parameter (ok, yea, we break spec...) + uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?req-message=Wikipedia Example Address")); + QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv)); + + uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=1,000&label=Wikipedia Example")); + QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv)); + + uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=1,000.0&label=Wikipedia Example")); + QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv)); +} diff --git a/src/qt/test/uritests.h b/src/qt/test/uritests.h new file mode 100644 index 00000000..1237516e --- /dev/null +++ b/src/qt/test/uritests.h @@ -0,0 +1,15 @@ +#ifndef URITESTS_H +#define URITESTS_H + +#include +#include + +class URITests : public QObject +{ + Q_OBJECT + +private slots: + void uriTests(); +}; + +#endif // URITESTS_H diff --git a/src/qt/trafficgraphwidget.cpp b/src/qt/trafficgraphwidget.cpp new file mode 100644 index 00000000..5f14b807 --- /dev/null +++ b/src/qt/trafficgraphwidget.cpp @@ -0,0 +1,175 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "trafficgraphwidget.h" +#include "clientmodel.h" + +#include +#include +#include + +#include + +#define DESIRED_SAMPLES 800 + +#define XMARGIN 10 +#define YMARGIN 10 + +TrafficGraphWidget::TrafficGraphWidget(QWidget *parent) : + QWidget(parent), + timer(0), + fMax(0.0f), + nMins(0), + vSamplesIn(), + vSamplesOut(), + nLastBytesIn(0), + nLastBytesOut(0), + clientModel(0) +{ + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), SLOT(updateRates())); +} + +void TrafficGraphWidget::setClientModel(ClientModel *model) +{ + clientModel = model; + if(model) { + nLastBytesIn = model->getTotalBytesRecv(); + nLastBytesOut = model->getTotalBytesSent(); + } +} + +int TrafficGraphWidget::getGraphRangeMins() const +{ + return nMins; +} + +void TrafficGraphWidget::paintPath(QPainterPath &path, QQueue &samples) +{ + int h = height() - YMARGIN * 2, w = width() - XMARGIN * 2; + int sampleCount = samples.size(), x = XMARGIN + w, y; + if(sampleCount > 0) { + path.moveTo(x, YMARGIN + h); + for(int i = 0; i < sampleCount; ++i) { + x = XMARGIN + w - w * i / DESIRED_SAMPLES; + y = YMARGIN + h - (int)(h * samples.at(i) / fMax); + path.lineTo(x, y); + } + path.lineTo(x, YMARGIN + h); + } +} + +void TrafficGraphWidget::paintEvent(QPaintEvent *) +{ + QPainter painter(this); + painter.fillRect(rect(), Qt::black); + + if(fMax <= 0.0f) return; + + QColor axisCol(Qt::gray); + int h = height() - YMARGIN * 2; + painter.setPen(axisCol); + painter.drawLine(XMARGIN, YMARGIN + h, width() - XMARGIN, YMARGIN + h); + + // decide what order of magnitude we are + int base = floor(log10(fMax)); + float val = pow(10.0f, base); + + const QString units = tr("KB/s"); + const float yMarginText = 2.0; + + // draw lines + painter.setPen(axisCol); + painter.drawText(XMARGIN, YMARGIN + h - h * val / fMax-yMarginText, QString("%1 %2").arg(val).arg(units)); + for(float y = val; y < fMax; y += val) { + int yy = YMARGIN + h - h * y / fMax; + painter.drawLine(XMARGIN, yy, width() - XMARGIN, yy); + } + // if we drew 3 or fewer lines, break them up at the next lower order of magnitude + if(fMax / val <= 3.0f) { + axisCol = axisCol.darker(); + val = pow(10.0f, base - 1); + painter.setPen(axisCol); + painter.drawText(XMARGIN, YMARGIN + h - h * val / fMax-yMarginText, QString("%1 %2").arg(val).arg(units)); + int count = 1; + for(float y = val; y < fMax; y += val, count++) { + // don't overwrite lines drawn above + if(count % 10 == 0) + continue; + int yy = YMARGIN + h - h * y / fMax; + painter.drawLine(XMARGIN, yy, width() - XMARGIN, yy); + } + } + + if(!vSamplesIn.empty()) { + QPainterPath p; + paintPath(p, vSamplesIn); + painter.fillPath(p, QColor(0, 255, 0, 128)); + painter.setPen(Qt::green); + painter.drawPath(p); + } + if(!vSamplesOut.empty()) { + QPainterPath p; + paintPath(p, vSamplesOut); + painter.fillPath(p, QColor(255, 0, 0, 128)); + painter.setPen(Qt::red); + painter.drawPath(p); + } +} + +void TrafficGraphWidget::updateRates() +{ + if(!clientModel) return; + + quint64 bytesIn = clientModel->getTotalBytesRecv(), + bytesOut = clientModel->getTotalBytesSent(); + float inRate = (bytesIn - nLastBytesIn) / 1024.0f * 1000 / timer->interval(); + float outRate = (bytesOut - nLastBytesOut) / 1024.0f * 1000 / timer->interval(); + vSamplesIn.push_front(inRate); + vSamplesOut.push_front(outRate); + nLastBytesIn = bytesIn; + nLastBytesOut = bytesOut; + + while(vSamplesIn.size() > DESIRED_SAMPLES) { + vSamplesIn.pop_back(); + } + while(vSamplesOut.size() > DESIRED_SAMPLES) { + vSamplesOut.pop_back(); + } + + float tmax = 0.0f; + foreach(float f, vSamplesIn) { + if(f > tmax) tmax = f; + } + foreach(float f, vSamplesOut) { + if(f > tmax) tmax = f; + } + fMax = tmax; + update(); +} + +void TrafficGraphWidget::setGraphRangeMins(int mins) +{ + nMins = mins; + int msecsPerSample = nMins * 60 * 1000 / DESIRED_SAMPLES; + timer->stop(); + timer->setInterval(msecsPerSample); + + clear(); +} + +void TrafficGraphWidget::clear() +{ + timer->stop(); + + vSamplesOut.clear(); + vSamplesIn.clear(); + fMax = 0.0f; + + if(clientModel) { + nLastBytesIn = clientModel->getTotalBytesRecv(); + nLastBytesOut = clientModel->getTotalBytesSent(); + } + timer->start(); +} diff --git a/src/qt/trafficgraphwidget.h b/src/qt/trafficgraphwidget.h new file mode 100644 index 00000000..50571e0b --- /dev/null +++ b/src/qt/trafficgraphwidget.h @@ -0,0 +1,48 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_QT_TRAFFICGRAPHWIDGET_H +#define BITCOIN_QT_TRAFFICGRAPHWIDGET_H + +#include +#include + +class ClientModel; + +QT_BEGIN_NAMESPACE +class QPaintEvent; +class QTimer; +QT_END_NAMESPACE + +class TrafficGraphWidget : public QWidget +{ + Q_OBJECT + +public: + explicit TrafficGraphWidget(QWidget *parent = 0); + void setClientModel(ClientModel *model); + int getGraphRangeMins() const; + +protected: + void paintEvent(QPaintEvent *); + +public slots: + void updateRates(); + void setGraphRangeMins(int mins); + void clear(); + +private: + void paintPath(QPainterPath &path, QQueue &samples); + + QTimer *timer; + float fMax; + int nMins; + QQueue vSamplesIn; + QQueue vSamplesOut; + quint64 nLastBytesIn; + quint64 nLastBytesOut; + ClientModel *clientModel; +}; + +#endif // BITCOIN_QT_TRAFFICGRAPHWIDGET_H diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp new file mode 100644 index 00000000..40336217 --- /dev/null +++ b/src/qt/transactiondesc.cpp @@ -0,0 +1,309 @@ +#include "transactiondesc.h" + +#include "guiutil.h" +#include "bitcoinunits.h" + +#include "main.h" +#include "wallet.h" +#include "txdb.h" +#include "ui_interface.h" +#include "base58.h" + +#include +#include + +QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx) +{ + if (!wtx.IsFinal()) + { + if (wtx.nLockTime < LOCKTIME_THRESHOLD) + return tr("Open for %n block(s)", "", nBestHeight - wtx.nLockTime); + else + 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 tr("%1/offline").arg(nDepth); + else if (nDepth < 6) + return tr("%1/unconfirmed").arg(nDepth); + else + return tr("%1 confirmations").arg(nDepth); + } +} + +QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) +{ + QString strHTML; + + { + LOCK(wallet->cs_wallet); + strHTML.reserve(4000); + strHTML += ""; + + int64_t nTime = wtx.GetTxTime(); + int64_t nCredit = wtx.GetCredit(MINE_ALL); + int64_t nDebit = wtx.GetDebit(MINE_ALL); + int64_t nNet = nCredit - nDebit; + + strHTML += "" + tr("Status") + ": " + FormatTxStatus(wtx); + int nRequests = wtx.GetRequestCount(); + if (nRequests != -1) + { + if (nRequests == 0) + strHTML += tr(", has not been successfully broadcast yet"); + else if (nRequests > 0) + strHTML += tr(", broadcast through %n node(s)", "", nRequests); + } + strHTML += "
"; + + strHTML += "" + tr("Date") + ": " + (nTime ? GUIUtil::dateTimeStr(nTime) : "") + "
"; + + // + // From + // + if (wtx.IsCoinBase() || wtx.IsCoinStake()) + { + strHTML += "" + tr("Source") + ": " + tr("Generated") + "
"; + } + else if (wtx.mapValue.count("from") && !wtx.mapValue["from"].empty()) + { + // Online transaction + strHTML += "" + tr("From") + ": " + GUIUtil::HtmlEscape(wtx.mapValue["from"]) + "
"; + } + else + { + // Offline transaction + if (nNet > 0) + { + // Credit + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + { + if (wallet->IsMine(txout)) + { + CTxDestination address; + if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address)) + { + if (wallet->mapAddressBook.count(address)) + { + std::vector addedAddresses; + for (unsigned int i = 0; i < wtx.vin.size(); i++) + { + uint256 hash; + const CTxIn& vin = wtx.vin[i]; + hash.SetHex(vin.prevout.hash.ToString()); + CTransaction wtxPrev; + uint256 hashBlock = 0; + if (!GetTransaction(hash, wtxPrev, hashBlock)) + { + strHTML += "" + tr("From") + ": " + tr("unknown") + "
"; + continue; + } + CTxDestination senderAddress; + if (!ExtractDestination(wtxPrev.vout[vin.prevout.n].scriptPubKey, senderAddress) ) + { + strHTML += "" + tr("From") + ": " + tr("unknown") + "
"; + } + else if(std::find(addedAddresses.begin(), addedAddresses.end(), senderAddress) + == addedAddresses.end() ) + { + addedAddresses.push_back(senderAddress); + strHTML += "" + tr("From") + ": "; + strHTML += GUIUtil::HtmlEscape(CBitcoinAddress(senderAddress).ToString()); + if(wallet->mapAddressBook.find(senderAddress) != wallet->mapAddressBook.end()) + if (!wallet->mapAddressBook[senderAddress].empty()) + { + strHTML += " (" + tr("label") + ": " + GUIUtil::HtmlEscape(wallet->mapAddressBook[senderAddress]) + ")"; + } + strHTML += "
"; + } + } + strHTML += "" + tr("To") + ": "; + strHTML += GUIUtil::HtmlEscape(CBitcoinAddress(address).ToString()); + if (!wallet->mapAddressBook[address].empty()) + strHTML += " (" + tr("own address") + ", " + tr("label") + ": " + GUIUtil::HtmlEscape(wallet->mapAddressBook[address]) + ")"; + else + strHTML += " (" + tr("own address") + ")"; + strHTML += "
"; + } + } + break; + } + } + } + } + + // + // To + // + if (wtx.mapValue.count("to") && !wtx.mapValue["to"].empty()) + { + // Online transaction + std::string strAddress = wtx.mapValue["to"]; + strHTML += "" + tr("To") + ": "; + CTxDestination dest = CBitcoinAddress(strAddress).Get(); + if (wallet->mapAddressBook.count(dest) && !wallet->mapAddressBook[dest].empty()) + strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[dest]) + " "; + strHTML += GUIUtil::HtmlEscape(strAddress) + "
"; + } + + // + // Amount + // + if (wtx.IsCoinBase() && nCredit == 0) + { + // + // Coinbase + // + int64_t nUnmatured = 0; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + nUnmatured += wallet->GetCredit(txout, MINE_ALL); + strHTML += "" + tr("Credit") + ": "; + if (wtx.IsInMainChain()) + strHTML += BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", wtx.GetBlocksToMaturity()) + ")"; + else + strHTML += "(" + tr("not accepted") + ")"; + strHTML += "
"; + } + else if (nNet > 0) + { + // + // Credit + // + strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nNet) + "
"; + } + else + { + bool fAllFromMe = true; + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + fAllFromMe = fAllFromMe && wallet->IsMine(txin); + + bool fAllToMe = true; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + fAllToMe = fAllToMe && wallet->IsMine(txout); + + if (fAllFromMe) + { + // + // Debit + // + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + { + if (wallet->IsMine(txout)) + continue; + + if (!wtx.mapValue.count("to") || wtx.mapValue["to"].empty()) + { + // Offline transaction + CTxDestination address; + if (ExtractDestination(txout.scriptPubKey, address)) + { + strHTML += "" + tr("To") + ": "; + if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].empty()) + strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address]) + " "; + strHTML += GUIUtil::HtmlEscape(CBitcoinAddress(address).ToString()); + strHTML += "
"; + } + } + + strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, -txout.nValue) + "
"; + } + + if (fAllToMe) + { + // Payment to self + int64_t nChange = wtx.GetChange(); + int64_t nValue = nCredit - nChange; + strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, -nValue) + "
"; + strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nValue) + "
"; + } + + int64_t nTxFee = nDebit - wtx.GetValueOut(); + if (nTxFee > 0) + strHTML += "" + tr("Transaction fee") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, -nTxFee) + "
"; + } + else + { + // + // Mixed debit transaction + // + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + if (wallet->IsMine(txin)) + strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, -wallet->GetDebit(txin, MINE_ALL)) + "
"; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + if (wallet->IsMine(txout)) + strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, wallet->GetCredit(txout, MINE_ALL)) + "
"; + } + } + + strHTML += "" + tr("Net amount") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nNet, true) + "
"; + + // + // Message + // + if (wtx.mapValue.count("message") && !wtx.mapValue["message"].empty()) + strHTML += "
" + tr("Message") + ":
" + GUIUtil::HtmlEscape(wtx.mapValue["message"], true) + "
"; + if (wtx.mapValue.count("comment") && !wtx.mapValue["comment"].empty()) + strHTML += "
" + tr("Comment") + ":
" + GUIUtil::HtmlEscape(wtx.mapValue["comment"], true) + "
"; + + strHTML += "" + tr("Transaction ID") + ": " + wtx.GetHash().ToString().c_str() + "
"; + + if (wtx.IsCoinBase() || wtx.IsCoinStake()) + strHTML += "
" + tr("Generated coins must mature 520 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, its state will change to \"not accepted\" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.") + "
"; + + // + // Debug view + // + if (fDebug) + { + strHTML += "

" + tr("Debug information") + "

"; + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + if(wallet->IsMine(txin)) + strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, -wallet->GetDebit(txin, MINE_ALL)) + "
"; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + if(wallet->IsMine(txout)) + strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, wallet->GetCredit(txout, MINE_ALL)) + "
"; + + strHTML += "
" + tr("Transaction") + ":
"; + strHTML += GUIUtil::HtmlEscape(wtx.ToString(), true); + + CTxDB txdb("r"); // To fetch source txouts + + strHTML += "
" + tr("Inputs") + ":"; + strHTML += "
    "; + + { + LOCK(wallet->cs_wallet); + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + { + COutPoint prevout = txin.prevout; + + CTransaction prev; + if(txdb.ReadDiskTx(prevout.hash, prev)) + { + if (prevout.n < prev.vout.size()) + { + strHTML += "
  • "; + const CTxOut &vout = prev.vout[prevout.n]; + CTxDestination address; + if (ExtractDestination(vout.scriptPubKey, address)) + { + if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].empty()) + strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address]) + " "; + strHTML += QString::fromStdString(CBitcoinAddress(address).ToString()); + } + strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, vout.nValue); + strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) ? tr("true") : tr("false")) + "
  • "; + } + } + } + } + + strHTML += "
"; + } + + strHTML += "
"; + } + return strHTML; +} diff --git a/src/qt/transactiondesc.h b/src/qt/transactiondesc.h new file mode 100644 index 00000000..2523f9aa --- /dev/null +++ b/src/qt/transactiondesc.h @@ -0,0 +1,24 @@ +#ifndef TRANSACTIONDESC_H +#define TRANSACTIONDESC_H + +#include +#include +#include + +class CWallet; +class CWalletTx; + +/** Provide a human-readable extended HTML description of a transaction. + */ +class TransactionDesc: public QObject +{ + Q_OBJECT +public: + static QString toHTML(CWallet *wallet, CWalletTx &wtx); +private: + TransactionDesc() {} + + static QString FormatTxStatus(const CWalletTx& wtx); +}; + +#endif // TRANSACTIONDESC_H diff --git a/src/qt/transactiondescdialog.cpp b/src/qt/transactiondescdialog.cpp new file mode 100644 index 00000000..4c62e4bc --- /dev/null +++ b/src/qt/transactiondescdialog.cpp @@ -0,0 +1,43 @@ +#include "transactiondescdialog.h" +#include "ui_transactiondescdialog.h" + +#include "transactiontablemodel.h" +#include "dialogwindowflags.h" + +#include +#include + +TransactionDescDialog::TransactionDescDialog(const QModelIndex &idx, QWidget *parent) : + QWidget(parent, DIALOGWINDOWHINTS), + ui(new Ui::TransactionDescDialog) +{ + ui->setupUi(this); + QString desc = idx.data(TransactionTableModel::LongDescriptionRole).toString(); + ui->detailText->setHtml(desc); +} + +TransactionDescDialog::~TransactionDescDialog() +{ + delete ui; +} + +void TransactionDescDialog::keyPressEvent(QKeyEvent *event) +{ +#ifdef ANDROID + if(event->key() == Qt::Key_Back) + { + close(); + } +#else + if(event->key() == Qt::Key_Escape) + { + close(); + } +#endif +} + +void TransactionDescDialog::closeEvent(QCloseEvent *e) +{ + emit(stopExec()); + QWidget::closeEvent(e); +} diff --git a/src/qt/transactiondescdialog.h b/src/qt/transactiondescdialog.h new file mode 100644 index 00000000..3f39e415 --- /dev/null +++ b/src/qt/transactiondescdialog.h @@ -0,0 +1,32 @@ +#ifndef TRANSACTIONDESCDIALOG_H +#define TRANSACTIONDESCDIALOG_H + +#include + +namespace Ui { + class TransactionDescDialog; +} +QT_BEGIN_NAMESPACE +class QModelIndex; +QT_END_NAMESPACE + +/** Dialog showing transaction details. */ +class TransactionDescDialog : public QWidget +{ + Q_OBJECT +protected: + void keyPressEvent(QKeyEvent *); + void closeEvent(QCloseEvent *e); + +public: + explicit TransactionDescDialog(const QModelIndex &idx, QWidget *parent = 0); + ~TransactionDescDialog(); + +private: + Ui::TransactionDescDialog *ui; + +signals: + void stopExec(); +}; + +#endif // TRANSACTIONDESCDIALOG_H diff --git a/src/qt/transactionfilterproxy.cpp b/src/qt/transactionfilterproxy.cpp new file mode 100644 index 00000000..16fb4dab --- /dev/null +++ b/src/qt/transactionfilterproxy.cpp @@ -0,0 +1,86 @@ +#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), + limitRows(-1) +{ +} + +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 = llabs(index.data(TransactionTableModel::AmountRole).toLongLong()); + + if(!(TYPE(type) & typeFilter)) + return false; + if(datetime < dateFrom || datetime > dateTo) + return false; + if (!address.contains(addrPrefix, Qt::CaseInsensitive) && !label.contains(addrPrefix, Qt::CaseInsensitive)) + 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(); +} + +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 new file mode 100644 index 00000000..30b98588 --- /dev/null +++ b/src/qt/transactionfilterproxy.h @@ -0,0 +1,52 @@ +#ifndef TRANSACTIONFILTERPROXY_H +#define TRANSACTIONFILTERPROXY_H + +#include +#include + +/** Filter the 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< TransactionRecord::decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx) +{ + QList parts; + int64_t nTime = wtx.GetTxTime(); + int64_t nCredit = wtx.GetCredit(MINE_ALL); + int64_t nDebit = wtx.GetDebit(MINE_ALL); + int64_t nNet = nCredit - nDebit; + uint256 hash = wtx.GetHash(), hashPrev = 0; + std::map mapValue = wtx.mapValue; + + if (nNet > 0 || wtx.IsCoinBase() || wtx.IsCoinStake()) + { + // + // Credit + // + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + { + if(wallet->IsMine(txout)) + { + TransactionRecord sub(hash, nTime); + CTxDestination address; + sub.idx = parts.size(); // sequence number + sub.credit = txout.nValue; + if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address)) + { + // Received by Bitcoin Address + sub.type = TransactionRecord::RecvWithAddress; + sub.address = CBitcoinAddress(address).ToString(); + } + else + { + // Received by IP connection (deprecated features), or a multisignature or other non-simple transaction + sub.type = TransactionRecord::RecvFromOther; + sub.address = mapValue["from"]; + } + if (wtx.IsCoinBase()) + { + // Generated (proof-of-work) + sub.type = TransactionRecord::Generated; + } + if (wtx.IsCoinStake()) + { + // Generated (proof-of-stake) + + if (hashPrev == hash) + continue; // last coinstake output + + sub.type = TransactionRecord::Generated; + sub.credit = nNet > 0 ? nNet : wtx.GetValueOut() - nDebit; + hashPrev = hash; + } + + parts.append(sub); + } + } + } + else + { + bool fAllFromMe = true; + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + fAllFromMe = fAllFromMe && wallet->IsMine(txin); + + bool fAllToMe = true; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + fAllToMe = fAllToMe && wallet->IsMine(txout); + + if (fAllFromMe && fAllToMe) + { + // Payment to self + int64_t nChange = wtx.GetChange(); + + parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "", + -(nDebit - nChange), nCredit - nChange)); + } + else if (fAllFromMe) + { + // + // Debit + // + int64_t nTxFee = nDebit - wtx.GetValueOut(); + + for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++) + { + const CTxOut& txout = wtx.vout[nOut]; + TransactionRecord sub(hash, nTime); + sub.idx = parts.size(); + + 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; + } + + CTxDestination address; + if (ExtractDestination(txout.scriptPubKey, address)) + { + // Sent to Bitcoin Address + sub.type = TransactionRecord::SendToAddress; + sub.address = CBitcoinAddress(address).ToString(); + } + else + { + // Sent to IP, or other non-address transaction like OP_EVAL + sub.type = TransactionRecord::SendToOther; + sub.address = mapValue["to"]; + } + + int64_t nValue = txout.nValue; + /* Add fee to first output */ + if (nTxFee > 0) + { + nValue += nTxFee; + nTxFee = 0; + } + sub.debit = -nValue; + + parts.append(sub); + } + } + else + { + // + // Mixed debit transaction, can't break down payees + // + 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 : std::numeric_limits::max()), + (wtx.IsCoinBase() ? 1 : 0), + wtx.nTimeReceived, + idx); + status.confirmed = wtx.IsTrusted(); + status.depth = wtx.GetDepthInMainChain(); + status.cur_num_blocks = nBestHeight; + + if (!wtx.IsFinal()) + { + if (wtx.nLockTime < LOCKTIME_THRESHOLD) + { + 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 < NumConfirmations) + { + status.status = TransactionStatus::Unconfirmed; + } + else + { + status.status = TransactionStatus::HaveConfirmations; + } + } + + // For generated transactions, determine maturity + if(type == TransactionRecord::Generated) + { + if (wtx.GetBlocksToMaturity() > 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; +} + +std::string TransactionRecord::getTxID() +{ + return hash.ToString() + strprintf("-%03d", idx); +} + diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h new file mode 100644 index 00000000..15b76cc0 --- /dev/null +++ b/src/qt/transactionrecord.h @@ -0,0 +1,129 @@ +#ifndef TRANSACTIONRECORD_H +#define TRANSACTIONRECORD_H + +#include "uint256.h" + +#include + +class CWallet; +class CWalletTx; + +/** UI model for transaction status. The transaction status is the part of a transaction that will change over time. + */ +class TransactionStatus +{ +public: + TransactionStatus(): + confirmed(false), sortKey(""), maturity(Mature), + matures_in(0), status(Offline), depth(0), open_for(0), cur_num_blocks(-1) + { } + + enum Maturity + { + Immature, + Mature, + MaturesWarning, /**< Transaction will likely not mature because no nodes have confirmed */ + NotAccepted + }; + + enum Status { + OpenUntilDate, + OpenUntilBlock, + Offline, + Unconfirmed, + HaveConfirmations + }; + + bool confirmed; + std::string sortKey; + + /** @name Generated (mined) transactions + @{*/ + Maturity maturity; + int matures_in; + /**@}*/ + + /** @name Reported status + @{*/ + Status status; + int64_t depth; + int64_t 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; +}; + +/** UI model for a transaction. A core transaction can be represented by multiple UI transactions if it has + multiple outputs. + */ +class TransactionRecord +{ +public: + enum Type + { + Other, + Generated, + SendToAddress, + SendToOther, + RecvWithAddress, + RecvFromOther, + 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) + { + } + + TransactionRecord(uint256 hash, int64_t time): + hash(hash), time(time), type(Other), address(""), debit(0), + credit(0), idx(0) + { + } + + TransactionRecord(uint256 hash, int64_t time, + Type type, const std::string &address, + int64_t debit, int64_t credit): + hash(hash), time(time), type(type), address(address), debit(debit), credit(credit), + idx(0) + { + } + + /** Decompose CWallet transaction to model transaction records. + */ + static bool showTransaction(const CWalletTx &wtx); + static QList decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx); + + /** @name Immutable transaction attributes + @{*/ + uint256 hash; + int64_t time; + Type type; + std::string address; + int64_t debit; + int64_t credit; + /**@}*/ + + /** Subtransaction index, for sort key */ + int idx; + + /** Status: can change with block chain update */ + TransactionStatus status; + + /** Return the unique identifier for this transaction (part) */ + std::string getTxID(); + + /** Update status from core wallet tx. + */ + void updateStatus(const CWalletTx &wtx); + + /** Return whether a status update is needed. + */ + bool statusUpdateNeeded(); +}; + +#endif // TRANSACTIONRECORD_H diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp new file mode 100644 index 00000000..ee4742a9 --- /dev/null +++ b/src/qt/transactiontablemodel.cpp @@ -0,0 +1,657 @@ +#include "transactiontablemodel.h" +#include "guiutil.h" +#include "transactionrecord.h" +#include "guiconstants.h" +#include "transactiondesc.h" +#include "walletmodel.h" +#include "optionsmodel.h" +#include "addresstablemodel.h" +#include "bitcoinunits.h" + +#include "wallet.h" +#include "ui_interface.h" + +#include +#include +#include +#include +#include +#include +#include + +// Amount column is right-aligned it contains 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 +{ + 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 +class TransactionTablePriv +{ +public: + TransactionTablePriv(CWallet *wallet, TransactionTableModel *parent): + wallet(wallet), + parent(parent) + { + } + CWallet *wallet; + TransactionTableModel *parent; + + /* Local cache of wallet. + * As it is in the same order as the CWallet, by definition + * this is sorted by sha256. + */ + QList cachedWallet; + + /* Query entire wallet anew from core. + */ + void refreshWallet() + { + OutputDebugStringF("refreshWallet\n"); + cachedWallet.clear(); + { + LOCK(wallet->cs_wallet); + for(std::map::iterator it = wallet->mapWallet.begin(); it != wallet->mapWallet.end(); ++it) + { + if(TransactionRecord::showTransaction(it->second)) + cachedWallet.append(TransactionRecord::decomposeTransaction(wallet, it->second)); + } + } + } + + /* Update our model of the wallet incrementally, to synchronize our model of the wallet + with that of the core. + + Call with transaction that was added, removed or changed. + */ + void updateWallet(const uint256 &hash, int status) + { + OutputDebugStringF("updateWallet %s %i\n", hash.ToString().c_str(), status); + { + LOCK(wallet->cs_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 + 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 = (lower != upper); + + // Determine whether to show transaction or not + bool showTransaction = (inWallet && TransactionRecord::showTransaction(mi->second)); + + if(status == CT_UPDATED) + { + if(showTransaction && !inModel) + status = CT_NEW; /* Not in model, but want to show, treat as new */ + if(!showTransaction && inModel) + status = CT_DELETED; /* In model, but want to hide, treat as deleted */ + } + + OutputDebugStringF(" inWallet=%i inModel=%i Index=%i-%i showTransaction=%i derivedStatus=%i\n", + inWallet, inModel, lowerIndex, upperIndex, showTransaction, status); + + switch(status) + { + case CT_NEW: + if(inModel) + { + OutputDebugStringF("Warning: updateWallet: Got CT_NEW, but transaction is already in model\n"); + break; + } + if(!inWallet) + { + OutputDebugStringF("Warning: updateWallet: Got CT_NEW, but transaction is not in wallet\n"); + break; + } + if(showTransaction) + { + // Added -- insert at the right position + QList toInsert = + TransactionRecord::decomposeTransaction(wallet, 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(); + } + } + break; + case CT_DELETED: + if(!inModel) + { + OutputDebugStringF("Warning: updateWallet: Got CT_DELETED, but transaction is not in model\n"); + break; + } + // Removed -- remove entire transaction from table + parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1); + cachedWallet.erase(lower, upper); + parent->endRemoveRows(); + break; + case CT_UPDATED: + // Miscellaneous updates -- nothing to do, status update will take care of this, and is only computed for + // visible transactions. + break; + } + } + } + + int size() + { + return cachedWallet.size(); + } + + TransactionRecord *index(int idx) + { + if(idx >= 0 && idx < cachedWallet.size()) + { + TransactionRecord *rec = &cachedWallet[idx]; + + // If a status update is needed (blocks came in since last check), + // update the status of this transaction from the wallet. Otherwise, + // simply re-use the cached status. + if(rec->statusUpdateNeeded()) + { + { + LOCK(wallet->cs_wallet); + std::map::iterator mi = wallet->mapWallet.find(rec->hash); + + if(mi != wallet->mapWallet.end()) + { + rec->updateStatus(mi->second); + } + } + } + return rec; + } + else + { + return 0; + } + } + + QString describe(TransactionRecord *rec) + { + { + LOCK(wallet->cs_wallet); + std::map::iterator mi = wallet->mapWallet.find(rec->hash); + if(mi != wallet->mapWallet.end()) + { + return TransactionDesc::toHTML(wallet, mi->second); + } + } + return QString(""); + } + +}; + +TransactionTableModel::TransactionTableModel(CWallet* wallet, WalletModel *parent): + QAbstractTableModel(parent), + wallet(wallet), + walletModel(parent), + priv(new TransactionTablePriv(wallet, this)), + cachedNumBlocks(0) +{ + columns << QString() << tr("Date") << tr("Type") << tr("Address") << BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit()); + + priv->refreshWallet(); + + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(updateConfirmations())); + timer->start(MODEL_UPDATE_DELAY); + + connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); +} + +TransactionTableModel::~TransactionTableModel() +{ + delete priv; +} + +/** Updates the column title to "Amount (DisplayUnit)" and emits headerDataChanged() signal for table headers to react. */ +void TransactionTableModel::updateAmountColumnTitle() +{ + columns[Amount] = BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit()); + emit headerDataChanged(Qt::Horizontal,Amount,Amount); +} + +void TransactionTableModel::updateTransaction(const QString &hash, int status) +{ + uint256 updated; + updated.SetHex(hash.toStdString()); + + priv->updateWallet(updated, status); +} + +void TransactionTableModel::updateConfirmations() +{ + if(nBestHeight != cachedNumBlocks) + { + cachedNumBlocks = nBestHeight; + // Blocks came in since last poll. + // Invalidate status (number of confirmations) and (possibly) description + // for all rows. Qt is smart enough to only actually request the data for the + // visible rows. + emit dataChanged(index(0, Status), index(priv->size()-1, Status)); + emit dataChanged(index(0, ToAddress), index(priv->size()-1, ToAddress)); + } +} + +void TransactionTableModel::refresh() +{ + priv->refreshWallet(); + emit dataChanged(index(0, 0), index(priv->size() - 1, Amount)); +} + +int TransactionTableModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return priv->size(); +} + +int TransactionTableModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return columns.length(); +} + +QString TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) const +{ + QString status; + int nNumConf = TransactionRecord::NumConfirmations; + + if (wtx->type == TransactionRecord::Generated) + { + nNumConf = nCoinbaseMaturity + 20; + } + + switch(wtx->status.status) + { + case TransactionStatus::OpenUntilBlock: + status = tr("Open for %n block(s)","",wtx->status.open_for); + break; + case TransactionStatus::OpenUntilDate: + status = tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx->status.open_for)); + break; + case TransactionStatus::Offline: + status = tr("Offline (%1 confirmations)").arg(wtx->status.depth); + break; + case TransactionStatus::Unconfirmed: + status = tr("Unconfirmed (%1 of %2 confirmations)").arg(wtx->status.depth).arg(nNumConf); + break; + case TransactionStatus::HaveConfirmations: + status = tr("Confirmed (%1 confirmations)").arg(wtx->status.depth); + break; + } + + if(wtx->type == TransactionRecord::Generated) + { + switch(wtx->status.maturity) + { + case TransactionStatus::Immature: + status += "\n" + tr("Mined balance will be available when it matures in %n more block(s)", "", wtx->status.matures_in); + break; + case TransactionStatus::Mature: + break; + case TransactionStatus::MaturesWarning: + status += "\n" + tr("This block was not received by any other nodes and will probably not be accepted!"); + break; + case TransactionStatus::NotAccepted: + status += "\n" + tr("Generated but not accepted"); + break; + } + } + + return status; +} + +QString TransactionTableModel::formatTxDate(const TransactionRecord *wtx) const +{ + if(wtx->time) + { + return GUIUtil::dateTimeStr(wtx->time); + } + else + { + return QString(); + } +} + +/* Look up address in address book, if found return label (address) + otherwise just return (address) + */ +QString TransactionTableModel::lookupAddress(const std::string &address, bool tooltip) const +{ + QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(address)); + QString description; + if(!label.isEmpty()) + { + description += label + QString(" "); + } + if(label.isEmpty() || walletModel->getOptionsModel()->getDisplayAddresses() || tooltip) + { + description += QString("(") + QString::fromStdString(address) + QString(")"); + } + return description; +} + +QString TransactionTableModel::formatTxType(const TransactionRecord *wtx) const +{ + switch(wtx->type) + { + case TransactionRecord::RecvWithAddress: + return tr("Received with"); + case TransactionRecord::RecvFromOther: + return tr("Received from"); + case TransactionRecord::SendToAddress: + case TransactionRecord::SendToOther: + return tr("Sent to"); + case TransactionRecord::SendToSelf: + return tr("Payment to yourself"); + case TransactionRecord::Generated: + return tr("Mined"); + default: + return QString(); + } +} + +QVariant TransactionTableModel::txAddressDecoration(const TransactionRecord *wtx) const +{ + switch(wtx->type) + { + case TransactionRecord::Generated: + return QIcon(":/icons/tx_mined"); + case TransactionRecord::RecvWithAddress: + case TransactionRecord::RecvFromOther: + return QIcon(":/icons/tx_input"); + case TransactionRecord::SendToAddress: + case TransactionRecord::SendToOther: + return QIcon(":/icons/tx_output"); + default: + return QIcon(":/icons/tx_inout"); + } +} + +QString TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const +{ + switch(wtx->type) + { + case TransactionRecord::RecvFromOther: + return QString::fromStdString(wtx->address); + case TransactionRecord::RecvWithAddress: + case TransactionRecord::SendToAddress: + case TransactionRecord::Generated: + return lookupAddress(wtx->address, tooltip); + case TransactionRecord::SendToOther: + return QString::fromStdString(wtx->address); + case TransactionRecord::SendToSelf: + default: + return tr("(n/a)"); + } +} + +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: + case TransactionRecord::Generated: + { + QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(wtx->address)); + if(label.isEmpty()) + return COLOR_BAREADDRESS; + } break; + case TransactionRecord::SendToSelf: + return COLOR_BAREADDRESS; + default: + break; + } + return QVariant(); +} + +QString TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed) const +{ + QString str = BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->credit + wtx->debit); + if(showUnconfirmed) + { + if(!wtx->status.confirmed || wtx->status.maturity != TransactionStatus::Mature) + { + str = QString("[") + str + QString("]"); + } + } + return QString(str); +} + +QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx) const +{ + if(wtx->type == TransactionRecord::Generated) + { + switch(wtx->status.maturity) + { + 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); +} + +QString TransactionTableModel::formatTooltip(const TransactionRecord *rec) const +{ + QString tooltip = formatTxStatus(rec) + QString("\n") + formatTxType(rec); + if(rec->type==TransactionRecord::RecvFromOther || rec->type==TransactionRecord::SendToOther || + rec->type==TransactionRecord::SendToAddress || rec->type==TransactionRecord::RecvWithAddress) + { + tooltip += QString(" ") + formatTxToAddress(rec, true); + } + return tooltip; +} + +QVariant TransactionTableModel::data(const QModelIndex &index, int role) const +{ + if(!index.isValid()) + return QVariant(); + TransactionRecord *rec = static_cast(index.internalPointer()); + + switch(role) + { + case Qt::DecorationRole: + switch(index.column()) + { + case Status: + return txStatusDecoration(rec); + case ToAddress: + return txAddressDecoration(rec); + } + break; + case Qt::DisplayRole: + switch(index.column()) + { + case Date: + return formatTxDate(rec); + case Type: + return formatTxType(rec); + case ToAddress: + return formatTxToAddress(rec, false); + case Amount: + return formatTxAmount(rec); + } + break; + case Qt::EditRole: + // Edit role is used for sorting, so return the unformatted values + switch(index.column()) + { + case Status: + return QString::fromStdString(rec->status.sortKey); + case Date: + // We need cast here to prevent ambigious conversion error + return static_cast(rec->time); + case Type: + return formatTxType(rec); + case ToAddress: + return formatTxToAddress(rec, true); + case Amount: + // Same here + return static_cast(rec->credit + rec->debit); + } + break; + case Qt::ToolTipRole: + return formatTooltip(rec); + case Qt::TextAlignmentRole: + return column_alignments[index.column()]; + case Qt::ForegroundRole: + // Non-confirmed transactions are grey + if(!rec->status.confirmed) + { + return COLOR_UNCONFIRMED; + } + if(index.column() == Amount && (rec->credit+rec->debit) < 0) + { + return COLOR_NEGATIVE; + } + if(index.column() == ToAddress) + { + return addressColor(rec); + } + break; + case TypeRole: + return rec->type; + case DateRole: + return QDateTime::fromTime_t(static_cast(rec->time)); + case LongDescriptionRole: + return priv->describe(rec); + case AddressRole: + return QString::fromStdString(rec->address); + case LabelRole: + return walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(rec->address)); + case AmountRole: + // And here + return static_cast(rec->credit + rec->debit); + case TxIDRole: + return QString::fromStdString(rec->getTxID()); + case TxHashRole: + return QString::fromStdString(rec->hash.ToString()); + case ConfirmedRole: + // Return True if transaction counts for balance + return rec->status.confirmed && !(rec->type == TransactionRecord::Generated && rec->status.maturity != TransactionStatus::Mature); + case FormattedAmountRole: + return formatTxAmount(rec, false); + } + return QVariant(); +} + +QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if(orientation == Qt::Horizontal) + { + if(role == Qt::DisplayRole) + { + return columns[section]; + } + else if (role == Qt::TextAlignmentRole) + { + return column_alignments[section]; + } else if (role == Qt::ToolTipRole) + { + switch(section) + { + case Status: + 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 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."); + } + } + } + return QVariant(); +} + +QModelIndex TransactionTableModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_UNUSED(parent); + TransactionRecord *data = priv->index(row); + if(data) + { + return createIndex(row, column, priv->index(row)); + } + else + { + return QModelIndex(); + } +} + +void TransactionTableModel::updateDisplayUnit() +{ + updateAmountColumnTitle(); + // emit dataChanged to update Amount column with the current unit + emit dataChanged(index(0, Amount), index(priv->size()-1, Amount)); +} diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h new file mode 100644 index 00000000..9608948a --- /dev/null +++ b/src/qt/transactiontablemodel.h @@ -0,0 +1,90 @@ +#ifndef TRANSACTIONTABLEMODEL_H +#define TRANSACTIONTABLEMODEL_H + +#include +#include + +class CWallet; +class TransactionTablePriv; +class TransactionRecord; +class WalletModel; + +/** UI model for the transaction table of a wallet. + */ +class TransactionTableModel : public QAbstractTableModel +{ + Q_OBJECT +public: + explicit TransactionTableModel(CWallet* wallet, WalletModel *parent = 0); + ~TransactionTableModel(); + + enum ColumnIndex { + Status = 0, + Date = 1, + Type = 2, + ToAddress = 3, + Amount = 4 + }; + + /** Roles to get specific information from a transaction row. + These are independent of column. + */ + enum RoleIndex { + /** Type of transaction */ + TypeRole = Qt::UserRole, + /** Date and time this transaction was created */ + DateRole, + /** Long description (HTML format) */ + LongDescriptionRole, + /** Address of transaction */ + AddressRole, + /** Label of address related to transaction */ + LabelRole, + /** Net amount of transaction */ + AmountRole, + /** Unique identifier */ + TxIDRole, + /** Transaction hash */ + TxHashRole, + /** Is transaction confirmed? */ + ConfirmedRole, + /** Formatted amount, without brackets when unconfirmed */ + FormattedAmountRole + }; + + 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; + QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const; + void refresh(); +private: + CWallet* wallet; + WalletModel *walletModel; + QStringList columns; + TransactionTablePriv *priv; + int cachedNumBlocks; + + QString lookupAddress(const std::string &address, bool tooltip) const; + QVariant addressColor(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; + 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; + +public slots: + void updateTransaction(const QString &hash, int status); + void updateConfirmations(); + void updateDisplayUnit(); + /** Updates the column title to "Amount (DisplayUnit)" and emits headerDataChanged() signal for table headers to react. */ + void updateAmountColumnTitle(); + + friend class TransactionTablePriv; +}; + +#endif + diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp new file mode 100644 index 00000000..321d5877 --- /dev/null +++ b/src/qt/transactionview.cpp @@ -0,0 +1,504 @@ +#include "transactionview.h" + +#include "transactionfilterproxy.h" +#include "transactionrecord.h" +#include "walletmodel.h" +#include "addresstablemodel.h" +#include "transactiontablemodel.h" +#include "bitcoinunits.h" +#include "csvmodelwriter.h" +#include "transactiondescdialog.h" +#include "editaddressdialog.h" +#include "optionsmodel.h" +#include "guiutil.h" +#include "wallet.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +TransactionView::TransactionView(QWidget *parent) : + QWidget(parent), model(0), transactionProxyModel(0), + transactionView(0) +{ + // Build filter row + setContentsMargins(0,0,0,0); + + QHBoxLayout *hlayout = new QHBoxLayout(); + hlayout->setContentsMargins(0,0,0,0); +#ifdef Q_OS_MAC + hlayout->setSpacing(5); + hlayout->addSpacing(26); +#else + hlayout->setSpacing(0); + hlayout->addSpacing(23); +#endif + + dateWidget = new QComboBox(this); +#ifdef Q_OS_MAC + dateWidget->setFixedWidth(121); +#else + dateWidget->setFixedWidth(120); +#endif + 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("Last month"), LastMonth); + dateWidget->addItem(tr("This year"), ThisYear); + dateWidget->addItem(tr("Range..."), Range); + hlayout->addWidget(dateWidget); + + typeWidget = new QComboBox(this); +#ifdef Q_OS_MAC + typeWidget->setFixedWidth(121); +#else + typeWidget->setFixedWidth(120); +#endif + + typeWidget->addItem(tr("All"), TransactionFilterProxy::ALL_TYPES); + typeWidget->addItem(tr("Received with"), TransactionFilterProxy::TYPE(TransactionRecord::RecvWithAddress) | + TransactionFilterProxy::TYPE(TransactionRecord::RecvFromOther)); + typeWidget->addItem(tr("Sent to"), TransactionFilterProxy::TYPE(TransactionRecord::SendToAddress) | + TransactionFilterProxy::TYPE(TransactionRecord::SendToOther)); + typeWidget->addItem(tr("To yourself"), TransactionFilterProxy::TYPE(TransactionRecord::SendToSelf)); + typeWidget->addItem(tr("Mined"), TransactionFilterProxy::TYPE(TransactionRecord::Generated)); + typeWidget->addItem(tr("Other"), TransactionFilterProxy::TYPE(TransactionRecord::Other)); + + hlayout->addWidget(typeWidget); + + addressWidget = new QLineEdit(this); +#if QT_VERSION >= 0x040700 + /* Do not move this to the XML file, Qt before 4.7 will choke on it */ + addressWidget->setPlaceholderText(tr("Enter address or label to search")); +#endif + hlayout->addWidget(addressWidget); + + amountWidget = new QLineEdit(this); +#if QT_VERSION >= 0x040700 + /* Do not move this to the XML file, Qt before 4.7 will choke on it */ + amountWidget->setPlaceholderText(tr("Min amount")); +#endif +#ifdef Q_OS_MAC + amountWidget->setFixedWidth(97); +#else + amountWidget->setFixedWidth(100); +#endif + amountWidget->setValidator(new QDoubleValidator(0, 1e20, 8, this)); + 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); + vlayout->addWidget(createDateRangeWidget()); + vlayout->addWidget(view); + vlayout->setSpacing(0); + int width = view->verticalScrollBar()->sizeHint().width(); + // Cover scroll bar width with spacing +#ifdef Q_OS_MAC + hlayout->addSpacing(width+2); +#else + hlayout->addSpacing(width); +#endif + // Always show scroll bar + view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + view->setTabKeyNavigation(false); + view->setContextMenuPolicy(Qt::CustomContextMenu); + + transactionView = view; + + // Actions + QAction *copyAddressAction = new QAction(tr("Copy address"), this); + QAction *copyLabelAction = new QAction(tr("Copy label"), this); + QAction *copyAmountAction = new QAction(tr("Copy amount"), this); + QAction *copyTxIDAction = new QAction(tr("Copy transaction ID"), this); + QAction *editLabelAction = new QAction(tr("Edit label"), this); + QAction *showDetailsAction = new QAction(tr("Show transaction details"), this); + QAction *clearOrphansAction = new QAction(tr("Clear orphans"), this); + + contextMenu = new QMenu(); + contextMenu->addAction(copyAddressAction); + contextMenu->addAction(copyLabelAction); + contextMenu->addAction(copyAmountAction); + contextMenu->addAction(copyTxIDAction); + contextMenu->addAction(editLabelAction); + contextMenu->addAction(showDetailsAction); + contextMenu->addSeparator(); + contextMenu->addAction(clearOrphansAction); + + mapperThirdPartyTxUrls = new QSignalMapper(this); + + // Connect actions + connect(mapperThirdPartyTxUrls, SIGNAL(mapped(QString)), this, SLOT(openThirdPartyTxUrl(QString))); + + connect(dateWidget, SIGNAL(activated(int)), this, SLOT(chooseDate(int))); + connect(typeWidget, SIGNAL(activated(int)), this, SLOT(chooseType(int))); + connect(addressWidget, SIGNAL(textChanged(QString)), this, SLOT(changedPrefix(QString))); + connect(amountWidget, SIGNAL(textChanged(QString)), this, SLOT(changedAmount(QString))); + + connect(view, SIGNAL(doubleClicked(QModelIndex)), this, SIGNAL(doubleClicked(QModelIndex))); + connect(view, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint))); + + connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(copyAddress())); + connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel())); + connect(copyAmountAction, SIGNAL(triggered()), this, SLOT(copyAmount())); + connect(copyTxIDAction, SIGNAL(triggered()), this, SLOT(copyTxID())); + connect(editLabelAction, SIGNAL(triggered()), this, SLOT(editLabel())); + connect(showDetailsAction, SIGNAL(triggered()), this, SLOT(showDetails())); + connect(clearOrphansAction, SIGNAL(triggered()), this, SLOT(clearOrphans())); +} + +void TransactionView::setModel(WalletModel *model, bool fShoudAddThirdPartyURL) +{ + this->model = model; + if(model) + { + transactionProxyModel = new TransactionFilterProxy(this); + transactionProxyModel->setSourceModel(model->getTransactionTableModel()); + transactionProxyModel->setDynamicSortFilter(true); + transactionProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); + transactionProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + + transactionProxyModel->setSortRole(Qt::EditRole); +// transactionProxyModel->setSortRole(TransactionTableModel::DateRole); + + 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); +#if QT_VERSION < 0x050000 + transactionView->horizontalHeader()->setResizeMode( + TransactionTableModel::ToAddress, QHeaderView::Stretch); +#else + transactionView->horizontalHeader()->setSectionResizeMode(TransactionTableModel::ToAddress, QHeaderView::Stretch); +#endif + transactionView->horizontalHeader()->resizeSection( + TransactionTableModel::Amount, 130); + + if (model->getOptionsModel() && fShoudAddThirdPartyURL) + { + // Add third party transaction URLs to context menu + QStringList listUrls = model->getOptionsModel()->getThirdPartyTxUrls().split("|", QString::SkipEmptyParts); + for (int i = 0; i < listUrls.size(); ++i) + { + QString host = QUrl(listUrls[i].trimmed(), QUrl::StrictMode).host(); + if (!host.isEmpty()) + { + QAction *thirdPartyTxUrlAction = new QAction(host, this); // use host as menu item label + if (i == 0) + contextMenu->addSeparator(); + contextMenu->addAction(thirdPartyTxUrlAction); + connect(thirdPartyTxUrlAction, SIGNAL(triggered()), mapperThirdPartyTxUrls, SLOT(map())); + mapperThirdPartyTxUrls->setMapping(thirdPartyTxUrlAction, listUrls[i].trimmed()); + } + } + } + } +} + +void TransactionView::chooseDate(int idx) +{ + if(!transactionProxyModel) + return; + QDate current = QDate::currentDate(); + dateRangeWidget->setVisible(false); + 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 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)), + TransactionFilterProxy::MAX_DATE); + break; + case Range: + dateRangeWidget->setVisible(true); + dateRangeChanged(); + break; + } +} + +void TransactionView::chooseType(int idx) +{ + if(!transactionProxyModel) + return; + transactionProxyModel->setTypeFilter( + typeWidget->itemData(idx).toInt()); +} + +void TransactionView::changedPrefix(const QString &prefix) +{ + if(!transactionProxyModel) + return; + transactionProxyModel->setAddressPrefix(prefix); +} + +void TransactionView::changedAmount(const QString &amount) +{ + if(!transactionProxyModel) + return; + qint64 amount_parsed = 0; + if(BitcoinUnits::parse(model->getOptionsModel()->getDisplayUnit(), amount, &amount_parsed)) + { + transactionProxyModel->setMinAmount(amount_parsed); + } + else + { + transactionProxyModel->setMinAmount(0); + } +} + +void TransactionView::exportClicked() +{ + // CSV is currently the only supported format + QString filename = GUIUtil::getSaveFileName( + this, + tr("Export Transaction Data"), QString(), + tr("Comma separated file (*.csv)")); + + if (filename.isNull()) return; + + CSVModelWriter writer(filename); + + // name, column, role + writer.setModel(transactionProxyModel); + 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()) + { + QMessageBox::critical(this, tr("Error exporting"), tr("Could not write to file %1.").arg(filename), + QMessageBox::Abort, QMessageBox::Abort); + } +} + +void TransactionView::contextualMenu(const QPoint &point) +{ + QModelIndex index = transactionView->indexAt(point); + if(index.isValid()) + { + contextMenu->exec(QCursor::pos()); + } +} + +void TransactionView::copyAddress() +{ + GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::AddressRole); +} + +void TransactionView::copyLabel() +{ + GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::LabelRole); +} + +void TransactionView::copyAmount() +{ + GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::FormattedAmountRole); +} + +void TransactionView::copyTxID() +{ + GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::TxIDRole); +} + +void TransactionView::editLabel() +{ + if(!transactionView->selectionModel() ||!model) + return; + QModelIndexList selection = transactionView->selectionModel()->selectedRows(); + if(!selection.isEmpty()) + { + AddressTableModel *addressBook = model->getAddressTableModel(); + if(!addressBook) + return; + QString address = selection.at(0).data(TransactionTableModel::AddressRole).toString(); + if(address.isEmpty()) + { + // If this transaction has no associated address, exit + return; + } + // Is address in address book? Address book can miss address when a transaction is + // sent from outside the UI. + 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.setModel(addressBook); + dlg.setAddress(address); + dlg.exec(); + } + } +} + +void TransactionView::showDetails() +{ + if(!transactionView->selectionModel()) + return; + QModelIndexList selection = transactionView->selectionModel()->selectedRows(); + if(!selection.isEmpty()) + { + TransactionDescDialog dlg(selection.at(0)); + dlg.setWindowModality(Qt::ApplicationModal); + dlg.show(); + + // This loop will wait for the window is closed + QEventLoop loop; + connect(&dlg, SIGNAL(stopExec()), &loop, SLOT(quit())); + loop.exec(); + } +} + +void TransactionView::clearOrphans() +{ + if(!model) + return; + + model->clearOrphans(); + model->getTransactionTableModel()->refresh(); + delete transactionProxyModel; + setModel(model, false); + transactionView->sortByColumn(TransactionTableModel::Status, Qt::DescendingOrder); + transactionView->sortByColumn(TransactionTableModel::Date, Qt::DescendingOrder); +} + +void TransactionView::openThirdPartyTxUrl(QString url) +{ + if(!transactionView->selectionModel()) + return; + QModelIndexList selection = transactionView->selectionModel()->selectedRows(0); + if(!selection.isEmpty()) + QDesktopServices::openUrl(QUrl::fromUserInput(url.replace("%s", selection.at(0).data(TransactionTableModel::TxHashRole).toString()))); +} + +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(tr("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(tr("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() +{ + if(!transactionProxyModel) + return; + transactionProxyModel->setDateRange( + QDateTime(dateFrom->date()), + QDateTime(dateTo->date()).addDays(1)); +} + +void TransactionView::focusTransaction(const QModelIndex &idx) +{ + if(!transactionProxyModel) + return; + QModelIndex targetIdx = transactionProxyModel->mapFromSource(idx); + transactionView->scrollTo(targetIdx); + transactionView->setCurrentIndex(targetIdx); + transactionView->setFocus(); +} diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h new file mode 100644 index 00000000..2fa1c390 --- /dev/null +++ b/src/qt/transactionview.h @@ -0,0 +1,87 @@ +#ifndef TRANSACTIONVIEW_H +#define TRANSACTIONVIEW_H + +#include + +class WalletModel; +class TransactionFilterProxy; + +QT_BEGIN_NAMESPACE +class QTableView; +class QComboBox; +class QLineEdit; +class QModelIndex; +class QSignalMapper; +class QMenu; +class QFrame; +class QDateTimeEdit; +QT_END_NAMESPACE + +/** Widget showing the transaction list for a wallet, including a filter row. + Using the filter row, the user can view or export a subset of the transactions. + */ +class TransactionView : public QWidget +{ + Q_OBJECT +public: + explicit TransactionView(QWidget *parent = 0); + + void setModel(WalletModel *model, bool fShoudAddThirdPartyURL = true); + + // Date ranges for filter + enum DateEnum + { + All, + Today, + ThisWeek, + ThisMonth, + LastMonth, + ThisYear, + Range + }; + +private: + WalletModel *model; + TransactionFilterProxy *transactionProxyModel; + QTableView *transactionView; + + QComboBox *dateWidget; + QComboBox *typeWidget; + QLineEdit *addressWidget; + QLineEdit *amountWidget; + + QMenu *contextMenu; + QSignalMapper *mapperThirdPartyTxUrls; + + QFrame *dateRangeWidget; + QDateTimeEdit *dateFrom; + QDateTimeEdit *dateTo; + + QWidget *createDateRangeWidget(); + +private slots: + void contextualMenu(const QPoint &); + void dateRangeChanged(); + void showDetails(); + void copyAddress(); + void editLabel(); + void copyLabel(); + void copyAmount(); + void copyTxID(); + void clearOrphans(); + void openThirdPartyTxUrl(QString url); + +signals: + void doubleClicked(const QModelIndex&); + +public slots: + void chooseDate(int idx); + void chooseType(int idx); + void changedPrefix(const QString &prefix); + void changedAmount(const QString &amount); + void exportClicked(); + void focusTransaction(const QModelIndex&); + +}; + +#endif // TRANSACTIONVIEW_H diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp new file mode 100644 index 00000000..4769fdaf --- /dev/null +++ b/src/qt/walletmodel.cpp @@ -0,0 +1,533 @@ +#include "walletmodel.h" +#include "guiconstants.h" +#include "optionsmodel.h" +#include "addresstablemodel.h" +#include "mintingtablemodel.h" +#include "transactiontablemodel.h" + +#include "ui_interface.h" +#include "wallet.h" +#include "walletdb.h" // for BackupWallet +#include "base58.h" + +#include +#include + +WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *parent) : + QObject(parent), wallet(wallet), optionsModel(optionsModel), addressTableModel(0), + transactionTableModel(0), + cachedBalance(0), cachedStake(0), cachedUnconfirmedBalance(0), cachedImmatureBalance(0), + cachedNumTransactions(0), + cachedEncryptionStatus(Unencrypted), + cachedNumBlocks(0) +{ + fHaveWatchOnly = wallet->HaveWatchOnly(); + + addressTableModel = new AddressTableModel(wallet, this); + mintingTableModel = new MintingTableModel(wallet, this); + transactionTableModel = new TransactionTableModel(wallet, this); + + // This timer will be fired repeatedly to update the balance + pollTimer = new QTimer(this); + connect(pollTimer, SIGNAL(timeout()), this, SLOT(pollBalanceChanged())); + pollTimer->start(MODEL_UPDATE_DELAY); + + subscribeToCoreSignals(); +} + +WalletModel::~WalletModel() +{ + unsubscribeFromCoreSignals(); +} + +bool WalletModel::haveWatchOnly() const +{ + return fHaveWatchOnly; +} + +qint64 WalletModel::getBalance() const +{ + return wallet->GetBalance(); +} + +qint64 WalletModel::getBalanceWatchOnly() const +{ + return wallet->GetWatchOnlyBalance(); +} + +qint64 WalletModel::getUnconfirmedBalance() const +{ + return wallet->GetUnconfirmedBalance(); +} + +qint64 WalletModel::getStake() const +{ + return wallet->GetStake(); +} + +qint64 WalletModel::getImmatureBalance() const +{ + return wallet->GetImmatureBalance(); +} + +int WalletModel::getNumTransactions() const +{ + int numTransactions = 0; + { + LOCK(wallet->cs_wallet); + numTransactions = (int)(wallet->mapWallet.size()); + } + return numTransactions; +} + +void WalletModel::updateStatus() +{ + EncryptionStatus newEncryptionStatus = getEncryptionStatus(); + + if(cachedEncryptionStatus != newEncryptionStatus) + emit encryptionStatusChanged(newEncryptionStatus); +} + +void WalletModel::pollBalanceChanged() +{ + if(nBestHeight != cachedNumBlocks) + { + // Balance and number of transactions might have changed + cachedNumBlocks = nBestHeight; + checkBalanceChanged(); + } +} + +void WalletModel::checkBalanceChanged() +{ + qint64 newBalanceTotal=getBalance(), newBalanceWatchOnly=getBalanceWatchOnly(); + qint64 newStake = getStake(); + qint64 newUnconfirmedBalance = getUnconfirmedBalance(); + qint64 newImmatureBalance = getImmatureBalance(); + + if(cachedBalance != newBalanceTotal || cachedStake != newStake || cachedUnconfirmedBalance != newUnconfirmedBalance || cachedImmatureBalance != newImmatureBalance) + { + cachedBalance = newBalanceTotal; + cachedStake = newStake; + cachedUnconfirmedBalance = newUnconfirmedBalance; + cachedImmatureBalance = newImmatureBalance; + emit balanceChanged(newBalanceTotal, newBalanceWatchOnly, newStake, newUnconfirmedBalance, newImmatureBalance); + } +} + +void WalletModel::updateTransaction(const QString &hash, int status) +{ + if(transactionTableModel) + transactionTableModel->updateTransaction(hash, status); + + // Balance and number of transactions might have changed + checkBalanceChanged(); + + int newNumTransactions = getNumTransactions(); + if(cachedNumTransactions != newNumTransactions) + { + cachedNumTransactions = newNumTransactions; + emit numTransactionsChanged(newNumTransactions); + } +} + +void WalletModel::updateAddressBook(const QString &address, const QString &label, bool isMine, int status) +{ + if(addressTableModel) + addressTableModel->updateEntry(address, label, isMine, status); +} + +void WalletModel::updateWatchOnlyFlag(bool fHaveWatchonly) +{ + fHaveWatchOnly = fHaveWatchonly; + emit notifyWatchonlyChanged(fHaveWatchonly); +} + +bool WalletModel::validateAddress(const QString &address) +{ + CBitcoinAddress addressParsed(address.toStdString()); + return addressParsed.IsValid(); +} + +WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList &recipients, const CCoinControl *coinControl) +{ + qint64 total = 0; + QSet setAddress; + QString hex; + + if(recipients.empty()) + { + return OK; + } + + // Pre-check input data for validity + foreach(const SendCoinsRecipient &rcp, recipients) + { + if(!validateAddress(rcp.address)) + { + return InvalidAddress; + } + setAddress.insert(rcp.address); + + if(rcp.amount <= 0) + { + return InvalidAmount; + } + total += rcp.amount; + } + + if(recipients.size() > setAddress.size()) + { + return DuplicateAddress; + } + + int64_t nBalance = 0; + std::vector vCoins; + wallet->AvailableCoins(vCoins, true, coinControl); + + BOOST_FOREACH(const COutput& out, vCoins) + if(out.fSpendable) + nBalance += out.tx->vout[out.i].nValue; + + if(total > nBalance) + { + return AmountExceedsBalance; + } + + if((total + nTransactionFee) > nBalance) + { + return SendCoinsReturn(AmountWithFeeExceedsBalance, nTransactionFee); + } + + { + LOCK2(cs_main, wallet->cs_wallet); + + // Sendmany + std::vector > vecSend; + foreach(const SendCoinsRecipient &rcp, recipients) + { + CScript scriptPubKey; + scriptPubKey.SetDestination(CBitcoinAddress(rcp.address.toStdString()).Get()); + vecSend.push_back(make_pair(scriptPubKey, rcp.amount)); + } + + CWalletTx wtx; + CReserveKey keyChange(wallet); + int64_t nFeeRequired = 0; + bool fCreated = wallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, coinControl); + + if(!fCreated) + { + if((total + nFeeRequired) > nBalance) // FIXME: could cause collisions in the future + { + return SendCoinsReturn(AmountWithFeeExceedsBalance, nFeeRequired); + } + return TransactionCreationFailed; + } + if(!uiInterface.ThreadSafeAskFee(nFeeRequired, tr("Sending...").toStdString())) + { + return Aborted; + } + if(!wallet->CommitTransaction(wtx, keyChange)) + { + return TransactionCommitFailed; + } + hex = QString::fromStdString(wtx.GetHash().GetHex()); + } + + // Add addresses / update labels that we've sent to to the address book + foreach(const SendCoinsRecipient &rcp, recipients) + { + std::string strAddress = rcp.address.toStdString(); + CTxDestination dest = CBitcoinAddress(strAddress).Get(); + std::string strLabel = rcp.label.toStdString(); + { + LOCK(wallet->cs_wallet); + + std::map::iterator mi = wallet->mapAddressBook.find(dest); + + // Check if we have a new address or an updated label + if (mi == wallet->mapAddressBook.end() || mi->second != strLabel) + { + wallet->SetAddressBookName(dest, strLabel); + } + } + } + + return SendCoinsReturn(OK, 0, hex); +} + +OptionsModel *WalletModel::getOptionsModel() +{ + return optionsModel; +} + +AddressTableModel *WalletModel::getAddressTableModel() +{ + return addressTableModel; +} + +MintingTableModel *WalletModel::getMintingTableModel() +{ + return mintingTableModel; +} + +TransactionTableModel *WalletModel::getTransactionTableModel() +{ + return transactionTableModel; +} + +WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const +{ + if(!wallet->IsCrypted()) + { + return Unencrypted; + } + else if(wallet->IsLocked()) + { + return Locked; + } + else + { + return Unlocked; + } +} + +bool WalletModel::setWalletEncrypted(bool encrypted, const SecureString &passphrase) +{ + if(encrypted) + { + // Encrypt + return wallet->EncryptWallet(passphrase); + } + else + { + // Decrypt + return wallet->DecryptWallet(passphrase); + } +} + +bool WalletModel::setWalletLocked(bool locked, const SecureString &passPhrase) +{ + if(locked) + { + // Lock + return wallet->Lock(); + } + else + { + // Unlock + return wallet->Unlock(passPhrase); + } +} + +bool WalletModel::changePassphrase(const SecureString &oldPass, const SecureString &newPass) +{ + bool retval; + { + LOCK(wallet->cs_wallet); + wallet->Lock(); // Make sure wallet is locked before attempting pass change + retval = wallet->ChangeWalletPassphrase(oldPass, newPass); + } + return retval; +} + +void WalletModel::getStakeWeightFromValue(const int64_t& nTime, const int64_t& nValue, uint64_t& nWeight) +{ + wallet->GetStakeWeightFromValue(nTime, nValue, nWeight); +} + +bool WalletModel::dumpWallet(const QString &filename) +{ + return DumpWallet(wallet, filename.toLocal8Bit().data()); +} + +bool WalletModel::importWallet(const QString &filename) +{ + return ImportWallet(wallet, filename.toLocal8Bit().data()); +} + +bool WalletModel::backupWallet(const QString &filename) +{ + return BackupWallet(*wallet, filename.toLocal8Bit().data()); +} + +// Handlers for core signals +static void NotifyKeyStoreStatusChanged(WalletModel *walletmodel, CCryptoKeyStore *wallet) +{ + OutputDebugStringF("NotifyKeyStoreStatusChanged\n"); + QMetaObject::invokeMethod(walletmodel, "updateStatus", Qt::QueuedConnection); +} + +static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet, const CTxDestination &address, const std::string &label, bool isMine, ChangeType status) +{ + OutputDebugStringF("NotifyAddressBookChanged %s %s isMine=%i status=%i\n", CBitcoinAddress(address).ToString().c_str(), label.c_str(), isMine, status); + QMetaObject::invokeMethod(walletmodel, "updateAddressBook", Qt::QueuedConnection, + Q_ARG(QString, QString::fromStdString(CBitcoinAddress(address).ToString())), + Q_ARG(QString, QString::fromStdString(label)), + Q_ARG(bool, isMine), + Q_ARG(int, status)); +} + +static void NotifyTransactionChanged(WalletModel *walletmodel, CWallet *wallet, const uint256 &hash, ChangeType status) +{ + OutputDebugStringF("NotifyTransactionChanged %s status=%i\n", hash.GetHex().c_str(), status); + QMetaObject::invokeMethod(walletmodel, "updateTransaction", Qt::QueuedConnection, + Q_ARG(QString, QString::fromStdString(hash.GetHex())), + Q_ARG(int, status)); +} + +static void NotifyWatchonlyChanged(WalletModel *walletmodel, bool fHaveWatchonly) +{ + QMetaObject::invokeMethod(walletmodel, "updateWatchOnlyFlag", Qt::QueuedConnection, + Q_ARG(bool, fHaveWatchonly)); +} + +void WalletModel::subscribeToCoreSignals() +{ + // Connect signals to wallet + wallet->NotifyStatusChanged.connect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1)); + wallet->NotifyAddressBookChanged.connect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5)); + wallet->NotifyTransactionChanged.connect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3)); + wallet->NotifyWatchonlyChanged.connect(boost::bind(NotifyWatchonlyChanged, this, _1)); +} + +void WalletModel::unsubscribeFromCoreSignals() +{ + // Disconnect signals from wallet + wallet->NotifyStatusChanged.disconnect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1)); + wallet->NotifyAddressBookChanged.disconnect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5)); + wallet->NotifyTransactionChanged.disconnect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3)); + wallet->NotifyWatchonlyChanged.disconnect(boost::bind(NotifyWatchonlyChanged, this, _1)); +} + +// WalletModel::UnlockContext implementation +WalletModel::UnlockContext WalletModel::requestUnlock() +{ + bool was_locked = getEncryptionStatus() == Locked; + bool mintflag = fWalletUnlockMintOnly; + + if ((!was_locked) && fWalletUnlockMintOnly) + { + setWalletLocked(true); + 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, mintflag); +} + +WalletModel::UnlockContext::UnlockContext(WalletModel *wallet, bool valid, bool relock, bool mintflag): + wallet(wallet), + valid(valid), + relock(relock), + mintflag(mintflag) +{ +} + +WalletModel::UnlockContext::~UnlockContext() +{ + if(valid && relock) + { + if (mintflag) + { + // Restore unlock minting flag + fWalletUnlockMintOnly = mintflag; + return; + } + wallet->setWalletLocked(true); + + } +} + +void WalletModel::UnlockContext::CopyFrom(const UnlockContext& rhs) +{ + // Transfer context; old object no longer relocks wallet + *this = rhs; + rhs.relock = false; +} + +bool WalletModel::getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const +{ + return wallet->GetPubKey(address, vchPubKeyOut); +} + +// returns a list of COutputs from COutPoints +void WalletModel::getOutputs(const std::vector& vOutpoints, std::vector& vOutputs) +{ + BOOST_FOREACH(const COutPoint& outpoint, vOutpoints) + { + if (!wallet->mapWallet.count(outpoint.hash)) continue; + COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, wallet->mapWallet[outpoint.hash].GetDepthInMainChain(), true); + vOutputs.push_back(out); + } +} + +// AvailableCoins + LockedCoins grouped by wallet address (put change in one group with wallet address) +void WalletModel::listCoins(std::map >& mapCoins) const +{ + std::vector vCoins; + wallet->AvailableCoins(vCoins); + std::vector vLockedCoins; + + // add locked coins + BOOST_FOREACH(const COutPoint& outpoint, vLockedCoins) + { + if (!wallet->mapWallet.count(outpoint.hash)) continue; + COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, wallet->mapWallet[outpoint.hash].GetDepthInMainChain(), true); + if (outpoint.n < out.tx->vout.size() && wallet->IsMine(out.tx->vout[outpoint.n]) == MINE_SPENDABLE) + vCoins.push_back(out); + } + + BOOST_FOREACH(const COutput& out, vCoins) + { + COutput cout = out; + + while (wallet->IsChange(cout.tx->vout[cout.i]) && cout.tx->vin.size() > 0 && wallet->IsMine(cout.tx->vin[0])) + { + if (!wallet->mapWallet.count(cout.tx->vin[0].prevout.hash)) break; + cout = COutput(&wallet->mapWallet[cout.tx->vin[0].prevout.hash], cout.tx->vin[0].prevout.n, 0, true); + } + + CTxDestination address; + if(!out.fSpendable || !ExtractDestination(cout.tx->vout[cout.i].scriptPubKey, address)) + continue; + mapCoins[CBitcoinAddress(address).ToString().c_str()].push_back(out); + } +} + +bool WalletModel::isLockedCoin(uint256 hash, unsigned int n) const +{ + return false; +} + +void WalletModel::lockCoin(COutPoint& output) +{ + return; +} + +void WalletModel::unlockCoin(COutPoint& output) +{ + return; +} + +void WalletModel::listLockedCoins(std::vector& vOutpts) +{ + return; +} + +void WalletModel::clearOrphans() +{ + wallet->ClearOrphans(); +} + +CWallet* WalletModel::getWallet() +{ + return wallet; +} \ No newline at end of file diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h new file mode 100644 index 00000000..6aeace74 --- /dev/null +++ b/src/qt/walletmodel.h @@ -0,0 +1,203 @@ +#ifndef WALLETMODEL_H +#define WALLETMODEL_H + +#include +#include +#include + +#include "allocators.h" /* for SecureString */ + +class OptionsModel; +class AddressTableModel; +class TransactionTableModel; +class MintingTableModel; +class CWallet; +class CKeyID; +class CPubKey; +class COutput; +class COutPoint; +class uint256; +class CCoinControl; + +QT_BEGIN_NAMESPACE +class QTimer; +QT_END_NAMESPACE + +class SendCoinsRecipient +{ +public: + QString address; + QString label; + qint64 amount; +}; + +/** Interface to Bitcoin wallet from Qt view code. */ +class WalletModel : public QObject +{ + Q_OBJECT + +public: + explicit WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *parent = 0); + ~WalletModel(); + + enum StatusCode // Returned by sendCoins + { + OK, + InvalidAmount, + InvalidAddress, + AmountExceedsBalance, + AmountWithFeeExceedsBalance, + DuplicateAddress, + TransactionCreationFailed, // Error returned when wallet is still locked + TransactionCommitFailed, + Aborted + }; + + enum EncryptionStatus + { + Unencrypted, // !wallet->IsCrypted() + Locked, // wallet->IsCrypted() && wallet->IsLocked() + Unlocked // wallet->IsCrypted() && !wallet->IsLocked() + }; + + OptionsModel *getOptionsModel(); + AddressTableModel *getAddressTableModel(); + MintingTableModel *getMintingTableModel(); + TransactionTableModel *getTransactionTableModel(); + + bool haveWatchOnly() const; + qint64 getBalance() const; + qint64 getBalanceWatchOnly() const; + qint64 getStake() const; + qint64 getUnconfirmedBalance() const; + qint64 getImmatureBalance() const; + int getNumTransactions() const; + EncryptionStatus getEncryptionStatus() const; + + // Check address for validity + bool validateAddress(const QString &address); + + // Return status record for SendCoins, contains error id + information + struct SendCoinsReturn + { + SendCoinsReturn(StatusCode status=Aborted, + qint64 fee=0, + QString hex=QString()): + status(status), fee(fee), hex(hex) {} + StatusCode status; + qint64 fee; // is used in case status is "AmountWithFeeExceedsBalance" + QString hex; // is filled with the transaction hash if status is "OK" + }; + + // Send coins to a list of recipients + SendCoinsReturn sendCoins(const QList &recipients, const CCoinControl *coinControl=NULL); + + // Wallet encryption + bool setWalletEncrypted(bool encrypted, const SecureString &passphrase); + // Passphrase only needed when unlocking + bool setWalletLocked(bool locked, const SecureString &passPhrase=SecureString()); + bool changePassphrase(const SecureString &oldPass, const SecureString &newPass); + // Wallet backup + bool backupWallet(const QString &filename); + + bool dumpWallet(const QString &filename); + bool importWallet(const QString &filename); + + void getStakeWeightFromValue(const int64_t& nTime, const int64_t& nValue, uint64_t& nWeight); + + // RAI object for unlocking wallet, returned by requestUnlock() + class UnlockContext + { + public: + UnlockContext(WalletModel *wallet, bool valid, bool relock, bool mintflag); + ~UnlockContext(); + + bool isValid() const { return valid; } + + // Copy operator and constructor transfer the context + UnlockContext(const UnlockContext& obj) { CopyFrom(obj); } + UnlockContext& operator=(const UnlockContext& rhs) { CopyFrom(rhs); return *this; } + private: + WalletModel *wallet; + bool valid; + mutable bool relock; // mutable, as it can be set to false by copying + bool mintflag; + + void CopyFrom(const UnlockContext& rhs); + }; + + UnlockContext requestUnlock(); + + bool getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const; + void getOutputs(const std::vector& vOutpoints, std::vector& vOutputs); + void listCoins(std::map >& mapCoins) const; + bool isLockedCoin(uint256 hash, unsigned int n) const; + void lockCoin(COutPoint& output); + void unlockCoin(COutPoint& output); + void listLockedCoins(std::vector& vOutpts); + void clearOrphans(); + CWallet* getWallet(); + +private: + CWallet *wallet; + bool fHaveWatchOnly; + + // Wallet has an options model for wallet-specific options + // (transaction fee, for example) + OptionsModel *optionsModel; + + AddressTableModel *addressTableModel; + MintingTableModel *mintingTableModel; + TransactionTableModel *transactionTableModel; + + // Cache some values to be able to detect changes + qint64 cachedBalance; + qint64 cachedStake; + qint64 cachedUnconfirmedBalance; + qint64 cachedImmatureBalance; + qint64 cachedNumTransactions; + EncryptionStatus cachedEncryptionStatus; + int cachedNumBlocks; + + QTimer *pollTimer; + + void subscribeToCoreSignals(); + void unsubscribeFromCoreSignals(); + void checkBalanceChanged(); + +public slots: + /* Wallet status might have changed */ + void updateStatus(); + /* New transaction, or transaction changed status */ + void updateTransaction(const QString &hash, int status); + /* New, updated or removed address book entry */ + void updateAddressBook(const QString &address, const QString &label, bool isMine, int status); + /* Watchonly added */ + void updateWatchOnlyFlag(bool fHaveWatchonly); + /* Current, immature or unconfirmed balance might have changed - emit 'balanceChanged' if so */ + void pollBalanceChanged(); + +signals: + // Signal that balance in wallet changed + void balanceChanged(qint64 total, qint64 watchOnly, qint64 stake, qint64 unconfirmedBalance, qint64 immatureBalance); + + // 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 + void error(const QString &title, const QString &message, bool modal); + + // Watch-only address added + void notifyWatchonlyChanged(bool fHaveWatchonly); +}; + + +#endif // WALLETMODEL_H diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp new file mode 100644 index 00000000..858fc812 --- /dev/null +++ b/src/rpcblockchain.cpp @@ -0,0 +1,413 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "main.h" +#include "bitcoinrpc.h" +#include +#include +#include +#include + +using namespace json_spirit; +using namespace std; + +extern void TxToJSON(const CTransaction& tx, const uint256& hashBlock, json_spirit::Object& entry); +extern enum Checkpoints::CPMode CheckpointsMode; + +double GetDifficulty(const CBlockIndex* blockindex) +{ + // Floating point number that is a multiple of the minimum difficulty, + // minimum difficulty = 1.0. + if (blockindex == NULL) + { + if (pindexBest == NULL) + return 1.0; + else + blockindex = GetLastBlockIndex(pindexBest, false); + } + + int nShift = (blockindex->nBits >> 24) & 0xff; + + double dDiff = + (double)0x0000ffff / (double)(blockindex->nBits & 0x00ffffff); + + while (nShift < 29) + { + dDiff *= 256.0; + nShift++; + } + while (nShift > 29) + { + dDiff /= 256.0; + nShift--; + } + + return dDiff; +} + +double GetPoWMHashPS() +{ + if (pindexBest->nHeight >= LAST_POW_BLOCK) + return 0; + + int nPoWInterval = 72; + int64_t nTargetSpacingWorkMin = 30, nTargetSpacingWork = 30; + + CBlockIndex* pindex = pindexGenesisBlock; + CBlockIndex* pindexPrevWork = pindexGenesisBlock; + + while (pindex) + { + if (pindex->IsProofOfWork()) + { + int64_t nActualSpacingWork = pindex->GetBlockTime() - pindexPrevWork->GetBlockTime(); + nTargetSpacingWork = ((nPoWInterval - 1) * nTargetSpacingWork + nActualSpacingWork + nActualSpacingWork) / (nPoWInterval + 1); + nTargetSpacingWork = max(nTargetSpacingWork, nTargetSpacingWorkMin); + pindexPrevWork = pindex; + } + + pindex = pindex->pnext; + } + + return GetDifficulty() * 4294.967296 / nTargetSpacingWork; +} + +double GetPoSKernelPS() +{ + int nPoSInterval = 72; + double dStakeKernelsTriedAvg = 0; + int nStakesHandled = 0, nStakesTime = 0; + + CBlockIndex* pindex = pindexBest;; + CBlockIndex* pindexPrevStake = NULL; + + while (pindex && nStakesHandled < nPoSInterval) + { + if (pindex->IsProofOfStake()) + { + dStakeKernelsTriedAvg += GetDifficulty(pindex) * 4294967296.0; + nStakesTime += pindexPrevStake ? (pindexPrevStake->nTime - pindex->nTime) : 0; + pindexPrevStake = pindex; + nStakesHandled++; + } + + pindex = pindex->pprev; + } + + if (!nStakesHandled) + return 0; + + return dStakeKernelsTriedAvg / nStakesTime; +} + +Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool fPrintTransactionDetail) +{ + Object result; + result.push_back(Pair("hash", block.GetHash().GetHex())); + CMerkleTx txGen(block.vtx[0]); + txGen.SetMerkleBranch(&block); + result.push_back(Pair("confirmations", (int)txGen.GetDepthInMainChain())); + result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION))); + result.push_back(Pair("height", blockindex->nHeight)); + result.push_back(Pair("version", block.nVersion)); + result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); + result.push_back(Pair("mint", ValueFromAmount(blockindex->nMint))); + result.push_back(Pair("time", (int64_t)block.GetBlockTime())); + result.push_back(Pair("nonce", (uint64_t)block.nNonce)); + result.push_back(Pair("bits", HexBits(block.nBits))); + result.push_back(Pair("difficulty", GetDifficulty(blockindex))); + result.push_back(Pair("blocktrust", leftTrim(blockindex->GetBlockTrust().GetHex(), '0'))); + result.push_back(Pair("chaintrust", leftTrim(blockindex->nChainTrust.GetHex(), '0'))); + if (blockindex->pprev) + result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); + if (blockindex->pnext) + result.push_back(Pair("nextblockhash", blockindex->pnext->GetBlockHash().GetHex())); + + result.push_back(Pair("flags", strprintf("%s%s", blockindex->IsProofOfStake()? "proof-of-stake" : "proof-of-work", blockindex->GeneratedStakeModifier()? " stake-modifier": ""))); + result.push_back(Pair("proofhash", blockindex->IsProofOfStake()? blockindex->hashProofOfStake.GetHex() : blockindex->GetBlockHash().GetHex())); + result.push_back(Pair("entropybit", (int)blockindex->GetStakeEntropyBit())); + result.push_back(Pair("modifier", strprintf("%016" PRIx64, blockindex->nStakeModifier))); + result.push_back(Pair("modifierchecksum", strprintf("%08x", blockindex->nStakeModifierChecksum))); + Array txinfo; + BOOST_FOREACH (const CTransaction& tx, block.vtx) + { + if (fPrintTransactionDetail) + { + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); + ssTx << tx; + string strHex = HexStr(ssTx.begin(), ssTx.end()); + + txinfo.push_back(strHex); + } + else + txinfo.push_back(tx.GetHash().GetHex()); + } + + result.push_back(Pair("tx", txinfo)); + + if ( block.IsProofOfStake() ) + result.push_back(Pair("signature", HexStr(block.vchBlockSig.begin(), block.vchBlockSig.end()))); + + return result; +} + +Value getbestblockhash(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getbestblockhash\n" + "Returns the hash of the best block in the longest block chain."); + + return hashBestChain.GetHex(); +} + +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 getdifficulty(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getdifficulty\n" + "Returns the difficulty as a multiple of the minimum difficulty."); + + Object obj; + obj.push_back(Pair("proof-of-work", GetDifficulty())); + obj.push_back(Pair("proof-of-stake", GetDifficulty(GetLastBlockIndex(pindexBest, true)))); + obj.push_back(Pair("search-interval", (int)nLastCoinStakeSearchInterval)); + return obj; +} + + +Value settxfee(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 1 || AmountFromValue(params[0]) < MIN_TX_FEE) + throw runtime_error( + "settxfee \n" + " is a real and is rounded to the nearest " + FormatMoney(MIN_TX_FEE)); + + nTransactionFee = AmountFromValue(params[0]); + nTransactionFee = (nTransactionFee / MIN_TX_FEE) * MIN_TX_FEE; // round to minimum fee + + return true; +} + +Value getrawmempool(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getrawmempool\n" + "Returns all transaction ids in memory pool."); + + vector vtxid; + mempool.queryHashes(vtxid); + + Array a; + BOOST_FOREACH(const uint256& hash, vtxid) + a.push_back(hash.ToString()); + + return a; +} + +Value getblockhash(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getblockhash \n" + "Returns hash of block in best-block-chain at ."); + + int nHeight = params[0].get_int(); + if (nHeight < 0 || nHeight > nBestHeight) + throw runtime_error("Block number out of range."); + + CBlockIndex* pblockindex = FindBlockByHeight(nHeight); + return pblockindex->phashBlock->GetHex(); +} + +Value getblock(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "getblock [txinfo]\n" + "txinfo optional to print more detailed tx info\n" + "Returns details of a block with given block-hash."); + + std::string strHash = params[0].get_str(); + uint256 hash(strHash); + + if (mapBlockIndex.count(hash) == 0) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + + CBlock block; + CBlockIndex* pblockindex = mapBlockIndex[hash]; + block.ReadFromDisk(pblockindex, true); + + return blockToJSON(block, pblockindex, params.size() > 1 ? params[1].get_bool() : false); +} + +Value getblockbynumber(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "getblockbynumber [txinfo]\n" + "txinfo optional to print more detailed tx info\n" + "Returns details of a block with given block-number."); + + int nHeight = params[0].get_int(); + if (nHeight < 0 || nHeight > nBestHeight) + throw runtime_error("Block number out of range."); + + CBlock block; + CBlockIndex* pblockindex = mapBlockIndex[hashBestChain]; + while (pblockindex->nHeight > nHeight) + pblockindex = pblockindex->pprev; + + pblockindex = mapBlockIndex[*pblockindex->phashBlock]; + block.ReadFromDisk(pblockindex, true); + + return blockToJSON(block, pblockindex, params.size() > 1 ? params[1].get_bool() : false); +} + +bool ExportBlock(const string& strBlockHash, const CDataStream& ssBlock) +{ + boost::filesystem::path pathDest = GetDataDir() / strBlockHash; + if (boost::filesystem::is_directory(pathDest)) + pathDest /= strBlockHash; + + try { + boost::iostreams::stream_buffer buf(pathDest.string()); + ostream exportStream(&buf); + exportStream << HexStr(ssBlock.begin(), ssBlock.end()); + exportStream.flush(); + + printf("Successfully exported block to %s\n", pathDest.string().c_str()); + return true; + } catch(const boost::filesystem::filesystem_error &e) { + printf("error exporting the block data %s (%s)\n", pathDest.string().c_str(), e.what()); + return false; + } +} + + +Value dumpblock(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "dumpblock [destination]\n" + "Returns serialized contents of a block with given block-hash."); + + std::string strHash = params[0].get_str(); + uint256 hash(strHash); + + if (mapBlockIndex.count(hash) == 0) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + + CBlock block; + CBlockIndex* pblockindex = mapBlockIndex[hash]; + block.ReadFromDisk(pblockindex, true); + + CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); + ssBlock << block; + + if (params.size() > 1) + { + return ExportBlock(params[1].get_str(), ssBlock); + } + + return HexStr(ssBlock.begin(), ssBlock.end()); +} + + +Value dumpblockbynumber(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "dumpblockbynumber [destination]\n" + "Returns serialized contents of a block with given block-number."); + + int nHeight = params[0].get_int(); + if (nHeight < 0 || nHeight > nBestHeight) + throw runtime_error("Block number out of range."); + + CBlock block; + CBlockIndex* pblockindex = mapBlockIndex[hashBestChain]; + while (pblockindex->nHeight > nHeight) + pblockindex = pblockindex->pprev; + + pblockindex = mapBlockIndex[*pblockindex->phashBlock]; + block.ReadFromDisk(pblockindex, true); + + CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); + ssBlock << block; + + if (params.size() > 1) + { + return ExportBlock(params[1].get_str(), ssBlock); + } + + return HexStr(ssBlock.begin(), ssBlock.end()); +} + + +// get information of sync-checkpoint +Value getcheckpoint(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getcheckpoint\n" + "Show info of synchronized checkpoint.\n"); + + Object result; + CBlockIndex* pindexCheckpoint; + + result.push_back(Pair("synccheckpoint", Checkpoints::hashSyncCheckpoint.ToString().c_str())); + pindexCheckpoint = mapBlockIndex[Checkpoints::hashSyncCheckpoint]; + result.push_back(Pair("height", pindexCheckpoint->nHeight)); + result.push_back(Pair("timestamp", DateTimeStrFormat(pindexCheckpoint->GetBlockTime()).c_str())); + + if (Checkpoints::checkpointMessage.vchSig.size() != 0) + { + Object msgdata; + CUnsignedSyncCheckpoint checkpoint; + + CDataStream sMsg(Checkpoints::checkpointMessage.vchMsg, SER_NETWORK, PROTOCOL_VERSION); + sMsg >> checkpoint; + + Object parsed; // message version and data (block hash) + parsed.push_back(Pair("version", checkpoint.nVersion)); + parsed.push_back(Pair("hash", checkpoint.hashCheckpoint.GetHex().c_str())); + msgdata.push_back(Pair("parsed", parsed)); + + Object raw; // raw checkpoint message data + raw.push_back(Pair("data", HexStr(Checkpoints::checkpointMessage.vchMsg).c_str())); + raw.push_back(Pair("signature", HexStr(Checkpoints::checkpointMessage.vchSig).c_str())); + msgdata.push_back(Pair("raw", raw)); + + result.push_back(Pair("data", msgdata)); + } + + // Check that the block satisfies synchronized checkpoint + if (CheckpointsMode == Checkpoints::STRICT) + result.push_back(Pair("policy", "strict")); + + if (CheckpointsMode == Checkpoints::ADVISORY) + result.push_back(Pair("policy", "advisory")); + + if (CheckpointsMode == Checkpoints::PERMISSIVE) + result.push_back(Pair("policy", "permissive")); + + if (mapArgs.count("-checkpointkey")) + result.push_back(Pair("checkpointmaster", true)); + + return result; +} diff --git a/src/rpcdump.cpp b/src/rpcdump.cpp new file mode 100644 index 00000000..b9270671 --- /dev/null +++ b/src/rpcdump.cpp @@ -0,0 +1,241 @@ +// Copyright (c) 2009-2012 Bitcoin Developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "init.h" // for pwalletMain +#include "bitcoinrpc.h" +#include "ui_interface.h" +#include "base58.h" + +#define printf OutputDebugStringF + +using namespace json_spirit; +using namespace std; + +void EnsureWalletIsUnlocked(); + +class CTxDump +{ +public: + CBlockIndex *pindex; + int64_t nValue; + bool fSpent; + CWalletTx* ptx; + int nOut; + CTxDump(CWalletTx* ptx = NULL, int nOut = -1) + { + pindex = NULL; + nValue = 0; + fSpent = false; + this->ptx = ptx; + this->nOut = nOut; + } +}; + +Value importprivkey(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 3) + throw runtime_error( + "importprivkey [label] [rescan=true]\n" + "Adds a private key (as returned by dumpprivkey) to your wallet."); + + string strSecret = params[0].get_str(); + string strLabel = ""; + if (params.size() > 1) + strLabel = params[1].get_str(); + + // Whether to perform rescan after import + bool fRescan = true; + if (params.size() > 2) + fRescan = params[2].get_bool(); + + CBitcoinSecret vchSecret; + bool fGood = vchSecret.SetString(strSecret); + + if (!fGood) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); + if (fWalletUnlockMintOnly) // ppcoin: no importprivkey in mint-only mode + throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Wallet is unlocked for minting only."); + + CKey key; + bool fCompressed; + CSecret secret = vchSecret.GetSecret(fCompressed); + key.SetSecret(secret, fCompressed); + CKeyID vchAddress = key.GetPubKey().GetID(); + { + LOCK2(cs_main, pwalletMain->cs_wallet); + + pwalletMain->MarkDirty(); + pwalletMain->SetAddressBookName(vchAddress, strLabel); + + // Don't throw error in case a key is already there + if (pwalletMain->HaveKey(vchAddress)) + return Value::null; + + pwalletMain->mapKeyMetadata[vchAddress].nCreateTime = 1; + + if (!pwalletMain->AddKey(key)) + throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet"); + + // whenever a key is imported, we need to scan the whole chain + pwalletMain->nTimeFirstKey = 1; // 0 would be considered 'no value' + + if (fRescan) + { + pwalletMain->ScanForWalletTransactions(pindexGenesisBlock, true); + pwalletMain->ReacceptWalletTransactions(); + } + } + + return Value::null; +} + +Value importaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 3) + throw runtime_error( + "importaddress
[label] [rescan=true]\n" + "Adds an address or script (in hex) that can be watched as if it were in your wallet but cannot be used to spend."); + + CScript script; + CBitcoinAddress address(params[0].get_str()); + if (address.IsValid()) { + script.SetDestination(address.Get()); + } else if (IsHex(params[0].get_str())) { + std::vector data(ParseHex(params[0].get_str())); + script = CScript(data.begin(), data.end()); + } else { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid XP address or script"); + } + + string strLabel = ""; + if (params.size() > 1) + strLabel = params[1].get_str(); + + // Whether to perform rescan after import + bool fRescan = true; + if (params.size() > 2) + fRescan = params[2].get_bool(); + + { + LOCK2(cs_main, pwalletMain->cs_wallet); + if (::IsMine(*pwalletMain, script) == MINE_SPENDABLE) + throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script"); + + // Don't throw error in case an address is already there + if (pwalletMain->HaveWatchOnly(script)) + return Value::null; + + pwalletMain->MarkDirty(); + + if (address.IsValid()) + pwalletMain->SetAddressBookName(address.Get(), strLabel); + + if (!pwalletMain->AddWatchOnly(script)) + throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); + + if (fRescan) + { + pwalletMain->ScanForWalletTransactions(pindexGenesisBlock, true); + pwalletMain->ReacceptWalletTransactions(); + } + } + + return Value::null; +} + +Value removeaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "removeaddress 'address'\n" + "\nRemoves watch-only address or script (in hex) added by importaddress.\n" + "\nArguments:\n" + "1. 'address' (string, required) The address\n" + "\nExamples:\n" + "\nremoveaddress 4EqHMPgEAf56CQmU6ZWS8Ug4d7N3gsQVQA\n" + "\nRemove watch-only address 4EqHMPgEAf56CQmU6ZWS8Ug4d7N3gsQVQA\n"); + + CScript script; + + CBitcoinAddress address(params[0].get_str()); + if (address.IsValid()) { + script.SetDestination(address.Get()); + } else if (IsHex(params[0].get_str())) { + std::vector data(ParseHex(params[0].get_str())); + script = CScript(data.begin(), data.end()); + } else { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address or script"); + } + + if (::IsMine(*pwalletMain, script) == MINE_SPENDABLE) + throw JSONRPCError(RPC_WALLET_ERROR, "The wallet contains the private key for this address or script - can't remove it"); + + if (!pwalletMain->HaveWatchOnly(script)) + throw JSONRPCError(RPC_WALLET_ERROR, "The wallet does not contain this address or script"); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + pwalletMain->MarkDirty(); + + if (!pwalletMain->RemoveWatchOnly(script)) + throw JSONRPCError(RPC_WALLET_ERROR, "Error removing address from wallet"); + + return Value::null; +} + +Value importwallet(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "importwallet \n" + "Imports keys from a wallet dump file (see dumpwallet)." + + HelpRequiringPassphrase()); + + EnsureWalletIsUnlocked(); + + if(!ImportWallet(pwalletMain, params[0].get_str().c_str())) + throw JSONRPCError(RPC_WALLET_ERROR, "Error adding some keys to wallet"); + + return Value::null; +} + +Value dumpprivkey(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "dumpprivkey \n" + "Reveals the private key corresponding to ."); + + EnsureWalletIsUnlocked(); + + string strAddress = params[0].get_str(); + CBitcoinAddress address; + if (!address.SetString(strAddress)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid XP address"); + if (fWalletUnlockMintOnly) // ppcoin: no dumpprivkey in mint-only mode + throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Wallet is unlocked for minting only."); + CKeyID keyID; + if (!address.GetKeyID(keyID)) + throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key"); + CSecret vchSecret; + bool fCompressed; + if (!pwalletMain->GetSecret(keyID, vchSecret, fCompressed)) + throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known"); + return CBitcoinSecret(vchSecret, fCompressed).ToString(); +} + +Value dumpwallet(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "dumpwallet \n" + "Dumps all wallet keys in a human-readable format." + + HelpRequiringPassphrase()); + + EnsureWalletIsUnlocked(); + + if(!DumpWallet(pwalletMain, params[0].get_str().c_str() )) + throw JSONRPCError(RPC_WALLET_ERROR, "Error dumping wallet keys to file"); + + return Value::null; +} diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp new file mode 100644 index 00000000..0efaa933 --- /dev/null +++ b/src/rpcmining.cpp @@ -0,0 +1,664 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "main.h" +#include "db.h" +#include "txdb.h" +#include "init.h" +#include "miner.h" +#include "kernel.h" +#include "bitcoinrpc.h" + +#include +#include +#include + +using namespace json_spirit; +using namespace std; + +extern uint256 nPoWBase; +extern uint64_t nStakeInputsMapSize; + +Value getsubsidy(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1 || fHelp || params.size() < 99 ) + throw runtime_error( + "getsubsidy [nTarget]\n" + "Currently not in use."); + + unsigned int nBits = 0; + + if (params.size() != 0) + { + CBigNum bnTarget(uint256(params[0].get_str())); + nBits = bnTarget.GetCompact(); + } + else + { + nBits = GetNextTargetRequired(pindexBest, false); + } + + return (uint64_t)GetProofOfWorkReward(nBits, 0, 0); +} + +Value getmininginfo(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getmininginfo\n" + "Returns an object containing mining-related information."); + + Object obj, diff, weight; + obj.push_back(Pair("blocks", (int)nBestHeight)); + obj.push_back(Pair("currentblocksize",(uint64_t)nLastBlockSize)); + obj.push_back(Pair("currentblocktx",(uint64_t)nLastBlockTx)); + + diff.push_back(Pair("proof-of-work", GetDifficulty())); + diff.push_back(Pair("proof-of-stake", GetDifficulty(GetLastBlockIndex(pindexBest, true)))); + diff.push_back(Pair("search-interval", (int)nLastCoinStakeSearchInterval)); + obj.push_back(Pair("difficulty", diff)); + + obj.push_back(Pair("blockvalue", (uint64_t)GetProofOfWorkReward(GetLastBlockIndex(pindexBest, false)->nBits, 0, pindexBest->nHeight))); + obj.push_back(Pair("netmhashps", GetPoWMHashPS())); + obj.push_back(Pair("netstakeweight",GetPoSKernelPS())); + obj.push_back(Pair("errors", GetWarnings("statusbar"))); + obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); + + obj.push_back(Pair("stakeinputs", (uint64_t)nStakeInputsMapSize)); + obj.push_back(Pair("stakeinterest", GetProofOfStakeReward(0, GetLastBlockIndex(pindexBest, true)->nBits, GetLastBlockIndex(pindexBest, true)->nTime, true))); + + obj.push_back(Pair("testnet", fTestNet)); + return obj; +} + +// scaninput '{"txid":"95d640426fe66de866a8cf2d0601d2c8cf3ec598109b4d4ffa7fd03dad6d35ce","difficulty":0.01, "days":10}' +Value scaninput(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "scaninput '{\"txid\":\"txid\", \"vout\":[vout1, vout2, ..., voutN], \"difficulty\":difficulty, \"days\":days}'\n" + "Scan specified transaction or input for suitable kernel solutions.\n" + " difficulty - upper limit for difficulty, current difficulty by default;\n" + " days - time window, 90 days by default.\n" + ); + + RPCTypeCheck(params, boost::assign::list_of(obj_type)); + + Object scanParams = params[0].get_obj(); + + const Value& txid_v = find_value(scanParams, "txid"); + if (txid_v.type() != str_type) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing txid key"); + + string txid = txid_v.get_str(); + if (!IsHex(txid)) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid"); + + uint256 hash(txid); + int32_t nDays = 90; + uint32_t nBits = GetNextTargetRequired(pindexBest, true); + + const Value& diff_v = find_value(scanParams, "difficulty"); + if (diff_v.type() == real_type || diff_v.type() == int_type) + { + double dDiff = diff_v.get_real(); + if (dDiff <= 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, diff must be greater than zero"); + + CBigNum bnTarget(nPoWBase); + bnTarget *= 1000; + bnTarget /= (int) (dDiff * 1000); + nBits = bnTarget.GetCompact(); + } + + const Value& days_v = find_value(scanParams, "days"); + if (days_v.type() == int_type) + { + nDays = days_v.get_int(); + if (nDays <= 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, interval length must be greater than zero"); + } + + + CTransaction tx; + uint256 hashBlock = 0; + if (GetTransaction(hash, tx, hashBlock)) + { + if (hashBlock == 0) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to find transaction in the blockchain"); + + vector vInputs(0); + const Value& inputs_v = find_value(scanParams, "vout"); + if (inputs_v.type() == array_type) + { + Array inputs = inputs_v.get_array(); + BOOST_FOREACH(const Value &v_out, inputs) + { + int nOut = v_out.get_int(); + if (nOut < 0 || nOut > (int)tx.vout.size() - 1) + { + stringstream strErrorMsg; + strErrorMsg << boost::format("Invalid parameter, input number %d is out of range") % nOut; + throw JSONRPCError(RPC_INVALID_PARAMETER, strErrorMsg.str()); + } + + vInputs.push_back(nOut); + } + } + else if(inputs_v.type() == int_type) + { + int nOut = inputs_v.get_int(); + if (nOut < 0 || nOut > (int)tx.vout.size() - 1) + { + stringstream strErrorMsg; + strErrorMsg << boost::format("Invalid parameter, input number %d is out of range") % nOut; + throw JSONRPCError(RPC_INVALID_PARAMETER, strErrorMsg.str()); + } + + vInputs.push_back(nOut); + } + else + { + vInputs = vector(boost::counting_iterator( 0 ), boost::counting_iterator( tx.vout.size() )); + } + + CTxDB txdb("r"); + + CBlock block; + CTxIndex txindex; + + // Load transaction index item + if (!txdb.ReadTxIndex(tx.GetHash(), txindex)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to read block index item"); + + // Read block header + if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "CBlock::ReadFromDisk() failed"); + + uint64_t nStakeModifier = 0; + if (!GetKernelStakeModifier(block.GetHash(), nStakeModifier)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No kernel stake modifier generated yet"); + + std::pair interval; + interval.first = GetTime(); + // Only count coins meeting min age requirement + if (nStakeMinAge + block.nTime > interval.first) + interval.first += (nStakeMinAge + block.nTime - interval.first); + interval.second = interval.first + nDays * nOneDay; + + Array results; + BOOST_FOREACH(const int &nOut, vInputs) + { + // Check for spent flag + // It doesn't make sense to scan spent inputs. + if (!txindex.vSpent[nOut].IsNull()) + continue; + + // Skip zero value outputs + if (tx.vout[nOut].nValue == 0) + continue; + + // Build static part of kernel + CDataStream ssKernel(SER_GETHASH, 0); + ssKernel << nStakeModifier; + ssKernel << block.nTime << (txindex.pos.nTxPos - txindex.pos.nBlockPos) << tx.nTime << nOut; + CDataStream::const_iterator itK = ssKernel.begin(); + + std::vector > result; + if (ScanKernelForward((unsigned char *)&itK[0], nBits, tx.nTime, tx.vout[nOut].nValue, interval, result)) + { + BOOST_FOREACH(const PAIRTYPE(uint256, uint32_t) solution, result) + { + Object item; + item.push_back(Pair("nout", nOut)); + item.push_back(Pair("hash", solution.first.GetHex())); + item.push_back(Pair("time", DateTimeStrFormat(solution.second))); + + results.push_back(item); + } + } + } + + if (results.size() == 0) + return false; + + return results; + } + else + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction"); +} + +Value getworkex(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "getworkex [data, coinbase]\n" + "If [data, coinbase] is not specified, returns extended work data.\n" + ); + + if (vNodes.empty()) + throw JSONRPCError(-9, "XP is not connected!"); + + if (IsInitialBlockDownload()) + throw JSONRPCError(-10, "XP is downloading blocks..."); + + if (pindexBest->nHeight >= LAST_POW_BLOCK) + throw JSONRPCError(RPC_MISC_ERROR, "No more PoW blocks"); + + typedef map > mapNewBlock_t; + static mapNewBlock_t mapNewBlock; + static vector vNewBlock; + static CReserveKey reservekey(pwalletMain); + + if (params.size() == 0) + { + // Update block + static unsigned int nTransactionsUpdatedLast; + static CBlockIndex* pindexPrev; + static int64_t 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(pwalletMain); + 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; + IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); + + // Save + mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig); + + // Prebuild hash buffers + char pmidstate[32]; + char pdata[128]; + char phash1[64]; + FormatHashBuffers(pblock, pmidstate, pdata, phash1); + + uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + + CTransaction coinbaseTx = pblock->vtx[0]; + std::vector merkle = pblock->GetMerkleBranch(0); + + Object result; + result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata)))); + result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget)))); + + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); + ssTx << coinbaseTx; + result.push_back(Pair("coinbase", HexStr(ssTx.begin(), ssTx.end()))); + + Array merkle_arr; + + BOOST_FOREACH(uint256 merkleh, merkle) { + merkle_arr.push_back(HexStr(BEGIN(merkleh), END(merkleh))); + } + + result.push_back(Pair("merkle", merkle_arr)); + + + return result; + } + else + { + // Parse parameters + vector vchData = ParseHex(params[0].get_str()); + vector coinbase; + + if(params.size() == 2) + coinbase = ParseHex(params[1].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] = ByteReverse(((unsigned int*)pdata)[i]); + + // Get saved block + if (!mapNewBlock.count(pdata->hashMerkleRoot)) + return false; + CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first; + + pblock->nTime = pdata->nTime; + pblock->nNonce = pdata->nNonce; + + if(coinbase.size() == 0) + pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second; + else + CDataStream(coinbase, SER_NETWORK, PROTOCOL_VERSION) >> pblock->vtx[0]; // FIXME - HACK! + + pblock->hashMerkleRoot = pblock->BuildMerkleTree(); + + return CheckWork(pblock, *pwalletMain, reservekey); + } +} + + +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 (DEPRECATED)\n" // deprecated + " \"data\" : block data\n" + " \"hash1\" : formatted hash buffer for second hash (DEPRECATED)\n" // deprecated + " \"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(RPC_CLIENT_NOT_CONNECTED, "XP is not connected!"); + + if (IsInitialBlockDownload()) + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "XP is downloading blocks..."); + + if (pindexBest->nHeight >= LAST_POW_BLOCK) + throw JSONRPCError(RPC_MISC_ERROR, "No more PoW blocks"); + + typedef map > mapNewBlock_t; + static mapNewBlock_t mapNewBlock; // FIXME: thread safety + static vector vNewBlock; + static CReserveKey reservekey(pwalletMain); + + if (params.size() == 0) + { + // Update block + static unsigned int nTransactionsUpdatedLast; + static CBlockIndex* pindexPrev; + static int64_t 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(); + } + + // Clear pindexPrev so future getworks make a new block, despite any failures from here on + pindexPrev = NULL; + + // Store the pindexBest used before CreateNewBlock, to avoid races + nTransactionsUpdatedLast = nTransactionsUpdated; + CBlockIndex* pindexPrevNew = pindexBest; + nStart = GetTime(); + + // Create new block + pblock = CreateNewBlock(pwalletMain); + if (!pblock) + throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); + vNewBlock.push_back(pblock); + + // Need to update only after we know CreateNewBlock succeeded + pindexPrev = pindexPrevNew; + } + + // Update nTime + pblock->UpdateTime(pindexPrev); + pblock->nNonce = 0; + + // Update nExtraNonce + static unsigned int nExtraNonce = 0; + IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); + + // Save + mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig); + + // Pre-build 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)))); // deprecated + result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata)))); + result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); // deprecated + 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(RPC_INVALID_PARAMETER, "Invalid parameter"); + CBlock* pdata = (CBlock*)&vchData[0]; + + // Byte reverse + for (int i = 0; i < 128/4; i++) + ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]); + + // Get saved block + if (!mapNewBlock.count(pdata->hashMerkleRoot)) + return false; + CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first; + + pblock->nTime = pdata->nTime; + pblock->nNonce = pdata->nNonce; + pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second; + pblock->hashMerkleRoot = pblock->BuildMerkleTree(); + + return CheckWork(pblock, *pwalletMain, reservekey); + } +} + + +Value getblocktemplate(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "getblocktemplate [params]\n" + "Returns data needed to construct a block to work on:\n" + " \"version\" : block version\n" + " \"previousblockhash\" : hash of current highest block\n" + " \"transactions\" : contents of non-coinbase transactions that should be included in the next block\n" + " \"coinbaseaux\" : data that should be included in coinbase\n" + " \"coinbasevalue\" : maximum allowable input to coinbase transaction, including the generation award and transaction fees\n" + " \"target\" : hash target\n" + " \"mintime\" : minimum timestamp appropriate for next block\n" + " \"curtime\" : current timestamp\n" + " \"mutable\" : list of ways the block template may be changed\n" + " \"noncerange\" : range of valid nonces\n" + " \"sigoplimit\" : limit of sigops in blocks\n" + " \"sizelimit\" : limit of block size\n" + " \"bits\" : compressed target of next block\n" + " \"height\" : height of the next block\n" + "See https://en.bitcoin.it/wiki/BIP_0022 for full specification."); + + std::string strMode = "template"; + if (params.size() > 0) + { + const Object& oparam = params[0].get_obj(); + const Value& modeval = find_value(oparam, "mode"); + if (modeval.type() == str_type) + strMode = modeval.get_str(); + else if (modeval.type() == null_type) + { + /* Do nothing */ + } + else + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); + } + + if (strMode != "template") + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); + + if (vNodes.empty()) + throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "XP is not connected!"); + + if (IsInitialBlockDownload()) + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "XP is downloading blocks..."); + + if (pindexBest->nHeight >= LAST_POW_BLOCK) + throw JSONRPCError(RPC_MISC_ERROR, "No more PoW blocks"); + + static CReserveKey reservekey(pwalletMain); + + // Update block + static unsigned int nTransactionsUpdatedLast; + static CBlockIndex* pindexPrev; + static int64_t nStart; + static CBlock* pblock; + if (pindexPrev != pindexBest || + (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5)) + { + // Clear pindexPrev so future calls make a new block, despite any failures from here on + pindexPrev = NULL; + + // Store the pindexBest used before CreateNewBlock, to avoid races + nTransactionsUpdatedLast = nTransactionsUpdated; + CBlockIndex* pindexPrevNew = pindexBest; + nStart = GetTime(); + + // Create new block + if(pblock) + { + delete pblock; + pblock = NULL; + } + pblock = CreateNewBlock(pwalletMain); + if (!pblock) + throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); + + // Need to update only after we know CreateNewBlock succeeded + pindexPrev = pindexPrevNew; + } + + // Update nTime + pblock->UpdateTime(pindexPrev); + pblock->nNonce = 0; + + Array transactions; + map setTxIndex; + int i = 0; + CTxDB txdb("r"); + BOOST_FOREACH (CTransaction& tx, pblock->vtx) + { + uint256 txHash = tx.GetHash(); + setTxIndex[txHash] = i++; + + if (tx.IsCoinBase() || tx.IsCoinStake()) + continue; + + Object entry; + + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); + ssTx << tx; + entry.push_back(Pair("data", HexStr(ssTx.begin(), ssTx.end()))); + + entry.push_back(Pair("hash", txHash.GetHex())); + + MapPrevTx mapInputs; + map mapUnused; + bool fInvalid = false; + if (tx.FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid)) + { + entry.push_back(Pair("fee", (int64_t)(tx.GetValueIn(mapInputs) - tx.GetValueOut()))); + + Array deps; + BOOST_FOREACH (MapPrevTx::value_type& inp, mapInputs) + { + if (setTxIndex.count(inp.first)) + deps.push_back(setTxIndex[inp.first]); + } + entry.push_back(Pair("depends", deps)); + + int64_t nSigOps = tx.GetLegacySigOpCount(); + nSigOps += tx.GetP2SHSigOpCount(mapInputs); + entry.push_back(Pair("sigops", nSigOps)); + } + + transactions.push_back(entry); + } + + Object aux; + aux.push_back(Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end()))); + + uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + + static Array aMutable; + if (aMutable.empty()) + { + aMutable.push_back("time"); + aMutable.push_back("transactions"); + aMutable.push_back("prevblock"); + } + + Object result; + result.push_back(Pair("version", pblock->nVersion)); + result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex())); + result.push_back(Pair("transactions", transactions)); + result.push_back(Pair("coinbaseaux", aux)); + result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue)); + result.push_back(Pair("target", hashTarget.GetHex())); + result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1)); + result.push_back(Pair("mutable", aMutable)); + result.push_back(Pair("noncerange", "00000000ffffffff")); + result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS)); + result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_SIZE)); + result.push_back(Pair("curtime", (int64_t)pblock->nTime)); + result.push_back(Pair("bits", HexBits(pblock->nBits))); + result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight+1))); + + return result; +} + +Value submitblock(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "submitblock [optional-params-obj]\n" + "[optional-params-obj] parameter is currently ignored.\n" + "Attempts to submit new block to network.\n" + "See https://en.bitcoin.it/wiki/BIP_0022 for full specification."); + + vector blockData(ParseHex(params[0].get_str())); + CDataStream ssBlock(blockData, SER_NETWORK, PROTOCOL_VERSION); + CBlock block; + try { + ssBlock >> block; + } + catch (const std::exception&) { + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed"); + } + + bool fAccepted = ProcessBlock(NULL, &block); + if (!fAccepted) + return "rejected"; + + return Value::null; +} + diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp new file mode 100644 index 00000000..35e4cb9c --- /dev/null +++ b/src/rpcnet.cpp @@ -0,0 +1,408 @@ +// Copyright (c) 2009-2012 Bitcoin Developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "bitcoinrpc.h" +#include "alert.h" +#include "wallet.h" +#include "db.h" +#include "walletdb.h" +#include "net.h" +#include "ntp.h" + +using namespace json_spirit; +using namespace std; + +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."); + + LOCK(cs_vNodes); + return (int)vNodes.size(); +} + +static void CopyNodeStats(std::vector& vstats) +{ + vstats.clear(); + + LOCK(cs_vNodes); + vstats.reserve(vNodes.size()); + BOOST_FOREACH(CNode* pnode, vNodes) { + CNodeStats stats; + pnode->copyStats(stats); + vstats.push_back(stats); + } +} + +struct addrManItemSort { + bool operator()(const CAddrInfo &leftItem, const CAddrInfo &rightItem) { + int64_t nTime = GetTime(); + return leftItem.GetChance(nTime) > rightItem.GetChance(nTime); + } +}; + +Value getaddrmaninfo(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "getaddrmaninfo [networkType]\n" + "Returns a dump of addrman data."); + + // Get a full list of "online" address items + vector vAddr = addrman.GetOnlineAddr(); + + // Sort by the GetChance result backwardly + sort(vAddr.begin(), vAddr.end(), addrManItemSort()); + + string strFilterNetType = ""; + if (params.size() == 1) + strFilterNetType = params[0].get_str(); + + Array ret; + BOOST_FOREACH(const CAddrInfo &addr, vAddr) { + if (!addr.IsRoutable() || addr.IsLocal()) + continue; + + Object addrManItem; + addrManItem.push_back(Pair("address", addr.ToString())); + + string strNetType; + switch(addr.GetNetwork()) + { + case NET_TOR: + strNetType = "tor"; + break; +// case NET_I2P: +// strNetType = "i2p"; +// break; + case NET_IPV6: + strNetType = "ipv6"; + break; + default: + case NET_IPV4: + strNetType = "ipv4"; + + } + + if (strFilterNetType.size() != 0 && strNetType != strFilterNetType) + continue; + + addrManItem.push_back(Pair("chance", addr.GetChance(GetTime()))); + addrManItem.push_back(Pair("type", strNetType)); + addrManItem.push_back(Pair("time", (int64_t)addr.nTime)); + + ret.push_back(addrManItem); + } + + return ret; +} + +Value getpeerinfo(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getpeerinfo\n" + "Returns data about each connected network node."); + + vector vstats; + CopyNodeStats(vstats); + + Array ret; + + BOOST_FOREACH(const CNodeStats& stats, vstats) { + Object obj; + + obj.push_back(Pair("addr", stats.addrName)); + obj.push_back(Pair("services", strprintf("%08" PRIx64, stats.nServices))); + obj.push_back(Pair("lastsend", (int64_t)stats.nLastSend)); + obj.push_back(Pair("lastrecv", (int64_t)stats.nLastRecv)); + obj.push_back(Pair("bytessent", (int64_t)stats.nSendBytes)); + obj.push_back(Pair("bytesrecv", (int64_t)stats.nRecvBytes)); + obj.push_back(Pair("conntime", (int64_t)stats.nTimeConnected)); + obj.push_back(Pair("version", stats.nVersion)); + obj.push_back(Pair("subver", stats.strSubVer)); + obj.push_back(Pair("inbound", stats.fInbound)); + obj.push_back(Pair("releasetime", (int64_t)stats.nReleaseTime)); + obj.push_back(Pair("startingheight", stats.nStartingHeight)); + obj.push_back(Pair("banscore", stats.nMisbehavior)); + if (stats.fSyncNode) + obj.push_back(Pair("syncnode", true)); + ret.push_back(obj); + } + + return ret; +} + +Value addnode(const Array& params, bool fHelp) +{ + string strCommand; + if (params.size() == 2) + strCommand = params[1].get_str(); + if (fHelp || params.size() != 2 || + (strCommand != "onetry" && strCommand != "add" && strCommand != "remove")) + throw runtime_error( + "addnode \n" + "Attempts add or remove from the addnode list or try a connection to once."); + + string strNode = params[0].get_str(); + + if (strCommand == "onetry") + { + CAddress addr; + OpenNetworkConnection(addr, NULL, strNode.c_str()); + return Value::null; + } + + LOCK(cs_vAddedNodes); + vector::iterator it = vAddedNodes.begin(); + for(; it != vAddedNodes.end(); it++) + if (strNode == *it) + break; + + if (strCommand == "add") + { + if (it != vAddedNodes.end()) + throw JSONRPCError(-23, "Error: Node already added"); + vAddedNodes.push_back(strNode); + } + else if(strCommand == "remove") + { + if (it == vAddedNodes.end()) + throw JSONRPCError(-24, "Error: Node has not been added."); + vAddedNodes.erase(it); + } + + return Value::null; +} + +Value getaddednodeinfo(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "getaddednodeinfo [node]\n" + "Returns information about the given added node, or all added nodes\n" + "(note that onetry addnodes are not listed here)\n" + "If dns is false, only a list of added nodes will be provided,\n" + "otherwise connected information will also be available."); + + bool fDns = params[0].get_bool(); + + list laddedNodes(0); + if (params.size() == 1) + { + LOCK(cs_vAddedNodes); + BOOST_FOREACH(string& strAddNode, vAddedNodes) + laddedNodes.push_back(strAddNode); + } + else + { + string strNode = params[1].get_str(); + LOCK(cs_vAddedNodes); + BOOST_FOREACH(string& strAddNode, vAddedNodes) + if (strAddNode == strNode) + { + laddedNodes.push_back(strAddNode); + break; + } + if (laddedNodes.size() == 0) + throw JSONRPCError(-24, "Error: Node has not been added."); + } + + if (!fDns) + { + Object ret; + BOOST_FOREACH(string& strAddNode, laddedNodes) + ret.push_back(Pair("addednode", strAddNode)); + return ret; + } + + Array ret; + + list > > laddedAddreses(0); + BOOST_FOREACH(string& strAddNode, laddedNodes) + { + vector vservNode(0); + if(Lookup(strAddNode.c_str(), vservNode, GetDefaultPort(), fNameLookup, 0)) + laddedAddreses.push_back(make_pair(strAddNode, vservNode)); + else + { + Object obj; + obj.push_back(Pair("addednode", strAddNode)); + obj.push_back(Pair("connected", false)); + Array addresses; + obj.push_back(Pair("addresses", addresses)); + } + } + + LOCK(cs_vNodes); + for (list > >::iterator it = laddedAddreses.begin(); it != laddedAddreses.end(); it++) + { + Object obj; + obj.push_back(Pair("addednode", it->first)); + + Array addresses; + bool fConnected = false; + BOOST_FOREACH(CService& addrNode, it->second) + { + bool fFound = false; + Object node; + node.push_back(Pair("address", addrNode.ToString())); + BOOST_FOREACH(CNode* pnode, vNodes) + if (pnode->addr == addrNode) + { + fFound = true; + fConnected = true; + node.push_back(Pair("connected", pnode->fInbound ? "inbound" : "outbound")); + break; + } + if (!fFound) + node.push_back(Pair("connected", "false")); + addresses.push_back(node); + } + obj.push_back(Pair("connected", fConnected)); + obj.push_back(Pair("addresses", addresses)); + ret.push_back(obj); + } + + return ret; +} + +// There is a known deadlock situation with ThreadMessageHandler +// ThreadMessageHandler: holds cs_vSend and acquiring cs_main in SendMessages() +// ThreadRPCServer: holds cs_main and acquiring cs_vSend in alert.RelayTo()/PushMessage()/BeginMessage() +Value sendalert(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 6) + throw runtime_error( + "sendalert [cancelupto]\n" + " is the alert text message\n" + " is hex string of alert master private key\n" + " is the minimum applicable internal client version\n" + " is the maximum applicable internal client version\n" + " is integer priority number\n" + " is the alert id\n" + "[cancelupto] cancels all alert id's up to this number\n" + "Returns true or false."); + + CAlert alert; + CKey key; + + alert.strStatusBar = params[0].get_str(); + alert.nMinVer = params[2].get_int(); + alert.nMaxVer = params[3].get_int(); + alert.nPriority = params[4].get_int(); + alert.nID = params[5].get_int(); + if (params.size() > 6) + alert.nCancel = params[6].get_int(); + alert.nVersion = PROTOCOL_VERSION; + alert.nRelayUntil = GetAdjustedTime() + 365*24*60*60; + alert.nExpiration = GetAdjustedTime() + 365*24*60*60; + + CDataStream sMsg(SER_NETWORK, PROTOCOL_VERSION); + sMsg << (CUnsignedAlert)alert; + alert.vchMsg = vector(sMsg.begin(), sMsg.end()); + + vector vchPrivKey = ParseHex(params[1].get_str()); + key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash + if (!key.Sign(Hash(alert.vchMsg.begin(), alert.vchMsg.end()), alert.vchSig)) + throw runtime_error( + "Unable to sign alert, check private key?\n"); + if(!alert.ProcessAlert()) + throw runtime_error( + "Failed to process alert.\n"); + // Relay alert + { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + alert.RelayTo(pnode); + } + + Object result; + result.push_back(Pair("strStatusBar", alert.strStatusBar)); + result.push_back(Pair("nVersion", alert.nVersion)); + result.push_back(Pair("nMinVer", alert.nMinVer)); + result.push_back(Pair("nMaxVer", alert.nMaxVer)); + result.push_back(Pair("nPriority", alert.nPriority)); + result.push_back(Pair("nID", alert.nID)); + if (alert.nCancel > 0) + result.push_back(Pair("nCancel", alert.nCancel)); + return result; +} + +Value getnettotals(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 0) + throw runtime_error( + "getnettotals\n" + "Returns information about network traffic, including bytes in, bytes out,\n" + "and current time."); + + Object obj; + obj.push_back(Pair("totalbytesrecv", static_cast(CNode::GetTotalBytesRecv()))); + obj.push_back(Pair("totalbytessent", static_cast(CNode::GetTotalBytesSent()))); + obj.push_back(Pair("timemillis", static_cast(GetTimeMillis()))); + return obj; +} + +/* +05:53:45 ntptime +05:53:48 +{ +"epoch" : 1442494427, +"time" : "2015-09-17 12:53:47 UTC" +} + +05:53:56 ntptime time.windows.com +05:53:57 +{ +"epoch" : 1442494436, +"time" : "2015-09-17 12:53:56 UTC" +} + +05:54:33 ntptime time-a.nist.gov +05:54:34 +{ +"epoch" : 1442494473, +"time" : "2015-09-17 12:54:33 UTC" +}*/ + +Value ntptime(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "ntptime [ntpserver]\n" + "Returns current time from specific or random NTP server."); + + int64_t nTime; + if (params.size() > 0) { + string strHostName = params[0].get_str(); + nTime = NtpGetTime(strHostName); + } + else { + CNetAddr ip; + nTime = NtpGetTime(ip); + } + + Object obj; + switch (nTime) { + case -1: + throw runtime_error("Socket initialization error"); + case -2: + throw runtime_error("Switching socket mode to non-blocking failed"); + case -3: + throw runtime_error("Unable to send data"); + case -4: + throw runtime_error("Receive timed out"); + default: + if (nTime > 0 && nTime != 2085978496) { + obj.push_back(Pair("epoch", nTime)); + obj.push_back(Pair("time", DateTimeStrFormat(nTime))); + } + else throw runtime_error("Unexpected response"); + } + + return obj; +} diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp new file mode 100644 index 00000000..76113496 --- /dev/null +++ b/src/rpcrawtransaction.cpp @@ -0,0 +1,667 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include "base58.h" +#include "bitcoinrpc.h" +#include "txdb.h" +#include "init.h" +#include "main.h" +#include "net.h" +#include "wallet.h" + +using namespace std; +using namespace boost; +using namespace boost::assign; +using namespace json_spirit; + +void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out, bool fIncludeHex) +{ + txnouttype type; + vector addresses; + int nRequired; + + out.push_back(Pair("asm", scriptPubKey.ToString())); + + if (fIncludeHex) + out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); + + if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) + { + out.push_back(Pair("type", GetTxnOutputType(TX_NONSTANDARD))); + return; + } + + out.push_back(Pair("reqSigs", nRequired)); + out.push_back(Pair("type", GetTxnOutputType(type))); + + Array a; + BOOST_FOREACH(const CTxDestination& addr, addresses) + a.push_back(CBitcoinAddress(addr).ToString()); + out.push_back(Pair("addresses", a)); +} + +void TxToJSON(const CTransaction& tx, const uint256& hashBlock, Object& entry) +{ + entry.push_back(Pair("txid", tx.GetHash().GetHex())); + entry.push_back(Pair("version", tx.nVersion)); + entry.push_back(Pair("time", (int64_t)tx.nTime)); + entry.push_back(Pair("locktime", (int64_t)tx.nLockTime)); + Array vin; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + Object in; + if (tx.IsCoinBase()) + in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); + else + { + in.push_back(Pair("txid", txin.prevout.hash.GetHex())); + in.push_back(Pair("vout", (int64_t)txin.prevout.n)); + Object o; + o.push_back(Pair("asm", txin.scriptSig.ToString())); + o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); + in.push_back(Pair("scriptSig", o)); + } + in.push_back(Pair("sequence", (int64_t)txin.nSequence)); + vin.push_back(in); + } + entry.push_back(Pair("vin", vin)); + Array vout; + for (unsigned int i = 0; i < tx.vout.size(); i++) + { + const CTxOut& txout = tx.vout[i]; + Object out; + out.push_back(Pair("value", ValueFromAmount(txout.nValue))); + out.push_back(Pair("n", (int64_t)i)); + Object o; + ScriptPubKeyToJSON(txout.scriptPubKey, o, true); + out.push_back(Pair("scriptPubKey", o)); + vout.push_back(out); + } + entry.push_back(Pair("vout", vout)); + + if (hashBlock != 0) + { + entry.push_back(Pair("blockhash", hashBlock.GetHex())); + map::iterator mi = mapBlockIndex.find(hashBlock); + if (mi != mapBlockIndex.end() && (*mi).second) + { + CBlockIndex* pindex = (*mi).second; + if (pindex->IsInMainChain()) + { + entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight)); + entry.push_back(Pair("time", (int64_t)pindex->nTime)); + entry.push_back(Pair("blocktime", (int64_t)pindex->nTime)); + } + else + entry.push_back(Pair("confirmations", 0)); + } + } +} + +Value getrawtransaction(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "getrawtransaction [verbose=0]\n" + "If verbose=0, returns a string that is\n" + "serialized, hex-encoded data for .\n" + "If verbose is non-zero, returns an Object\n" + "with information about ."); + + uint256 hash; + hash.SetHex(params[0].get_str()); + + bool fVerbose = false; + if (params.size() > 1) + fVerbose = (params[1].get_int() != 0); + + CTransaction tx; + uint256 hashBlock = 0; + if (!GetTransaction(hash, tx, hashBlock)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction"); + + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); + ssTx << tx; + string strHex = HexStr(ssTx.begin(), ssTx.end()); + + if (!fVerbose) + return strHex; + + Object result; + result.push_back(Pair("hex", strHex)); + TxToJSON(tx, hashBlock, result); + return result; +} + +Value listunspent(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 3) + throw runtime_error( + "listunspent [minconf=1] [maxconf=9999999] [\"address\",...]\n" + "Returns array of unspent transaction outputs\n" + "with between minconf and maxconf (inclusive) confirmations.\n" + "Optionally filtered to only include txouts paid to specified addresses.\n" + "Results are an array of Objects, each of which has:\n" + "{txid, vout, scriptPubKey, amount, confirmations}"); + + RPCTypeCheck(params, list_of(int_type)(int_type)(array_type)); + + int nMinDepth = 1; + if (params.size() > 0) + nMinDepth = params[0].get_int(); + + int nMaxDepth = 9999999; + if (params.size() > 1) + nMaxDepth = params[1].get_int(); + + set setAddress; + if (params.size() > 2) + { + Array inputs = params[2].get_array(); + BOOST_FOREACH(Value& input, inputs) + { + CBitcoinAddress address(input.get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid XP address: ")+input.get_str()); + if (setAddress.count(address)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+input.get_str()); + setAddress.insert(address); + } + } + + Array results; + vector vecOutputs; + pwalletMain->AvailableCoins(vecOutputs, false); + BOOST_FOREACH(const COutput& out, vecOutputs) + { + if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) + continue; + + if(setAddress.size()) + { + CTxDestination address; + if(!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) + continue; + + if (!setAddress.count(address)) + continue; + } + + int64_t nValue = out.tx->vout[out.i].nValue; + const CScript& pk = out.tx->vout[out.i].scriptPubKey; + Object entry; + entry.push_back(Pair("txid", out.tx->GetHash().GetHex())); + entry.push_back(Pair("vout", out.i)); + CTxDestination address; + if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) + { + entry.push_back(Pair("address", CBitcoinAddress(address).ToString())); + if (pwalletMain->mapAddressBook.count(address)) + entry.push_back(Pair("account", pwalletMain->mapAddressBook[address])); + } + entry.push_back(Pair("scriptPubKey", HexStr(pk.begin(), pk.end()))); + if (pk.IsPayToScriptHash()) + { + CTxDestination address; + if (ExtractDestination(pk, address)) + { + const CScriptID& hash = boost::get(address); + CScript redeemScript; + if (pwalletMain->GetCScript(hash, redeemScript)) + entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()))); + } + } + entry.push_back(Pair("amount",ValueFromAmount(nValue))); + entry.push_back(Pair("confirmations",out.nDepth)); + entry.push_back(Pair("spendable", out.fSpendable)); + results.push_back(entry); + } + + return results; +} + +Value createrawtransaction(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 2) + throw runtime_error( + "createrawtransaction '[{\"txid\":txid,\"vout\":n},...]' '{address:amount,...}'\n" + "Create a transaction spending given inputs\n" + "(array of objects containing transaction id and output number),\n" + "sending to given address(es).\n" + "Returns hex-encoded raw transaction.\n" + "Note that the transaction's inputs are not signed, and\n" + "it is not stored in the wallet or transmitted to the network."); + + RPCTypeCheck(params, list_of(array_type)(obj_type)); + + Array inputs = params[0].get_array(); + Object sendTo = params[1].get_obj(); + + CTransaction rawTx; + + BOOST_FOREACH(Value& input, inputs) + { + const Object& o = input.get_obj(); + + const Value& txid_v = find_value(o, "txid"); + if (txid_v.type() != str_type) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing txid key"); + string txid = txid_v.get_str(); + if (!IsHex(txid)) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid"); + + const Value& vout_v = find_value(o, "vout"); + if (vout_v.type() != int_type) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key"); + int nOutput = vout_v.get_int(); + if (nOutput < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); + + CTxIn in(COutPoint(uint256(txid), nOutput)); + rawTx.vin.push_back(in); + } + + set setAddress; + BOOST_FOREACH(const Pair& s, sendTo) + { + CBitcoinAddress address(s.name_); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid XP address: ")+s.name_); + + if (setAddress.count(address)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_); + setAddress.insert(address); + + CScript scriptPubKey; + scriptPubKey.SetDestination(address.Get()); + int64_t nAmount = AmountFromValue(s.value_); + + CTxOut out(nAmount, scriptPubKey); + rawTx.vout.push_back(out); + } + + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss << rawTx; + return HexStr(ss.begin(), ss.end()); +} + +Value decoderawtransaction(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "decoderawtransaction \n" + "Return a JSON object representing the serialized, hex-encoded transaction."); + + RPCTypeCheck(params, list_of(str_type)); + + vector txData(ParseHex(params[0].get_str())); + CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); + CTransaction tx; + try { + ssData >> tx; + } + catch (const std::exception&) { + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + } + + Object result; + TxToJSON(tx, 0, result); + + return result; +} + +Value decodescript(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "decodescript \n" + "Decode a hex-encoded script."); + + RPCTypeCheck(params, list_of(str_type)); + + Object r; + CScript script; + if (params[0].get_str().size() > 0){ + vector scriptData(ParseHexV(params[0], "argument")); + script = CScript(scriptData.begin(), scriptData.end()); + } else { + // Empty scripts are valid + } + ScriptPubKeyToJSON(script, r, false); + + r.push_back(Pair("p2sh", CBitcoinAddress(script.GetID()).ToString())); + return r; +} + +Value signrawtransaction(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 4) + throw runtime_error( + "signrawtransaction '[{\"txid\":txid,\"vout\":n,\"scriptPubKey\":hex,\"redeemScript\":hex},...]' '[,...]' [sighashtype=\"ALL\"]\n" + "Sign inputs for raw transaction (serialized, hex-encoded).\n" + "Second optional argument (may be null) is an array of previous transaction outputs that\n" + "this transaction depends on but may not yet be in the blockchain.\n" + "Third optional argument (may be null) is an array of base58-encoded private\n" + "keys that, if given, will be the only keys used to sign the transaction.\n" + "Fourth optional argument is a string that is one of six values; ALL, NONE, SINGLE or\n" + "ALL|ANYONECANPAY, NONE|ANYONECANPAY, SINGLE|ANYONECANPAY.\n" + "Returns json object with keys:\n" + " hex : raw transaction with signature(s) (hex-encoded string)\n" + " complete : 1 if transaction has a complete set of signature (0 if not)" + + HelpRequiringPassphrase()); + + RPCTypeCheck(params, list_of(str_type)(array_type)(array_type)(str_type), true); + + vector txData(ParseHex(params[0].get_str())); + CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); + vector txVariants; + while (!ssData.empty()) + { + try { + CTransaction tx; + ssData >> tx; + txVariants.push_back(tx); + } + catch (const std::exception&) { + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + } + } + + if (txVariants.empty()) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transaction"); + + // mergedTx will end up with all the signatures; it + // starts as a clone of the rawtx: + CTransaction mergedTx(txVariants[0]); + bool fComplete = true; + + // Fetch previous transactions (inputs): + map mapPrevOut; + for (unsigned int i = 0; i < mergedTx.vin.size(); i++) + { + CTransaction tempTx; + MapPrevTx mapPrevTx; + CTxDB txdb("r"); + map unused; + bool fInvalid; + + // FetchInputs aborts on failure, so we go one at a time. + tempTx.vin.push_back(mergedTx.vin[i]); + tempTx.FetchInputs(txdb, unused, false, false, mapPrevTx, fInvalid); + + // Copy results into mapPrevOut: + BOOST_FOREACH(const CTxIn& txin, tempTx.vin) + { + const uint256& prevHash = txin.prevout.hash; + if (mapPrevTx.count(prevHash) && mapPrevTx[prevHash].second.vout.size()>txin.prevout.n) + mapPrevOut[txin.prevout] = mapPrevTx[prevHash].second.vout[txin.prevout.n].scriptPubKey; + } + } + + bool fGivenKeys = false; + CBasicKeyStore tempKeystore; + if (params.size() > 2 && params[2].type() != null_type) + { + fGivenKeys = true; + Array keys = params[2].get_array(); + BOOST_FOREACH(Value k, keys) + { + CBitcoinSecret vchSecret; + bool fGood = vchSecret.SetString(k.get_str()); + if (!fGood) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); + CKey key; + bool fCompressed; + CSecret secret = vchSecret.GetSecret(fCompressed); + key.SetSecret(secret, fCompressed); + tempKeystore.AddKey(key); + } + } + else + EnsureWalletIsUnlocked(); + + // Add previous txouts given in the RPC call: + if (params.size() > 1 && params[1].type() != null_type) + { + Array prevTxs = params[1].get_array(); + BOOST_FOREACH(Value& p, prevTxs) + { + if (p.type() != obj_type) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}"); + + Object prevOut = p.get_obj(); + + RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type)); + + string txidHex = find_value(prevOut, "txid").get_str(); + if (!IsHex(txidHex)) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "txid must be hexadecimal"); + uint256 txid; + txid.SetHex(txidHex); + + int nOut = find_value(prevOut, "vout").get_int(); + if (nOut < 0) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive"); + + string pkHex = find_value(prevOut, "scriptPubKey").get_str(); + if (!IsHex(pkHex)) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "scriptPubKey must be hexadecimal"); + vector pkData(ParseHex(pkHex)); + CScript scriptPubKey(pkData.begin(), pkData.end()); + + COutPoint outpoint(txid, nOut); + if (mapPrevOut.count(outpoint)) + { + // Complain if scriptPubKey doesn't match + if (mapPrevOut[outpoint] != scriptPubKey) + { + string err("Previous output scriptPubKey mismatch:\n"); + err = err + mapPrevOut[outpoint].ToString() + "\nvs:\n"+ + scriptPubKey.ToString(); + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err); + } + } + else + mapPrevOut[outpoint] = scriptPubKey; + + // if redeemScript given and not using the local wallet (private keys + // given), add redeemScript to the tempKeystore so it can be signed: + Value v = find_value(prevOut, "redeemScript"); + if (fGivenKeys && scriptPubKey.IsPayToScriptHash()) + { + RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type)("redeemScript",str_type)); + Value v = find_value(prevOut, "redeemScript"); + if (!(v == Value::null)) + { + vector rsData(ParseHexV(v, "redeemScript")); + CScript redeemScript(rsData.begin(), rsData.end()); + tempKeystore.AddCScript(redeemScript); + } + } + } + } + + const CKeyStore& keystore = (fGivenKeys ? tempKeystore : *pwalletMain); + + int nHashType = SIGHASH_ALL; + if (params.size() > 3 && params[3].type() != null_type) + { + static map mapSigHashValues = + boost::assign::map_list_of + (string("ALL"), int(SIGHASH_ALL)) + (string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)) + (string("NONE"), int(SIGHASH_NONE)) + (string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY)) + (string("SINGLE"), int(SIGHASH_SINGLE)) + (string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY)) + ; + string strHashType = params[3].get_str(); + if (mapSigHashValues.count(strHashType)) + nHashType = mapSigHashValues[strHashType]; + else + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param"); + } + + bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE); + + // Sign what we can: + for (unsigned int i = 0; i < mergedTx.vin.size(); i++) + { + CTxIn& txin = mergedTx.vin[i]; + if (mapPrevOut.count(txin.prevout) == 0) + { + fComplete = false; + continue; + } + const CScript& prevPubKey = mapPrevOut[txin.prevout]; + + txin.scriptSig.clear(); + // Only sign SIGHASH_SINGLE if there's a corresponding output: + if (!fHashSingle || (i < mergedTx.vout.size())) + SignSignature(keystore, prevPubKey, mergedTx, i, nHashType); + + // ... and merge in other signatures: + BOOST_FOREACH(const CTransaction& txv, txVariants) + { + txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig); + } + if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, STRICT_FLAGS, 0)) + fComplete = false; + } + + Object result; + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); + ssTx << mergedTx; + result.push_back(Pair("hex", HexStr(ssTx.begin(), ssTx.end()))); + result.push_back(Pair("complete", fComplete)); + + return result; +} + +Value sendrawtransaction(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 1) + throw runtime_error( + "sendrawtransaction \n" + "Submits raw transaction (serialized, hex-encoded) to local node and network."); + + RPCTypeCheck(params, list_of(str_type)); + + // parse hex string from parameter + vector txData(ParseHex(params[0].get_str())); + CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); + CTransaction tx; + + // deserialize binary data stream + try { + ssData >> tx; + } + catch (const std::exception&) { + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + } + uint256 hashTx = tx.GetHash(); + + // See if the transaction is already in a block + // or in the memory pool: + CTransaction existingTx; + uint256 hashBlock = 0; + if (GetTransaction(hashTx, existingTx, hashBlock)) + { + if (hashBlock != 0) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("transaction already in block ")+hashBlock.GetHex()); + // Not in block, but already in the memory pool; will drop + // through to re-relay it. + } + else + { + // push to local node + CTxDB txdb("r"); + if (!tx.AcceptToMemoryPool(txdb)) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected"); + + SyncWithWallets(tx, NULL, true); + } + RelayTransaction(tx, hashTx); + + return hashTx.GetHex(); +} + +Value createmultisig(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 3) + { + string msg = "createmultisig <'[\"key\",\"key\"]'>\n" + "\nCreates a multi-signature address with n signature of m keys required.\n" + "It returns a json object with the address and redeemScript."; + throw runtime_error(msg); + } + + int nRequired = params[0].get_int(); + const Array& keys = params[1].get_array(); + string strAccount; + + // Gather public keys + if (nRequired < 1) + throw runtime_error("a multisignature address must require at least one key to redeem"); + if ((int)keys.size() < nRequired) + throw runtime_error( + strprintf("not enough keys supplied " + "(got %" PRIszu " keys, but need at least %d to redeem)", keys.size(), nRequired)); + if (keys.size() > 16) + throw runtime_error("Number of addresses involved in the multisignature address creation > 16\nReduce the number"); + std::vector pubkeys; + pubkeys.resize(keys.size()); + for (unsigned int i = 0; i < keys.size(); i++) + { + const std::string& ks = keys[i].get_str(); + + // Case 1: Bitcoin address and we have full public key: + CBitcoinAddress address(ks); + if (address.IsValid()) + { + CKeyID keyID; + if (!address.GetKeyID(keyID)) + throw runtime_error( + strprintf("%s does not refer to a key",ks.c_str())); + CPubKey vchPubKey; + if (!pwalletMain->GetPubKey(keyID, vchPubKey)) + throw runtime_error( + strprintf("no full public key for address %s",ks.c_str())); + if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey)) + throw runtime_error(" Invalid public key: "+ks); + } + + // Case 2: hex public key + else if (IsHex(ks)) + { + CPubKey vchPubKey(ParseHex(ks)); + if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey)) + throw runtime_error(" Invalid public key: "+ks); + } + else + { + throw runtime_error(" Invalid public key: "+ks); + } + } + + // Construct using pay-to-script-hash: + CScript inner; + inner.SetMultisig(nRequired, pubkeys); + + if (inner.size() > MAX_SCRIPT_ELEMENT_SIZE) + throw runtime_error( + strprintf("redeemScript exceeds size limit: %" PRIszu " > %d", inner.size(), MAX_SCRIPT_ELEMENT_SIZE)); + + CScriptID innerID = inner.GetID(); + CBitcoinAddress address(innerID); + + Object result; + result.push_back(Pair("address", address.ToString())); + result.push_back(Pair("redeemScript", HexStr(inner.begin(), inner.end()))); + + return result; +} diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp new file mode 100644 index 00000000..0bb34c24 --- /dev/null +++ b/src/rpcwallet.cpp @@ -0,0 +1,1857 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "wallet.h" +#include "walletdb.h" +#include "bitcoinrpc.h" +#include "init.h" +#include "util.h" +#include "ntp.h" +#include "base58.h" + +using namespace json_spirit; +using namespace std; + +int64_t nWalletUnlockTime; +static CCriticalSection cs_nWalletUnlockTime; + +extern int64_t nReserveBalance; +extern void TxToJSON(const CTransaction& tx, const uint256& hashBlock, json_spirit::Object& entry); + +std::string HelpRequiringPassphrase() +{ + return pwalletMain->IsCrypted() + ? "\n\nRequires wallet passphrase to be set with walletpassphrase first" + : ""; +} + +void EnsureWalletIsUnlocked() +{ + if (pwalletMain->IsLocked()) + throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); + if (fWalletUnlockMintOnly) + throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Wallet unlocked for block minting only."); +} + +void WalletTxToJSON(const CWalletTx& wtx, Object& entry) +{ + int confirms = wtx.GetDepthInMainChain(); + entry.push_back(Pair("confirmations", confirms)); + if (wtx.IsCoinBase() || wtx.IsCoinStake()) + entry.push_back(Pair("generated", true)); + if (confirms) + { + entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex())); + entry.push_back(Pair("blockindex", wtx.nIndex)); + entry.push_back(Pair("blocktime", (int64_t)(mapBlockIndex[wtx.hashBlock]->nTime))); + } + entry.push_back(Pair("txid", wtx.GetHash().GetHex())); + entry.push_back(Pair("time", (int64_t)wtx.GetTxTime())); + entry.push_back(Pair("timereceived", (int64_t)wtx.nTimeReceived)); + 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(RPC_WALLET_INVALID_ACCOUNT_NAME, "Invalid account name"); + return strAccount; +} + +Value getinfo(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getinfo\n" + "Returns an object containing various state info."); + + proxyType proxy; + GetProxy(NET_IPV4, proxy); + + Object obj, diff, timestamping; + obj.push_back(Pair("version", FormatFullVersion())); + obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION)); + obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); + obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); + obj.push_back(Pair("unspendable", ValueFromAmount(pwalletMain->GetWatchOnlyBalance()))); + obj.push_back(Pair("newmint", ValueFromAmount(pwalletMain->GetNewMint()))); + obj.push_back(Pair("stake", ValueFromAmount(pwalletMain->GetStake()))); + obj.push_back(Pair("blocks", (int)nBestHeight)); + + timestamping.push_back(Pair("systemclock", GetTime())); + timestamping.push_back(Pair("adjustedtime", GetAdjustedTime())); + + int64_t nNtpOffset = GetNtpOffset(), + nP2POffset = GetNodesOffset(); + + timestamping.push_back(Pair("ntpoffset", nNtpOffset != INT64_MAX ? nNtpOffset : Value::null)); + timestamping.push_back(Pair("p2poffset", nP2POffset != INT64_MAX ? nP2POffset : Value::null)); + + obj.push_back(Pair("timestamping", timestamping)); + + obj.push_back(Pair("moneysupply", ValueFromAmount(pindexBest->nMoneySupply))); + obj.push_back(Pair("connections", (int)vNodes.size())); + obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string()))); + obj.push_back(Pair("ip", addrSeenByPeer.ToStringIP())); + + diff.push_back(Pair("proof-of-work", GetDifficulty())); + diff.push_back(Pair("proof-of-stake", GetDifficulty(GetLastBlockIndex(pindexBest, true)))); + obj.push_back(Pair("difficulty", diff)); + + obj.push_back(Pair("testnet", fTestNet)); + obj.push_back(Pair("keypoololdest", (int64_t)pwalletMain->GetOldestKeyPoolTime())); + obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize())); + obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee))); + obj.push_back(Pair("mininput", ValueFromAmount(nMinimumInputValue))); + if (pwalletMain->IsCrypted()) + obj.push_back(Pair("unlocked_until", (int64_t)nWalletUnlockTime / 1000)); + 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 XP 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]); + + if (!pwalletMain->IsLocked()) + pwalletMain->TopUpKeyPool(); + + // Generate a new key that is added to wallet + CPubKey newKey; + if (!pwalletMain->GetKeyFromPool(newKey, false)) + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); + CKeyID keyID = newKey.GetID(); + + pwalletMain->SetAddressBookName(keyID, strAccount); + + return CBitcoinAddress(keyID).ToString(); +} + + +CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false) +{ + CWalletDB walletdb(pwalletMain->strWalletFile); + + CAccount account; + walletdb.ReadAccount(strAccount, account); + + bool bKeyUsed = false; + + // Check if the current key has been used + if (account.vchPubKey.IsValid()) + { + CScript scriptPubKey; + scriptPubKey.SetDestination(account.vchPubKey.GetID()); + for (map::iterator it = pwalletMain->mapWallet.begin(); + it != pwalletMain->mapWallet.end() && account.vchPubKey.IsValid(); + ++it) + { + const CWalletTx& wtx = (*it).second; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + if (txout.scriptPubKey == scriptPubKey) + bKeyUsed = true; + } + } + + // Generate a new key + if (!account.vchPubKey.IsValid() || bForceNew || bKeyUsed) + { + if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false)) + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); + + pwalletMain->SetAddressBookName(account.vchPubKey.GetID(), strAccount); + walletdb.WriteAccount(strAccount, account); + } + + return CBitcoinAddress(account.vchPubKey.GetID()); +} + +Value getaccountaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getaccountaddress \n" + "Returns the current XP 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; + + ret = GetAccountAddress(strAccount).ToString(); + + 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."); + + CBitcoinAddress address(params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid XP 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: + if (pwalletMain->mapAddressBook.count(address.Get())) + { + string strOldAccount = pwalletMain->mapAddressBook[address.Get()]; + if (address == GetAccountAddress(strOldAccount)) + GetAccountAddress(strOldAccount, true); + } + + pwalletMain->SetAddressBookName(address.Get(), 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."); + + CBitcoinAddress address(params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid XP address"); + + string strAccount; + map::iterator mi = pwalletMain->mapAddressBook.find(address.Get()); + if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty()) + strAccount = (*mi).second; + return strAccount; +} + + +Value getaddressesbyaccount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getaddressesbyaccount \n" + "Returns the list of addresses for the given account."); + + string strAccount = AccountFromValue(params[0]); + + // Find all addresses that have the given account + Array ret; + BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook) + { + const CBitcoinAddress& address = item.first; + const string& strName = item.second; + if (strName == strAccount) + ret.push_back(address.ToString()); + } + return ret; +} + +Value mergecoins(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 3) + throw runtime_error( + "mergecoins \n" + " is resulting inputs sum\n" + " is minimum value of inputs which are used in join process\n" + " is resulting value of inputs which will be created\n" + "All values are real and and rounded to the nearest " + FormatMoney(nMinimumInputValue) + + HelpRequiringPassphrase()); + + if (pwalletMain->IsLocked()) + throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); + + // Total amount + int64_t nAmount = AmountFromValue(params[0]); + + // Min input amount + int64_t nMinValue = AmountFromValue(params[1]); + + // Output amount + int64_t nOutputValue = AmountFromValue(params[2]); + + if (nAmount < nMinimumInputValue) + throw JSONRPCError(-101, "Send amount too small"); + + if (nMinValue < nMinimumInputValue) + throw JSONRPCError(-101, "Max value too small"); + + if (nOutputValue < nMinimumInputValue) + throw JSONRPCError(-101, "Output value too small"); + + if (nOutputValue < nMinValue) + throw JSONRPCError(-101, "Output value is lower than min value"); + + list listMerged; + if (!pwalletMain->MergeCoins(nAmount, nMinValue, nOutputValue, listMerged)) + return Value::null; + + Array mergedHashes; + BOOST_FOREACH(const uint256 txHash, listMerged) + mergedHashes.push_back(txHash.GetHex()); + + return mergedHashes; +} + +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 " + FormatMoney(nMinimumInputValue) + + HelpRequiringPassphrase()); + + CBitcoinAddress address(params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid XP address"); + + // Amount + int64_t nAmount = AmountFromValue(params[1]); + + if (nAmount < nMinimumInputValue) + throw JSONRPCError(-101, "Send amount too small"); + + // 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(); + + if (pwalletMain->IsLocked()) + throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); + + string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx); + if (strError != "") + throw JSONRPCError(RPC_WALLET_ERROR, strError); + + return wtx.GetHash().GetHex(); +} + +Value listaddressgroupings(const Array& params, bool fHelp) +{ + if (fHelp) + throw runtime_error( + "listaddressgroupings\n" + "Lists groups of addresses which have had their common ownership\n" + "made public by common use as inputs or as the resulting change\n" + "in past transactions"); + + Array jsonGroupings; + map balances = pwalletMain->GetAddressBalances(); + BOOST_FOREACH(set grouping, pwalletMain->GetAddressGroupings()) + { + Array jsonGrouping; + BOOST_FOREACH(CTxDestination address, grouping) + { + Array addressInfo; + addressInfo.push_back(CBitcoinAddress(address).ToString()); + addressInfo.push_back(ValueFromAmount(balances[address])); + { + LOCK(pwalletMain->cs_wallet); + if (pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwalletMain->mapAddressBook.end()) + addressInfo.push_back(pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get())->second); + } + jsonGrouping.push_back(addressInfo); + } + jsonGroupings.push_back(jsonGrouping); + } + return jsonGroupings; +} + +Value signmessage(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 2) + throw runtime_error( + "signmessage \n" + "Sign a message with the private key of an address"); + + EnsureWalletIsUnlocked(); + + string strAddress = params[0].get_str(); + string strMessage = params[1].get_str(); + + CBitcoinAddress addr(strAddress); + if (!addr.IsValid()) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); + + CKeyID keyID; + if (!addr.GetKeyID(keyID)) + throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); + + CKey key; + if (!pwalletMain->GetKey(keyID, key)) + throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available"); + + CDataStream ss(SER_GETHASH, 0); + ss << strMessageMagic; + ss << strMessage; + + vector vchSig; + if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed"); + + return EncodeBase64(&vchSig[0], vchSig.size()); +} + +Value verifymessage(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 3) + throw runtime_error( + "verifymessage \n" + "Verify a signed message"); + + string strAddress = params[0].get_str(); + string strSign = params[1].get_str(); + string strMessage = params[2].get_str(); + + CBitcoinAddress addr(strAddress); + if (!addr.IsValid()) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); + + CKeyID keyID; + if (!addr.GetKeyID(keyID)) + throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); + + bool fInvalid = false; + vector vchSig = DecodeBase64(strSign.c_str(), &fInvalid); + + if (fInvalid) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding"); + + CDataStream ss(SER_GETHASH, 0); + ss << strMessageMagic; + ss << strMessage; + + CKey key; + if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig)) + return false; + + return (key.GetPubKey().GetID() == keyID); +} + + +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 + CBitcoinAddress address = CBitcoinAddress(params[0].get_str()); + CScript scriptPubKey; + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid XP address"); + scriptPubKey.SetDestination(address.Get()); + if (!IsMine(*pwalletMain,scriptPubKey)) + return (double)0.0; + + // Minimum confirmations + int nMinDepth = 1; + if (params.size() > 1) + nMinDepth = params[1].get_int(); + + // Tally + int64_t nAmount = 0; + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (wtx.IsCoinBase() || wtx.IsCoinStake() || !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 GetAccountAddresses(string strAccount, set& setAddress) +{ + BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& item, pwalletMain->mapAddressBook) + { + const CTxDestination& address = item.first; + const string& strName = item.second; + if (strName == strAccount) + setAddress.insert(address); + } +} + +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 assigned to account + string strAccount = AccountFromValue(params[0]); + set setAddress; + GetAccountAddresses(strAccount, setAddress); + + // Tally + int64_t nAmount = 0; + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal()) + continue; + + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + { + CTxDestination address; + if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address)) + if (wtx.GetDepthInMainChain() >= nMinDepth) + nAmount += txout.nValue; + } + } + + return (double)nAmount / (double)COIN; +} + + +int64_t GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth, const isminefilter& filter) +{ + int64_t nBalance = 0; + + // Tally wallet transactions + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (!wtx.IsFinal()) + continue; + + int64_t nGenerated, nReceived, nSent, nFee; + wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee, filter); + + if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) + nBalance += nReceived; + nBalance += nGenerated - nSent - nFee; + } + + // Tally internal accounting entries + nBalance += walletdb.GetAccountCreditDebit(strAccount); + + return nBalance; +} + +int64_t GetAccountBalance(const string& strAccount, int nMinDepth, const isminefilter& filter) +{ + CWalletDB walletdb(pwalletMain->strWalletFile); + return GetAccountBalance(walletdb, strAccount, nMinDepth, filter); +} + + +Value getbalance(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "getbalance [account] [minconf=1] [watchonly=0]\n" + "If [account] is not specified, returns the server's total available balance.\n" + "If [account] is specified, returns the balance in the account.\n" + "if [includeWatchonly] is specified, include balance in watchonly addresses (see 'importaddress')."); + + if (params.size() == 0) + return ValueFromAmount(pwalletMain->GetBalance()); + + int nMinDepth = 1; + if (params.size() > 1) + nMinDepth = params[1].get_int(); + isminefilter filter = MINE_SPENDABLE; + if(params.size() > 2) + if(params[2].get_bool()) + filter = filter | MINE_WATCH_ONLY; + + if (params[0].get_str() == "*") { + // Calculate total balance a different way from GetBalance() + // (GetBalance() sums up all unspent TxOuts) + // getbalance and getbalance '*' 0 should return the same number. + int64_t nBalance = 0; + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (!wtx.IsTrusted()) + continue; + + int64_t allGeneratedImmature, allGeneratedMature, allFee; + allGeneratedImmature = allGeneratedMature = allFee = 0; + + string strSentAccount; + list > listReceived; + list > listSent; + wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount, filter); + if (wtx.GetDepthInMainChain() >= nMinDepth) + { + BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& r, listReceived) + nBalance += r.second; + } + BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& r, listSent) + nBalance -= r.second; + nBalance -= allFee; + nBalance += allGeneratedMature; + } + return ValueFromAmount(nBalance); + } + + string strAccount = AccountFromValue(params[0]); + + int64_t nBalance = GetAccountBalance(strAccount, nMinDepth, filter); + + 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_t nAmount = AmountFromValue(params[2]); + + if (nAmount < nMinimumInputValue) + throw JSONRPCError(-101, "Send amount too small"); + + if (params.size() > 3) + // unused parameter, used to be nMinDepth, keep type-checking it though + (void)params[3].get_int(); + string strComment; + if (params.size() > 4) + strComment = params[4].get_str(); + + CWalletDB walletdb(pwalletMain->strWalletFile); + if (!walletdb.TxnBegin()) + throw JSONRPCError(RPC_DATABASE_ERROR, "database error"); + + int64_t nNow = GetAdjustedTime(); + + // Debit + CAccountingEntry debit; + debit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb); + debit.strAccount = strFrom; + debit.nCreditDebit = -nAmount; + debit.nTime = nNow; + debit.strOtherAccount = strTo; + debit.strComment = strComment; + walletdb.WriteAccountingEntry(debit); + + // Credit + CAccountingEntry credit; + credit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb); + credit.strAccount = strTo; + credit.nCreditDebit = nAmount; + credit.nTime = nNow; + credit.strOtherAccount = strFrom; + credit.strComment = strComment; + walletdb.WriteAccountingEntry(credit); + + if (!walletdb.TxnCommit()) + throw JSONRPCError(RPC_DATABASE_ERROR, "database error"); + + 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 " + FormatMoney(nMinimumInputValue) + + HelpRequiringPassphrase()); + + string strAccount = AccountFromValue(params[0]); + CBitcoinAddress address(params[1].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid XP address"); + int64_t nAmount = AmountFromValue(params[2]); + + if (nAmount < nMinimumInputValue) + throw JSONRPCError(-101, "Send amount too small"); + + 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(); + + EnsureWalletIsUnlocked(); + + // Check funds + int64_t nBalance = GetAccountBalance(strAccount, nMinDepth, MINE_SPENDABLE); + if (nAmount > nBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); + + // Send + string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx); + if (strError != "") + throw JSONRPCError(RPC_WALLET_ERROR, 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" + + HelpRequiringPassphrase()); + + 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_t totalAmount = 0; + BOOST_FOREACH(const Pair& s, sendTo) + { + CBitcoinAddress address(s.name_); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid XP address: ")+s.name_); + + if (setAddress.count(address)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_); + setAddress.insert(address); + + CScript scriptPubKey; + scriptPubKey.SetDestination(address.Get()); + int64_t nAmount = AmountFromValue(s.value_); + + if (nAmount < nMinimumInputValue) + throw JSONRPCError(-101, "Send amount too small"); + + totalAmount += nAmount; + + vecSend.push_back(make_pair(scriptPubKey, nAmount)); + } + + EnsureWalletIsUnlocked(); + + // Check funds + int64_t nBalance = GetAccountBalance(strAccount, nMinDepth, MINE_SPENDABLE); + if (totalAmount > nBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); + + // Send + CReserveKey keyChange(pwalletMain); + int64_t nFeeRequired = 0; + bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired); + if (!fCreated) + { + int64_t nTotal = pwalletMain->GetBalance(), nWatchOnly = pwalletMain->GetWatchOnlyBalance(); + if (totalAmount + nFeeRequired > nTotal - nWatchOnly) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); + throw JSONRPCError(RPC_WALLET_ERROR, "Transaction creation failed"); + } + if (!pwalletMain->CommitTransaction(wtx, keyChange)) + throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed"); + + return wtx.GetHash().GetHex(); +} + +Value addmultisigaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 3) + { + string msg = "addmultisigaddress <'[\"key\",\"key\"]'> [account]\n" + "Add a nrequired-to-sign multisignature address to the wallet\"\n" + "each key is a XP address or hex-encoded public key\n" + "If [account] is specified, assign address to [account]."; + throw runtime_error(msg); + } + + int nRequired = params[0].get_int(); + const Array& keys = params[1].get_array(); + string strAccount; + if (params.size() > 2) + strAccount = AccountFromValue(params[2]); + + // Gather public keys + if (nRequired < 1) + throw runtime_error("a multisignature address must require at least one key to redeem"); + if ((int)keys.size() < nRequired) + throw runtime_error( + strprintf("not enough keys supplied " + "(got %" PRIszu " keys, but need at least %d to redeem)", keys.size(), nRequired)); + if (keys.size() > 16) + throw runtime_error("Number of addresses involved in the multisignature address creation > 16\nReduce the number"); + std::vector pubkeys; + pubkeys.resize(keys.size()); + for (unsigned int i = 0; i < keys.size(); i++) + { + const std::string& ks = keys[i].get_str(); + + // Case 1: Bitcoin address and we have full public key: + CBitcoinAddress address(ks); + if (address.IsValid()) + { + CKeyID keyID; + if (!address.GetKeyID(keyID)) + throw runtime_error( + strprintf("%s does not refer to a key",ks.c_str())); + CPubKey vchPubKey; + if (!pwalletMain->GetPubKey(keyID, vchPubKey)) + throw runtime_error( + strprintf("no full public key for address %s",ks.c_str())); + if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey)) + throw runtime_error(" Invalid public key: "+ks); + } + + // Case 2: hex public key + else if (IsHex(ks)) + { + CPubKey vchPubKey(ParseHex(ks)); + if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey)) + throw runtime_error(" Invalid public key: "+ks); + } + else + { + throw runtime_error(" Invalid public key: "+ks); + } + } + + // Construct using pay-to-script-hash: + CScript inner; + inner.SetMultisig(nRequired, pubkeys); + + if (inner.size() > MAX_SCRIPT_ELEMENT_SIZE) + throw runtime_error( + strprintf("redeemScript exceeds size limit: %" PRIszu " > %d", inner.size(), MAX_SCRIPT_ELEMENT_SIZE)); + + CScriptID innerID = inner.GetID(); + pwalletMain->AddCScript(inner); + + pwalletMain->SetAddressBookName(innerID, strAccount); + return CBitcoinAddress(innerID).ToString(); +} + +Value addredeemscript(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + { + string msg = "addredeemscript [account]\n" + "Add a P2SH address with a specified redeemScript to the wallet.\n" + "If [account] is specified, assign address to [account]."; + throw runtime_error(msg); + } + + string strAccount; + if (params.size() > 1) + strAccount = AccountFromValue(params[1]); + + // Construct using pay-to-script-hash: + vector innerData = ParseHexV(params[0], "redeemScript"); + CScript inner(innerData.begin(), innerData.end()); + CScriptID innerID = inner.GetID(); + pwalletMain->AddCScript(inner); + + pwalletMain->SetAddressBookName(innerID, strAccount); + return CBitcoinAddress(innerID).ToString(); +} + +struct tallyitem +{ + int64_t nAmount; + int nConf; + tallyitem() + { + nAmount = 0; + nConf = std::numeric_limits::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; + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + + if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal()) + continue; + + int nDepth = wtx.GetDepthInMainChain(); + if (nDepth < nMinDepth) + continue; + + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + { + CTxDestination address; + if (!ExtractDestination(txout.scriptPubKey, address) || !IsMine(*pwalletMain, address)) + continue; + + tallyitem& item = mapTally[address]; + item.nAmount += txout.nValue; + item.nConf = min(item.nConf, nDepth); + } + } + + // Reply + Array ret; + map mapAccountTally; + BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook) + { + const CBitcoinAddress& address = item.first; + const string& strAccount = item.second; + map::iterator it = mapTally.find(address); + if (it == mapTally.end() && !fIncludeEmpty) + continue; + + int64_t nAmount = 0; + int nConf = std::numeric_limits::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", address.ToString())); + obj.push_back(Pair("account", strAccount)); + obj.push_back(Pair("amount", ValueFromAmount(nAmount))); + obj.push_back(Pair("confirmations", (nConf == std::numeric_limits::max() ? 0 : nConf))); + ret.push_back(obj); + } + } + + if (fByAccounts) + { + for (map::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it) + { + int64_t nAmount = (*it).second.nAmount; + int nConf = (*it).second.nConf; + Object obj; + obj.push_back(Pair("account", (*it).first)); + obj.push_back(Pair("amount", ValueFromAmount(nAmount))); + obj.push_back(Pair("confirmations", (nConf == std::numeric_limits::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); +} + +static void MaybePushAddress(Object & entry, const CTxDestination &dest) +{ + CBitcoinAddress addr; + if (addr.Set(dest)) + entry.push_back(Pair("address", addr.ToString())); +} + +void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret, const isminefilter& filter) +{ + int64_t nGeneratedImmature, nGeneratedMature, nFee; + string strSentAccount; + list > listReceived; + list > listSent; + + wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount, filter); + + bool fAllAccounts = (strAccount == string("*")); + bool involvesWatchonly = wtx.IsFromMe(MINE_WATCH_ONLY); + + // 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(CTxDestination, int64_t)& s, listSent) + { + Object entry; + entry.push_back(Pair("account", strSentAccount)); + if(involvesWatchonly || (::IsMine(*pwalletMain, s.first) & MINE_WATCH_ONLY)) + entry.push_back(Pair("involvesWatchonly", true)); + MaybePushAddress(entry, s.first); + + if (wtx.GetDepthInMainChain() < 0) { + entry.push_back(Pair("category", "conflicted")); + } else { + 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) + { + BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& r, listReceived) + { + string account; + if (pwalletMain->mapAddressBook.count(r.first)) + account = pwalletMain->mapAddressBook[r.first]; + if (fAllAccounts || (account == strAccount)) + { + Object entry; + entry.push_back(Pair("account", account)); + if(involvesWatchonly || (::IsMine(*pwalletMain, r.first) & MINE_WATCH_ONLY)) + entry.push_back(Pair("involvesWatchonly", true)); + MaybePushAddress(entry, r.first); + if (wtx.IsCoinBase()) + { + if (wtx.GetDepthInMainChain() < 1) + entry.push_back(Pair("category", "orphan")); + else if (wtx.GetBlocksToMaturity() > 0) + entry.push_back(Pair("category", "immature")); + else + entry.push_back(Pair("category", "generate")); + } + else + 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", (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(); + + isminefilter filter = MINE_SPENDABLE; + if(params.size() > 3) + if(params[3].get_bool()) + filter = filter | MINE_WATCH_ONLY; + + if (nCount < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count"); + if (nFrom < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from"); + + Array ret; + + std::list acentries; + CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount); + + // iterate backwards until we have nCount items to return: + for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) + { + CWalletTx *const pwtx = (*it).second.first; + if (pwtx != 0) + ListTransactions(*pwtx, strAccount, 0, true, ret, filter); + CAccountingEntry *const pacentry = (*it).second.second; + if (pacentry != 0) + AcentryToJSON(*pacentry, strAccount, ret); + + if ((int)ret.size() >= (nCount+nFrom)) break; + } + // ret is newest to oldest + + if (nFrom > (int)ret.size()) + nFrom = ret.size(); + if ((nFrom + nCount) > (int)ret.size()) + nCount = ret.size() - nFrom; + Array::iterator first = ret.begin(); + std::advance(first, nFrom); + Array::iterator last = ret.begin(); + std::advance(last, nFrom+nCount); + + if (last != ret.end()) ret.erase(last, ret.end()); + if (first != ret.begin()) ret.erase(ret.begin(), first); + + std::reverse(ret.begin(), ret.end()); // Return 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(); + + isminefilter includeWatchonly = MINE_SPENDABLE; + if(params.size() > 1) + if(params[1].get_bool()) + includeWatchonly = includeWatchonly | MINE_WATCH_ONLY; + + + map mapAccountBalances; + BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& entry, pwalletMain->mapAddressBook) { + if (IsMine(*pwalletMain, entry.first)) // This address belongs to me + mapAccountBalances[entry.second] = 0; + } + + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + int64_t nGeneratedImmature, nGeneratedMature, nFee; + string strSentAccount; + list > listReceived; + list > listSent; + wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount, includeWatchonly); + mapAccountBalances[strSentAccount] -= nFee; + BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent) + mapAccountBalances[strSentAccount] -= s.second; + if (wtx.GetDepthInMainChain() >= nMinDepth) + { + mapAccountBalances[""] += nGeneratedMature; + BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& r, listReceived) + if (pwalletMain->mapAddressBook.count(r.first)) + mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second; + else + mapAccountBalances[""] += r.second; + } + } + + list acentries; + CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries); + BOOST_FOREACH(const CAccountingEntry& entry, acentries) + mapAccountBalances[entry.strAccount] += entry.nCreditDebit; + + Object ret; + BOOST_FOREACH(const PAIRTYPE(string, int64_t)& accountBalance, mapAccountBalances) { + ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second))); + } + return ret; +} + +Value listsinceblock(const Array& params, bool fHelp) +{ + if (fHelp) + throw runtime_error( + "listsinceblock [blockhash] [target-confirmations]\n" + "Get all transactions in blocks since block [blockhash], or all transactions if omitted"); + + CBlockIndex *pindex = NULL; + int target_confirms = 1; + isminefilter filter = MINE_SPENDABLE; + + if (params.size() > 0) + { + uint256 blockId = 0; + + blockId.SetHex(params[0].get_str()); + pindex = CBlockLocator(blockId).GetBlockIndex(); + } + + if (params.size() > 1) + { + target_confirms = params[1].get_int(); + + if (target_confirms < 1) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); + } + + if(params.size() > 2) + if(params[2].get_bool()) + filter = filter | MINE_WATCH_ONLY; + + int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1; + + Array transactions; + + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++) + { + CWalletTx tx = (*it).second; + + if (depth == -1 || tx.GetDepthInMainChain() < depth) + ListTransactions(tx, "*", 0, true, transactions, filter); + } + + uint256 lastblock; + + if (target_confirms == 1) + { + lastblock = hashBestChain; + } + else + { + int target_height = pindexBest->nHeight + 1 - target_confirms; + + CBlockIndex *block; + for (block = pindexBest; + block && block->nHeight > target_height; + block = block->pprev) { } + + lastblock = block ? block->GetBlockHash() : 0; + } + + Object ret; + ret.push_back(Pair("transactions", transactions)); + ret.push_back(Pair("lastblock", lastblock.GetHex())); + + 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()); + + isminefilter filter = MINE_SPENDABLE; + if(params.size() > 1) + if(params[1].get_bool()) + filter = filter | MINE_WATCH_ONLY; + + Object entry; + + if (pwalletMain->mapWallet.count(hash)) + { + const CWalletTx& wtx = pwalletMain->mapWallet[hash]; + + TxToJSON(wtx, 0, entry); + + int64_t nCredit = wtx.GetCredit(filter); + int64_t nDebit = wtx.GetDebit(filter); + int64_t nNet = nCredit - nDebit; + int64_t nFee = (wtx.IsFromMe(filter) ? wtx.GetValueOut() - nDebit : 0); + + entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee))); + if (wtx.IsFromMe(filter)) + entry.push_back(Pair("fee", ValueFromAmount(nFee))); + + WalletTxToJSON(wtx, entry); + + Array details; + ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details, filter); + entry.push_back(Pair("details", details)); + } + else + { + CTransaction tx; + uint256 hashBlock = 0; + if (GetTransaction(hash, tx, hashBlock)) + { + TxToJSON(tx, 0, entry); + if (hashBlock == 0) + entry.push_back(Pair("confirmations", 0)); + else + { + entry.push_back(Pair("blockhash", hashBlock.GetHex())); + map::iterator mi = mapBlockIndex.find(hashBlock); + if (mi != mapBlockIndex.end() && (*mi).second) + { + CBlockIndex* pindex = (*mi).second; + if (pindex->IsInMainChain()) + entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight)); + else + entry.push_back(Pair("confirmations", 0)); + } + } + } + else + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction"); + } + + 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(); + if (!BackupWallet(*pwalletMain, strDest)) + throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!"); + + return Value::null; +} + + +Value keypoolrefill(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "keypoolrefill [new-size]\n" + "Fills the keypool.\n" + "IMPORTANT: Any previous backups you have made of your wallet file " + "should be replaced with the newly generated one." + + HelpRequiringPassphrase()); + + unsigned int nSize = max(GetArgUInt("-keypool", 100), 0); + if (params.size() > 0) { + if (params[0].get_int() < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size"); + nSize = (unsigned int) params[0].get_int(); + } + + EnsureWalletIsUnlocked(); + + pwalletMain->TopUpKeyPool(nSize); + + if (pwalletMain->GetKeyPoolSize() < nSize) + throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool."); + + return Value::null; +} + +Value keypoolreset(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "keypoolreset [new-size]\n" + "Resets the keypool.\n" + "IMPORTANT: Any previous backups you have made of your wallet file " + "should be replaced with the newly generated one." + + HelpRequiringPassphrase()); + + unsigned int nSize = max(GetArgUInt("-keypool", 100), 0); + if (params.size() > 0) { + if (params[0].get_int() < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size"); + nSize = (unsigned int) params[0].get_int(); + } + + EnsureWalletIsUnlocked(); + + pwalletMain->NewKeyPool(nSize); + + if (pwalletMain->GetKeyPoolSize() < nSize) + throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool."); + + return Value::null; +} + + +void ThreadTopUpKeyPool(void* parg) +{ + // Make this thread recognisable as the key-topping-up thread + RenameThread("XP-key-top"); + + pwalletMain->TopUpKeyPool(); +} + +void ThreadCleanWalletPassphrase(void* parg) +{ + // Make this thread recognisable as the wallet relocking thread + RenameThread("XP-lock-wa"); + + int64_t nMyWakeTime = GetTimeMillis() + *((int64_t*)parg) * 1000; + + ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime); + + if (nWalletUnlockTime == 0) + { + nWalletUnlockTime = nMyWakeTime; + + do + { + if (nWalletUnlockTime==0) + break; + int64_t nToSleep = nWalletUnlockTime - GetTimeMillis(); + if (nToSleep <= 0) + break; + + LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime); + Sleep(nToSleep); + ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime); + + } while(1); + + if (nWalletUnlockTime) + { + nWalletUnlockTime = 0; + pwalletMain->Lock(); + } + } + else + { + if (nWalletUnlockTime < nMyWakeTime) + nWalletUnlockTime = nMyWakeTime; + } + + LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime); + + delete (int64_t*)parg; +} + +Value walletpassphrase(const Array& params, bool fHelp) +{ + if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3)) + throw runtime_error( + "walletpassphrase [mintonly]\n" + "Stores the wallet decryption key in memory for seconds.\n" + "mintonly is optional true/false allowing only block minting."); + if (fHelp) + return true; + if (!pwalletMain->IsCrypted()) + throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called."); + + if (!pwalletMain->IsLocked()) + throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings."); + // Note that the walletpassphrase is stored in params[0] which is not mlock()ed + SecureString strWalletPass; + strWalletPass.reserve(100); + // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) + // Alternately, find a way to make params[0] mlock()'d to begin with. + strWalletPass = params[0].get_str().c_str(); + + if (strWalletPass.length() > 0) + { + if (!pwalletMain->Unlock(strWalletPass)) + throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect."); + } + else + throw runtime_error( + "walletpassphrase \n" + "Stores the wallet decryption key in memory for seconds."); + + NewThread(ThreadTopUpKeyPool, NULL); + int64_t* pnSleepTime = new int64_t(params[1].get_int64()); + NewThread(ThreadCleanWalletPassphrase, pnSleepTime); + + // ppcoin: if user OS account compromised prevent trivial sendmoney commands + if (params.size() > 2) + fWalletUnlockMintOnly = params[2].get_bool(); + else + fWalletUnlockMintOnly = false; + + return Value::null; +} + + +Value walletpassphrasechange(const Array& params, bool fHelp) +{ + if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2)) + throw runtime_error( + "walletpassphrasechange \n" + "Changes the wallet passphrase from to ."); + if (fHelp) + return true; + if (!pwalletMain->IsCrypted()) + throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called."); + + // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string) + // Alternately, find a way to make params[0] mlock()'d to begin with. + SecureString strOldWalletPass; + strOldWalletPass.reserve(100); + strOldWalletPass = params[0].get_str().c_str(); + + SecureString strNewWalletPass; + strNewWalletPass.reserve(100); + strNewWalletPass = params[1].get_str().c_str(); + + if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1) + throw runtime_error( + "walletpassphrasechange \n" + "Changes the wallet passphrase from to ."); + + if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) + throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect."); + + return Value::null; +} + + +Value walletlock(const Array& params, bool fHelp) +{ + if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0)) + throw runtime_error( + "walletlock\n" + "Removes the wallet encryption key from memory, locking the wallet.\n" + "After calling this method, you will need to call walletpassphrase again\n" + "before being able to call any methods which require the wallet to be unlocked."); + if (fHelp) + return true; + if (!pwalletMain->IsCrypted()) + throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called."); + + { + LOCK(cs_nWalletUnlockTime); + pwalletMain->Lock(); + nWalletUnlockTime = 0; + } + + return Value::null; +} + + +Value encryptwallet(const Array& params, bool fHelp) +{ + if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1)) + throw runtime_error( + "encryptwallet \n" + "Encrypts the wallet with ."); + if (fHelp) + return true; + if (pwalletMain->IsCrypted()) + throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called."); + + // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) + // Alternately, find a way to make params[0] mlock()'d to begin with. + SecureString strWalletPass; + strWalletPass.reserve(100); + strWalletPass = params[0].get_str().c_str(); + + if (strWalletPass.length() < 1) + throw runtime_error( + "encryptwallet \n" + "Encrypts the wallet with ."); + + if (!pwalletMain->EncryptWallet(strWalletPass)) + throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet."); + + // BDB seems to have a bad habit of writing old data into + // slack space in .dat files; that is bad if the old data is + // unencrypted private keys. So: + StartShutdown(); + return "wallet encrypted; XP server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup."; +} + +class DescribeAddressVisitor : public boost::static_visitor +{ +private: + isminetype mine; +public: + DescribeAddressVisitor(isminetype mineIn) : mine(mineIn) {} + + Object operator()(const CNoDestination &dest) const { return Object(); } + Object operator()(const CKeyID &keyID) const { + Object obj; + CPubKey vchPubKey; + pwalletMain->GetPubKey(keyID, vchPubKey); + obj.push_back(Pair("isscript", false)); + if (mine == MINE_SPENDABLE) { + pwalletMain->GetPubKey(keyID, vchPubKey); + obj.push_back(Pair("pubkey", HexStr(vchPubKey.Raw()))); + obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed())); + } + return obj; + } + + Object operator()(const CScriptID &scriptID) const { + Object obj; + obj.push_back(Pair("isscript", true)); + if (mine == MINE_SPENDABLE) { + CScript subscript; + pwalletMain->GetCScript(scriptID, subscript); + std::vector addresses; + txnouttype whichType; + int nRequired; + ExtractDestinations(subscript, whichType, addresses, nRequired); + obj.push_back(Pair("script", GetTxnOutputType(whichType))); + obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end()))); + Array a; + BOOST_FOREACH(const CTxDestination& addr, addresses) + a.push_back(CBitcoinAddress(addr).ToString()); + obj.push_back(Pair("addresses", a)); + if (whichType == TX_MULTISIG) + obj.push_back(Pair("sigsrequired", nRequired)); + } + return obj; + } +}; + +Value validateaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "validateaddress \n" + "Return information about ."); + + CBitcoinAddress address(params[0].get_str()); + bool isValid = address.IsValid(); + + Object ret; + ret.push_back(Pair("isvalid", isValid)); + if (isValid) + { + CTxDestination dest = address.Get(); + string currentAddress = address.ToString(); + ret.push_back(Pair("address", currentAddress)); + isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : MINE_NO; + ret.push_back(Pair("ismine", mine != MINE_NO)); + if (mine != MINE_NO) { + ret.push_back(Pair("watchonly", mine == MINE_WATCH_ONLY)); + Object detail = boost::apply_visitor(DescribeAddressVisitor(mine), dest); + ret.insert(ret.end(), detail.begin(), detail.end()); + } + if (pwalletMain->mapAddressBook.count(dest)) + ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest])); + } + return ret; +} + +// ppcoin: reserve balance from being staked for network protection +Value reservebalance(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "reservebalance [ [amount]]\n" + " is true or false to turn balance reserve on or off.\n" + " is a real and rounded to cent.\n" + "Set reserve amount not participating in network protection.\n" + "If no parameters provided current setting is printed.\n"); + + if (params.size() > 0) + { + bool fReserve = params[0].get_bool(); + if (fReserve) + { + if (params.size() == 1) + throw runtime_error("must provide amount to reserve balance.\n"); + int64_t nAmount = AmountFromValue(params[1]); + nAmount = (nAmount / CENT) * CENT; // round to cent + if (nAmount < 0) + throw runtime_error("amount cannot be negative.\n"); + mapArgs["-reservebalance"] = FormatMoney(nAmount).c_str(); + } + else + { + if (params.size() > 1) + throw runtime_error("cannot specify amount to turn off reserve.\n"); + mapArgs["-reservebalance"] = "0"; + } + } + + Object result; + if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance)) + throw runtime_error("invalid reserve balance amount\n"); + result.push_back(Pair("reserve", (nReserveBalance > 0))); + result.push_back(Pair("amount", ValueFromAmount(nReserveBalance))); + return result; +} + + +// ppcoin: check wallet integrity +Value checkwallet(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 0) + throw runtime_error( + "checkwallet\n" + "Check wallet for integrity.\n"); + + int nMismatchSpent; + int64_t nBalanceInQuestion; + pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion, true); + Object result; + if (nMismatchSpent == 0) + result.push_back(Pair("wallet check passed", true)); + else + { + result.push_back(Pair("mismatched spent coins", nMismatchSpent)); + result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion))); + } + return result; +} + + +// ppcoin: repair wallet +Value repairwallet(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 0) + throw runtime_error( + "repairwallet\n" + "Repair wallet if checkwallet reports any problem.\n"); + + int nMismatchSpent; + int64_t nBalanceInQuestion; + pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion); + Object result; + if (nMismatchSpent == 0) + result.push_back(Pair("wallet check passed", true)); + else + { + result.push_back(Pair("mismatched spent coins", nMismatchSpent)); + result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion))); + } + return result; +} + +// XP: resend unconfirmed wallet transactions +Value resendtx(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "resendtx\n" + "Re-send unconfirmed transactions.\n" + ); + + ResendWalletTransactions(); + + return Value::null; +} + +// ppcoin: make a public-private key pair +Value makekeypair(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "makekeypair [prefix]\n" + "Make a public/private key pair.\n" + "[prefix] is optional preferred prefix for the public key.\n"); + + string strPrefix = ""; + if (params.size() > 0) + strPrefix = params[0].get_str(); + + CKey key; + key.MakeNewKey(false); + + CPrivKey vchPrivKey = key.GetPrivKey(); + Object result; + result.push_back(Pair("PrivateKey", HexStr(vchPrivKey.begin(), vchPrivKey.end()))); + result.push_back(Pair("PublicKey", HexStr(key.GetPubKey().Raw()))); + return result; +} diff --git a/src/script.cpp b/src/script.cpp new file mode 100644 index 00000000..8057bdb5 --- /dev/null +++ b/src/script.cpp @@ -0,0 +1,1940 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include +#include + +using namespace std; +using namespace boost; + +#include "script.h" +#include "keystore.h" +#include "bignum.h" +#include "key.h" +#include "main.h" +#include "sync.h" +#include "util.h" + +bool CheckSig(vector vchSig, const vector &vchPubKey, const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, int flags); + +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 (unsigned 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; +} + +// +// WARNING: This does not work as expected for signed integers; the sign-bit +// is left in place as the integer is zero-extended. The correct behavior +// would be to move the most significant bit of the last byte during the +// resize process. MakeSameSize() is currently only used by the disabled +// opcodes OP_AND, OP_OR, and OP_XOR. +// +void MakeSameSize(valtype& vch1, valtype& vch2) +{ + // Lengthen the shorter one + if (vch1.size() < vch2.size()) + // PATCH: + // +unsigned char msb = vch1[vch1.size()-1]; + // +vch1[vch1.size()-1] &= 0x7f; + // vch1.resize(vch2.size(), 0); + // +vch1[vch1.size()-1] = msb; + vch1.resize(vch2.size(), 0); + if (vch2.size() < vch1.size()) + // PATCH: + // +unsigned char msb = vch2[vch2.size()-1]; + // +vch2[vch2.size()-1] &= 0x7f; + // vch2.resize(vch1.size(), 0); + // +vch2[vch2.size()-1] = msb; + 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(); +} + + +const char* GetTxnOutputType(txnouttype t) +{ + switch (t) + { + case TX_NONSTANDARD: return "nonstandard"; + case TX_PUBKEY: return "pubkey"; + case TX_PUBKEYHASH: return "pubkeyhash"; + case TX_SCRIPTHASH: return "scripthash"; + case TX_MULTISIG: return "multisig"; + case TX_NULL_DATA: return "nulldata"; + } + return NULL; +} + + +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_SMALLDATA : return "OP_SMALLDATA"; + + case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE"; + default: + return "OP_UNKNOWN"; + } +} + +bool IsCanonicalPubKey(const valtype &vchPubKey, unsigned int flags) { + if (!(flags & SCRIPT_VERIFY_STRICTENC)) + return true; + + if (vchPubKey.size() < 33) + return error("Non-canonical public key: too short"); + if (vchPubKey[0] == 0x04) { + if (vchPubKey.size() != 65) + return error("Non-canonical public key: invalid length for uncompressed key"); + } else if (vchPubKey[0] == 0x02 || vchPubKey[0] == 0x03) { + if (vchPubKey.size() != 33) + return error("Non-canonical public key: invalid length for compressed key"); + } else { + return error("Non-canonical public key: compressed nor uncompressed"); + } + return true; +} + +bool IsDERSignature(const valtype &vchSig, bool fWithHashType, bool fCheckLow) { + // See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623 + // A canonical signature exists of: <30> <02> <02> + // Where R and S are not negative (their first byte has its highest bit not set), and not + // excessively padded (do not start with a 0 byte, unless an otherwise negative number follows, + // in which case a single 0 byte is necessary and even required). + if (vchSig.size() < 9) + return error("Non-canonical signature: too short"); + if (vchSig.size() > 73) + return error("Non-canonical signature: too long"); + if (vchSig[0] != 0x30) + return error("Non-canonical signature: wrong type"); + if (vchSig[1] != vchSig.size() - (fWithHashType ? 3 : 2)) + return error("Non-canonical signature: wrong length marker"); + if (fWithHashType) { + unsigned char nHashType = vchSig[vchSig.size() - 1] & (~(SIGHASH_ANYONECANPAY)); + if (nHashType < SIGHASH_ALL || nHashType > SIGHASH_SINGLE) + return error("Non-canonical signature: unknown hashtype byte"); + } + unsigned int nLenR = vchSig[3]; + if (5 + nLenR >= vchSig.size()) + return error("Non-canonical signature: S length misplaced"); + unsigned int nLenS = vchSig[5+nLenR]; + if ((unsigned long)(nLenR + nLenS + (fWithHashType ? 7 : 6)) != vchSig.size()) + return error("Non-canonical signature: R+S length mismatch"); + + const unsigned char *R = &vchSig[4]; + if (R[-2] != 0x02) + return error("Non-canonical signature: R value type mismatch"); + if (nLenR == 0) + return error("Non-canonical signature: R length is zero"); + if (R[0] & 0x80) + return error("Non-canonical signature: R value negative"); + if (nLenR > 1 && (R[0] == 0x00) && !(R[1] & 0x80)) + return error("Non-canonical signature: R value excessively padded"); + + const unsigned char *S = &vchSig[6+nLenR]; + if (S[-2] != 0x02) + return error("Non-canonical signature: S value type mismatch"); + if (nLenS == 0) + return error("Non-canonical signature: S length is zero"); + if (S[0] & 0x80) + return error("Non-canonical signature: S value negative"); + if (nLenS > 1 && (S[0] == 0x00) && !(S[1] & 0x80)) + return error("Non-canonical signature: S value excessively padded"); + + if (fCheckLow) { + unsigned int nLenR = vchSig[3]; + unsigned int nLenS = vchSig[5+nLenR]; + const unsigned char *S = &vchSig[6+nLenR]; + // If the S value is above the order of the curve divided by two, its + // complement modulo the order could have been used instead, which is + // one byte shorter when encoded correctly. + if (!CKey::CheckSignatureElement(S, nLenS, true)) + return error("Non-canonical signature: S value is unnecessarily high"); + } + + return true; +} + +bool IsCanonicalSignature(const valtype &vchSig, unsigned int flags) { + if (!(flags & SCRIPT_VERIFY_STRICTENC)) + return true; + + return IsDERSignature(vchSig, true, (flags & SCRIPT_VERIFY_LOW_S) != 0); +} + +bool EvalScript(vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags, 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() > MAX_SCRIPT_ELEMENT_SIZE) + 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; // Disabled opcodes. + + 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((uint16_t) 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)).getint32(); + popstack(stack); + if (n < 0 || n >= (int)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; + + + case OP_SIZE: + { + // (in -- in size) + if (stack.size() < 1) + return false; + CBigNum bn((uint16_t) stacktop(-1).size()); + stack.push_back(bn.getvch()); + } + break; + + + // + // Bitwise logic + // + 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_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_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; + default: assert(!"invalid opcode"); break; + } + popstack(stack); + stack.push_back(bn.getvch()); + } + break; + + case OP_ADD: + case OP_SUB: + 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_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; + default: assert(!"invalid opcode"); 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 = IsCanonicalSignature(vchSig, flags) && IsCanonicalPubKey(vchPubKey, flags) && + CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType, flags); + + 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 ((int)stack.size() < i) + return false; + + int nKeysCount = CastToBigNum(stacktop(-i)).getint32(); + if (nKeysCount < 0 || nKeysCount > 20) + return false; + nOpCount += nKeysCount; + if (nOpCount > 201) + return false; + int ikey = ++i; + i += nKeysCount; + if ((int)stack.size() < i) + return false; + + int nSigsCount = CastToBigNum(stacktop(-i)).getint32(); + if (nSigsCount < 0 || nSigsCount > nKeysCount) + return false; + int isig = ++i; + i += nSigsCount; + if ((int)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 + bool fOk = IsCanonicalSignature(vchSig, flags) && IsCanonicalPubKey(vchPubKey, flags) && + CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType, flags); + + if (fOk) { + 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-- > 1) + popstack(stack); + + // A bug causes CHECKMULTISIG to consume one extra argument + // whose contents were not checked in any way. + // + // Unfortunately this is a potential source of mutability, + // so optionally verify it is exactly equal to zero prior + // to removing it from the stack. + if (stack.size() < 1) + return false; + if ((flags & SCRIPT_VERIFY_NULLDUMMY) && stacktop(-1).size()) + return error("CHECKMULTISIG dummy argument not null"); + 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 (unsigned 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 (unsigned int i = 0; i < txTmp.vin.size(); i++) + if (i != nIn) + txTmp.vin[i].nSequence = 0; + } + else if ((nHashType & 0x1f) == SIGHASH_SINGLE) + { + // Only lock-in 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 (unsigned int i = 0; i < nOut; i++) + txTmp.vout[i].SetNull(); + + // Let the others update at will + for (unsigned 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, 0); + ss.reserve(10000); + ss << txTmp << nHashType; + return Hash(ss.begin(), ss.end()); +} + + +// Valid signature cache, to avoid doing expensive ECDSA signature checking +// twice for every transaction (once when accepted into memory pool, and +// again when accepted into the block chain) + +class CSignatureCache +{ +private: + // sigdata_type is (signature hash, signature, public key): + typedef boost::tuple, CPubKey > sigdata_type; + std::set< sigdata_type> setValid; + boost::shared_mutex cs_sigcache; + +public: + bool + Get(const uint256 &hash, const std::vector& vchSig, const CPubKey& pubKey) + { + boost::shared_lock lock(cs_sigcache); + + sigdata_type k(hash, vchSig, pubKey); + std::set::iterator mi = setValid.find(k); + if (mi != setValid.end()) + return true; + return false; + } + + void Set(const uint256 &hash, const std::vector& vchSig, const CPubKey& pubKey) + { + // DoS prevention: limit cache size to less than 10MB + // (~200 bytes per cache entry times 50,000 entries) + // Since there are a maximum of 20,000 signature operations per block + // 50,000 is a reasonable default. + int64_t nMaxCacheSize = GetArg("-maxsigcachesize", 50000); + if (nMaxCacheSize <= 0) return; + + boost::shared_lock lock(cs_sigcache); + + while (static_cast(setValid.size()) > nMaxCacheSize) + { + // Evict a random entry. Random because that helps + // foil would-be DoS attackers who might try to pre-generate + // and re-use a set of valid signatures just-slightly-greater + // than our cache size. + uint256 randomHash = GetRandHash(); + std::vector unused; + std::set::iterator it = + setValid.lower_bound(sigdata_type(randomHash, unused, unused)); + if (it == setValid.end()) + it = setValid.begin(); + setValid.erase(*it); + } + + sigdata_type k(hash, vchSig, pubKey); + setValid.insert(k); + } +}; + +bool CheckSig(vector vchSig, const vector &vchPubKey, const CScript &scriptCode, + const CTransaction& txTo, unsigned int nIn, int nHashType, int flags) +{ + static CSignatureCache signatureCache; + + CKey key; + if (!key.SetPubKey(vchPubKey)) + return false; + CPubKey pubkey = key.GetPubKey(); + if (!pubkey.IsValid()) + 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(); + + uint256 sighash = SignatureHash(scriptCode, txTo, nIn, nHashType); + + if (signatureCache.Get(sighash, vchSig, pubkey)) + return true; + + if (!key.Verify(sighash, vchSig)) + return false; + + if (!(flags & SCRIPT_VERIFY_NOCACHE)) + signatureCache.Set(sighash, vchSig, pubkey); + + return true; +} + + + + + + + + +// +// Return public keys or hashes from scriptPubKey, for 'standard' transaction types. +// +bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector >& vSolutionsRet) +{ + // Templates + static map mTemplates; + if (mTemplates.empty()) + { + // Standard tx, sender provides pubkey, receiver adds signature + mTemplates.insert(make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG)); + + // Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey + mTemplates.insert(make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG)); + + // Sender provides N pubkeys, receivers provides M signatures + mTemplates.insert(make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG)); + + // Empty, provably prunable, data-carrying output + mTemplates.insert(make_pair(TX_NULL_DATA, CScript() << OP_RETURN << OP_SMALLDATA)); + } + + // Shortcut for pay-to-script-hash, which are more constrained than the other types: + // it is always OP_HASH160 20 [20 byte hash] OP_EQUAL + if (scriptPubKey.IsPayToScriptHash()) + { + typeRet = TX_SCRIPTHASH; + vector hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22); + vSolutionsRet.push_back(hashBytes); + return true; + } + + // Scan templates + const CScript& script1 = scriptPubKey; + BOOST_FOREACH(const PAIRTYPE(txnouttype, CScript)& tplate, mTemplates) + { + const CScript& script2 = tplate.second; + vSolutionsRet.clear(); + + opcodetype opcode1, opcode2; + vector vch1, vch2; + + // Compare + CScript::const_iterator pc1 = script1.begin(); + CScript::const_iterator pc2 = script2.begin(); + while (true) + { + if (pc1 == script1.end() && pc2 == script2.end()) + { + // Found a match + typeRet = tplate.first; + if (typeRet == TX_MULTISIG) + { + // Additional checks for TX_MULTISIG: + unsigned char m = vSolutionsRet.front()[0]; + unsigned char n = vSolutionsRet.back()[0]; + if (m < 1 || n < 1 || m > n || vSolutionsRet.size()-2 != n) + return false; + } + return true; + } + if (!script1.GetOp(pc1, opcode1, vch1)) + break; + if (!script2.GetOp(pc2, opcode2, vch2)) + break; + + // Template matching opcodes: + if (opcode2 == OP_PUBKEYS) + { + while (vch1.size() >= 33 && vch1.size() <= 120) + { + vSolutionsRet.push_back(vch1); + if (!script1.GetOp(pc1, opcode1, vch1)) + break; + } + if (!script2.GetOp(pc2, opcode2, vch2)) + break; + // Normal situation is to fall through + // to other if/else statements + } + + if (opcode2 == OP_PUBKEY) + { + if (vch1.size() < 33 || vch1.size() > 120) + break; + vSolutionsRet.push_back(vch1); + } + else if (opcode2 == OP_PUBKEYHASH) + { + if (vch1.size() != sizeof(uint160)) + break; + vSolutionsRet.push_back(vch1); + } + else if (opcode2 == OP_SMALLINTEGER) + { // Single-byte small integer pushed onto vSolutions + if (opcode1 == OP_0 || + (opcode1 >= OP_1 && opcode1 <= OP_16)) + { + char n = (char)CScript::DecodeOP_N(opcode1); + vSolutionsRet.push_back(valtype(1, n)); + } + else + break; + } + else if (opcode2 == OP_SMALLDATA) + { + // small pushdata, <= 80 bytes + if (vch1.size() > 80) + break; + } + else if (opcode1 != opcode2 || vch1 != vch2) + { + // Others must match exactly + break; + } + } + } + + vSolutionsRet.clear(); + typeRet = TX_NONSTANDARD; + return false; +} + + +bool Sign1(const CKeyID& address, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet) +{ + CKey key; + if (!keystore.GetKey(address, key)) + return false; + + vector vchSig; + if (!key.Sign(hash, vchSig)) + return false; + vchSig.push_back((unsigned char)nHashType); + scriptSigRet << vchSig; + + return true; +} + +bool SignN(const vector& multisigdata, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet) +{ + int nSigned = 0; + int nRequired = multisigdata.front()[0]; + for (unsigned int i = 1; i < multisigdata.size()-1 && nSigned < nRequired; i++) + { + const valtype& pubkey = multisigdata[i]; + CKeyID keyID = CPubKey(pubkey).GetID(); + if (Sign1(keyID, keystore, hash, nHashType, scriptSigRet)) + ++nSigned; + } + return nSigned==nRequired; +} + +// +// Sign scriptPubKey with private keys stored in keystore, given transaction hash and hash type. +// Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed), +// unless whichTypeRet is TX_SCRIPTHASH, in which case scriptSigRet is the redemption script. +// Returns false if scriptPubKey could not be completely satisfied. +// +bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash, int nHashType, + CScript& scriptSigRet, txnouttype& whichTypeRet) +{ + scriptSigRet.clear(); + + vector vSolutions; + if (!Solver(scriptPubKey, whichTypeRet, vSolutions)) + return false; + + CKeyID keyID; + switch (whichTypeRet) + { + case TX_NONSTANDARD: + case TX_NULL_DATA: + return false; + case TX_PUBKEY: + keyID = CPubKey(vSolutions[0]).GetID(); + return Sign1(keyID, keystore, hash, nHashType, scriptSigRet); + case TX_PUBKEYHASH: + keyID = CKeyID(uint160(vSolutions[0])); + if (!Sign1(keyID, keystore, hash, nHashType, scriptSigRet)) + return false; + else + { + CPubKey vch; + keystore.GetPubKey(keyID, vch); + scriptSigRet << vch; + } + return true; + case TX_SCRIPTHASH: + return keystore.GetCScript(uint160(vSolutions[0]), scriptSigRet); + + case TX_MULTISIG: + scriptSigRet << OP_0; // workaround CHECKMULTISIG bug + return (SignN(vSolutions, keystore, hash, nHashType, scriptSigRet)); + } + return false; +} + +int ScriptSigArgsExpected(txnouttype t, const std::vector >& vSolutions) +{ + switch (t) + { + case TX_NONSTANDARD: + return -1; + case TX_NULL_DATA: + return 1; + case TX_PUBKEY: + return 1; + case TX_PUBKEYHASH: + return 2; + case TX_MULTISIG: + if (vSolutions.size() < 1 || vSolutions[0].size() < 1) + return -1; + return vSolutions[0][0] + 1; + case TX_SCRIPTHASH: + return 1; // doesn't include args needed by the script + } + return -1; +} + +bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType) +{ + vector vSolutions; + if (!Solver(scriptPubKey, whichType, vSolutions)) + return false; + + if (whichType == TX_MULTISIG) + { + unsigned char m = vSolutions.front()[0]; + unsigned char n = vSolutions.back()[0]; + // Support up to x-of-3 multisig txns as standard + if (n < 1 || n > 3) + return false; + if (m < 1 || m > n) + return false; + } + + return whichType != TX_NONSTANDARD; +} + + +unsigned int HaveKeys(const vector& pubkeys, const CKeyStore& keystore) +{ + unsigned int nResult = 0; + BOOST_FOREACH(const valtype& pubkey, pubkeys) + { + CKeyID keyID = CPubKey(pubkey).GetID(); + if (keystore.HaveKey(keyID)) + ++nResult; + } + return nResult; +} + + +class CKeyStoreIsMineVisitor : public boost::static_visitor +{ +private: + const CKeyStore *keystore; +public: + CKeyStoreIsMineVisitor(const CKeyStore *keystoreIn) : keystore(keystoreIn) { } + bool operator()(const CNoDestination &dest) const { return false; } + bool operator()(const CKeyID &keyID) const { return keystore->HaveKey(keyID); } + bool operator()(const CScriptID &scriptID) const { return keystore->HaveCScript(scriptID); } +}; + +isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest) +{ + CScript script; + script.SetDestination(dest); + return IsMine(keystore, script); +} + +isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) +{ + vector vSolutions; + txnouttype whichType; + if (!Solver(scriptPubKey, whichType, vSolutions)) { + if (keystore.HaveWatchOnly(scriptPubKey)) + return MINE_WATCH_ONLY; + return MINE_NO; + } + + CKeyID keyID; + switch (whichType) + { + case TX_NONSTANDARD: + case TX_NULL_DATA: + break; + case TX_PUBKEY: + keyID = CPubKey(vSolutions[0]).GetID(); + if (keystore.HaveKey(keyID)) + return MINE_SPENDABLE; + break; + case TX_PUBKEYHASH: + keyID = CKeyID(uint160(vSolutions[0])); + if (keystore.HaveKey(keyID)) + return MINE_SPENDABLE; + break; + case TX_SCRIPTHASH: + { + CScriptID scriptID = CScriptID(uint160(vSolutions[0])); + CScript subscript; + if (keystore.GetCScript(scriptID, subscript)) { + isminetype ret = IsMine(keystore, subscript); + if (ret == MINE_SPENDABLE) + return ret; + } + break; + } + case TX_MULTISIG: + { + // Only consider transactions "mine" if we own ALL the + // keys involved. multi-signature transactions that are + // partially owned (somebody else has a key that can spend + // them) enable spend-out-from-under-you attacks, especially + // in shared-wallet situations. + vector keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1); + if (HaveKeys(keys, keystore) == keys.size()) + return MINE_SPENDABLE; + break; + } + } + + if (keystore.HaveWatchOnly(scriptPubKey)) + return MINE_WATCH_ONLY; + return MINE_NO; +} + +bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) +{ + vector vSolutions; + txnouttype whichType; + if (!Solver(scriptPubKey, whichType, vSolutions)) + return false; + + if (whichType == TX_PUBKEY) + { + addressRet = CPubKey(vSolutions[0]).GetID(); + return true; + } + else if (whichType == TX_PUBKEYHASH) + { + addressRet = CKeyID(uint160(vSolutions[0])); + return true; + } + else if (whichType == TX_SCRIPTHASH) + { + addressRet = CScriptID(uint160(vSolutions[0])); + return true; + } + // Multisig txns have more than one address... + return false; +} + +class CAffectedKeysVisitor : public boost::static_visitor { +private: + const CKeyStore &keystore; + CAffectedKeysVisitor& operator=(CAffectedKeysVisitor const&); + std::vector &vKeys; + +public: + CAffectedKeysVisitor(const CKeyStore &keystoreIn, std::vector &vKeysIn) : keystore(keystoreIn), vKeys(vKeysIn) {} + + void Process(const CScript &script) { + txnouttype type; + std::vector vDest; + int nRequired; + if (ExtractDestinations(script, type, vDest, nRequired)) { + BOOST_FOREACH(const CTxDestination &dest, vDest) + boost::apply_visitor(*this, dest); + } + } + + void operator()(const CKeyID &keyId) { + if (keystore.HaveKey(keyId)) + vKeys.push_back(keyId); + } + + void operator()(const CScriptID &scriptId) { + CScript script; + if (keystore.GetCScript(scriptId, script)) + Process(script); + } + + void operator()(const CNoDestination &none) {} +}; + + +void ExtractAffectedKeys(const CKeyStore &keystore, const CScript& scriptPubKey, std::vector &vKeys) { + CAffectedKeysVisitor(keystore, vKeys).Process(scriptPubKey); +} + +bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vector& addressRet, int& nRequiredRet) +{ + addressRet.clear(); + typeRet = TX_NONSTANDARD; + vector vSolutions; + if (!Solver(scriptPubKey, typeRet, vSolutions)) + return false; + if (typeRet == TX_NULL_DATA) + return true; + + if (typeRet == TX_MULTISIG) + { + nRequiredRet = vSolutions.front()[0]; + for (unsigned int i = 1; i < vSolutions.size()-1; i++) + { + CTxDestination address = CPubKey(vSolutions[i]).GetID(); + addressRet.push_back(address); + } + } + else + { + nRequiredRet = 1; + CTxDestination address; + if (!ExtractDestination(scriptPubKey, address)) + return false; + addressRet.push_back(address); + } + + return true; +} + +bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, + unsigned int flags, int nHashType) +{ + vector > stack, stackCopy; + if (!EvalScript(stack, scriptSig, txTo, nIn, flags, nHashType)) + return false; + if (flags & SCRIPT_VERIFY_P2SH) + stackCopy = stack; + if (!EvalScript(stack, scriptPubKey, txTo, nIn, flags, nHashType)) + return false; + if (stack.empty()) + return false; + + if (CastToBool(stack.back()) == false) + return false; + + // Additional validation for spend-to-script-hash transactions: + if ((flags & SCRIPT_VERIFY_P2SH) && scriptPubKey.IsPayToScriptHash()) + { + if (!scriptSig.IsPushOnly()) // scriptSig must be literals-only + return false; // or validation fails + + // stackCopy cannot be empty here, because if it was the + // P2SH HASH <> EQUAL scriptPubKey would be evaluated with + // an empty stack and the EvalScript above would return false. + assert(!stackCopy.empty()); + + const valtype& pubKeySerialized = stackCopy.back(); + CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end()); + popstack(stackCopy); + + if (!EvalScript(stackCopy, pubKey2, txTo, nIn, flags, nHashType)) + return false; + if (stackCopy.empty()) + return false; + return CastToBool(stackCopy.back()); + } + + return true; +} + +bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CTransaction& txTo, unsigned int nIn, int nHashType) +{ + assert(nIn < txTo.vin.size()); + CTxIn& txin = txTo.vin[nIn]; + + // 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(fromPubKey, txTo, nIn, nHashType); + + txnouttype whichType; + if (!Solver(keystore, fromPubKey, hash, nHashType, txin.scriptSig, whichType)) + return false; + + if (whichType == TX_SCRIPTHASH) + { + // Solver returns the subscript that need to be evaluated; + // the final scriptSig is the signatures from that + // and then the serialized subscript: + CScript subscript = txin.scriptSig; + + // Recompute txn hash using subscript in place of scriptPubKey: + uint256 hash2 = SignatureHash(subscript, txTo, nIn, nHashType); + + txnouttype subType; + bool fSolved = + Solver(keystore, subscript, hash2, nHashType, txin.scriptSig, subType) && subType != TX_SCRIPTHASH; + // Append serialized subscript whether or not it is completely signed: + txin.scriptSig << static_cast(subscript); + if (!fSolved) return false; + } + + // Test solution + return VerifyScript(txin.scriptSig, fromPubKey, txTo, nIn, STRICT_FLAGS, 0); +} + +bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType) +{ + assert(nIn < txTo.vin.size()); + CTxIn& txin = txTo.vin[nIn]; + assert(txin.prevout.n < txFrom.vout.size()); + assert(txin.prevout.hash == txFrom.GetHash()); + const CTxOut& txout = txFrom.vout[txin.prevout.n]; + + return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, nHashType); +} + +static CScript PushAll(const vector& values) +{ + CScript result; + BOOST_FOREACH(const valtype& v, values) + result << v; + return result; +} + +static CScript CombineMultisig(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn, + const vector& vSolutions, + vector& sigs1, vector& sigs2) +{ + // Combine all the signatures we've got: + set allsigs; + BOOST_FOREACH(const valtype& v, sigs1) + { + if (!v.empty()) + allsigs.insert(v); + } + BOOST_FOREACH(const valtype& v, sigs2) + { + if (!v.empty()) + allsigs.insert(v); + } + + // Build a map of pubkey -> signature by matching sigs to pubkeys: + assert(vSolutions.size() > 1); + unsigned int nSigsRequired = vSolutions.front()[0]; + unsigned int nPubKeys = (unsigned int)(vSolutions.size()-2); + map sigs; + BOOST_FOREACH(const valtype& sig, allsigs) + { + for (unsigned int i = 0; i < nPubKeys; i++) + { + const valtype& pubkey = vSolutions[i+1]; + if (sigs.count(pubkey)) + continue; // Already got a sig for this pubkey + + if (CheckSig(sig, pubkey, scriptPubKey, txTo, nIn, 0, 0)) + { + sigs[pubkey] = sig; + break; + } + } + } + // Now build a merged CScript: + unsigned int nSigsHave = 0; + CScript result; result << OP_0; // pop-one-too-many workaround + for (unsigned int i = 0; i < nPubKeys && nSigsHave < nSigsRequired; i++) + { + if (sigs.count(vSolutions[i+1])) + { + result << sigs[vSolutions[i+1]]; + ++nSigsHave; + } + } + // Fill any missing with OP_0: + for (unsigned int i = nSigsHave; i < nSigsRequired; i++) + result << OP_0; + + return result; +} + +static CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn, + const txnouttype txType, const vector& vSolutions, + vector& sigs1, vector& sigs2) +{ + switch (txType) + { + case TX_NONSTANDARD: + case TX_NULL_DATA: + // Don't know anything about this, assume bigger one is correct: + if (sigs1.size() >= sigs2.size()) + return PushAll(sigs1); + return PushAll(sigs2); + case TX_PUBKEY: + case TX_PUBKEYHASH: + // Signatures are bigger than placeholders or empty scripts: + if (sigs1.empty() || sigs1[0].empty()) + return PushAll(sigs2); + return PushAll(sigs1); + case TX_SCRIPTHASH: + if (sigs1.empty() || sigs1.back().empty()) + return PushAll(sigs2); + else if (sigs2.empty() || sigs2.back().empty()) + return PushAll(sigs1); + else + { + // Recur to combine: + valtype spk = sigs1.back(); + CScript pubKey2(spk.begin(), spk.end()); + + txnouttype txType2; + vector > vSolutions2; + Solver(pubKey2, txType2, vSolutions2); + sigs1.pop_back(); + sigs2.pop_back(); + CScript result = CombineSignatures(pubKey2, txTo, nIn, txType2, vSolutions2, sigs1, sigs2); + result << spk; + return result; + } + case TX_MULTISIG: + return CombineMultisig(scriptPubKey, txTo, nIn, vSolutions, sigs1, sigs2); + } + + return CScript(); +} + +CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn, + const CScript& scriptSig1, const CScript& scriptSig2) +{ + txnouttype txType; + vector > vSolutions; + Solver(scriptPubKey, txType, vSolutions); + + vector stack1; + EvalScript(stack1, scriptSig1, CTransaction(), 0, SCRIPT_VERIFY_STRICTENC, 0); + vector stack2; + EvalScript(stack2, scriptSig2, CTransaction(), 0, SCRIPT_VERIFY_STRICTENC, 0); + + return CombineSignatures(scriptPubKey, txTo, nIn, txType, vSolutions, stack1, stack2); +} + +unsigned int CScript::GetSigOpCount(bool fAccurate) const +{ + unsigned int n = 0; + const_iterator pc = begin(); + opcodetype lastOpcode = OP_INVALIDOPCODE; + 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) + { + if (fAccurate && lastOpcode >= OP_1 && lastOpcode <= OP_16) + n += DecodeOP_N(lastOpcode); + else + n += 20; + } + lastOpcode = opcode; + } + return n; +} + +unsigned int CScript::GetSigOpCount(const CScript& scriptSig) const +{ + if (!IsPayToScriptHash()) + return GetSigOpCount(true); + + // This is a pay-to-script-hash scriptPubKey; + // get the last item that the scriptSig + // pushes onto the stack: + const_iterator pc = scriptSig.begin(); + vector data; + while (pc < scriptSig.end()) + { + opcodetype opcode; + if (!scriptSig.GetOp(pc, opcode, data)) + return 0; + if (opcode > OP_16) + return 0; + } + + /// ... and return its opcount: + CScript subscript(data.begin(), data.end()); + return subscript.GetSigOpCount(true); +} + +bool CScript::IsPayToScriptHash() const +{ + // Extra-fast test for pay-to-script-hash CScripts: + return (this->size() == 23 && + this->at(0) == OP_HASH160 && + this->at(1) == 0x14 && + this->at(22) == OP_EQUAL); +} + +bool CScript::HasCanonicalPushes() const +{ + const_iterator pc = begin(); + while (pc < end()) + { + opcodetype opcode; + std::vector data; + if (!GetOp(pc, opcode, data)) + return false; + if (opcode > OP_16) + continue; + if (opcode < OP_PUSHDATA1 && opcode > OP_0 && (data.size() == 1 && data[0] <= 16)) + // Could have used an OP_n code, rather than a 1-byte push. + return false; + if (opcode == OP_PUSHDATA1 && data.size() < OP_PUSHDATA1) + // Could have used a normal n-byte push, rather than OP_PUSHDATA1. + return false; + if (opcode == OP_PUSHDATA2 && data.size() <= 0xFF) + // Could have used an OP_PUSHDATA1. + return false; + if (opcode == OP_PUSHDATA4 && data.size() <= 0xFFFF) + // Could have used an OP_PUSHDATA2. + return false; + } + return true; +} + +class CScriptVisitor : public boost::static_visitor +{ +private: + CScript *script; +public: + CScriptVisitor(CScript *scriptin) { script = scriptin; } + + bool operator()(const CNoDestination &dest) const { + script->clear(); + return false; + } + + bool operator()(const CKeyID &keyID) const { + script->clear(); + *script << OP_DUP << OP_HASH160 << keyID << OP_EQUALVERIFY << OP_CHECKSIG; + return true; + } + + bool operator()(const CScriptID &scriptID) const { + script->clear(); + *script << OP_HASH160 << scriptID << OP_EQUAL; + return true; + } +}; + +void CScript::SetDestination(const CTxDestination& dest) +{ + boost::apply_visitor(CScriptVisitor(this), dest); +} + +void CScript::SetMultisig(int nRequired, const std::vector& keys) +{ + this->clear(); + + *this << EncodeOP_N(nRequired); + BOOST_FOREACH(const CKey& key, keys) + *this << key.GetPubKey(); + *this << EncodeOP_N((int)(keys.size())) << OP_CHECKMULTISIG; +} diff --git a/src/script.h b/src/script.h new file mode 100644 index 00000000..0b94f41a --- /dev/null +++ b/src/script.h @@ -0,0 +1,622 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef H_BITCOIN_SCRIPT +#define H_BITCOIN_SCRIPT + +#include +#include + +#include + +#include "keystore.h" +#include "bignum.h" + +typedef std::vector valtype; + +class CTransaction; + +static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes + +/** IsMine() return codes */ +enum isminetype +{ + MINE_NO = 0, + MINE_WATCH_ONLY = 1, + MINE_SPENDABLE = 2, + MINE_ALL = MINE_WATCH_ONLY | MINE_SPENDABLE +}; + +typedef uint8_t isminefilter; + +/** Signature hash types/flags */ +enum +{ + SIGHASH_ALL = 1, + SIGHASH_NONE = 2, + SIGHASH_SINGLE = 3, + SIGHASH_ANYONECANPAY = 0x80, +}; + +/** Script verification flags */ +enum +{ + SCRIPT_VERIFY_NONE = 0, + SCRIPT_VERIFY_P2SH = (1U << 0), // evaluate P2SH (BIP16) subscripts + SCRIPT_VERIFY_STRICTENC = (1U << 1), // enforce strict conformance to DER and SEC2 for signatures and pubkeys + SCRIPT_VERIFY_LOW_S = (1U << 2), // enforce low S values in signatures (depends on STRICTENC) + SCRIPT_VERIFY_NOCACHE = (1U << 3), // do not store results in signature cache (but do query it) + SCRIPT_VERIFY_NULLDUMMY = (1U << 4), // verify dummy stack item consumed by CHECKMULTISIG is of zero-length +}; + +// Strict verification: +// +// * force DER encoding; +// * force low S; +// * ensure that CHECKMULTISIG dummy argument is null. +static const unsigned int STRICT_FORMAT_FLAGS = SCRIPT_VERIFY_STRICTENC | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_NULLDUMMY; + +// Mandatory script verification flags that all new blocks must comply with for +// them to be valid. (but old blocks may not comply with) Currently just P2SH, +// but in the future other flags may be added, such as a soft-fork to enforce +// strict DER encoding. +// +// Failing one of these tests may trigger a DoS ban - see ConnectInputs() for +// details. +static const unsigned int MANDATORY_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH; + +// Standard script verification flags that standard transactions will comply +// with. However scripts violating these flags may still be present in valid +// blocks and we must accept those blocks. +static const unsigned int STRICT_FLAGS = MANDATORY_SCRIPT_VERIFY_FLAGS | STRICT_FORMAT_FLAGS; + +enum txnouttype +{ + TX_NONSTANDARD, + // 'standard' transaction types: + TX_PUBKEY, + TX_PUBKEYHASH, + TX_SCRIPTHASH, + TX_MULTISIG, + TX_NULL_DATA, +}; + +const char* GetTxnOutputType(txnouttype t); + +/** Script opcodes */ +enum opcodetype +{ + // push value + OP_0 = 0x00, + OP_FALSE = OP_0, + OP_PUSHDATA1 = 0x4c, + OP_PUSHDATA2 = 0x4d, + OP_PUSHDATA4 = 0x4e, + OP_1NEGATE = 0x4f, + OP_RESERVED = 0x50, + OP_1 = 0x51, + OP_TRUE=OP_1, + OP_2 = 0x52, + OP_3 = 0x53, + OP_4 = 0x54, + OP_5 = 0x55, + OP_6 = 0x56, + OP_7 = 0x57, + OP_8 = 0x58, + OP_9 = 0x59, + OP_10 = 0x5a, + OP_11 = 0x5b, + OP_12 = 0x5c, + OP_13 = 0x5d, + OP_14 = 0x5e, + OP_15 = 0x5f, + OP_16 = 0x60, + + // control + OP_NOP = 0x61, + OP_VER = 0x62, + OP_IF = 0x63, + OP_NOTIF = 0x64, + OP_VERIF = 0x65, + OP_VERNOTIF = 0x66, + OP_ELSE = 0x67, + OP_ENDIF = 0x68, + OP_VERIFY = 0x69, + OP_RETURN = 0x6a, + + // stack ops + OP_TOALTSTACK = 0x6b, + OP_FROMALTSTACK = 0x6c, + OP_2DROP = 0x6d, + OP_2DUP = 0x6e, + OP_3DUP = 0x6f, + OP_2OVER = 0x70, + OP_2ROT = 0x71, + OP_2SWAP = 0x72, + OP_IFDUP = 0x73, + OP_DEPTH = 0x74, + OP_DROP = 0x75, + OP_DUP = 0x76, + OP_NIP = 0x77, + OP_OVER = 0x78, + OP_PICK = 0x79, + OP_ROLL = 0x7a, + OP_ROT = 0x7b, + OP_SWAP = 0x7c, + OP_TUCK = 0x7d, + + // splice ops + OP_CAT = 0x7e, + OP_SUBSTR = 0x7f, + OP_LEFT = 0x80, + OP_RIGHT = 0x81, + OP_SIZE = 0x82, + + // bit logic + OP_INVERT = 0x83, + OP_AND = 0x84, + OP_OR = 0x85, + OP_XOR = 0x86, + OP_EQUAL = 0x87, + OP_EQUALVERIFY = 0x88, + OP_RESERVED1 = 0x89, + OP_RESERVED2 = 0x8a, + + // numeric + OP_1ADD = 0x8b, + OP_1SUB = 0x8c, + OP_2MUL = 0x8d, + OP_2DIV = 0x8e, + OP_NEGATE = 0x8f, + OP_ABS = 0x90, + OP_NOT = 0x91, + OP_0NOTEQUAL = 0x92, + + OP_ADD = 0x93, + OP_SUB = 0x94, + OP_MUL = 0x95, + OP_DIV = 0x96, + OP_MOD = 0x97, + OP_LSHIFT = 0x98, + OP_RSHIFT = 0x99, + + OP_BOOLAND = 0x9a, + OP_BOOLOR = 0x9b, + OP_NUMEQUAL = 0x9c, + OP_NUMEQUALVERIFY = 0x9d, + OP_NUMNOTEQUAL = 0x9e, + OP_LESSTHAN = 0x9f, + OP_GREATERTHAN = 0xa0, + OP_LESSTHANOREQUAL = 0xa1, + OP_GREATERTHANOREQUAL = 0xa2, + OP_MIN = 0xa3, + OP_MAX = 0xa4, + + OP_WITHIN = 0xa5, + + // crypto + OP_RIPEMD160 = 0xa6, + OP_SHA1 = 0xa7, + OP_SHA256 = 0xa8, + OP_HASH160 = 0xa9, + OP_HASH256 = 0xaa, + OP_CODESEPARATOR = 0xab, + OP_CHECKSIG = 0xac, + OP_CHECKSIGVERIFY = 0xad, + OP_CHECKMULTISIG = 0xae, + OP_CHECKMULTISIGVERIFY = 0xaf, + + // expansion + OP_NOP1 = 0xb0, + OP_NOP2 = 0xb1, + OP_NOP3 = 0xb2, + OP_NOP4 = 0xb3, + OP_NOP5 = 0xb4, + OP_NOP6 = 0xb5, + OP_NOP7 = 0xb6, + OP_NOP8 = 0xb7, + OP_NOP9 = 0xb8, + OP_NOP10 = 0xb9, + + + + // template matching params + OP_SMALLDATA = 0xf9, + OP_SMALLINTEGER = 0xfa, + OP_PUBKEYS = 0xfb, + OP_PUBKEYHASH = 0xfd, + OP_PUBKEY = 0xfe, + + OP_INVALIDOPCODE = 0xff, +}; + +const char* GetOpName(opcodetype opcode); + +inline std::string ValueString(const std::vector& vch) +{ + if (vch.size() <= 4) + return strprintf("%d", CBigNum(vch).getint32()); + 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; +} + +/** Serialized script, used inside transaction inputs and outputs */ +class CScript : public std::vector +{ +protected: + CScript& push_int64(int64_t n) + { + if (n == -1 || (n >= 1 && n <= 16)) + { + push_back((uint8_t)n + (OP_1 - 1)); + } + else + { + CBigNum bn(n); + *this << bn.getvch(); + } + return *this; + } + + CScript& push_uint64(uint64_t n) + { + if (n >= 1 && n <= 16) + { + push_back((uint8_t)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 uint8_t* pbegin, const uint8_t* 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(int8_t b) { operator<<(b); } + explicit CScript(int16_t b) { operator<<(b); } + explicit CScript(int32_t b) { operator<<(b); } + explicit CScript(int64_t b) { operator<<(b); } + + explicit CScript(uint8_t b) { operator<<(b); } + explicit CScript(uint16_t b) { operator<<(b); } + explicit CScript(uint32_t b) { operator<<(b); } + explicit CScript(uint64_t 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<<(int8_t b) { return push_int64(b); } + CScript& operator<<(int16_t b) { return push_int64(b); } + CScript& operator<<(int32_t b) { return push_int64(b); } + CScript& operator<<(int64_t b) { return push_int64(b); } + + CScript& operator<<(uint8_t b) { return push_uint64(b); } + CScript& operator<<(uint16_t b) { return push_uint64(b); } + CScript& operator<<(uint32_t b) { return push_uint64(b); } + CScript& operator<<(uint64_t b) { return push_uint64(b); } + + CScript& operator<<(opcodetype opcode) + { + if (opcode < 0 || opcode > 0xff) + throw std::runtime_error("CScript::operator<<() : invalid opcode"); + insert(end(), (uint8_t)opcode); + return *this; + } + + CScript& operator<<(const uint160& b) + { + insert(end(), sizeof(b)); + insert(end(), (uint8_t*)&b, (uint8_t*)&b + sizeof(b)); + return *this; + } + + CScript& operator<<(const uint256& b) + { + insert(end(), sizeof(b)); + insert(end(), (uint8_t*)&b, (uint8_t*)&b + sizeof(b)); + return *this; + } + + CScript& operator<<(const CPubKey& key) + { + std::vector vchKey = key.Raw(); + return (*this) << vchKey; + } + + CScript& operator<<(const CBigNum& b) + { + *this << b.getvch(); + return *this; + } + + CScript& operator<<(const std::vector& b) + { + if (b.size() < OP_PUSHDATA1) + { + insert(end(), (uint8_t)b.size()); + } + else if (b.size() <= 0xff) + { + insert(end(), OP_PUSHDATA1); + insert(end(), (uint8_t)b.size()); + } + else if (b.size() <= 0xffff) + { + insert(end(), OP_PUSHDATA2); + uint16_t nSize = (uint16_t) b.size(); + insert(end(), (uint8_t*)&nSize, (uint8_t*)&nSize + sizeof(nSize)); + } + else + { + insert(end(), OP_PUSHDATA4); + uint32_t nSize = (uint32_t) b.size(); + insert(end(), (uint8_t*)&nSize, (uint8_t*)&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!"); + 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; + uint32_t opcode = *pc++; + + // Immediate operand + if (opcode <= OP_PUSHDATA4) + { + uint32_t nSize = OP_0; + 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; + 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 < 0 || (uint32_t)(end() - pc) < nSize) + return false; + if (pvchRet) + pvchRet->assign(pc, pc + nSize); + pc += nSize; + } + + opcodeRet = (opcodetype)opcode; + return true; + } + + // Encode/decode small integers: + static int DecodeOP_N(opcodetype opcode) + { + if (opcode == OP_0) + return 0; + assert(opcode >= OP_1 && opcode <= OP_16); + return (int)opcode - (int)(OP_1 - 1); + } + static opcodetype EncodeOP_N(int n) + { + assert(n >= 0 && n <= 16); + if (n == 0) + return OP_0; + return (opcodetype)(OP_1+n-1); + } + + int FindAndDelete(const CScript& b) + { + int nFound = 0; + if (b.empty()) + return nFound; + iterator pc = begin(); + opcodetype opcode; + do + { + while (end() - pc >= (long)b.size() && memcmp(&pc[0], &b[0], b.size()) == 0) + { + erase(pc, pc + b.size()); + ++nFound; + } + } + while (GetOp(pc, opcode)); + return nFound; + } + int Find(opcodetype op) const + { + int nFound = 0; + opcodetype opcode; + for (const_iterator pc = begin(); pc != end() && GetOp(pc, opcode);) + if (opcode == op) + ++nFound; + return nFound; + } + + // Pre-version-0.6, Bitcoin always counted CHECKMULTISIGs + // as 20 sigops. With pay-to-script-hash, that changed: + // CHECKMULTISIGs serialized in scriptSigs are + // counted more accurately, assuming they are of the form + // ... OP_N CHECKMULTISIG ... + unsigned int GetSigOpCount(bool fAccurate) const; + + // Accurately count sigOps, including sigOps in + // pay-to-script-hash transactions: + unsigned int GetSigOpCount(const CScript& scriptSig) const; + + bool IsPayToScriptHash() const; + + // Called by CTransaction::IsStandard and P2SH VerifyScript (which makes it consensus-critical). + bool IsPushOnly() const + { + const_iterator pc = begin(); + while (pc < end()) + { + opcodetype opcode; + if (!GetOp(pc, opcode)) + return false; + if (opcode > OP_16) + return false; + } + return true; + } + + // Called by CTransaction::IsStandard. + bool HasCanonicalPushes() const; + + void SetDestination(const CTxDestination& address); + void SetMultisig(int nRequired, const std::vector& keys); + + + void PrintHex() const + { + printf("CScript(%s)\n", HexStr(begin(), end(), true).c_str()); + } + + std::string ToString(bool fShort=false) 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 += fShort? ValueString(vch).substr(0, 10) : ValueString(vch); + else + str += GetOpName(opcode); + } + return str; + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } + + CScriptID GetID() const + { + return CScriptID(Hash160(*this)); + } +}; + +bool IsCanonicalPubKey(const std::vector &vchPubKey, unsigned int flags); +bool IsDERSignature(const valtype &vchSig, bool fWithHashType=false, bool fCheckLow=false); +bool IsCanonicalSignature(const std::vector &vchSig, unsigned int flags); + +bool EvalScript(std::vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); +bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector >& vSolutionsRet); +int ScriptSigArgsExpected(txnouttype t, const std::vector >& vSolutions); +bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType); +isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey); +isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest); +void ExtractAffectedKeys(const CKeyStore &keystore, const CScript& scriptPubKey, std::vector &vKeys); +bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet); +bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector& addressRet, int& nRequiredRet); +bool SignSignature(const CKeyStore& keystore, const CScript& fromPubKey, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); +bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); +bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); + +// Given two sets of signatures for scriptPubKey, possibly with OP_0 placeholders, +// combine them intelligently and return the result. +CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn, const CScript& scriptSig1, const CScript& scriptSig2); + +#endif diff --git a/src/scrypt.h b/src/scrypt.h new file mode 100644 index 00000000..7d5aadc1 --- /dev/null +++ b/src/scrypt.h @@ -0,0 +1,12 @@ +#ifndef SCRYPT_H +#define SCRYPT_H + +#include +#include +#include "uint256.h" + +#define SCRYPT_BUFFER_SIZE (131072 + 63) + +uint256 scrypt_blockhash(const uint8_t* input); + +#endif // SCRYPT_H diff --git a/src/serialize.h b/src/serialize.h new file mode 100644 index 00000000..f3b5a708 --- /dev/null +++ b/src/serialize.h @@ -0,0 +1,1431 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING 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 + +#ifndef Q_MOC_RUN +#include +#include +#include +#include +#endif + +#if defined __USE_MINGW_ANSI_STDIO +#undef __USE_MINGW_ANSI_STDIO // This constant forces MinGW to conduct stupid behavior +#endif +#include + +#include "allocators.h" +#include "version.h" + + +class CScript; +class CDataStream; +class CAutoFile; +static const unsigned int MAX_SIZE = 0x02000000; + +// 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 const_cast(val); +} + +///////////////////////////////////////////////////////////////// +// +// 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), + +}; + +#ifdef _MSC_VER +#define IMPLEMENT_SERIALIZE(statements) \ + unsigned int GetSerializeSize(int nType, int nVersion) const \ + { \ + CSerActionGetSerializeSize ser_action; \ + const bool fGetSize = true; \ + const bool fWrite = false; \ + const bool fRead = false; \ + unsigned int nSerSize = 0; \ + ser_streamplaceholder s; \ + assert(fGetSize||fWrite||fRead); /* suppress warning */ \ + s.nType = nType; \ + s.nVersion = nVersion; \ + std::map mapUnkIds; \ + {statements} \ + return nSerSize; \ + } \ + template \ + void Serialize(Stream& s, int nType, int nVersion) const \ + { \ + CSerActionSerialize ser_action; \ + const bool fGetSize = false; \ + const bool fWrite = true; \ + const bool fRead = false; \ + unsigned int nSerSize = 0; \ + assert(fGetSize||fWrite||fRead); /* suppress warning */ \ + std::map mapUnkIds; \ + {statements} \ + } \ + template \ + void Unserialize(Stream& s, int nType, int nVersion) \ + { \ + CSerActionUnserialize ser_action; \ + const bool fGetSize = false; \ + const bool fWrite = false; \ + const bool fRead = true; \ + unsigned int nSerSize = 0; \ + std::map mapUnkIds; \ + assert(fGetSize||fWrite||fRead); /* suppress warning */ \ + {statements} \ + } + +#else + +#define IMPLEMENT_SERIALIZE(statements) \ + unsigned int GetSerializeSize(int nType, int nVersion) const \ + { \ + CSerActionGetSerializeSize ser_action; \ + const bool fGetSize = true; \ + const bool fWrite = false; \ + const bool fRead = false; \ + unsigned int nSerSize = 0; \ + ser_streamplaceholder s; \ + assert(fGetSize||fWrite||fRead); /* suppress warning */ \ + s.nType = nType; \ + s.nVersion = nVersion; \ + {statements} \ + return nSerSize; \ + } \ + template \ + void Serialize(Stream& s, int nType, int nVersion) const \ + { \ + CSerActionSerialize ser_action; \ + const bool fGetSize = false; \ + const bool fWrite = true; \ + const bool fRead = false; \ + unsigned int nSerSize = 0; \ + assert(fGetSize||fWrite||fRead); /* suppress warning */ \ + {statements} \ + } \ + template \ + void Unserialize(Stream& s, int nType, int nVersion) \ + { \ + CSerActionUnserialize ser_action; \ + const bool fGetSize = false; \ + const bool fWrite = false; \ + const bool fRead = true; \ + unsigned int nSerSize = 0; \ + assert(fGetSize||fWrite||fRead); /* suppress warning */ \ + {statements} \ + } + +#endif + +#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_t a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(uint64_t 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_t a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, uint64_t 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_t& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, uint64_t& 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_t nSize) +{ + if (nSize < 253) return sizeof(unsigned char); + else if (nSize <= std::numeric_limits::max()) return sizeof(unsigned char) + sizeof(unsigned short); + else if (nSize <= std::numeric_limits::max()) return sizeof(unsigned char) + sizeof(unsigned int); + else return sizeof(unsigned char) + sizeof(uint64_t); +} + +template +void WriteCompactSize(Stream& os, uint64_t nSize) +{ + if (nSize < 253) + { + unsigned char chSize = (unsigned char)nSize; + WRITEDATA(os, chSize); + } + else if (nSize <= std::numeric_limits::max()) + { + unsigned char chSize = 253; + unsigned short xSize = (unsigned short)nSize; + WRITEDATA(os, chSize); + WRITEDATA(os, xSize); + } + else if (nSize <= std::numeric_limits::max()) + { + unsigned char chSize = 254; + unsigned int xSize = (unsigned int)nSize; + WRITEDATA(os, chSize); + WRITEDATA(os, xSize); + } + else + { + unsigned char chSize = 255; + uint64_t xSize = nSize; + WRITEDATA(os, chSize); + WRITEDATA(os, xSize); + } + return; +} + +template +uint64_t ReadCompactSize(Stream& is) +{ + unsigned char chSize; + READDATA(is, chSize); + uint64_t 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_t xSize; + READDATA(is, xSize); + nSizeRet = xSize; + } + if (nSizeRet > (uint64_t)MAX_SIZE) + throw std::ios_base::failure("ReadCompactSize() : size too large"); + return nSizeRet; +} + +// Variable-length integers: bytes are a MSB base-128 encoding of the number. +// The high bit in each byte signifies whether another digit follows. To make +// the encoding is one-to-one, one is subtracted from all but the last digit. +// Thus, the byte sequence a[] with length len, where all but the last byte +// has bit 128 set, encodes the number: +// +// (a[len-1] & 0x7F) + sum(i=1..len-1, 128^i*((a[len-i-1] & 0x7F)+1)) +// +// Properties: +// * Very small (0-127: 1 byte, 128-16511: 2 bytes, 16512-2113663: 3 bytes) +// * Every integer has exactly one encoding +// * Encoding does not depend on size of original integer type +// * No redundancy: every (infinite) byte sequence corresponds to a list +// of encoded integers. +// +// 0: [0x00] 256: [0x81 0x00] +// 1: [0x01] 16383: [0xFE 0x7F] +// 127: [0x7F] 16384: [0xFF 0x00] +// 128: [0x80 0x00] 16511: [0x80 0xFF 0x7F] +// 255: [0x80 0x7F] 65535: [0x82 0xFD 0x7F] +// 2^32: [0x8E 0xFE 0xFE 0xFF 0x00] + +template +inline unsigned int GetSizeOfVarInt(I n) +{ + int nRet = 0; + while(true) { + nRet++; + if (n <= 0x7F) + break; + n = (n >> 7) - 1; + } + return nRet; +} + +template +void WriteVarInt(Stream& os, I n) +{ + unsigned char tmp[(sizeof(n)*8+6)/7]; + int len=0; + while(true) { + tmp[len] = (n & 0x7F) | (len ? 0x80 : 0x00); + if (n <= 0x7F) + break; + n = (n >> 7) - 1; + len++; + } + do { + WRITEDATA(os, tmp[len]); + } while(len--); +} + +template +I ReadVarInt(Stream& is) +{ + I n = 0; + while(true) { + unsigned char chData; + READDATA(is, chData); + n = (n << 7) | (chData & 0x7F); + if (chData & 0x80) + n++; + else + return n; + } +} + +#define FLATDATA(obj) REF(CFlatData((char*)&(obj), (char*)&(obj) + sizeof(obj))) +#define VARINT(obj) REF(WrapVarInt(REF(obj))) + +/** Wrapper for serializing arrays and POD. + */ +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 (unsigned int)(pend - pbegin); + } + + template + void Serialize(Stream& s, int, int=0) const + { + s.write(pbegin, (int)(pend - pbegin)); + } + + template + void Unserialize(Stream& s, int, int=0) + { + s.read(pbegin, pend - pbegin); + } +}; + +template +class CVarInt +{ +protected: + I &n; +public: + CVarInt(I& nIn) : n(nIn) { } + + unsigned int GetSerializeSize(int, int) const { + return GetSizeOfVarInt(n); + } + + template + void Serialize(Stream &s, int, int) const { + WriteVarInt(s, n); + } + + template + void Unserialize(Stream& s, int, int) { + n = ReadVarInt(s); + } +}; + +template +CVarInt WrapVarInt(I& n) { return CVarInt(n); } + +// +// 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); +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); +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); + +// others derived from vector +extern inline unsigned int GetSerializeSize(const CScript& v, int nType, int nVersion); +template void Serialize(Stream& os, const CScript& v, int nType, int nVersion); +template void Unserialize(Stream& is, CScript& v, int nType, int nVersion); + +// pair +template unsigned int GetSerializeSize(const std::pair& item, int nType, int nVersion); +template void Serialize(Stream& os, const std::pair& item, int nType, int nVersion); +template void Unserialize(Stream& is, std::pair& item, int nType, int nVersion); + +// 3 tuple +template unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion); +template void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion); +template void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion); + +// 4 tuple +template unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion); +template void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion); +template void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion); + +// map +template unsigned int GetSerializeSize(const std::map& m, int nType, int nVersion); +template void Serialize(Stream& os, const std::map& m, int nType, int nVersion); +template void Unserialize(Stream& is, std::map& m, int nType, int nVersion); + +// set +template unsigned int GetSerializeSize(const std::set& m, int nType, int nVersion); +template void Serialize(Stream& os, const std::set& m, int nType, int nVersion); +template void Unserialize(Stream& is, std::set& m, int nType, int nVersion); + + + + + +// +// 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) +{ + return a.GetSerializeSize((int)nType, nVersion); +} + +template +inline void Serialize(Stream& os, const T& a, long nType, int nVersion) +{ + a.Serialize(os, (int)nType, nVersion); +} + +template +inline void Unserialize(Stream& is, T& a, long nType, int nVersion) +{ + a.Unserialize(is, (int)nType, nVersion); +} + + + + + +// +// string +// +template +unsigned int GetSerializeSize(const std::basic_string& str, int, int) +{ + return (unsigned int)(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], (int)(str.size() * sizeof(str[0]))); +} + +template +void Unserialize(Stream& is, std::basic_string& str, int, int) +{ + unsigned int nSize = (unsigned int)(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 (unsigned int)(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], (int)(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&) +{ + // Limit size per read so bogus size value won't cause out of memory + v.clear(); + unsigned int nSize = (unsigned int)(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&) +{ + v.clear(); + unsigned int nSize = (unsigned int)(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 = (unsigned int)(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; +}; + + + + + + + + + + + +typedef std::vector > CSerializeData; + +/** 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 CSerializeData 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, int nVersionIn) + { + Init(nTypeIn, nVersionIn); + } + + CDataStream(const_iterator pbegin, const_iterator pend, int nTypeIn, int nVersionIn) : vch(pbegin, pend) + { + Init(nTypeIn, nVersionIn); + } + +#if !defined(_MSC_VER) || _MSC_VER >= 1300 + CDataStream(const char* pbegin, const char* pend, int nTypeIn, int nVersionIn) : vch(pbegin, pend) + { + Init(nTypeIn, nVersionIn); + } +#endif + + CDataStream(const vector_type& vchIn, int nTypeIn, int nVersionIn) : vch(vchIn.begin(), vchIn.end()) + { + Init(nTypeIn, nVersionIn); + } + + CDataStream(const std::vector& vchIn, int nTypeIn, int nVersionIn) : vch(vchIn.begin(), vchIn.end()) + { + Init(nTypeIn, nVersionIn); + } + + CDataStream(const std::vector& vchIn, int nTypeIn, int nVersionIn) : vch(vchIn.begin(), vchIn.end()) + { + Init(nTypeIn, nVersionIn); + } + + void Init(int nTypeIn, int nVersionIn) + { + 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); } + +#ifdef _MSC_VER + void insert(iterator it, const_iterator first, const_iterator last) + { + assert(last - first >= 0); + if (it == vch.begin() + nReadPos && (unsigned int)(last - first) <= nReadPos) + { + // special case for inserting at the front when there's room + nReadPos -= (unsigned int)(last - first); + memcpy(&vch[nReadPos], &first[0], last - first); + } + else + vch.insert(it, first, last); + } +#endif + +#ifndef _MSC_VER + void insert(iterator it, std::vector::const_iterator first, std::vector::const_iterator last) + { + assert(last - first >= 0); + if (it == vch.begin() + nReadPos && (unsigned int)(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 + +#if !defined(_MSC_VER) || _MSC_VER >= 1300 + void insert(iterator it, const char* first, const char* last) + { + assert(last - first >= 0); + if (it == vch.begin() + nReadPos && (unsigned int)(last - first) <= nReadPos) + { + // special case for inserting at the front when there's room + nReadPos -= (unsigned int)(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 = (unsigned int)(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 -= (unsigned int)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)) != 0; } + 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 (int)(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 = (int)(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"); + 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, int nVersion) 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); + } + + void GetAndClear(CSerializeData &data) { + vch.swap(data); + CSerializeData().swap(vch); + } +}; + + + + + + + + + + +/** RAII 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; + + CAutoFile(FILE* filenew, int nTypeIn, int nVersionIn) + { + 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)) != 0; } + 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, size_t 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, size_t 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); + } +}; + +/** Wrapper around a FILE* that implements a ring buffer to + * deserialize from. It guarantees the ability to rewind + * a given number of bytes. */ +class CBufferedFile +{ +private: + FILE *src; // source file + uint64_t nSrcPos; // how many bytes have been read from source + uint64_t nReadPos; // how many bytes have been read from this + uint64_t nReadLimit; // up to which position we're allowed to read + uint64_t nRewind; // how many bytes we guarantee to rewind + std::vector vchBuf; // the buffer + + short state; + short exceptmask; + +protected: + void setstate(short bits, const char *psz) { + state |= bits; + if (state & exceptmask) + throw std::ios_base::failure(psz); + } + + // read data from the source to fill the buffer + bool Fill() { + unsigned int pos = (unsigned int)(nSrcPos % vchBuf.size()); + unsigned int readNow = (unsigned int)(vchBuf.size() - pos); + unsigned int nAvail = (unsigned int)(vchBuf.size() - (nSrcPos - nReadPos) - nRewind); + if (nAvail < readNow) + readNow = nAvail; + if (readNow == 0) + return false; + size_t read = fread((void*)&vchBuf[pos], 1, readNow, src); + if (read == 0) { + setstate(std::ios_base::failbit, feof(src) ? "CBufferedFile::Fill : end of file" : "CBufferedFile::Fill : fread failed"); + return false; + } else { + nSrcPos += read; + return true; + } + } + +public: + int nType; + int nVersion; + + CBufferedFile(FILE *fileIn, uint64_t nBufSize, uint64_t nRewindIn, int nTypeIn, int nVersionIn) : + src(fileIn), nSrcPos(0), nReadPos(0), nReadLimit(std::numeric_limits::max()), nRewind(nRewindIn), vchBuf(nBufSize, 0), + state(0), exceptmask(std::ios_base::badbit | std::ios_base::failbit), nType(nTypeIn), nVersion(nVersionIn) { + } + + // check whether no error occurred + bool good() const { + return state == 0; + } + + // check whether we're at the end of the source file + bool eof() const { + return nReadPos == nSrcPos && feof(src); + } + + // read a number of bytes + CBufferedFile& read(char *pch, size_t nSize) { + if (nSize + nReadPos > nReadLimit) + throw std::ios_base::failure("Read attempted past buffer limit"); + if (nSize + nRewind > vchBuf.size()) + throw std::ios_base::failure("Read larger than buffer size"); + while (nSize > 0) { + if (nReadPos == nSrcPos) + Fill(); + unsigned int pos = (unsigned int)(nReadPos % vchBuf.size()); + size_t nNow = nSize; + if (nNow + pos > vchBuf.size()) + nNow = (size_t)(vchBuf.size() - pos); + if (nNow + nReadPos > nSrcPos) + nNow = (size_t)(nSrcPos - nReadPos); + memcpy(pch, &vchBuf[pos], nNow); + nReadPos += nNow; + pch += nNow; + nSize -= nNow; + } + return (*this); + } + + // return the current reading position + uint64_t GetPos() { + return nReadPos; + } + + // rewind to a given reading position + bool SetPos(uint64_t nPos) { + nReadPos = nPos; + if (nReadPos + nRewind < nSrcPos) { + nReadPos = nSrcPos - nRewind; + return false; + } else if (nReadPos > nSrcPos) { + nReadPos = nSrcPos; + return false; + } else { + return true; + } + } + + bool Seek(uint64_t nPos) { + long nLongPos = (long)nPos; + if (nPos != (uint64_t)nLongPos) + return false; + if (fseek(src, nLongPos, SEEK_SET)) + return false; + nLongPos = ftell(src); + nSrcPos = nLongPos; + nReadPos = nLongPos; + state = 0; + return true; + } + + // prevent reading beyond a certain position + // no argument removes the limit + bool SetLimit(uint64_t nPos = std::numeric_limits::max()) { + if (nPos < nReadPos) + return false; + nReadLimit = nPos; + return true; + } + + template + CBufferedFile& operator>>(T& obj) { + // Unserialize from this stream + ::Unserialize(*this, obj, nType, nVersion); + return (*this); + } + + // search for a given byte in the stream, and remain positioned on it + void FindByte(char ch) { + for ( ; ; ) { + if (nReadPos == nSrcPos) + Fill(); + if (vchBuf[nReadPos % vchBuf.size()] == ch) + break; + nReadPos++; + } + } +}; + +#endif diff --git a/src/stun.cpp b/src/stun.cpp new file mode 100644 index 00000000..62e302ff --- /dev/null +++ b/src/stun.cpp @@ -0,0 +1,552 @@ +/* + * Get External IP address by STUN protocol + * + * Based on project Minimalistic STUN client "ministun" + * https://code.google.com/p/ministun/ + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + * + * STUN is described in RFC3489 and it is based on the exchange + * of UDP packets between a client and one or more servers to + * determine the externally visible address (and port) of the client + * once it has gone through the NAT boxes that connect it to the + * outside. + * The simplest request packet is just the header defined in + * struct stun_header, and from the response we may just look at + * one attribute, STUN_MAPPED_ADDRESS, that we find in the response. + * By doing more transactions with different server addresses we + * may determine more about the behaviour of the NAT boxes, of + * course - the details are in the RFC. + * + * All STUN packets start with a simple header made of a type, + * length (excluding the header) and a 16-byte random transaction id. + * Following the header we may have zero or more attributes, each + * structured as a type, length and a value (whose format depends + * on the type, but often contains addresses). + * Of course all fields are in network format. + */ + +#include +#include +#include +#include +#include +#include +#ifdef WIN32 +#include +#else +#include +#include +#include +#include +#endif +#ifndef WIN32 +#include +#endif +#include +#include + +#include "ministun.h" + +extern int GetRandInt(int nMax); +extern uint64_t GetRand(uint64_t nMax); + +/*---------------------------------------------------------------------*/ + +struct StunSrv { + char name[30]; + uint16_t port; +}; + +/*---------------------------------------------------------------------*/ +static const int StunSrvListQty = 263; // Must be PRIME!!!!! + +static struct StunSrv StunSrvList[263] = { + {"23.21.150.121", 3478}, + {"iphone-stun.strato-iphone.de", 3478}, + {"numb.viagenie.ca", 3478}, + {"s1.taraba.net", 3478}, + {"s2.taraba.net", 3478}, + {"stun.12connect.com", 3478}, + {"stun.12voip.com", 3478}, + {"stun.1und1.de", 3478}, + {"stun.2talk.co.nz", 3478}, + {"stun.2talk.com", 3478}, + {"stun.3clogic.com", 3478}, + {"stun.3cx.com", 3478}, + {"stun.a-mm.tv", 3478}, + {"stun.aa.net.uk", 3478}, + {"stun.acrobits.cz", 3478}, + {"stun.actionvoip.com", 3478}, + {"stun.advfn.com", 3478}, + {"stun.aeta-audio.com", 3478}, + {"stun.aeta.com", 3478}, + {"stun.alltel.com.au", 3478}, + {"stun.altar.com.pl", 3478}, + {"stun.annatel.net", 3478}, + {"stun.arbuz.ru", 3478}, + {"stun.avigora.com", 3478}, + {"stun.avigora.fr", 3478}, + {"stun.awa-shima.com", 3478}, + {"stun.awt.be", 3478}, + {"stun.b2b2c.ca", 3478}, + {"stun.bahnhof.net", 3478}, + {"stun.barracuda.com", 3478}, + {"stun.bluesip.net", 3478}, + {"stun.bmwgs.cz", 3478}, + {"stun.botonakis.com", 3478}, + {"stun.budgetphone.nl", 3478}, + {"stun.budgetsip.com", 3478}, + {"stun.cablenet-as.net", 3478}, + {"stun.callromania.ro", 3478}, + {"stun.callwithus.com", 3478}, + {"stun.cbsys.net", 3478}, + {"stun.chathelp.ru", 3478}, + {"stun.cheapvoip.com", 3478}, + {"stun.ciktel.com", 3478}, + {"stun.cloopen.com", 3478}, + {"stun.colouredlines.com.au", 3478}, + {"stun.comfi.com", 3478}, + {"stun.commpeak.com", 3478}, + {"stun.comtube.com", 3478}, + {"stun.comtube.ru", 3478}, + {"stun.cope.es", 3478}, + {"stun.counterpath.com", 3478}, + {"stun.counterpath.net", 3478}, + {"stun.cryptonit.net", 3478}, + {"stun.darioflaccovio.it", 3478}, + {"stun.datamanagement.it", 3478}, + {"stun.dcalling.de", 3478}, + {"stun.decanet.fr", 3478}, + {"stun.demos.ru", 3478}, + {"stun.develz.org", 3478}, + {"stun.dingaling.ca", 3478}, + {"stun.doublerobotics.com", 3478}, + {"stun.drogon.net", 3478}, + {"stun.duocom.es", 3478}, + {"stun.dus.net", 3478}, + {"stun.e-fon.ch", 3478}, + {"stun.easybell.de", 3478}, + {"stun.easycall.pl", 3478}, + {"stun.easyvoip.com", 3478}, + {"stun.efficace-factory.com", 3478}, + {"stun.einsundeins.com", 3478}, + {"stun.einsundeins.de", 3478}, + {"stun.ekiga.net", 3478}, + {"stun.epygi.com", 3478}, + {"stun.etoilediese.fr", 3478}, + {"stun.eyeball.com", 3478}, + {"stun.faktortel.com.au", 3478}, + {"stun.freecall.com", 3478}, + {"stun.freeswitch.org", 3478}, + {"stun.freevoipdeal.com", 3478}, + {"stun.fuzemeeting.com", 3478}, + {"stun.gmx.de", 3478}, + {"stun.gmx.net", 3478}, + {"stun.gradwell.com", 3478}, + {"stun.halonet.pl", 3478}, + {"stun.hoiio.com", 3478}, + {"stun.hosteurope.de", 3478}, + {"stun.ideasip.com", 3478}, + {"stun.imesh.com", 3478}, + {"stun.infra.net", 3478}, + {"stun.internetcalls.com", 3478}, + {"stun.intervoip.com", 3478}, + {"stun.ipcomms.net", 3478}, + {"stun.ipfire.org", 3478}, + {"stun.ippi.fr", 3478}, + {"stun.ipshka.com", 3478}, + {"stun.iptel.org", 3478}, + {"stun.irian.at", 3478}, + {"stun.it1.hr", 3478}, + {"stun.ivao.aero", 3478}, + {"stun.jappix.com", 3478}, + {"stun.jumblo.com", 3478}, + {"stun.justvoip.com", 3478}, + {"stun.kanet.ru", 3478}, + {"stun.kiwilink.co.nz", 3478}, + {"stun.kundenserver.de", 3478}, + {"stun.l.google.com", 19302}, + {"stun.linea7.net", 3478}, + {"stun.linphone.org", 3478}, + {"stun.liveo.fr", 3478}, + {"stun.lowratevoip.com", 3478}, + {"stun.lugosoft.com", 3478}, + {"stun.lundimatin.fr", 3478}, + {"stun.magnet.ie", 3478}, + {"stun.manle.com", 3478}, + {"stun.mgn.ru", 3478}, + {"stun.mit.de", 3478}, + {"stun.mitake.com.tw", 3478}, + {"stun.miwifi.com", 3478}, + {"stun.modulus.gr", 3478}, + {"stun.mozcom.com", 3478}, + {"stun.myvoiptraffic.com", 3478}, + {"stun.mywatson.it", 3478}, + {"stun.nas.net", 3478}, + {"stun.neotel.co.za", 3478}, + {"stun.netappel.com", 3478}, + {"stun.netappel.fr", 3478}, + {"stun.netgsm.com.tr", 3478}, + {"stun.nfon.net", 3478}, + {"stun.noblogs.org", 3478}, + {"stun.noc.ams-ix.net", 3478}, + {"stun.node4.co.uk", 3478}, + {"stun.nonoh.net", 3478}, + {"stun.nottingham.ac.uk", 3478}, + {"stun.nova.is", 3478}, + {"stun.nventure.com", 3478}, + {"stun.on.net.mk", 3478}, + {"stun.ooma.com", 3478}, + {"stun.ooonet.ru", 3478}, + {"stun.outland-net.de", 3478}, + {"stun.ozekiphone.com", 3478}, + {"stun.patlive.com", 3478}, + {"stun.personal-voip.de", 3478}, + {"stun.petcube.com", 3478}, + {"stun.phone.com", 3478}, + {"stun.phoneserve.com", 3478}, + {"stun.pjsip.org", 3478}, + {"stun.poivy.com", 3478}, + {"stun.powerpbx.org", 3478}, + {"stun.powervoip.com", 3478}, + {"stun.ppdi.com", 3478}, + {"stun.prizee.com", 3478}, + {"stun.qq.com", 3478}, + {"stun.qvod.com", 3478}, + {"stun.rackco.com", 3478}, + {"stun.rapidnet.de", 3478}, + {"stun.rb-net.com", 3478}, + {"stun.remote-learner.net", 3478}, + {"stun.rixtelecom.se", 3478}, + {"stun.rockenstein.de", 3478}, + {"stun.rolmail.net", 3478}, + {"stun.rounds.com", 3478}, + {"stun.rynga.com", 3478}, + {"stun.samsungsmartcam.com", 3478}, + {"stun.schlund.de", 3478}, + {"stun.services.mozilla.com", 3478}, + {"stun.sigmavoip.com", 3478}, + {"stun.sip.us", 3478}, + {"stun.sipdiscount.com", 3478}, + {"stun.sipgate.net", 10000}, + {"stun.sipgate.net", 3478}, + {"stun.siplogin.de", 3478}, + {"stun.sipnet.net", 3478}, + {"stun.sipnet.ru", 3478}, + {"stun.siportal.it", 3478}, + {"stun.sippeer.dk", 3478}, + {"stun.siptraffic.com", 3478}, + {"stun.skylink.ru", 3478}, + {"stun.sma.de", 3478}, + {"stun.smartvoip.com", 3478}, + {"stun.smsdiscount.com", 3478}, + {"stun.snafu.de", 3478}, + {"stun.softjoys.com", 3478}, + {"stun.solcon.nl", 3478}, + {"stun.solnet.ch", 3478}, + {"stun.sonetel.com", 3478}, + {"stun.sonetel.net", 3478}, + {"stun.sovtest.ru", 3478}, + {"stun.speedy.com.ar", 3478}, + {"stun.spokn.com", 3478}, + {"stun.srce.hr", 3478}, + {"stun.ssl7.net", 3478}, + {"stun.stunprotocol.org", 3478}, + {"stun.symform.com", 3478}, + {"stun.symplicity.com", 3478}, + {"stun.sysadminman.net", 3478}, + {"stun.t-online.de", 3478}, + {"stun.tagan.ru", 3478}, + {"stun.tatneft.ru", 3478}, + {"stun.teachercreated.com", 3478}, + {"stun.tel.lu", 3478}, + {"stun.telbo.com", 3478}, + {"stun.telefacil.com", 3478}, + {"stun.tis-dialog.ru", 3478}, + {"stun.tng.de", 3478}, + {"stun.twt.it", 3478}, + {"stun.u-blox.com", 3478}, + {"stun.ucallweconn.net", 3478}, + {"stun.ucsb.edu", 3478}, + {"stun.ucw.cz", 3478}, + {"stun.uls.co.za", 3478}, + {"stun.unseen.is", 3478}, + {"stun.usfamily.net", 3478}, + {"stun.veoh.com", 3478}, + {"stun.vidyo.com", 3478}, + {"stun.vipgroup.net", 3478}, + {"stun.virtual-call.com", 3478}, + {"stun.viva.gr", 3478}, + {"stun.vivox.com", 3478}, + {"stun.vline.com", 3478}, + {"stun.vo.lu", 3478}, + {"stun.vodafone.ro", 3478}, + {"stun.voicetrading.com", 3478}, + {"stun.voip.aebc.com", 3478}, + {"stun.voip.blackberry.com", 3478}, + {"stun.voip.eutelia.it", 3478}, + {"stun.voiparound.com", 3478}, + {"stun.voipblast.com", 3478}, + {"stun.voipbuster.com", 3478}, + {"stun.voipbusterpro.com", 3478}, + {"stun.voipcheap.co.uk", 3478}, + {"stun.voipcheap.com", 3478}, + {"stun.voipfibre.com", 3478}, + {"stun.voipgain.com", 3478}, + {"stun.voipgate.com", 3478}, + {"stun.voipinfocenter.com", 3478}, + {"stun.voipplanet.nl", 3478}, + {"stun.voippro.com", 3478}, + {"stun.voipraider.com", 3478}, + {"stun.voipstunt.com", 3478}, + {"stun.voipwise.com", 3478}, + {"stun.voipzoom.com", 3478}, + {"stun.voxgratia.org", 3478}, + {"stun.voxox.com", 3478}, + {"stun.voys.nl", 3478}, + {"stun.voztele.com", 3478}, + {"stun.vyke.com", 3478}, + {"stun.webcalldirect.com", 3478}, + {"stun.whoi.edu", 3478}, + {"stun.wifirst.net", 3478}, + {"stun.wwdl.net", 3478}, + {"stun.xs4all.nl", 3478}, + {"stun.xtratelecom.es", 3478}, + {"stun.yesss.at", 3478}, + {"stun.zadarma.com", 3478}, + {"stun.zadv.com", 3478}, + {"stun.zoiper.com", 3478}, + {"stun1.faktortel.com.au", 3478}, + {"stun1.l.google.com", 19302}, + {"stun1.voiceeclipse.net", 3478}, + {"stun2.l.google.com", 19302}, + {"stun3.l.google.com", 19302}, + {"stun4.l.google.com", 19302}, + {"stunserver.org", 3478}, + {"stun.antisip.com", 3478} +}; + + +/* wrapper to send an STUN message */ +static int stun_send(int s, struct sockaddr_in *dst, struct stun_header *resp) +{ + return sendto(s, (const char *)resp, ntohs(resp->msglen) + sizeof(*resp), 0, + (struct sockaddr *)dst, sizeof(*dst)); +} + +/* helper function to generate a random request id */ +static uint64_t randfiller = GetRand(std::numeric_limits::max()); +static void stun_req_id(struct stun_header *req) +{ + const uint64_t *S_block = (const uint64_t *)StunSrvList; + req->id.id[0] = GetRandInt(std::numeric_limits::max()); + req->id.id[1] = GetRandInt(std::numeric_limits::max()); + req->id.id[2] = GetRandInt(std::numeric_limits::max()); + req->id.id[3] = GetRandInt(std::numeric_limits::max()); + + req->id.id[0] |= 0x55555555; + req->id.id[1] &= 0x55555555; + req->id.id[2] |= 0x55555555; + req->id.id[3] &= 0x55555555; + char x = 20; + do { + uint32_t s_elm = S_block[(uint8_t)randfiller]; + randfiller ^= (randfiller << 5) | (randfiller >> (64 - 5)); + randfiller += s_elm ^ x; + req->id.id[x & 3] ^= randfiller + (randfiller >> 13); + } while(--x); +} + +/* callback type to be invoked on stun responses. */ +typedef int (stun_cb_f)(struct stun_attr *attr, void *arg); + +/* handle an incoming STUN message. + * + * Do some basic sanity checks on packet size and content, + * try to extract a bit of information, and possibly reply. + * At the moment this only processes BIND requests, and returns + * the externally visible address of the request. + * If a callback is specified, invoke it with the attribute. + */ +static int stun_handle_packet(int s, struct sockaddr_in *src, + unsigned char *data, size_t len, stun_cb_f *stun_cb, void *arg) +{ + struct stun_header *hdr = (struct stun_header *)data; + struct stun_attr *attr; + int ret = len; + unsigned int x; + + /* On entry, 'len' is the length of the udp payload. After the + * initial checks it becomes the size of unprocessed options, + * while 'data' is advanced accordingly. + */ + if (len < sizeof(struct stun_header)) + return -20; + + len -= sizeof(struct stun_header); + data += sizeof(struct stun_header); + x = ntohs(hdr->msglen); /* len as advertised in the message */ + if(x < len) + len = x; + + while (len) { + if (len < sizeof(struct stun_attr)) { + ret = -21; + break; + } + attr = (struct stun_attr *)data; + /* compute total attribute length */ + x = ntohs(attr->len) + sizeof(struct stun_attr); + if (x > len) { + ret = -22; + break; + } + stun_cb(attr, arg); + //if (stun_process_attr(&st, attr)) { + // ret = -23; + // break; + // } + /* Clear attribute id: in case previous entry was a string, + * this will act as the terminator for the string. + */ + attr->attr = 0; + data += x; + len -= x; + } // while + /* Null terminate any string. + * XXX NOTE, we write past the size of the buffer passed by the + * caller, so this is potentially dangerous. The only thing that + * saves us is that usually we read the incoming message in a + * much larger buffer + */ + *data = '\0'; + + /* Now prepare to generate a reply, which at the moment is done + * only for properly formed (len == 0) STUN_BINDREQ messages. + */ + + return ret; +} + +/* Extract the STUN_MAPPED_ADDRESS from the stun response. + * This is used as a callback for stun_handle_response + * when called from stun_request. + */ +static int stun_get_mapped(struct stun_attr *attr, void *arg) +{ + struct stun_addr *addr = (struct stun_addr *)(attr + 1); + struct sockaddr_in *sa = (struct sockaddr_in *)arg; + + if (ntohs(attr->attr) != STUN_MAPPED_ADDRESS || ntohs(attr->len) != 8) + return 1; /* not us. */ + sa->sin_port = addr->port; + sa->sin_addr.s_addr = addr->addr; + return 0; +} + +/*---------------------------------------------------------------------*/ + +static int StunRequest2(int sock, struct sockaddr_in *server, struct sockaddr_in *mapped) { + + struct stun_header *req; + unsigned char reqdata[1024]; + + req = (struct stun_header *)reqdata; + stun_req_id(req); + unsigned short reqlen = 0; + req->msgtype = 0; + req->msglen = 0; + req->msglen = htons(reqlen); + req->msgtype = htons(STUN_BINDREQ); + + unsigned char reply_buf[1024]; + fd_set rfds; + struct timeval to = { STUN_TIMEOUT, 0 }; + struct sockaddr_in src; +#ifdef WIN32 + int srclen; +#else + socklen_t srclen; +#endif + + int res = stun_send(sock, server, req); + if(res < 0) + return -10; + FD_ZERO(&rfds); + FD_SET(sock, &rfds); + res = select(sock + 1, &rfds, NULL, NULL, &to); + if (res <= 0) /* timeout or error */ + return -11; + memset(&src, 0, sizeof(src)); + srclen = sizeof(src); + /* XXX pass -1 in the size, because stun_handle_packet might + * write past the end of the buffer. + */ + res = recvfrom(sock, (char *)reply_buf, sizeof(reply_buf) - 1, + 0, (struct sockaddr *)&src, &srclen); + if (res <= 0) + return -12; + memset(mapped, 0, sizeof(struct sockaddr_in)); + return stun_handle_packet(sock, &src, reply_buf, res, stun_get_mapped, mapped); +} // StunRequest2 + +/*---------------------------------------------------------------------*/ +static int StunRequest(const char *host, uint16_t port, struct sockaddr_in *mapped) { + struct hostent *hostinfo = gethostbyname(host); + if(hostinfo == NULL) + return -1; + + struct sockaddr_in server, client; + memset(&server, 0, sizeof(server)); + memset(&client, 0, sizeof(client)); + server.sin_family = client.sin_family = AF_INET; + + server.sin_addr = *(struct in_addr*) hostinfo->h_addr; + server.sin_port = htons(port); + + int sock = socket(AF_INET, SOCK_DGRAM, 0); + if(sock < 0) + return -2; + + client.sin_addr.s_addr = htonl(INADDR_ANY); + + int rc = -3; + if(bind(sock, (struct sockaddr*)&client, sizeof(client)) >= 0) + rc = StunRequest2(sock, &server, mapped); +#ifndef WIN32 + close(sock); +#else + closesocket(sock); +#endif + return rc; +} // StunRequest + +/*---------------------------------------------------------------------*/ +// Input: two random values (pos, step) for generate uniuqe way over server +// list +// Output: populate struct struct mapped +// Retval: + +int GetExternalIPbySTUN(uint64_t rnd, struct sockaddr_in *mapped, const char **srv) { + randfiller = rnd; + uint16_t pos = rnd; + uint16_t step; + do { + rnd = (rnd >> 8) | 0xff00000000000000LL; + step = rnd % StunSrvListQty; + } while(step == 0); + + uint16_t attempt; + for(attempt = 1; attempt < StunSrvListQty * 2; attempt++) { + pos = (pos + step) % StunSrvListQty; + int rc = StunRequest(*srv = StunSrvList[pos].name, StunSrvList[pos].port, mapped); + if(rc >= 0) + return attempt; + // fprintf(stderr, "Lookup: %s:%u\t%s\t%d\n", StunSrvList[pos].name, + // StunSrvList[pos].port, inet_ntoa(mapped->sin_addr), rc); + } + return -1; +} diff --git a/src/sync.cpp b/src/sync.cpp new file mode 100644 index 00000000..1ac4403b --- /dev/null +++ b/src/sync.cpp @@ -0,0 +1,128 @@ +// Copyright (c) 2011-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "sync.h" +#include "util.h" + +#include + +#ifdef DEBUG_LOCKCONTENTION +void PrintLockContention(const char* pszName, const char* pszFile, int nLine) +{ + printf("LOCKCONTENTION: %s\n", pszName); + printf("Locker: %s:%d\n", pszFile, nLine); +} +#endif /* DEBUG_LOCKCONTENTION */ + +#ifdef DEBUG_LOCKORDER +// +// Early deadlock detection. +// Problem being solved: +// Thread 1 locks A, then B, then C +// Thread 2 locks D, then C, then A +// --> may result in deadlock between the two threads, depending on when they run. +// Solution implemented here: +// Keep track of pairs of locks: (A before B), (A before C), etc. +// Complain if any thread tries to lock in a different order. +// + +struct CLockLocation +{ + CLockLocation(const char* pszName, const char* pszFile, int nLine) + { + mutexName = pszName; + sourceFile = pszFile; + sourceLine = nLine; + } + + std::string ToString() const + { + return mutexName+" "+sourceFile+":"+itostr(sourceLine); + } + +private: + std::string mutexName; + std::string sourceFile; + int sourceLine; +}; + +typedef std::vector< std::pair > LockStack; + +static boost::mutex dd_mutex; +static std::map, LockStack> lockorders; +static boost::thread_specific_ptr lockstack; + + +static void potential_deadlock_detected(const std::pair& mismatch, const LockStack& s1, const LockStack& s2) +{ + printf("POTENTIAL DEADLOCK DETECTED\n"); + printf("Previous lock order was:\n"); + BOOST_FOREACH(const PAIRTYPE(void*, CLockLocation)& i, s2) + { + if (i.first == mismatch.first) printf(" (1)"); + if (i.first == mismatch.second) printf(" (2)"); + printf(" %s\n", i.second.ToString().c_str()); + } + printf("Current lock order is:\n"); + BOOST_FOREACH(const PAIRTYPE(void*, CLockLocation)& i, s1) + { + if (i.first == mismatch.first) printf(" (1)"); + if (i.first == mismatch.second) printf(" (2)"); + printf(" %s\n", i.second.ToString().c_str()); + } +} + +static void push_lock(void* c, const CLockLocation& locklocation, bool fTry) +{ + if (lockstack.get() == NULL) + lockstack.reset(new LockStack); + + if (fDebug) printf("Locking: %s\n", locklocation.ToString().c_str()); + dd_mutex.lock(); + + (*lockstack).push_back(std::make_pair(c, locklocation)); + + if (!fTry) { + BOOST_FOREACH(const PAIRTYPE(void*, CLockLocation)& i, (*lockstack)) { + if (i.first == c) break; + + std::pair p1 = std::make_pair(i.first, c); + if (lockorders.count(p1)) + continue; + lockorders[p1] = (*lockstack); + + std::pair p2 = std::make_pair(c, i.first); + if (lockorders.count(p2)) + { + potential_deadlock_detected(p1, lockorders[p2], lockorders[p1]); + break; + } + } + } + dd_mutex.unlock(); +} + +static void pop_lock() +{ + if (fDebug) + { + const CLockLocation& locklocation = (*lockstack).rbegin()->second; + printf("Unlocked: %s\n", locklocation.ToString().c_str()); + } + dd_mutex.lock(); + (*lockstack).pop_back(); + dd_mutex.unlock(); +} + +void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry) +{ + push_lock(cs, CLockLocation(pszName, pszFile, nLine), fTry); +} + +void LeaveCritical() +{ + pop_lock(); +} + +#endif /* DEBUG_LOCKORDER */ diff --git a/src/sync.h b/src/sync.h new file mode 100644 index 00000000..e80efbe0 --- /dev/null +++ b/src/sync.h @@ -0,0 +1,212 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_SYNC_H +#define BITCOIN_SYNC_H + +#include +#include +#include +#include + + + + +/** Wrapped boost mutex: supports recursive locking, but no waiting */ +typedef boost::recursive_mutex CCriticalSection; + +/** Wrapped boost mutex: supports waiting but not recursive locking */ +typedef boost::mutex CWaitableCriticalSection; + +#ifdef DEBUG_LOCKORDER +void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false); +void LeaveCritical(); +#else +void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {} +void static inline LeaveCritical() {} +#endif + +#ifdef DEBUG_LOCKCONTENTION +void PrintLockContention(const char* pszName, const char* pszFile, int nLine); +#endif + +/** Wrapper around boost::unique_lock */ +template +class CMutexLock +{ +private: + boost::unique_lock lock; +public: + + void Enter(const char* pszName, const char* pszFile, int nLine) + { + if (!lock.owns_lock()) + { + EnterCritical(pszName, pszFile, nLine, (void*)(lock.mutex())); +#ifdef DEBUG_LOCKCONTENTION + if (!lock.try_lock()) + { + PrintLockContention(pszName, pszFile, nLine); +#endif + lock.lock(); +#ifdef DEBUG_LOCKCONTENTION + } +#endif + } + } + + void Leave() + { + if (lock.owns_lock()) + { + lock.unlock(); + LeaveCritical(); + } + } + + bool TryEnter(const char* pszName, const char* pszFile, int nLine) + { + if (!lock.owns_lock()) + { + EnterCritical(pszName, pszFile, nLine, (void*)(lock.mutex()), true); + lock.try_lock(); + if (!lock.owns_lock()) + LeaveCritical(); + } + return lock.owns_lock(); + } + + CMutexLock(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) : lock(mutexIn, boost::defer_lock) + { + if (fTry) + TryEnter(pszName, pszFile, nLine); + else + Enter(pszName, pszFile, nLine); + } + + ~CMutexLock() + { + if (lock.owns_lock()) + LeaveCritical(); + } + + operator bool() + { + return lock.owns_lock(); + } + + boost::unique_lock &GetLock() + { + return lock; + } +}; + +typedef CMutexLock CCriticalBlock; + +#define LOCK(cs) CCriticalBlock criticalblock(cs, #cs, __FILE__, __LINE__) +#define LOCK2(cs1,cs2) CCriticalBlock criticalblock1(cs1, #cs1, __FILE__, __LINE__),criticalblock2(cs2, #cs2, __FILE__, __LINE__) +#define TRY_LOCK(cs,name) CCriticalBlock name(cs, #cs, __FILE__, __LINE__, true) + +#define ENTER_CRITICAL_SECTION(cs) \ + { \ + EnterCritical(#cs, __FILE__, __LINE__, (void*)(&cs)); \ + (cs).lock(); \ + } + +#define LEAVE_CRITICAL_SECTION(cs) \ + { \ + (cs).unlock(); \ + LeaveCritical(); \ + } + +class CSemaphore +{ +private: + boost::condition_variable condition; + boost::mutex mutex; + int value; + +public: + CSemaphore(int init) : value(init) {} + + void wait() { + boost::unique_lock lock(mutex); + while (value < 1) { + condition.wait(lock); + } + value--; + } + + bool try_wait() { + boost::unique_lock lock(mutex); + if (value < 1) + return false; + value--; + return true; + } + + void post() { + { + boost::unique_lock lock(mutex); + value++; + } + condition.notify_one(); + } +}; + +/** RAII-style semaphore lock */ +class CSemaphoreGrant +{ +private: + CSemaphore *sem; + bool fHaveGrant; + +public: + void Acquire() { + if (fHaveGrant) + return; + sem->wait(); + fHaveGrant = true; + } + + void Release() { + if (!fHaveGrant) + return; + sem->post(); + fHaveGrant = false; + } + + bool TryAcquire() { + if (!fHaveGrant && sem->try_wait()) + fHaveGrant = true; + return fHaveGrant; + } + + void MoveTo(CSemaphoreGrant &grant) { + grant.Release(); + grant.sem = sem; + grant.fHaveGrant = fHaveGrant; + sem = NULL; + fHaveGrant = false; + } + + CSemaphoreGrant() : sem(NULL), fHaveGrant(false) {} + + CSemaphoreGrant(CSemaphore &sema, bool fTry = false) : sem(&sema), fHaveGrant(false) { + if (fTry) + TryAcquire(); + else + Acquire(); + } + + ~CSemaphoreGrant() { + Release(); + } + + operator bool() { + return fHaveGrant; + } +}; +#endif + diff --git a/src/timestamps.h b/src/timestamps.h new file mode 100644 index 00000000..0d876bd6 --- /dev/null +++ b/src/timestamps.h @@ -0,0 +1,7 @@ +#ifndef BITCOIN_TIMESTAMPS_H +#define BITCOIN_TIMESTAMPS_H + +static const unsigned int TARGETS_SWITCH_TIME = 1374278400; // Saturday, 20-Jul-2013 00:00:00 UTC +static const unsigned int COINBASE_SIGOPS_SWITCH_TIME = 1447977600; // Friday, 20-Nov-15 00:00:00 UTC + +#endif diff --git a/src/txdb-bdb.cpp b/src/txdb-bdb.cpp new file mode 100644 index 00000000..bbfb656c --- /dev/null +++ b/src/txdb-bdb.cpp @@ -0,0 +1,436 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "db.h" +#include "kernel.h" +#include "checkpoints.h" +#include "txdb-bdb.h" +#include "util.h" +#include "main.h" +#include +#include +#include + +#ifndef WIN32 +#include "sys/stat.h" +#endif + +using namespace std; +using namespace boost; + +void MakeMockTXDB() { + // In practice this won't do anything because the test framework always initializes + // an in-memory BDB via bitdb.MakeMock() first, as we use BDB for addresses and wallets. + if (!bitdb.IsMock()) + bitdb.MakeMock(); +} + +// +// 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::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::ReadHashBestChain(uint256& hashBestChain) +{ + return Read(string("hashBestChain"), hashBestChain); +} + +bool CTxDB::WriteHashBestChain(uint256 hashBestChain) +{ + return Write(string("hashBestChain"), hashBestChain); +} + +bool CTxDB::ReadBestInvalidTrust(CBigNum& bnBestInvalidTrust) +{ + return Read(string("bnBestInvalidTrust"), bnBestInvalidTrust); +} + +bool CTxDB::WriteBestInvalidTrust(CBigNum bnBestInvalidTrust) +{ + return Write(string("bnBestInvalidTrust"), bnBestInvalidTrust); +} + +bool CTxDB::ReadSyncCheckpoint(uint256& hashCheckpoint) +{ + return Read(string("hashSyncCheckpoint"), hashCheckpoint); +} + +bool CTxDB::WriteSyncCheckpoint(uint256 hashCheckpoint) +{ + return Write(string("hashSyncCheckpoint"), hashCheckpoint); +} + +bool CTxDB::ReadCheckpointPubKey(string& strPubKey) +{ + return Read(string("strCheckpointPubKey"), strPubKey); +} + +bool CTxDB::WriteCheckpointPubKey(const string& strPubKey) +{ + return Write(string("strCheckpointPubKey"), strPubKey); +} + +bool CTxDB::ReadModifierUpgradeTime(unsigned int& nUpgradeTime) +{ + return Read(string("nUpgradeTime"), nUpgradeTime); +} + +bool CTxDB::WriteModifierUpgradeTime(const unsigned int& nUpgradeTime) +{ + return Write(string("nUpgradeTime"), nUpgradeTime); +} + +CBlockIndex static * 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() +{ + if (!LoadBlockIndexGuts()) + return false; + + if (fRequestShutdown) + return true; + + // Calculate nChainTrust + 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->nChainTrust = (pindex->pprev ? pindex->pprev->nChainTrust : 0) + pindex->GetBlockTrust(); + // ppcoin: calculate stake modifier checksum + pindex->nStakeModifierChecksum = GetStakeModifierChecksum(pindex); + if (!CheckStakeModifierCheckpoints(pindex->nHeight, pindex->nStakeModifierChecksum)) + return error("CTxDB::LoadBlockIndex() : Failed stake modifier checkpoint height=%d, modifier=0x%016" PRIx64, pindex->nHeight, pindex->nStakeModifier); + } + + // 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; + nBestChainTrust = pindexBest->nChainTrust; + printf("LoadBlockIndex(): hashBestChain=%s height=%d trust=%s date=%s\n", + hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, CBigNum(nBestChainTrust).ToString().c_str(), + DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str()); + + // ppcoin: load hashSyncCheckpoint + if (!ReadSyncCheckpoint(Checkpoints::hashSyncCheckpoint)) + return error("CTxDB::LoadBlockIndex() : hashSyncCheckpoint not loaded"); + printf("LoadBlockIndex(): synchronized checkpoint %s\n", Checkpoints::hashSyncCheckpoint.ToString().c_str()); + + // Load bnBestInvalidTrust, OK if it doesn't exist + CBigNum bnBestInvalidTrust; + ReadBestInvalidTrust(bnBestInvalidTrust); + nBestInvalidTrust = bnBestInvalidTrust.getuint256(); + + // Verify blocks in the best chain + int nCheckLevel = GetArgInt("-checklevel", 1); + int nCheckDepth = GetArgInt( "-checkblocks", 2500); + if (nCheckDepth == 0) + nCheckDepth = 1000000000; // suffices until the year 19000 + if (nCheckDepth > nBestHeight) + nCheckDepth = nBestHeight; + printf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); + CBlockIndex* pindexFork = NULL; + map, CBlockIndex*> mapBlockPos; + for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev) + { + if (fRequestShutdown || pindex->nHeight < nBestHeight-nCheckDepth) + break; + CBlock block; + if (!block.ReadFromDisk(pindex)) + return error("LoadBlockIndex() : block.ReadFromDisk failed"); + // check level 1: verify block validity + // check level 7: verify block signature too + if (nCheckLevel>0 && !block.CheckBlock(true, true, (nCheckLevel>6))) + { + printf("LoadBlockIndex() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + pindexFork = pindex->pprev; + } + // check level 2: verify transaction index validity + if (nCheckLevel>1) + { + pair pos = make_pair(pindex->nFile, pindex->nBlockPos); + mapBlockPos[pos] = pindex; + BOOST_FOREACH(const CTransaction &tx, block.vtx) + { + uint256 hashTx = tx.GetHash(); + CTxIndex txindex; + if (ReadTxIndex(hashTx, txindex)) + { + // check level 3: checker transaction hashes + if (nCheckLevel>2 || pindex->nFile != txindex.pos.nFile || pindex->nBlockPos != txindex.pos.nBlockPos) + { + // either an error or a duplicate transaction + CTransaction txFound; + if (!txFound.ReadFromDisk(txindex.pos)) + { + printf("LoadBlockIndex() : *** cannot read mislocated transaction %s\n", hashTx.ToString().c_str()); + pindexFork = pindex->pprev; + } + else + if (txFound.GetHash() != hashTx) // not a duplicate tx + { + printf("LoadBlockIndex(): *** invalid tx position for %s\n", hashTx.ToString().c_str()); + pindexFork = pindex->pprev; + } + } + // check level 4: check whether spent txouts were spent within the main chain + unsigned int nOutput = 0; + if (nCheckLevel>3) + { + BOOST_FOREACH(const CDiskTxPos &txpos, txindex.vSpent) + { + if (!txpos.IsNull()) + { + pair posFind = make_pair(txpos.nFile, txpos.nBlockPos); + if (!mapBlockPos.count(posFind)) + { + printf("LoadBlockIndex(): *** found bad spend at %d, hashBlock=%s, hashTx=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str(), hashTx.ToString().c_str()); + pindexFork = pindex->pprev; + } + // check level 6: check whether spent txouts were spent by a valid transaction that consume them + if (nCheckLevel>5) + { + CTransaction txSpend; + if (!txSpend.ReadFromDisk(txpos)) + { + printf("LoadBlockIndex(): *** cannot read spending transaction of %s:%i from disk\n", hashTx.ToString().c_str(), nOutput); + pindexFork = pindex->pprev; + } + else if (!txSpend.CheckTransaction()) + { + printf("LoadBlockIndex(): *** spending transaction of %s:%i is invalid\n", hashTx.ToString().c_str(), nOutput); + pindexFork = pindex->pprev; + } + else + { + bool fFound = false; + BOOST_FOREACH(const CTxIn &txin, txSpend.vin) + if (txin.prevout.hash == hashTx && txin.prevout.n == nOutput) + fFound = true; + if (!fFound) + { + printf("LoadBlockIndex(): *** spending transaction of %s:%i does not spend it\n", hashTx.ToString().c_str(), nOutput); + pindexFork = pindex->pprev; + } + } + } + } + nOutput++; + } + } + } + // check level 5: check whether all prevouts are marked spent + if (nCheckLevel>4) + { + BOOST_FOREACH(const CTxIn &txin, tx.vin) + { + CTxIndex txindex; + if (ReadTxIndex(txin.prevout.hash, txindex)) + if (txindex.vSpent.size()-1 < txin.prevout.n || txindex.vSpent[txin.prevout.n].IsNull()) + { + printf("LoadBlockIndex(): *** found unspent prevout %s:%i in %s\n", txin.prevout.hash.ToString().c_str(), txin.prevout.n, hashTx.ToString().c_str()); + pindexFork = pindex->pprev; + } + } + } + } + } + } + if (pindexFork && !fRequestShutdown) + { + // 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; +} + + + +bool CTxDB::LoadBlockIndexGuts() +{ + // Get database cursor + Dbc* pcursor = GetCursor(); + if (!pcursor) + return false; + + // Load mapBlockIndex + unsigned int fFlags = DB_SET_RANGE; + while (true) + { + // Read next record + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + if (fFlags == DB_SET_RANGE) + ssKey << make_pair(string("blockindex"), uint256(0)); + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); + fFlags = DB_NEXT; + if (ret == DB_NOTFOUND) + break; + else if (ret != 0) + return false; + + // Unserialize + + try { + string strType; + ssKey >> strType; + if (strType == "blockindex" && !fRequestShutdown) + { + CDiskBlockIndex diskindex; + ssValue >> diskindex; + + uint256 blockHash = diskindex.GetBlockHash(); + + // Construct block index object + CBlockIndex* pindexNew = InsertBlockIndex(blockHash); + pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev); + pindexNew->pnext = InsertBlockIndex(diskindex.hashNext); + pindexNew->nFile = diskindex.nFile; + pindexNew->nBlockPos = diskindex.nBlockPos; + pindexNew->nHeight = diskindex.nHeight; + pindexNew->nMint = diskindex.nMint; + pindexNew->nMoneySupply = diskindex.nMoneySupply; + pindexNew->nFlags = diskindex.nFlags; + pindexNew->nStakeModifier = diskindex.nStakeModifier; + pindexNew->prevoutStake = diskindex.prevoutStake; + pindexNew->nStakeTime = diskindex.nStakeTime; + pindexNew->hashProofOfStake = diskindex.hashProofOfStake; + 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 && blockHash == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)) + pindexGenesisBlock = pindexNew; + + if (!pindexNew->CheckIndex()) + return error("LoadBlockIndex() : CheckIndex failed at %d", pindexNew->nHeight); + + // ppcoin: build setStakeSeen + if (pindexNew->IsProofOfStake()) + setStakeSeen.insert(make_pair(pindexNew->prevoutStake, pindexNew->nStakeTime)); + } + else + { + break; // if shutdown requested or finished loading block index + } + } // try + catch (const std::exception&) { + return error("%s() : deserialize error", BOOST_CURRENT_FUNCTION); + } + } + pcursor->close(); + + return true; +} + + diff --git a/src/txdb-bdb.h b/src/txdb-bdb.h new file mode 100644 index 00000000..45372756 --- /dev/null +++ b/src/txdb-bdb.h @@ -0,0 +1,45 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The 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_TXDB_BDB_H +#define BITCOIN_TXDB_BDB_H + + +/** Access to the transaction database (blkindex.dat) */ +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 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 ReadHashBestChain(uint256& hashBestChain); + bool WriteHashBestChain(uint256 hashBestChain); + bool ReadBestInvalidTrust(CBigNum& bnBestInvalidTrust); + bool WriteBestInvalidTrust(CBigNum bnBestInvalidTrust); + bool ReadSyncCheckpoint(uint256& hashCheckpoint); + bool WriteSyncCheckpoint(uint256 hashCheckpoint); + bool ReadCheckpointPubKey(std::string& strPubKey); + bool WriteCheckpointPubKey(const std::string& strPubKey); + bool ReadModifierUpgradeTime(unsigned int& nUpgradeTime); + bool WriteModifierUpgradeTime(const unsigned int& nUpgradeTime); + bool LoadBlockIndex(); +private: + bool LoadBlockIndexGuts(); +}; + +#endif diff --git a/src/txdb-leveldb.cpp b/src/txdb-leveldb.cpp new file mode 100644 index 00000000..1da8d5be --- /dev/null +++ b/src/txdb-leveldb.cpp @@ -0,0 +1,583 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The 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 + +#include +#include +#include + +#include +#include +#include +#include + +#include "kernel.h" +#include "checkpoints.h" +#include "txdb.h" +#include "util.h" +#include "main.h" + +using namespace std; +using namespace boost; + +leveldb::DB *txdb; // global pointer for LevelDB object instance + +static leveldb::Options GetOptions() { + leveldb::Options options; + int nCacheSizeMB = GetArgInt("-dbcache", 25); + options.block_cache = leveldb::NewLRUCache(nCacheSizeMB * 1048576); + options.filter_policy = leveldb::NewBloomFilterPolicy(10); + return options; +} + +void init_blockindex(leveldb::Options& options, bool fRemoveOld = false) { + // First time init. + filesystem::path directory = GetDataDir() / "txleveldb"; + + if (fRemoveOld) { + filesystem::remove_all(directory); // remove directory + unsigned int nFile = 1; + + while (true) + { + filesystem::path strBlockFile = GetDataDir() / strprintf("blk%04u.dat", nFile); + + // Break if no such file + if( !filesystem::exists( strBlockFile ) ) + break; + + filesystem::remove(strBlockFile); + + nFile++; + } + } + + filesystem::create_directory(directory); + printf("Opening LevelDB in %s\n", directory.string().c_str()); + leveldb::Status status = leveldb::DB::Open(options, directory.string(), &txdb); + if (!status.ok()) { + throw runtime_error(strprintf("init_blockindex(): error opening database environment %s", status.ToString().c_str())); + } +} + +// CDB subclasses are created and destroyed VERY OFTEN. That's why +// we shouldn't treat this as a free operations. +CTxDB::CTxDB(const char* pszMode) +{ + assert(pszMode); + activeBatch = NULL; + fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w')); + + if (txdb) { + pdb = txdb; + return; + } + + bool fCreate = strchr(pszMode, 'c'); + + options = GetOptions(); + options.create_if_missing = fCreate; + options.filter_policy = leveldb::NewBloomFilterPolicy(10); + + init_blockindex(options); // Init directory + pdb = txdb; + + if (Exists(string("version"))) + { + ReadVersion(nVersion); + printf("Transaction index version is %d\n", nVersion); + + if (nVersion < DATABASE_VERSION) + { + printf("Required index version is %d, removing old database\n", DATABASE_VERSION); + + // Leveldb instance destruction + delete txdb; + txdb = pdb = NULL; + delete activeBatch; + activeBatch = NULL; + + init_blockindex(options, true); // Remove directory and create new database + pdb = txdb; + + bool fTmp = fReadOnly; + fReadOnly = false; + WriteVersion(DATABASE_VERSION); // Save transaction index version + fReadOnly = fTmp; + } + } + else if (fCreate) + { + bool fTmp = fReadOnly; + fReadOnly = false; + WriteVersion(DATABASE_VERSION); + fReadOnly = fTmp; + } + + printf("Opened LevelDB successfully\n"); +} + +void CTxDB::Close() +{ + delete txdb; + txdb = pdb = NULL; + delete options.filter_policy; + options.filter_policy = NULL; + delete options.block_cache; + options.block_cache = NULL; + delete activeBatch; + activeBatch = NULL; +} + +bool CTxDB::TxnBegin() +{ + assert(!activeBatch); + activeBatch = new leveldb::WriteBatch(); + return true; +} + +bool CTxDB::TxnCommit() +{ + assert(activeBatch); + leveldb::Status status = pdb->Write(leveldb::WriteOptions(), activeBatch); + delete activeBatch; + activeBatch = NULL; + if (!status.ok()) { + printf("LevelDB batch commit failure: %s\n", status.ToString().c_str()); + return false; + } + return true; +} + +class CBatchScanner : public leveldb::WriteBatch::Handler { +public: + std::string needle; + bool *deleted; + std::string *foundValue; + bool foundEntry; + + CBatchScanner() : foundEntry(false) {} + + virtual void Put(const leveldb::Slice& key, const leveldb::Slice& value) { + if (key.ToString() == needle) { + foundEntry = true; + *deleted = false; + *foundValue = value.ToString(); + } + } + + virtual void Delete(const leveldb::Slice& key) { + if (key.ToString() == needle) { + foundEntry = true; + *deleted = true; + } + } +}; + +// When performing a read, if we have an active batch we need to check it first +// before reading from the database, as the rest of the code assumes that once +// a database transaction begins reads are consistent with it. It would be good +// to change that assumption in future and avoid the performance hit, though in +// practice it does not appear to be large. +bool CTxDB::ScanBatch(const CDataStream &key, string *value, bool *deleted) const { + assert(activeBatch); + *deleted = false; + CBatchScanner scanner; + scanner.needle = key.str(); + scanner.deleted = deleted; + scanner.foundValue = value; + leveldb::Status status = activeBatch->Iterate(&scanner); + if (!status.ok()) { + throw runtime_error(status.ToString()); + } + return scanner.foundEntry; +} + +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::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::ReadHashBestChain(uint256& hashBestChain) +{ + return Read(string("hashBestChain"), hashBestChain); +} + +bool CTxDB::WriteHashBestChain(uint256 hashBestChain) +{ + return Write(string("hashBestChain"), hashBestChain); +} + +bool CTxDB::ReadBestInvalidTrust(CBigNum& bnBestInvalidTrust) +{ + return Read(string("bnBestInvalidTrust"), bnBestInvalidTrust); +} + +bool CTxDB::WriteBestInvalidTrust(CBigNum bnBestInvalidTrust) +{ + return Write(string("bnBestInvalidTrust"), bnBestInvalidTrust); +} + +bool CTxDB::ReadSyncCheckpoint(uint256& hashCheckpoint) +{ + return Read(string("hashSyncCheckpoint"), hashCheckpoint); +} + +bool CTxDB::WriteSyncCheckpoint(uint256 hashCheckpoint) +{ + return Write(string("hashSyncCheckpoint"), hashCheckpoint); +} + +bool CTxDB::ReadCheckpointPubKey(string& strPubKey) +{ + return Read(string("strCheckpointPubKey"), strPubKey); +} + +bool CTxDB::WriteCheckpointPubKey(const string& strPubKey) +{ + return Write(string("strCheckpointPubKey"), strPubKey); +} + +bool CTxDB::ReadModifierUpgradeTime(unsigned int& nUpgradeTime) +{ + return Read(string("nUpgradeTime"), nUpgradeTime); +} + +bool CTxDB::WriteModifierUpgradeTime(const unsigned int& nUpgradeTime) +{ + return Write(string("nUpgradeTime"), nUpgradeTime); +} + +static 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() +{ + if (mapBlockIndex.size() > 0) { + // Already loaded once in this session. It can happen during migration + // from BDB. + return true; + } + // The block index is an in-memory structure that maps hashes to on-disk + // locations where the contents of the block can be found. Here, we scan it + // out of the DB and into mapBlockIndex. + leveldb::Iterator *iterator = pdb->NewIterator(leveldb::ReadOptions()); + // Seek to start key. + CDataStream ssStartKey(SER_DISK, CLIENT_VERSION); + ssStartKey << make_pair(string("blockindex"), uint256(0)); + iterator->Seek(ssStartKey.str()); + // Now read each entry. + while (iterator->Valid()) + { + // Unpack keys and values. + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.write(iterator->key().data(), iterator->key().size()); + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + ssValue.write(iterator->value().data(), iterator->value().size()); + string strType; + ssKey >> strType; + // Did we reach the end of the data to read? + if (fRequestShutdown || strType != "blockindex") + break; + CDiskBlockIndex diskindex; + ssValue >> diskindex; + + uint256 blockHash = diskindex.GetBlockHash(); + + // Construct block index object + CBlockIndex* pindexNew = InsertBlockIndex(blockHash); + pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev); + pindexNew->pnext = InsertBlockIndex(diskindex.hashNext); + pindexNew->nFile = diskindex.nFile; + pindexNew->nBlockPos = diskindex.nBlockPos; + pindexNew->nHeight = diskindex.nHeight; + pindexNew->nMint = diskindex.nMint; + pindexNew->nMoneySupply = diskindex.nMoneySupply; + pindexNew->nFlags = diskindex.nFlags; + pindexNew->nStakeModifier = diskindex.nStakeModifier; + pindexNew->prevoutStake = diskindex.prevoutStake; + pindexNew->nStakeTime = diskindex.nStakeTime; + pindexNew->hashProofOfStake = diskindex.hashProofOfStake; + 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 && blockHash == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)) + pindexGenesisBlock = pindexNew; + + if (!pindexNew->CheckIndex()) { + delete iterator; + return error("LoadBlockIndex() : CheckIndex failed at %d", pindexNew->nHeight); + } + + // XP: build setStakeSeen + if (pindexNew->IsProofOfStake()) + setStakeSeen.insert(make_pair(pindexNew->prevoutStake, pindexNew->nStakeTime)); + + iterator->Next(); + } + delete iterator; + + if (fRequestShutdown) + return true; + + // Calculate nChainTrust + 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->nChainTrust = (pindex->pprev ? pindex->pprev->nChainTrust : 0) + pindex->GetBlockTrust(); + // XP: calculate stake modifier checksum + pindex->nStakeModifierChecksum = GetStakeModifierChecksum(pindex); + if (!CheckStakeModifierCheckpoints(pindex->nHeight, pindex->nStakeModifierChecksum)) + return error("CTxDB::LoadBlockIndex() : Failed stake modifier checkpoint height=%d, modifier=0x%016" PRIx64, pindex->nHeight, pindex->nStakeModifier); + } + + // 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; + nBestChainTrust = pindexBest->nChainTrust; + + printf("LoadBlockIndex(): hashBestChain=%s height=%d trust=%s date=%s\n", + hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, CBigNum(nBestChainTrust).ToString().c_str(), + DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str()); + + // XP: load hashSyncCheckpoint + if (!ReadSyncCheckpoint(Checkpoints::hashSyncCheckpoint)) + return error("CTxDB::LoadBlockIndex() : hashSyncCheckpoint not loaded"); + printf("LoadBlockIndex(): synchronized checkpoint %s\n", Checkpoints::hashSyncCheckpoint.ToString().c_str()); + + // Load bnBestInvalidTrust, OK if it doesn't exist + CBigNum bnBestInvalidTrust; + ReadBestInvalidTrust(bnBestInvalidTrust); + nBestInvalidTrust = bnBestInvalidTrust.getuint256(); + + // Verify blocks in the best chain + int nCheckLevel = GetArgInt("-checklevel", 1); + int nCheckDepth = GetArgInt( "-checkblocks", 2500); + if (nCheckDepth == 0) + nCheckDepth = 1000000000; // suffices until the year 19000 + if (nCheckDepth > nBestHeight) + nCheckDepth = nBestHeight; + printf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); + CBlockIndex* pindexFork = NULL; + map, CBlockIndex*> mapBlockPos; + for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev) + { + if (fRequestShutdown || pindex->nHeight < nBestHeight-nCheckDepth) + break; + CBlock block; + if (!block.ReadFromDisk(pindex)) + return error("LoadBlockIndex() : block.ReadFromDisk failed"); + // check level 1: verify block validity + // check level 7: verify block signature too + if (nCheckLevel>0 && !block.CheckBlock(true, true, (nCheckLevel>6))) + { + printf("LoadBlockIndex() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + pindexFork = pindex->pprev; + } + // check level 2: verify transaction index validity + if (nCheckLevel>1) + { + pair pos = make_pair(pindex->nFile, pindex->nBlockPos); + mapBlockPos[pos] = pindex; + BOOST_FOREACH(const CTransaction &tx, block.vtx) + { + uint256 hashTx = tx.GetHash(); + CTxIndex txindex; + if (ReadTxIndex(hashTx, txindex)) + { + // check level 3: checker transaction hashes + if (nCheckLevel>2 || pindex->nFile != txindex.pos.nFile || pindex->nBlockPos != txindex.pos.nBlockPos) + { + // either an error or a duplicate transaction + CTransaction txFound; + if (!txFound.ReadFromDisk(txindex.pos)) + { + printf("LoadBlockIndex() : *** cannot read mislocated transaction %s\n", hashTx.ToString().c_str()); + pindexFork = pindex->pprev; + } + else + if (txFound.GetHash() != hashTx) // not a duplicate tx + { + printf("LoadBlockIndex(): *** invalid tx position for %s\n", hashTx.ToString().c_str()); + pindexFork = pindex->pprev; + } + } + // check level 4: check whether spent txouts were spent within the main chain + unsigned int nOutput = 0; + if (nCheckLevel>3) + { + BOOST_FOREACH(const CDiskTxPos &txpos, txindex.vSpent) + { + if (!txpos.IsNull()) + { + pair posFind = make_pair(txpos.nFile, txpos.nBlockPos); + if (!mapBlockPos.count(posFind)) + { + printf("LoadBlockIndex(): *** found bad spend at %d, hashBlock=%s, hashTx=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str(), hashTx.ToString().c_str()); + pindexFork = pindex->pprev; + } + // check level 6: check whether spent txouts were spent by a valid transaction that consume them + if (nCheckLevel>5) + { + CTransaction txSpend; + if (!txSpend.ReadFromDisk(txpos)) + { + printf("LoadBlockIndex(): *** cannot read spending transaction of %s:%i from disk\n", hashTx.ToString().c_str(), nOutput); + pindexFork = pindex->pprev; + } + else if (!txSpend.CheckTransaction()) + { + printf("LoadBlockIndex(): *** spending transaction of %s:%i is invalid\n", hashTx.ToString().c_str(), nOutput); + pindexFork = pindex->pprev; + } + else + { + bool fFound = false; + BOOST_FOREACH(const CTxIn &txin, txSpend.vin) + if (txin.prevout.hash == hashTx && txin.prevout.n == nOutput) + fFound = true; + if (!fFound) + { + printf("LoadBlockIndex(): *** spending transaction of %s:%i does not spend it\n", hashTx.ToString().c_str(), nOutput); + pindexFork = pindex->pprev; + } + } + } + } + nOutput++; + } + } + } + // check level 5: check whether all prevouts are marked spent + if (nCheckLevel>4) + { + BOOST_FOREACH(const CTxIn &txin, tx.vin) + { + CTxIndex txindex; + if (ReadTxIndex(txin.prevout.hash, txindex)) + if (txindex.vSpent.size()-1 < txin.prevout.n || txindex.vSpent[txin.prevout.n].IsNull()) + { + printf("LoadBlockIndex(): *** found unspent prevout %s:%i in %s\n", txin.prevout.hash.ToString().c_str(), txin.prevout.n, hashTx.ToString().c_str()); + pindexFork = pindex->pprev; + } + } + } + } + } + } + if (pindexFork && !fRequestShutdown) + { + // 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; +} diff --git a/src/txdb-leveldb.h b/src/txdb-leveldb.h new file mode 100644 index 00000000..c73dda68 --- /dev/null +++ b/src/txdb-leveldb.h @@ -0,0 +1,211 @@ +// Copyright (c) 2009-2012 The Bitcoin Developers. +// Authored by Google, Inc. +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_LEVELDB_H +#define BITCOIN_LEVELDB_H + +#include "main.h" + +#include +#include +#include + +#include +#include + +// Class that provides access to a LevelDB. Note that this class is frequently +// instantiated on the stack and then destroyed again, so instantiation has to +// be very cheap. Unfortunately that means, a CTxDB instance is actually just a +// wrapper around some global state. +// +// A LevelDB is a key/value store that is optimized for fast usage on hard +// disks. It prefers long read/writes to seeks and is based on a series of +// sorted key/value mapping files that are stacked on top of each other, with +// newer files overriding older files. A background thread compacts them +// together when too many files stack up. +// +// Learn more: http://code.google.com/p/leveldb/ +class CTxDB +{ +public: + CTxDB(const char* pszMode="r+"); + ~CTxDB() { + // Note that this is not the same as Close() because it deletes only + // data scoped to this TxDB object. + delete activeBatch; + } + + // Destroys the underlying shared global state accessed by this TxDB. + void Close(); + +private: + leveldb::DB *pdb; // Points to the global instance. + + // A batch stores up writes and deletes for atomic application. When this + // field is non-NULL, writes/deletes go there instead of directly to disk. + leveldb::WriteBatch *activeBatch; + leveldb::Options options; + bool fReadOnly; + int nVersion; + +protected: + // Returns true and sets (value,false) if activeBatch contains the given key + // or leaves value alone and sets deleted = true if activeBatch contains a + // delete for it. + bool ScanBatch(const CDataStream &key, std::string *value, bool *deleted) const; + + template + bool Read(const K& key, T& value) + { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(1000); + ssKey << key; + std::string strValue; + + bool readFromDb = true; + if (activeBatch) { + // First we must search for it in the currently pending set of + // changes to the db. If not found in the batch, go on to read disk. + bool deleted = false; + readFromDb = ScanBatch(ssKey, &strValue, &deleted) == false; + if (deleted) { + return false; + } + } + if (readFromDb) { + leveldb::Status status = pdb->Get(leveldb::ReadOptions(), + ssKey.str(), &strValue); + if (!status.ok()) { + if (status.IsNotFound()) + return false; + // Some unexpected error. + printf("LevelDB read failure: %s\n", status.ToString().c_str()); + return false; + } + } + // Unserialize value + try { + CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), + SER_DISK, CLIENT_VERSION); + ssValue >> value; + } + catch (std::exception &e) { + (void)e; + return false; + } + return true; + } + + template + bool Write(const K& key, const T& value) + { + if (fReadOnly) + assert(!"Write called on database in read-only mode"); + + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(1000); + ssKey << key; + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + ssValue.reserve(10000); + ssValue << value; + + if (activeBatch) { + activeBatch->Put(ssKey.str(), ssValue.str()); + return true; + } + leveldb::Status status = pdb->Put(leveldb::WriteOptions(), ssKey.str(), ssValue.str()); + if (!status.ok()) { + printf("LevelDB write failure: %s\n", status.ToString().c_str()); + return false; + } + return true; + } + + template + bool Erase(const K& key) + { + if (!pdb) + return false; + if (fReadOnly) + assert(!"Erase called on database in read-only mode"); + + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(1000); + ssKey << key; + if (activeBatch) { + activeBatch->Delete(ssKey.str()); + return true; + } + leveldb::Status status = pdb->Delete(leveldb::WriteOptions(), ssKey.str()); + return (status.ok() || status.IsNotFound()); + } + + template + bool Exists(const K& key) + { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(1000); + ssKey << key; + std::string unused; + + if (activeBatch) { + bool deleted; + if (ScanBatch(ssKey, &unused, &deleted) && !deleted) { + return true; + } + } + + + leveldb::Status status = pdb->Get(leveldb::ReadOptions(), ssKey.str(), &unused); + return status.IsNotFound() == false; + } + + +public: + bool TxnBegin(); + bool TxnCommit(); + bool TxnAbort() + { + delete activeBatch; + activeBatch = NULL; + return true; + } + + bool ReadVersion(int& nVersion) + { + nVersion = 0; + return Read(std::string("version"), nVersion); + } + + bool WriteVersion(int nVersion) + { + return Write(std::string("version"), nVersion); + } + + 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 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 ReadHashBestChain(uint256& hashBestChain); + bool WriteHashBestChain(uint256 hashBestChain); + bool ReadBestInvalidTrust(CBigNum& bnBestInvalidTrust); + bool WriteBestInvalidTrust(CBigNum bnBestInvalidTrust); + bool ReadSyncCheckpoint(uint256& hashCheckpoint); + bool WriteSyncCheckpoint(uint256 hashCheckpoint); + bool ReadCheckpointPubKey(std::string& strPubKey); + bool WriteCheckpointPubKey(const std::string& strPubKey); + bool ReadModifierUpgradeTime(unsigned int& nUpgradeTime); + bool WriteModifierUpgradeTime(const unsigned int& nUpgradeTime); + bool LoadBlockIndex(); +}; + + +#endif // BITCOIN_DB_H diff --git a/src/txdb.h b/src/txdb.h new file mode 100644 index 00000000..939e59dd --- /dev/null +++ b/src/txdb.h @@ -0,0 +1,20 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The 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_TXDB_H +#define BITCOIN_TXDB_H + +// Allow switching between LevelDB and BerkelyDB here in case we need to temporarily +// go back to BDB for any reason. Once we're confident enough with LevelDB to stick +// with it, this can be deleted. + +#ifdef USE_LEVELDB +#include "txdb-leveldb.h" +#else +#include "db.h" +#include "txdb-bdb.h" +#endif + +#endif // BITCOIN_TXDB_H diff --git a/src/ui_interface.h b/src/ui_interface.h new file mode 100644 index 00000000..d62e64bc --- /dev/null +++ b/src/ui_interface.h @@ -0,0 +1,112 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_UI_INTERFACE_H +#define BITCOIN_UI_INTERFACE_H + +#include +#include +#include + +#include "util.h" + +class CBasicKeyStore; +class CWallet; +class uint256; + +/** General change type (added, updated, removed). */ +enum ChangeType +{ + CT_NEW, + CT_UPDATED, + CT_DELETED +}; + +/** Signals for UI communication. */ +class CClientUIInterface +{ +public: + /** Flags for CClientUIInterface::ThreadSafeMessageBox */ + enum MessageBoxFlags + { + YES = 0x00000002, + OK = 0x00000004, + NO = 0x00000008, + YES_NO = (YES|NO), + CANCEL = 0x00000010, + APPLY = 0x00000020, + CLOSE = 0x00000040, + OK_DEFAULT = 0x00000000, + YES_DEFAULT = 0x00000000, + NO_DEFAULT = 0x00000080, + CANCEL_DEFAULT = 0x80000000, + ICON_EXCLAMATION = 0x00000100, + ICON_HAND = 0x00000200, + ICON_WARNING = ICON_EXCLAMATION, + ICON_ERROR = ICON_HAND, + ICON_QUESTION = 0x00000400, + ICON_INFORMATION = 0x00000800, + ICON_STOP = ICON_HAND, + ICON_ASTERISK = ICON_INFORMATION, + ICON_MASK = (0x00000100|0x00000200|0x00000400|0x00000800), + FORWARD = 0x00001000, + BACKWARD = 0x00002000, + RESET = 0x00004000, + HELP = 0x00008000, + MORE = 0x00010000, + SETUP = 0x00020000, + // Force blocking, modal message box dialog (not just OS notification) + MODAL = 0x00040000, + + /** Predefined combinations for certain default usage cases */ + MSG_INFORMATION = ICON_INFORMATION, + MSG_WARNING = (ICON_WARNING | OK | MODAL), + MSG_ERROR = (ICON_ERROR | OK | MODAL) + + }; + + /** Show message box. */ + boost::signals2::signal ThreadSafeMessageBox; + + /** Ask the user whether they want to pay a fee or not. */ + boost::signals2::signal > ThreadSafeAskFee; + + /** Handle a URL passed at the command line. */ + boost::signals2::signal ThreadSafeHandleURI; + + /** Progress message during initialization. */ + boost::signals2::signal InitMessage; + + /** Initiate client shutdown. */ + boost::signals2::signal QueueShutdown; + + /** Translate a message to the native language of the user. */ + boost::signals2::signal Translate; + + /** Block chain changed. */ + boost::signals2::signal NotifyBlocksChanged; + + /** Number of network connections changed. */ + boost::signals2::signal NotifyNumConnectionsChanged; + + /** + * New, updated or cancelled alert. + * @note called with lock cs_mapAlerts held. + */ + boost::signals2::signal NotifyAlertChanged; +}; + +extern CClientUIInterface uiInterface; + +/** + * Translation function: Call Translate signal on UI interface, which returns a boost::optional result. + * If no translation slot is registered, nothing is returned, and simply return the input. + */ +inline std::string _(const char* psz) +{ + boost::optional rv = uiInterface.Translate(psz); + return rv ? (*rv) : psz; +} + +#endif diff --git a/src/uint256.h b/src/uint256.h new file mode 100644 index 00000000..3bb0167f --- /dev/null +++ b/src/uint256.h @@ -0,0 +1,629 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_UINT256_H +#define BITCOIN_UINT256_H + +#include +#include +#include +#include +#include +#include + +inline int Testuint256AdHoc(std::vector vArg); + + + +/** Base class without constructors for uint256 and uint160. + * This makes the compiler let u use it in a union. + */ +template +class base_uint +{ +protected: + enum { WIDTH=BITS/32 }; + uint32_t 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; + } + + double getdouble() const + { + double ret = 0.0; + double fact = 1.0; + for (int i = 0; i < WIDTH; i++) { + ret += fact * pn[i]; + fact *= 4294967296.0; + } + return ret; + } + + base_uint& operator=(uint64_t b) + { + pn[0] = (uint32_t)b; + pn[1] = (uint32_t)(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_t b) + { + pn[0] ^= (uint32_t)b; + pn[1] ^= (uint32_t)(b >> 32); + return *this; + } + + base_uint& operator|=(uint64_t b) + { + pn[0] |= (uint32_t)b; + pn[1] |= (uint32_t)(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_t carry = 0; + for (int i = 0; i < WIDTH; i++) + { + uint64_t 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_t b64) + { + base_uint b; + b = b64; + *this += b; + return *this; + } + + base_uint& operator-=(uint64_t 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_t b) + { + if (a.pn[0] != (uint32_t)b) + return false; + if (a.pn[1] != (uint32_t)(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_t b) + { + return (!(a == b)); + } + + std::string GetHex() const + { + char psz[sizeof(pn)*2 + 1]; + for (unsigned int i = 0; i < sizeof(pn); i++) + sprintf(psz + i*2, "%02x", ((unsigned char*)pn)[sizeof(pn) - i - 1]); + return std::string(psz, psz + sizeof(pn)*2); + } + + void SetHex(const char* psz) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + + // skip leading spaces + while (isspace(*psz)) + psz++; + + // skip 0x + if (psz[0] == '0' && tolower(psz[1]) == 'x') + psz += 2; + + // hex string to uint + static const unsigned 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[(unsigned char)*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]; + } + + std::vector getBytes() + { + return std::vector(begin(), end()); + } + + size_t size() + { + return sizeof(pn); + } + + uint64_t Get64(int n=0) const + { + return pn[2*n] | (uint64_t)pn[2*n+1] << 32; + } + + uint32_t Get32(int n=0) const + { + return pn[n]; + } + + unsigned int GetSerializeSize(int nType, int nVersion) const + { + return sizeof(pn); + } + + template + void Serialize(Stream& s, int nType, int nVersion) const + { + s.write((char*)pn, sizeof(pn)); + } + + template + void Unserialize(Stream& s, int nType, int nVersion) + { + 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 +// + +/** 160-bit unsigned integer */ +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_t b) + { + pn[0] = (uint32_t)b; + pn[1] = (uint32_t)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + } + + uint160& operator=(uint64_t b) + { + pn[0] = (uint32_t)b; + pn[1] = (uint32_t)(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_t b) { return (base_uint160)a == b; } +inline bool operator!=(const uint160& a, uint64_t 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 +// + +/** 256-bit unsigned integer */ +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_t b) + { + pn[0] = (uint32_t)b; + pn[1] = (uint32_t)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + } + + uint256& operator=(uint64_t b) + { + pn[0] = (uint32_t)b; + pn[1] = (uint32_t)(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_t b) { return (base_uint256)a == b; } +inline bool operator!=(const uint256& a, uint64_t 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; } + +#endif diff --git a/src/util.cpp b/src/util.cpp new file mode 100644 index 00000000..efd6d801 --- /dev/null +++ b/src/util.cpp @@ -0,0 +1,1497 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "util.h" +#include "sync.h" +#include "version.h" +#include "ui_interface.h" +#include +#include // for to_lower() +#include // for startswith() and endswith() + +// Work around clang compilation problem in Boost 1.46: +// /usr/include/boost/program_options/detail/config_file.hpp:163:17: error: call to function 'to_internal' that is neither visible in the template definition nor found by argument-dependent lookup +// See also: http://stackoverflow.com/questions/10020179/compilation-fail-in-boost-librairies-program-options +// http://clang.debian.net/status.php?version=3.0&key=CANNOT_FIND_FUNCTION +namespace boost { + namespace program_options { + std::string to_internal(const std::string&); + } +} + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef WIN32 +#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 0x0501 +#ifdef _WIN32_IE +#undef _WIN32_IE +#endif +#define _WIN32_IE 0x0501 +#define WIN32_LEAN_AND_MEAN 1 +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include /* for _commit */ +#include "shlobj.h" +#elif defined(__linux__) +# include +#endif + +#if !defined(WIN32) && !defined(ANDROID) +#include +#endif + + +using namespace std; +namespace bt = boost::posix_time; + +map mapArgs; +map > mapMultiArgs; +bool fDebug = false; +bool fDebugNet = false; +bool fPrintToConsole = false; +bool fPrintToDebugger = false; +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; +CMedianFilter vTimeOffsets(200,0); +bool fReopenDebugLog = false; + +// Extended DecodeDumpTime implementation, see this page for details: +// http://stackoverflow.com/questions/3786201/parsing-of-date-time-from-string-boost +const std::locale formats[] = { + std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%dT%H:%M:%SZ")), + std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d %H:%M:%S")), + std::locale(std::locale::classic(),new bt::time_input_facet("%Y/%m/%d %H:%M:%S")), + std::locale(std::locale::classic(),new bt::time_input_facet("%d.%m.%Y %H:%M:%S")), + std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d")) +}; + +const size_t formats_n = sizeof(formats)/sizeof(formats[0]); + +std::time_t pt_to_time_t(const bt::ptime& pt) +{ + bt::ptime timet_start(boost::gregorian::date(1970,1,1)); + bt::time_duration diff = pt - timet_start; + return diff.ticks()/bt::time_duration::rep_type::ticks_per_second; +} + +// Init OpenSSL library multithreading support +static CCriticalSection** ppmutexOpenSSL; +void locking_callback(int mode, int i, const char* file, int line) +{ + if (mode & CRYPTO_LOCK) { + ENTER_CRITICAL_SECTION(*ppmutexOpenSSL[i]); + } else { + LEAVE_CRITICAL_SECTION(*ppmutexOpenSSL[i]); + } +} + +LockedPageManager LockedPageManager::instance; + +// Init +class CInit +{ +public: + CInit() + { + // Init OpenSSL library multithreading support + ppmutexOpenSSL = (CCriticalSection**)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(CCriticalSection*)); + for (int i = 0; i < CRYPTO_num_locks(); i++) + ppmutexOpenSSL[i] = new CCriticalSection(); + CRYPTO_set_locking_callback(locking_callback); + +#ifdef WIN32 + // 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_t 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_t nLastPerfmon; + if (GetTime() < nLastPerfmon + 10 * 60) + return; + nLastPerfmon = GetTime(); + +#ifdef WIN32 + // 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); + OPENSSL_cleanse(pdata, nSize); + printf("RandAddSeed() %lu bytes\n", nSize); + } +#endif +} + +uint64_t GetRand(uint64_t 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_t nRange = (std::numeric_limits::max() / nMax) * nMax; + uint64_t nRand = 0; + do + RAND_bytes((unsigned char*)&nRand, sizeof(nRand)); + while (nRand >= nRange); + return (nRand % nMax); +} + +int GetRandInt(int nMax) +{ + return static_cast(GetRand(nMax)); +} + +uint256 GetRandHash() +{ + uint256 hash; + RAND_bytes((unsigned char*)&hash, sizeof(hash)); + return hash; +} + + + + + + +static FILE* fileout = NULL; + +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 if (!fPrintToDebugger) + { + // print to debug.log + + if (!fileout) + { + boost::filesystem::path pathDebug = GetDataDir() / "debug.log"; + fileout = fopen(pathDebug.string().c_str(), "a"); + if (fileout) setbuf(fileout, NULL); // unbuffered + } + if (fileout) + { + static bool fStartedNewLine = true; + + // This routine may be called by global destructors during shutdown. + // Since the order of destruction of static/global objects is undefined, + // allocate mutexDebugLog on the heap the first time this routine + // is called to avoid crashes during shutdown. + static boost::mutex* mutexDebugLog = NULL; + if (mutexDebugLog == NULL) mutexDebugLog = new boost::mutex(); + boost::mutex::scoped_lock scoped_lock(*mutexDebugLog); + + // reopen the log file, if requested + if (fReopenDebugLog) { + fReopenDebugLog = false; + boost::filesystem::path pathDebug = GetDataDir() / "debug.log"; + if (freopen(pathDebug.string().c_str(),"a",fileout) != NULL) + setbuf(fileout, NULL); // unbuffered + } + + // 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 WIN32 + if (fPrintToDebugger) + { + static CCriticalSection cs_OutputDebugStringF; + + // accumulate and output a line at a time + { + LOCK(cs_OutputDebugStringF); + static std::string buffer; + + va_list arg_ptr; + va_start(arg_ptr, pszFormat); + buffer += vstrprintf(pszFormat, arg_ptr); + va_end(arg_ptr); + + size_t line_start = 0, line_end; + while((line_end = buffer.find('\n', line_start)) != -1) + { + OutputDebugStringA(buffer.substr(line_start, line_end - line_start).c_str()); + line_start = line_end + 1; + } + buffer.erase(0, line_start); + } + } +#endif + return ret; +} + +string vstrprintf(const char *format, va_list ap) +{ + char buffer[50000]; + char* p = buffer; + int limit = sizeof(buffer); + int ret; + while (true) + { +#ifndef _MSC_VER + va_list arg_ptr; + va_copy(arg_ptr, ap); +#else + va_list arg_ptr = ap; +#endif +#ifdef WIN32 + ret = _vsnprintf(p, limit, format, arg_ptr); +#else + ret = vsnprintf(p, limit, format, arg_ptr); +#endif + 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; +} + +string real_strprintf(const char *format, int dummy, ...) +{ + va_list arg_ptr; + va_start(arg_ptr, dummy); + string str = vstrprintf(format, arg_ptr); + va_end(arg_ptr); + return str; +} + +string real_strprintf(const std::string &format, int dummy, ...) +{ + va_list arg_ptr; + va_start(arg_ptr, dummy); + string str = vstrprintf(format.c_str(), arg_ptr); + va_end(arg_ptr); + return str; +} + +bool error(const char *format, ...) +{ + va_list arg_ptr; + va_start(arg_ptr, format); + std::string str = vstrprintf(format, arg_ptr); + va_end(arg_ptr); + printf("ERROR: %s\n", str.c_str()); + return false; +} + + +void ParseString(const string& str, char c, vector& v) +{ + if (str.empty()) + return; + string::size_type i1 = 0; + string::size_type i2; + while (true) + { + 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_t n, bool fPlus) +{ + // Note: not using straight sprintf here because we do NOT want + // localized number formatting. + int64_t n_abs = (n > 0 ? n : -n); + int64_t quotient = n_abs/COIN; + int64_t remainder = n_abs%COIN; + string str = strprintf("%" PRId64 ".%06" PRId64, quotient, remainder); + + // Right-trim excess zeros before the decimal point: + int nTrim = 0; + for (size_t i = str.size()-1; (str[i] == '0' && isdigit(str[i-2])); --i) + ++nTrim; + if (nTrim) + str.erase(str.size()-nTrim, nTrim); + + 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_t& nRet) +{ + return ParseMoney(str.c_str(), nRet); +} + +bool ParseMoney(const char* pszIn, int64_t& nRet) +{ + string strWhole; + int64_t nUnits = 0; + const char* p = pszIn; + while (isspace(*p)) + p++; + for (; *p; p++) + { + if (*p == '.') + { + p++; + int64_t 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() > 10) // guard against 63 bit overflow + return false; + if (nUnits < 0 || nUnits > COIN) + return false; + int64_t nWhole = atoi64(strWhole); + int64_t nValue = nWhole*COIN + nUnits; + + nRet = nValue; + return true; +} + + +static const signed 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, }; + +bool IsHex(const string& str) +{ + BOOST_FOREACH(unsigned char c, str) + { + if (phexdigit[c] < 0) + return false; + } + return (str.size() > 0) && (str.size()%2 == 0); +} + +vector ParseHex(const char* psz) +{ + // convert hex dump to vector + vector vch; + while (true) + { + while (isspace(*psz)) + psz++; + signed char c = phexdigit[(unsigned char)*psz++]; + if (c == (signed char)-1) + break; + unsigned char n = (c << 4); + c = phexdigit[(unsigned char)*psz++]; + if (c == (signed char)-1) + break; + n |= c; + vch.push_back(n); + } + return vch; +} + +vector ParseHex(const string& str) +{ + return ParseHex(str.c_str()); +} + +static void InterpretNegativeSetting(string name, map& mapSettingsRet) +{ + // interpret -nofoo as -foo=0 (and -nofoo=0 as -foo=1) as long as -foo not set + if (name.find("-no") == 0) + { + std::string positive("-"); + positive.append(name.begin()+3, name.end()); + if (mapSettingsRet.count(positive) == 0) + { + bool value = !GetBoolArg(name); + mapSettingsRet[positive] = (value ? "1" : "0"); + } + } +} + +void ParseParameters(int argc, const char* const argv[]) +{ + mapArgs.clear(); + mapMultiArgs.clear(); + for (int i = 1; i < argc; i++) + { + std::string str(argv[i]); + std::string strValue; + size_t is_index = str.find('='); + if (is_index != std::string::npos) + { + strValue = str.substr(is_index+1); + str = str.substr(0, is_index); + } +#ifdef WIN32 + boost::to_lower(str); + if (boost::algorithm::starts_with(str, "/")) + str = "-" + str.substr(1); +#endif + if (str[0] != '-') + break; + + mapArgs[str] = strValue; + mapMultiArgs[str].push_back(strValue); + } + + // New 0.6 features: + BOOST_FOREACH(const PAIRTYPE(string,string)& entry, mapArgs) + { + string name = entry.first; + + // interpret --foo as -foo (as long as both are not set) + if (name.find("--") == 0) + { + std::string singleDash(name.begin()+1, name.end()); + if (mapArgs.count(singleDash) == 0) + mapArgs[singleDash] = entry.second; + name = singleDash; + } + + // interpret -nofoo as -foo=0 (and -nofoo=0 as -foo=1) as long as -foo not set + InterpretNegativeSetting(name, mapArgs); + } +} + +std::string GetArg(const std::string& strArg, const std::string& strDefault) +{ + if (mapArgs.count(strArg)) + return mapArgs[strArg]; + return strDefault; +} + +int64_t GetArg(const std::string& strArg, int64_t nDefault) +{ + if (mapArgs.count(strArg)) + return atoi64(mapArgs[strArg]); + return nDefault; +} + +int32_t GetArgInt(const std::string& strArg, int32_t nDefault) +{ + if (mapArgs.count(strArg)) + return strtol(mapArgs[strArg]); + return nDefault; +} + +uint32_t GetArgUInt(const std::string& strArg, uint32_t nDefault) +{ + if (mapArgs.count(strArg)) + return strtoul(mapArgs[strArg]); + return nDefault; +} + +bool GetBoolArg(const std::string& strArg, bool fDefault) +{ + if (mapArgs.count(strArg)) + { + if (mapArgs[strArg].empty()) + return true; + return (atoi(mapArgs[strArg]) != 0); + } + return fDefault; +} + +bool SoftSetArg(const std::string& strArg, const std::string& strValue) +{ + if (mapArgs.count(strArg) || mapMultiArgs.count(strArg)) + return false; + mapArgs[strArg] = strValue; + mapMultiArgs[strArg].push_back(strValue); + + return true; +} + +bool SoftSetBoolArg(const std::string& strArg, bool fValue) +{ + if (fValue) + return SoftSetArg(strArg, std::string("1")); + else + return SoftSetArg(strArg, std::string("0")); +} + + +string EncodeBase64(const unsigned char* pch, size_t len) +{ + static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + string strRet=""; + strRet.reserve((len+2)/3*4); + + int mode=0, left=0; + const unsigned char *pchEnd = pch+len; + + while (pch> 2]; + left = (enc & 3) << 4; + mode = 1; + break; + + case 1: // we have two bits + strRet += pbase64[left | (enc >> 4)]; + left = (enc & 15) << 2; + mode = 2; + break; + + case 2: // we have four bits + strRet += pbase64[left | (enc >> 6)]; + strRet += pbase64[enc & 63]; + mode = 0; + break; + } + } + + if (mode) + { + strRet += pbase64[left]; + strRet += '='; + if (mode == 1) + strRet += '='; + } + + return strRet; +} + +string EncodeBase64(const string& str) +{ + return EncodeBase64((const unsigned char*)str.c_str(), str.size()); +} + +vector DecodeBase64(const char* p, bool* pfInvalid) +{ + static const int decode64_table[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, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, + -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + }; + + if (pfInvalid) + *pfInvalid = false; + + vector vchRet; + vchRet.reserve(strlen(p)*3/4); + + int mode = 0; + int left = 0; + + while (1) + { + int dec = decode64_table[(unsigned char)*p]; + if (dec == -1) break; + p++; + switch (mode) + { + case 0: // we have no bits and get 6 + left = dec; + mode = 1; + break; + + case 1: // we have 6 bits and keep 4 + vchRet.push_back((left<<2) | (dec>>4)); + left = dec & 15; + mode = 2; + break; + + case 2: // we have 4 bits and get 6, we keep 2 + vchRet.push_back((left<<4) | (dec>>2)); + left = dec & 3; + mode = 3; + break; + + case 3: // we have 2 bits and get 6 + vchRet.push_back((left<<6) | dec); + mode = 0; + break; + } + } + + if (pfInvalid) + switch (mode) + { + case 0: // 4n base64 characters processed: ok + break; + + case 1: // 4n+1 base64 character processed: impossible + *pfInvalid = true; + break; + + case 2: // 4n+2 base64 characters processed: require '==' + if (left || p[0] != '=' || p[1] != '=' || decode64_table[(unsigned char)p[2]] != -1) + *pfInvalid = true; + break; + + case 3: // 4n+3 base64 characters processed: require '=' + if (left || p[0] != '=' || decode64_table[(unsigned char)p[1]] != -1) + *pfInvalid = true; + break; + } + + return vchRet; +} + +string DecodeBase64(const string& str) +{ + vector vchRet = DecodeBase64(str.c_str()); + return string((const char*)&vchRet[0], vchRet.size()); +} + +string EncodeBase32(const unsigned char* pch, size_t len) +{ + static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567"; + + string strRet=""; + strRet.reserve((len+4)/5*8); + + int mode=0, left=0; + const unsigned char *pchEnd = pch+len; + + while (pch> 3]; + left = (enc & 7) << 2; + mode = 1; + break; + + case 1: // we have three bits + strRet += pbase32[left | (enc >> 6)]; + strRet += pbase32[(enc >> 1) & 31]; + left = (enc & 1) << 4; + mode = 2; + break; + + case 2: // we have one bit + strRet += pbase32[left | (enc >> 4)]; + left = (enc & 15) << 1; + mode = 3; + break; + + case 3: // we have four bits + strRet += pbase32[left | (enc >> 7)]; + strRet += pbase32[(enc >> 2) & 31]; + left = (enc & 3) << 3; + mode = 4; + break; + + case 4: // we have two bits + strRet += pbase32[left | (enc >> 5)]; + strRet += pbase32[enc & 31]; + mode = 0; + } + } + + static const int nPadding[5] = {0, 6, 4, 3, 1}; + if (mode) + { + strRet += pbase32[left]; + for (int n=0; n DecodeBase32(const char* p, bool* pfInvalid) +{ + static const int decode32_table[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, -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 0, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + }; + + if (pfInvalid) + *pfInvalid = false; + + vector vchRet; + vchRet.reserve((strlen(p))*5/8); + + int mode = 0; + int left = 0; + + while (1) + { + int dec = decode32_table[(unsigned char)*p]; + if (dec == -1) break; + p++; + switch (mode) + { + case 0: // we have no bits and get 5 + left = dec; + mode = 1; + break; + + case 1: // we have 5 bits and keep 2 + vchRet.push_back((left<<3) | (dec>>2)); + left = dec & 3; + mode = 2; + break; + + case 2: // we have 2 bits and keep 7 + left = left << 5 | dec; + mode = 3; + break; + + case 3: // we have 7 bits and keep 4 + vchRet.push_back((left<<1) | (dec>>4)); + left = dec & 15; + mode = 4; + break; + + case 4: // we have 4 bits, and keep 1 + vchRet.push_back((left<<4) | (dec>>1)); + left = dec & 1; + mode = 5; + break; + + case 5: // we have 1 bit, and keep 6 + left = left << 5 | dec; + mode = 6; + break; + + case 6: // we have 6 bits, and keep 3 + vchRet.push_back((left<<2) | (dec>>3)); + left = dec & 7; + mode = 7; + break; + + case 7: // we have 3 bits, and keep 0 + vchRet.push_back((left<<5) | dec); + mode = 0; + break; + } + } + + if (pfInvalid) + switch (mode) + { + case 0: // 8n base32 characters processed: ok + break; + + case 1: // 8n+1 base32 characters processed: impossible + case 3: // +3 + case 6: // +6 + *pfInvalid = true; + break; + + case 2: // 8n+2 base32 characters processed: require '======' + if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || p[3] != '=' || p[4] != '=' || p[5] != '=' || decode32_table[(unsigned char)p[6]] != -1) + *pfInvalid = true; + break; + + case 4: // 8n+4 base32 characters processed: require '====' + if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || p[3] != '=' || decode32_table[(unsigned char)p[4]] != -1) + *pfInvalid = true; + break; + + case 5: // 8n+5 base32 characters processed: require '===' + if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || decode32_table[(unsigned char)p[3]] != -1) + *pfInvalid = true; + break; + + case 7: // 8n+7 base32 characters processed: require '=' + if (left || p[0] != '=' || decode32_table[(unsigned char)p[1]] != -1) + *pfInvalid = true; + break; + } + + return vchRet; +} + +string DecodeBase32(const string& str) +{ + vector vchRet = DecodeBase32(str.c_str()); + return string((const char*)&vchRet[0], vchRet.size()); +} + + +int64_t DecodeDumpTime(const std::string& s) +{ + bt::ptime pt; + + for(size_t i=0; i> pt; + if(pt != bt::ptime()) break; + } + + return pt_to_time_t(pt); +} + +std::string EncodeDumpTime(int64_t nTime) { + return DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", nTime); +} + +std::string EncodeDumpString(const std::string &str) { + std::stringstream ret; + BOOST_FOREACH(unsigned char c, str) { + if (c <= 32 || c >= 128 || c == '%') { + ret << '%' << HexStr(&c, &c + 1); + } else { + ret << c; + } + } + return ret.str(); +} + +std::string DecodeDumpString(const std::string &str) { + std::stringstream ret; + for (unsigned int pos = 0; pos < str.length(); pos++) { + unsigned char c = str[pos]; + if (c == '%' && pos+2 < str.length()) { + c = (((str[pos+1]>>6)*9+((str[pos+1]-'0')&15)) << 4) | + ((str[pos+2]>>6)*9+((str[pos+2]-'0')&15)); + pos += 2; + } + ret << c; + } + return ret.str(); +} + +bool WildcardMatch(const char* psz, const char* mask) +{ + while (true) + { + 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()); +} + + + + + + + + +static std::string FormatException(std::exception* pex, const char* pszThread) +{ +#ifdef WIN32 + char pszModule[MAX_PATH] = ""; + GetModuleFileNameA(NULL, pszModule, sizeof(pszModule)); +#else + const char* pszModule = "XP"; +#endif + if (pex) + return strprintf( + "EXCEPTION: %s \n%s \n%s in %s \n", typeid(*pex).name(), pex->what(), pszModule, pszThread); + else + return strprintf( + "UNKNOWN EXCEPTION \n%s in %s \n", pszModule, pszThread); +} + +void LogException(std::exception* pex, const char* pszThread) +{ + std::string message = FormatException(pex, pszThread); + printf("\n%s", message.c_str()); +} + +void PrintException(std::exception* pex, const char* pszThread) +{ + std::string message = FormatException(pex, pszThread); + printf("\n\n************************\n%s\n", message.c_str()); + fprintf(stderr, "\n\n************************\n%s\n", message.c_str()); + strMiscWarning = message; + throw; +} + +void LogStackTrace() { + printf("\n\n******* exception encountered *******\n"); + if (fileout) + { +#if !defined(WIN32) && !defined(ANDROID) + void* pszBuffer[32]; + size_t size; + size = backtrace(pszBuffer, 32); + backtrace_symbols_fd(pszBuffer, size, fileno(fileout)); +#endif + } +} + +void PrintExceptionContinue(std::exception* pex, const char* pszThread) +{ + std::string message = FormatException(pex, pszThread); + printf("\n\n************************\n%s\n", message.c_str()); + fprintf(stderr, "\n\n************************\n%s\n", message.c_str()); + strMiscWarning = message; +} + +boost::filesystem::path GetDefaultDataDir() +{ + namespace fs = boost::filesystem; + // Windows < Vista: C:\Documents and Settings\Username\Application Data\XP + // Windows >= Vista: C:\Users\Username\AppData\Roaming\XP + // Mac: ~/Library/Application Support/XP + // Unix: ~/.XP +#ifdef WIN32 + // Windows + return GetSpecialFolderPath(CSIDL_APPDATA) / "XP"; +#else + fs::path pathRet; + char* pszHome = getenv("HOME"); + if (pszHome == NULL || strlen(pszHome) == 0) + pathRet = fs::path("/"); + else + pathRet = fs::path(pszHome); +#ifdef MAC_OSX + // Mac + pathRet /= "Library/Application Support"; + fs::create_directory(pathRet); + return pathRet / "XP"; +#else + // Unix + return pathRet / ".XP"; +#endif +#endif +} + +const boost::filesystem::path &GetDataDir(bool fNetSpecific) +{ + namespace fs = boost::filesystem; + + static fs::path pathCached[2]; + static CCriticalSection csPathCached; + static bool cachedPath[2] = {false, false}; + + fs::path &path = pathCached[fNetSpecific]; + + // This can be called during exceptions by printf, so we cache the + // value so we don't have to do memory allocations after that. + if (cachedPath[fNetSpecific]) + return path; + + LOCK(csPathCached); + + if (mapArgs.count("-datadir")) { + path = fs::system_complete(mapArgs["-datadir"]); + if (!fs::is_directory(path)) { + path = ""; + return path; + } + } else { + path = GetDefaultDataDir(); + } + if (fNetSpecific && GetBoolArg("-testnet", false)) + path /= "testnet2"; + + fs::create_directory(path); + + cachedPath[fNetSpecific]=true; + return path; +} + +string randomStrGen(int length) { + static string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; + string result; + result.resize(length); + for (int32_t i = 0; i < length; i++) + result[i] = charset[rand() % charset.length()]; + + return result; +} + +void createConf() +{ + srand(static_cast(time(NULL))); + + ofstream pConf; +#if BOOST_FILESYSTEM_VERSION >= 3 + pConf.open(GetConfigFile().generic_string().c_str()); +#else + pConf.open(GetConfigFile().string().c_str()); +#endif + pConf << "rpcuser=user\nrpcpassword=" + + randomStrGen(15) + + "\nrpcport=28191" + + "\nport=28192" + + "\n#(0=off, 1=on) daemon - run in the background as a daemon and accept commands" + + "\ndaemon=0" + + "\n#(0=off, 1=on) server - accept command line and JSON-RPC commands" + + "\nserver=0" + + "\nrpcallowip=127.0.0.1" + + "\ntestnet=0"; + pConf.close(); +} + +boost::filesystem::path GetConfigFile() +{ + boost::filesystem::path pathConfigFile(GetArg("-conf", "XP.conf")); + if (!pathConfigFile.is_complete()) pathConfigFile = GetDataDir(false) / pathConfigFile; + return pathConfigFile; +} + +void ReadConfigFile(map& mapSettingsRet, + map >& mapMultiSettingsRet) +{ + boost::filesystem::ifstream streamConfig(GetConfigFile()); + if (!streamConfig.good()) + { + createConf(); + new(&streamConfig) boost::filesystem::ifstream(GetConfigFile()); + if(!streamConfig.good()) + return; + } + + set setOptions; + setOptions.insert("*"); + + for (boost::program_options::detail::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]; + // interpret nofoo=1 as foo=0 (and nofoo=0 as foo=1) as long as foo not set) + InterpretNegativeSetting(strKey, mapSettingsRet); + } + mapMultiSettingsRet[strKey].push_back(it->value[0]); + } +} + +boost::filesystem::path GetPidFile() +{ + boost::filesystem::path pathPidFile(GetArg("-pid", "XPd.pid")); + if (!pathPidFile.is_complete()) pathPidFile = GetDataDir() / pathPidFile; + return pathPidFile; +} + +#ifndef WIN32 +void CreatePidFile(const boost::filesystem::path &path, pid_t pid) +{ + FILE* file = fopen(path.string().c_str(), "w"); + if (file) + { + fprintf(file, "%d\n", pid); + fclose(file); + } +} +#endif + +bool RenameOver(boost::filesystem::path src, boost::filesystem::path dest) +{ +#ifdef WIN32 + return MoveFileExA(src.string().c_str(), dest.string().c_str(), + MOVEFILE_REPLACE_EXISTING) != 0; +#else + int rc = std::rename(src.string().c_str(), dest.string().c_str()); + return (rc == 0); +#endif /* WIN32 */ +} + +void FileCommit(FILE *fileout) +{ + fflush(fileout); // harmless if redundantly called +#ifdef WIN32 + _commit(_fileno(fileout)); +#else + fsync(fileno(fileout)); +#endif +} + +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 + boost::filesystem::path pathLog = GetDataDir() / "debug.log"; + FILE* file = fopen(pathLog.string().c_str(), "r"); + if (file && GetFilesize(file) > 10 * 1000000) + { + // Restart the file with some of the end + char pch[200000]; + fseek(file, -((long long)sizeof(pch)), SEEK_END); + size_t nBytes = fread(pch, 1, sizeof(pch), file); + fclose(file); + + file = fopen(pathLog.string().c_str(), "w"); + if (file) + { + 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 clocks +// - The user (asking the user to fix the system clock if the first two disagree) +// + +// System clock +int64_t GetTime() +{ + return time(NULL); +} + +// Trusted NTP offset or median of NTP samples. +extern int64_t nNtpOffset; + +// Median of time samples given by other nodes. +static int64_t nNodesOffset = INT64_MAX; + +// Select time offset: +int64_t GetTimeOffset() +{ + // If NTP and system clock are in agreement within 40 minutes, then use NTP. + if (abs64(nNtpOffset) < 40 * 60) + return nNtpOffset; + + // If not, then choose between median peer time and system clock. + if (abs64(nNodesOffset) < 70 * 60) + return nNodesOffset; + + return 0; +} + +int64_t GetNodesOffset() +{ + return nNodesOffset; +} + +int64_t GetAdjustedTime() +{ + return GetTime() + GetTimeOffset(); +} + +void AddTimeData(const CNetAddr& ip, int64_t nTime) +{ + int64_t nOffsetSample = nTime - GetTime(); + + // Ignore duplicates + static set setKnown; + if (!setKnown.insert(ip).second) + return; + + // Add data + vTimeOffsets.input(nOffsetSample); + printf("Added time data, samples %d, offset %+" PRId64 " (%+" PRId64 " minutes)\n", vTimeOffsets.size(), nOffsetSample, nOffsetSample/60); + if (vTimeOffsets.size() >= 5 && vTimeOffsets.size() % 2 == 1) + { + int64_t nMedian = vTimeOffsets.median(); + std::vector vSorted = vTimeOffsets.sorted(); + // Only let other nodes change our time by so much + if (abs64(nMedian) < 70 * 60) + { + nNodesOffset = nMedian; + } + else + { + nNodesOffset = INT64_MAX; + + static bool fDone; + if (!fDone) + { + bool fMatch = false; + + // If nobody has a time different than ours but within 5 minutes of ours, give a warning + BOOST_FOREACH(int64_t nOffset, vSorted) + 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 XP will not work properly."); + strMiscWarning = strMessage; + printf("*** %s\n", strMessage.c_str()); + uiInterface.ThreadSafeMessageBox(strMessage+" ", string("XP"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION); + } + } + } + if (fDebug) { + BOOST_FOREACH(int64_t n, vSorted) + printf("%+" PRId64 " ", n); + printf("| "); + } + if (nNodesOffset != INT64_MAX) + printf("nNodesOffset = %+" PRId64 " (%+" PRId64 " minutes)\n", nNodesOffset, nNodesOffset/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() +{ + return CLIENT_BUILD; +} + +// Format the subversion field according to BIP 14 spec (https://en.bitcoin.it/wiki/BIP_0014) +std::string FormatSubVersion(const std::string& name, int nClientVersion, const std::vector& comments) +{ + std::ostringstream ss; + ss << "/"; + ss << name << ":" << FormatVersion(nClientVersion); + if (!comments.empty()) + ss << "(" << boost::algorithm::join(comments, "; ") << ")"; + ss << "/"; + return ss.str(); +} + +#ifdef WIN32 +boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate) +{ + namespace fs = boost::filesystem; + + char pszPath[MAX_PATH] = ""; + + if(SHGetSpecialFolderPathA(NULL, pszPath, nFolder, fCreate)) + { + return fs::path(pszPath); + } + + printf("SHGetSpecialFolderPathA() failed, could not obtain requested path.\n"); + return fs::path(""); +} +#endif + +void runCommand(std::string strCommand) +{ + int nErr = ::system(strCommand.c_str()); + if (nErr) + printf("runCommand error: system(%s) returned %d\n", strCommand.c_str(), nErr); +} + +void RenameThread(const char* name) +{ +#if defined(PR_SET_NAME) + // Only the first 15 characters are used (16 - NUL terminator) + ::prctl(PR_SET_NAME, name, 0, 0, 0); +#elif 0 && (defined(__FreeBSD__) || defined(__OpenBSD__)) + // TODO: This is currently disabled because it needs to be verified to work + // on FreeBSD or OpenBSD first. When verified the '0 &&' part can be + // removed. + pthread_set_name_np(pthread_self(), name); + +// This is XCode 10.6-and-later; bring back if we drop 10.5 support: +// #elif defined(MAC_OSX) +// pthread_setname_np(name); + +#else + // Prevent warnings for unused parameters... + (void)name; +#endif +} + +bool NewThread(void(*pfn)(void*), void* parg) +{ + try + { + boost::thread(pfn, parg); // thread detaches when out of scope + } catch(boost::thread_resource_error &e) { + printf("Error creating thread: %s\n", e.what()); + return false; + } + return true; +} + +std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime) +{ + // std::locale takes ownership of the pointer + std::locale loc(std::locale::classic(), new boost::posix_time::time_facet(pszFormat)); + std::stringstream ss; + ss.imbue(loc); + ss << boost::posix_time::from_time_t(nTime); + return ss.str(); +} \ No newline at end of file diff --git a/src/util.h b/src/util.h new file mode 100644 index 00000000..f4b872c7 --- /dev/null +++ b/src/util.h @@ -0,0 +1,588 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_UTIL_H +#define BITCOIN_UTIL_H + + +#include "uint256.h" + +#ifndef WIN32 +#include +#include +#include +#endif + +#include +#include +#include + +#ifndef Q_MOC_RUN +#include +#include +#include +#include +#include +#endif + +#include + +#if defined(__USE_MINGW_ANSI_STDIO) +#undef __USE_MINGW_ANSI_STDIO // This constant forces MinGW to conduct stupid behavior +#endif +#include + +#include "netbase.h" // for AddTimeData + +static const int32_t nOneHour = 60 * 60; +static const int32_t nOneDay = 24 * 60 * 60; + +static const int64_t COIN = 1000000; +static const int64_t CENT = 10000; + +#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 UVOIDBEGIN(a) ((void*)&(a)) +#define CVOIDBEGIN(a) ((const void*)&(a)) +#define UINTBEGIN(a) ((uint32_t*)&(a)) +#define CUINTBEGIN(a) ((const uint32_t*)&(a)) + +#ifndef THROW_WITH_STACKTRACE +#define THROW_WITH_STACKTRACE(exception) \ +{ \ + LogStackTrace(); \ + throw (exception); \ +} +void LogStackTrace(); +#endif + +#if defined(_MSC_VER) || defined(__MSVCRT__) + /* Silence compiler warnings on Windows + related to MinGWs inttypes.h */ + #undef PRIu64 + #undef PRId64 + #undef PRIx64 + + #define PRIu64 "I64u" + #define PRId64 "I64d" + #define PRIx64 "I64x" + +#endif + +/* Format characters for (s)size_t and ptrdiff_t */ +#if defined(_MSC_VER) || defined(__MSVCRT__) + /* (s)size_t and ptrdiff_t have the same size specifier in MSVC: + http://msdn.microsoft.com/en-us/library/tcxf1dw6%28v=vs.100%29.aspx + */ + #define PRIszx "Ix" + #define PRIszu "Iu" + #define PRIszd "Id" + #define PRIpdx "Ix" + #define PRIpdu "Iu" + #define PRIpdd "Id" +#else /* C99 standard */ + #define PRIszx "zx" + #define PRIszu "zu" + #define PRIszd "zd" + #define PRIpdx "tx" + #define PRIpdu "tu" + #define PRIpdd "td" +#endif + +// This is needed because the foreach macro can't get over the comma in pair +#define PAIRTYPE(t1, t2) std::pair + +// 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 WIN32 +#define MSG_NOSIGNAL 0 +#define MSG_DONTWAIT 0 + +#ifndef S_IRUSR +#define S_IRUSR 0400 +#define S_IWUSR 0200 +#endif +#else +#define MAX_PATH 1024 +inline void Sleep(int64_t n) +{ + /*Boost has a year 2038 problem— if the request sleep time is past epoch+2^31 seconds the sleep returns instantly. + So we clamp our sleeps here to 10 years and hope that boost is fixed by 2028.*/ + boost::thread::sleep(boost::get_system_time() + boost::posix_time::milliseconds(n>315576000000LL?315576000000LL:n)); +} +#endif + +/* This GNU C extension enables the compiler to check the format string against the parameters provided. + * X is the number of the "format string" parameter, and Y is the number of the first variadic parameter. + * Parameters count from 1. + */ +#ifdef __GNUC__ +#define ATTR_WARN_PRINTF(X,Y) __attribute__((format(printf,X,Y))) +#else +#define ATTR_WARN_PRINTF(X,Y) +#endif + + + + + + + + +extern std::map mapArgs; +extern std::map > mapMultiArgs; +extern bool fDebug; +extern bool fDebugNet; +extern bool fPrintToConsole; +extern bool fPrintToDebugger; +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; +extern bool fReopenDebugLog; + +void RandAddSeed(); +void RandAddSeedPerfmon(); +int ATTR_WARN_PRINTF(1,2) OutputDebugStringF(const char* pszFormat, ...); + +/* + Rationale for the real_strprintf / strprintf construction: + It is not allowed to use va_start with a pass-by-reference argument. + (C++ standard, 18.7, paragraph 3). Use a dummy argument to work around this, and use a + macro to keep similar semantics. +*/ + +/** Overload strprintf for char*, so that GCC format type warnings can be given */ +std::string ATTR_WARN_PRINTF(1,3) real_strprintf(const char *format, int dummy, ...); +/** Overload strprintf for std::string, to be able to use it with _ (translation). + * This will not support GCC format type warnings (-Wformat) so be careful. + */ +std::string real_strprintf(const std::string &format, int dummy, ...); +#define strprintf(format, ...) real_strprintf(format, 0, __VA_ARGS__) +std::string vstrprintf(const char *format, va_list ap); + +bool ATTR_WARN_PRINTF(1,2) error(const char *format, ...); + +/* Redefine printf so that it directs output to debug.log + * + * Do this *after* defining the other printf-like functions, because otherwise the + * __attribute__((format(printf,X,Y))) gets expanded to __attribute__((format(OutputDebugStringF,X,Y))) + * which confuses gcc. + */ +#define printf OutputDebugStringF + +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_t n, bool fPlus=false); +bool ParseMoney(const std::string& str, int64_t& nRet); +bool ParseMoney(const char* pszIn, int64_t& nRet); +std::vector ParseHex(const char* psz); +std::vector ParseHex(const std::string& str); +bool IsHex(const std::string& str); +std::vector DecodeBase64(const char* p, bool* pfInvalid = NULL); +std::string DecodeBase64(const std::string& str); +std::string EncodeBase64(const unsigned char* pch, size_t len); +std::string EncodeBase64(const std::string& str); +std::vector DecodeBase32(const char* p, bool* pfInvalid = NULL); +std::string DecodeBase32(const std::string& str); +std::string EncodeBase32(const unsigned char* pch, size_t len); +std::string EncodeBase32(const std::string& str); +std::string EncodeDumpTime(int64_t nTime); +int64_t DecodeDumpTime(const std::string& s); +std::string EncodeDumpString(const std::string &str); +std::string DecodeDumpString(const std::string &str); +void ParseParameters(int argc, const char*const argv[]); +bool WildcardMatch(const char* psz, const char* mask); +bool WildcardMatch(const std::string& str, const std::string& mask); +void FileCommit(FILE *fileout); +int GetFilesize(FILE* file); +bool RenameOver(boost::filesystem::path src, boost::filesystem::path dest); +boost::filesystem::path GetDefaultDataDir(); +const boost::filesystem::path &GetDataDir(bool fNetSpecific = true); +boost::filesystem::path GetConfigFile(); +boost::filesystem::path GetPidFile(); +#ifndef WIN32 +void CreatePidFile(const boost::filesystem::path &path, pid_t pid); +#endif +void ReadConfigFile(std::map& mapSettingsRet, std::map >& mapMultiSettingsRet); +#ifdef WIN32 +boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate = true); +#endif +void ShrinkDebugFile(); +int GetRandInt(int nMax); +uint64_t GetRand(uint64_t nMax); +uint256 GetRandHash(); +int64_t GetTime(); +void SetMockTime(int64_t nMockTimeIn); +int64_t GetAdjustedTime(); +int64_t GetTimeOffset(); +int64_t GetNodesOffset(); +std::string FormatFullVersion(); +std::string FormatSubVersion(const std::string& name, int nClientVersion, const std::vector& comments); +void AddTimeData(const CNetAddr& ip, int64_t nTime); +void runCommand(std::string strCommand); + + + + + + + + + +inline std::string i64tostr(int64_t n) +{ + return strprintf("%" PRId64, n); +} + +inline std::string itostr(int n) +{ + return strprintf("%d", n); +} + +inline int64_t atoi64(const char* psz) +{ +#ifdef _MSC_VER + return _atoi64(psz); +#else + return strtoll(psz, NULL, 10); +#endif +} + +inline int64_t atoi64(const std::string& str) +{ +#ifdef _MSC_VER + return _atoi64(str.c_str()); +#else + return strtoll(str.c_str(), NULL, 10); +#endif +} + +inline int32_t strtol(const char* psz) +{ + return strtol(psz, NULL, 10); +} + +inline int32_t strtol(const std::string& str) +{ + return strtol(str.c_str(), NULL, 10); +} + +inline uint32_t strtoul(const char* psz) +{ + return strtoul(psz, NULL, 10); +} + +inline uint32_t strtoul(const std::string& str) +{ + return strtoul(str.c_str(), NULL, 10); +} + +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_t roundint64(double d) +{ + return (int64_t)(d > 0 ? d + 0.5 : d - 0.5); +} + +inline int64_t abs64(int64_t n) +{ + return (n >= 0 ? n : -n); +} + +inline std::string leftTrim(std::string src, char chr) +{ + std::string::size_type pos = src.find_first_not_of(chr, 0); + + if(pos > 0) + src.erase(0, pos); + + return src; +} + +template +std::string HexStr(const T itbegin, const T itend, bool fSpaces=false) +{ + std::string rv; + static const char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + rv.reserve((itend-itbegin)*3); + for(T it = itbegin; it < itend; ++it) + { + unsigned char val = (unsigned char)(*it); + if(fSpaces && it != itbegin) + rv.push_back(' '); + rv.push_back(hexmap[val>>4]); + rv.push_back(hexmap[val&15]); + } + + return rv; +} + +inline std::string HexStr(const std::vector& vch, bool fSpaces=false) +{ + return HexStr(vch.begin(), vch.end(), fSpaces); +} + +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_t GetPerformanceCounter() +{ + int64_t nCounter = 0; +#ifdef WIN32 + QueryPerformanceCounter((LARGE_INTEGER*)&nCounter); +#else + timeval t; + gettimeofday(&t, NULL); + nCounter = (int64_t) t.tv_sec * 1000000 + t.tv_usec; +#endif + return nCounter; +} + +inline int64_t GetTimeMillis() +{ + return (boost::posix_time::microsec_clock::universal_time() - + boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_milliseconds(); +} + +std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime); + +static const std::string strTimestampFormat = "%Y-%m-%d %H:%M:%S UTC"; +inline std::string DateTimeStrFormat(int64_t nTime) +{ + return DateTimeStrFormat(strTimestampFormat.c_str(), nTime); +} + + +template +void skipspaces(T& it) +{ + while (isspace(*it)) + ++it; +} + +inline bool IsSwitchChar(char c) +{ +#ifdef WIN32 + return c == '-' || c == '/'; +#else + return c == '-'; +#endif +} + +/** + * Return string argument or default value + * + * @param strArg Argument to get (e.g. "-foo") + * @param default (e.g. "1") + * @return command-line argument or default value + */ +std::string GetArg(const std::string& strArg, const std::string& strDefault); + +/** + * Return 64-bit integer argument or default value + * + * @param strArg Argument to get (e.g. "-foo") + * @param default (e.g. 1) + * @return command-line argument (0 if invalid number) or default value + */ +int64_t GetArg(const std::string& strArg, int64_t nDefault); + +/** + * Return 32-bit integer argument or default value + * + * @param strArg Argument to get (e.g. "-foo") + * @param default (e.g. 1) + * @return command-line argument (0 if invalid number) or default value + */ +int32_t GetArgInt(const std::string& strArg, int32_t nDefault); + +/** + * Return 32-bit unsigned integer argument or default value + * + * @param strArg Argument to get (e.g. "-foo") + * @param default (e.g. 1) + * @return command-line argument (0 if invalid number) or default value + */ +uint32_t GetArgUInt(const std::string& strArg, uint32_t nDefault); + +/** + * Return boolean argument or default value + * + * @param strArg Argument to get (e.g. "-foo") + * @param default (true or false) + * @return command-line argument or default value + */ +bool GetBoolArg(const std::string& strArg, bool fDefault=false); + +/** + * Set an argument if it doesn't already have a value + * + * @param strArg Argument to set (e.g. "-foo") + * @param strValue Value (e.g. "1") + * @return true if argument gets set, false if it already had a value + */ +bool SoftSetArg(const std::string& strArg, const std::string& strValue); + +/** + * Set a boolean argument if it doesn't already have a value + * + * @param strArg Argument to set (e.g. "-foo") + * @param fValue Value (e.g. false) + * @return true if argument gets set, false if it already had a value + */ +bool SoftSetBoolArg(const std::string& strArg, bool fValue); + +/** + * Timing-attack-resistant comparison. + * Takes time proportional to length + * of first argument. + */ +template +bool TimingResistantEqual(const T& a, const T& b) +{ + if (b.size() == 0) return a.size() == 0; + size_t accumulator = a.size() ^ b.size(); + for (size_t i = 0; i < a.size(); i++) + accumulator |= a[i] ^ b[i%b.size()]; + return accumulator == 0; +} + +/** Median filter over a stream of values. + * Returns the median of the last N numbers + */ +template class CMedianFilter +{ +private: + std::vector vValues; + std::vector vSorted; + unsigned int nSize; +public: + CMedianFilter(unsigned int size, T initial_value): + nSize(size) + { + vValues.reserve(size); + vValues.push_back(initial_value); + vSorted = vValues; + } + + void input(T value) + { + if(vValues.size() == nSize) + { + vValues.erase(vValues.begin()); + } + vValues.push_back(value); + + vSorted.resize(vValues.size()); + std::copy(vValues.begin(), vValues.end(), vSorted.begin()); + std::sort(vSorted.begin(), vSorted.end()); + } + + T median() const + { + int size = vSorted.size(); + assert(size>0); + if(size & 1) // Odd number of elements + { + return vSorted[size/2]; + } + else // Even number of elements + { + return (vSorted[size/2-1] + vSorted[size/2]) / 2; + } + } + + int size() const + { + return static_cast(vValues.size()); + } + + std::vector sorted () const + { + return vSorted; + } +}; + +bool NewThread(void(*pfn)(void*), void* parg); + +#ifdef WIN32 +inline void SetThreadPriority(int nPriority) +{ + SetThreadPriority(GetCurrentThread(), nPriority); +} +#else + +#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 void ExitThread(size_t nExitCode) +{ + pthread_exit((void*)nExitCode); +} +#endif + +void RenameThread(const char* name); + +inline uint32_t ByteReverse(uint32_t value) +{ + value = ((value & 0xFF00FF00) >> 8) | ((value & 0x00FF00FF) << 8); + return (value<<16) | (value>>16); +} + +#endif + diff --git a/src/version.cpp b/src/version.cpp new file mode 100644 index 00000000..3109843d --- /dev/null +++ b/src/version.cpp @@ -0,0 +1,92 @@ +// Copyright (c) 2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include + +#include "version.h" + +// Name of client reported in the 'version' message. Report the same name +// for both bitcoind and bitcoin-qt, to make it harder for attackers to +// target servers or GUI users specifically. +const std::string CLIENT_NAME("XP"); + +// Client version number +#ifdef USE_LEVELDB +#define CLIENT_VERSION_SUFFIX "-leveldb" +#else +#define CLIENT_VERSION_SUFFIX "-bdb" +#endif + +// Compiler name +#ifdef __GNUC__ +/*code for GNU C compiler */ +#define CL_NAME "-gcc" +#elif _MSC_VER +/*usually has the version number in _MSC_VER*/ +/*code specific to MSVC compiler*/ +#define CL_NAME "-msvc" +#elif __clang__ +/*code specific to clang compilers*/ +#define CL_NAME "-clang" +#elif __MINGW32__ +/*code specific to mingw compilers*/ +#define CL_NAME "-mingw" +#else +#define CL_NAME "-genericcl" +/*others*/ +#endif + +// The following part of the code determines the CLIENT_BUILD variable. +// Several mechanisms are used for this: +// * first, if HAVE_BUILD_INFO is defined, include build.h, a file that is +// generated by the build environment, possibly containing the output +// of git-describe in a macro called BUILD_DESC +// * secondly, if this is an exported version of the code, GIT_ARCHIVE will +// be defined (automatically using the export-subst git attribute), and +// GIT_COMMIT will contain the commit id. +// * then, three options exist for determining CLIENT_BUILD: +// * if BUILD_DESC is defined, use that literally (output of git-describe) +// * if not, but GIT_COMMIT is defined, use v[maj].[min].[rev].[build]-g[commit] +// * otherwise, use v[maj].[min].[rev].[build]-unk +// finally CLIENT_VERSION_SUFFIX is added + +// First, include build.h if requested +#ifdef HAVE_BUILD_INFO +# include "build.h" +#endif + +// git will put "#define GIT_ARCHIVE 1" on the next line inside archives. +#define GIT_ARCHIVE 1 +#ifdef GIT_ARCHIVE +# define GIT_COMMIT_ID "" +# define GIT_COMMIT_DATE "" +#endif + +#define BUILD_DESC_FROM_COMMIT(maj,min,rev,commit) \ + "XP-v" DO_STRINGIZE(maj) "." DO_STRINGIZE(min) "." DO_STRINGIZE(rev) "." DO_STRINGIZE(build) "-g" commit + +#define BUILD_DESC_FROM_UNKNOWN(maj,min,rev) \ + "XP-v" DO_STRINGIZE(maj) "." DO_STRINGIZE(min) "." DO_STRINGIZE(rev) "." DO_STRINGIZE(build) "-unk" + +#ifndef BUILD_DESC +# ifdef GIT_COMMIT_ID +# define BUILD_DESC BUILD_DESC_FROM_COMMIT(DISPLAY_VERSION_MAJOR, DISPLAY_VERSION_MINOR, DISPLAY_VERSION_REVISION, GIT_COMMIT_ID) +# else +# define BUILD_DESC BUILD_DESC_FROM_UNKNOWN(DISPLAY_VERSION_MAJOR, DISPLAY_VERSION_MINOR, DISPLAY_VERSION_REVISION) +# endif +#endif + +#ifdef _MSC_VER +#undef GIT_COMMIT_DATE +#endif + +#ifndef BUILD_DATE +# ifdef GIT_COMMIT_DATE +# define BUILD_DATE GIT_COMMIT_DATE +# else +# define BUILD_DATE __DATE__ ", " __TIME__ +# endif +#endif + +const std::string CLIENT_BUILD(BUILD_DESC CLIENT_VERSION_SUFFIX CL_NAME); +const std::string CLIENT_DATE(BUILD_DATE); diff --git a/src/version.h b/src/version.h new file mode 100644 index 00000000..4d269f79 --- /dev/null +++ b/src/version.h @@ -0,0 +1,56 @@ +// Copyright (c) 2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_VERSION_H +#define BITCOIN_VERSION_H + +#include "clientversion.h" +#include + +// +// client versioning +// + +static const int CLIENT_VERSION = + 1000000 * CLIENT_VERSION_MAJOR + + 10000 * CLIENT_VERSION_MINOR + + 100 * CLIENT_VERSION_REVISION + + 1 * CLIENT_VERSION_BUILD; + +extern const std::string CLIENT_NAME; +extern const std::string CLIENT_BUILD; +extern const std::string CLIENT_DATE; + +// +// database format versioning +// +static const int DATABASE_VERSION = 70507; + +// +// network protocol versioning +// + +static const int PROTOCOL_VERSION = 90000; + +// earlier versions not supported as of Feb 2012, and are disconnected +static const int MIN_PROTO_VERSION = 209; + +// nTime field added to CAddress, starting with this version; +// if possible, avoid requesting addresses nodes older than this +static const int CADDR_TIME_VERSION = 31402; + +// only request blocks from nodes outside this range of versions +static const int NOBLKS_VERSION_START = 60002; +static const int NOBLKS_VERSION_END = 60006; + +// BIP 0031, pong message, is enabled for all versions AFTER this one +static const int BIP0031_VERSION = 60000; + +// "mempool" command, enhanced "getdata" behavior starts with this version: +static const int MEMPOOL_GD_VERSION = 60002; + +#define DISPLAY_VERSION_MAJOR 1 +#define DISPLAY_VERSION_MINOR 0 +#define DISPLAY_VERSION_REVISION 0 + +#endif diff --git a/src/wallet.cpp b/src/wallet.cpp new file mode 100644 index 00000000..dda68080 --- /dev/null +++ b/src/wallet.cpp @@ -0,0 +1,2740 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "txdb.h" +#include "wallet.h" +#include "walletdb.h" +#include "crypter.h" +#include "ui_interface.h" +#include "base58.h" +#include "kernel.h" +#include "coincontrol.h" +#include + +#include "main.h" + +using namespace std; +extern int64_t nReserveBalance; + +////////////////////////////////////////////////////////////////////////////// +// +// mapWallet +// + +struct CompareValueOnly +{ + bool operator()(const pair >& t1, + const pair >& t2) const + { + return t1.first < t2.first; + } +}; + +CPubKey CWallet::GenerateNewKey() +{ + bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets + + RandAddSeedPerfmon(); + CKey key; + key.MakeNewKey(fCompressed); + + // Compressed public keys were introduced in version 0.6.0 + if (fCompressed) + SetMinVersion(FEATURE_COMPRPUBKEY); + + CPubKey pubkey = key.GetPubKey(); + + // Create new metadata + int64_t nCreationTime = GetTime(); + mapKeyMetadata[pubkey.GetID()] = CKeyMetadata(nCreationTime); + if (!nTimeFirstKey || nCreationTime < nTimeFirstKey) + nTimeFirstKey = nCreationTime; + + if (!AddKey(key)) + throw std::runtime_error("CWallet::GenerateNewKey() : AddKey failed"); + return key.GetPubKey(); +} + +bool CWallet::AddKey(const CKey& key) +{ + CPubKey pubkey = key.GetPubKey(); + if (!CCryptoKeyStore::AddKey(key)) + return false; + if (!fFileBacked) + return true; + if (!IsCrypted()) + return CWalletDB(strWalletFile).WriteKey(pubkey, key.GetPrivKey(), mapKeyMetadata[pubkey.GetID()]); + return true; +} + +bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, const vector &vchCryptedSecret) +{ + if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret)) + return false; + + // check if we need to remove from watch-only + CScript script; + script.SetDestination(vchPubKey.GetID()); + if (HaveWatchOnly(script)) + RemoveWatchOnly(script); + + if (!fFileBacked) + return true; + { + LOCK(cs_wallet); + if (pwalletdbEncryption) + return pwalletdbEncryption->WriteCryptedKey(vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]); + else + return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]); + } + return false; +} + +bool CWallet::LoadKeyMetadata(const CPubKey &pubkey, const CKeyMetadata &meta) +{ + if (meta.nCreateTime && (!nTimeFirstKey || meta.nCreateTime < nTimeFirstKey)) + nTimeFirstKey = meta.nCreateTime; + + mapKeyMetadata[pubkey.GetID()] = meta; + return true; +} + +bool CWallet::AddCScript(const CScript& redeemScript) +{ + if (!CCryptoKeyStore::AddCScript(redeemScript)) + return false; + if (!fFileBacked) + return true; + return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript); +} + +bool CWallet::LoadCScript(const CScript& redeemScript) +{ + /* A sanity check was added in commit 5ed0a2b to avoid adding redeemScripts + * that never can be redeemed. However, old wallets may still contain + * these. Do not add them to the wallet and warn. */ + if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE) + { + std::string strAddr = CBitcoinAddress(redeemScript.GetID()).ToString(); + printf("LoadCScript() : Warning: This wallet contains a redeemScript of size %" PRIszu " which exceeds maximum size %i thus can never be redeemed. Do not use address %s.\n", + redeemScript.size(), MAX_SCRIPT_ELEMENT_SIZE, strAddr.c_str()); + return true; + } + + return CCryptoKeyStore::AddCScript(redeemScript); +} + + +bool CWallet::AddWatchOnly(const CScript &dest) +{ + if (!CCryptoKeyStore::AddWatchOnly(dest)) + return false; + nTimeFirstKey = 1; // No birthday information for watch-only keys. + NotifyWatchonlyChanged(true); + if (!fFileBacked) + return true; + return CWalletDB(strWalletFile).WriteWatchOnly(dest); +} + +bool CWallet::RemoveWatchOnly(const CScript &dest) +{ + LOCK(cs_wallet); + if (!CCryptoKeyStore::RemoveWatchOnly(dest)) + return false; + if (!HaveWatchOnly()) + NotifyWatchonlyChanged(false); + if (fFileBacked) + if (!CWalletDB(strWalletFile).EraseWatchOnly(dest)) + return false; + + return true; +} + +bool CWallet::LoadWatchOnly(const CScript &dest) +{ + return CCryptoKeyStore::AddWatchOnly(dest); +} + +// ppcoin: optional setting to unlock wallet for block minting only; +// serves to disable the trivial sendmoney when OS account compromised +bool fWalletUnlockMintOnly = false; + +bool CWallet::Unlock(const SecureString& strWalletPassphrase) +{ + if (!IsLocked()) + return false; + + CCrypter crypter; + CKeyingMaterial vMasterKey; + + { + LOCK(cs_wallet); + BOOST_FOREACH(const MasterKeyMap::value_type& pMasterKey, mapMasterKeys) + { + if(!crypter.SetKeyFromPassphrase(strWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) + return false; + if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) + return false; + if (CCryptoKeyStore::Unlock(vMasterKey)) + return true; + } + } + return false; +} + +bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase) +{ + bool fWasLocked = IsLocked(); + + { + LOCK(cs_wallet); + Lock(); + + CCrypter crypter; + CKeyingMaterial vMasterKey; + BOOST_FOREACH(MasterKeyMap::value_type& pMasterKey, mapMasterKeys) + { + if(!crypter.SetKeyFromPassphrase(strOldWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) + return false; + if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) + return false; + if (CCryptoKeyStore::Unlock(vMasterKey)) + { + int64_t nStartTime = GetTimeMillis(); + crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); + double nFirstMultiplier = 1e2 / (GetTimeMillis() - nStartTime); + pMasterKey.second.nDeriveIterations = (uint32_t)(pMasterKey.second.nDeriveIterations *nFirstMultiplier); + + nStartTime = GetTimeMillis(); + crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); + double nSecondMultiplier = 1e2 / (GetTimeMillis() - nStartTime); + pMasterKey.second.nDeriveIterations = (uint32_t)((pMasterKey.second.nDeriveIterations + pMasterKey.second.nDeriveIterations * nSecondMultiplier) / 2); + + if (pMasterKey.second.nDeriveIterations < 25000) + pMasterKey.second.nDeriveIterations = 25000; + + printf("Wallet passphrase changed to an nDeriveIterations of %i\n", pMasterKey.second.nDeriveIterations); + + if (!crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) + return false; + if (!crypter.Encrypt(vMasterKey, pMasterKey.second.vchCryptedKey)) + return false; + CWalletDB(strWalletFile).WriteMasterKey(pMasterKey.first, pMasterKey.second); + if (fWasLocked) + Lock(); + return true; + } + } + } + + return false; +} + +void CWallet::SetBestChain(const CBlockLocator& loc) +{ + CWalletDB walletdb(strWalletFile); + walletdb.WriteBestBlock(loc); +} + +// This class implements an addrIncoming entry that causes pre-0.4 +// clients to crash on startup if reading a private-key-encrypted wallet. +class CCorruptAddress +{ +public: + IMPLEMENT_SERIALIZE + ( + if (nType & SER_DISK) + READWRITE(nVersion); + ) +}; + +bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn, bool fExplicit) +{ + if (nWalletVersion >= nVersion) + return true; + + // when doing an explicit upgrade, if we pass the max version permitted, upgrade all the way + if (fExplicit && nVersion > nWalletMaxVersion) + nVersion = FEATURE_LATEST; + + nWalletVersion = nVersion; + + if (nVersion > nWalletMaxVersion) + nWalletMaxVersion = nVersion; + + if (fFileBacked) + { + CWalletDB* pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(strWalletFile); + if (nWalletVersion > 40000) + pwalletdb->WriteMinVersion(nWalletVersion); + if (!pwalletdbIn) + delete pwalletdb; + } + + return true; +} + +bool CWallet::SetMaxVersion(int nVersion) +{ + // cannot downgrade below current version + if (nWalletVersion > nVersion) + return false; + + nWalletMaxVersion = nVersion; + + return true; +} + +bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) +{ + if (IsCrypted()) + return false; + + CKeyingMaterial vMasterKey; + RandAddSeedPerfmon(); + + vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE); + RAND_bytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE); + + CMasterKey kMasterKey; + + RandAddSeedPerfmon(); + kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE); + RAND_bytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE); + + CCrypter crypter; + int64_t nStartTime = GetTimeMillis(); + crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod); + int64_t nDivider = GetTimeMillis() - nStartTime; + kMasterKey.nDeriveIterations = (uint32_t)(25e5 / (double)(nDivider)); + + nStartTime = GetTimeMillis(); + crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod); + double nMultiplier = 1e2 / (GetTimeMillis() - nStartTime); + kMasterKey.nDeriveIterations = (uint32_t)((kMasterKey.nDeriveIterations + kMasterKey.nDeriveIterations * nMultiplier) / 2); + + if (kMasterKey.nDeriveIterations < 25000) + kMasterKey.nDeriveIterations = 25000; + + printf("Encrypting Wallet with an nDeriveIterations of %i\n", kMasterKey.nDeriveIterations); + + if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod)) + return false; + if (!crypter.Encrypt(vMasterKey, kMasterKey.vchCryptedKey)) + return false; + + { + LOCK(cs_wallet); + mapMasterKeys[++nMasterKeyMaxID] = kMasterKey; + if (fFileBacked) + { + pwalletdbEncryption = new CWalletDB(strWalletFile); + if (!pwalletdbEncryption->TxnBegin()) + return false; + pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey); + } + + if (!EncryptKeys(vMasterKey)) + { + if (fFileBacked) + pwalletdbEncryption->TxnAbort(); + exit(1); //We now probably have half of our keys encrypted in memory, and half not...die and let the user reload their unencrypted wallet. + } + + // Encryption was introduced in version 0.4.0 + SetMinVersion(FEATURE_WALLETCRYPT, pwalletdbEncryption, true); + + if (fFileBacked) + { + if (!pwalletdbEncryption->TxnCommit()) + exit(1); //We now have keys encrypted in memory, but no on disk...die to avoid confusion and let the user reload their unencrypted wallet. + + delete pwalletdbEncryption; + pwalletdbEncryption = NULL; + } + + Lock(); + Unlock(strWalletPassphrase); + NewKeyPool(); + Lock(); + + // Need to completely rewrite the wallet file; if we don't, bdb might keep + // bits of the unencrypted private key in slack space in the database file. + CDB::Rewrite(strWalletFile); + + } + NotifyStatusChanged(this); + + return true; +} + +bool CWallet::DecryptWallet(const SecureString& strWalletPassphrase) +{ + if (!IsCrypted()) + return false; + + CCrypter crypter; + CKeyingMaterial vMasterKey; + + { + LOCK(cs_wallet); + BOOST_FOREACH(const MasterKeyMap::value_type& pMasterKey, mapMasterKeys) + { + if(!crypter.SetKeyFromPassphrase(strWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) + return false; + if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) + return false; + if (!CCryptoKeyStore::Unlock(vMasterKey)) + return false; + } + + if (fFileBacked) + { + pwalletdbDecryption = new CWalletDB(strWalletFile); + if (!pwalletdbDecryption->TxnBegin()) + return false; + } + + if (!DecryptKeys(vMasterKey)) + { + if (fFileBacked) + pwalletdbDecryption->TxnAbort(); + exit(1); //We now probably have half of our keys decrypted in memory, and half not...die and let the user reload their encrypted wallet. + } + + if (fFileBacked) + { + // Overwrite crypted keys + KeyMap::const_iterator mi = mapKeys.begin(); + while (mi != mapKeys.end()) + { + CKey key; + key.SetSecret((*mi).second.first, (*mi).second.second); + pwalletdbDecryption->EraseCryptedKey(key.GetPubKey()); + pwalletdbDecryption->WriteKey(key.GetPubKey(), key.GetPrivKey(), mapKeyMetadata[(*mi).first]); + mi++; + } + + // Erase master keys + MasterKeyMap::const_iterator mk = mapMasterKeys.begin(); + while (mk != mapMasterKeys.end()) + { + pwalletdbDecryption->EraseMasterKey((*mk).first); + mk++; + } + + if (!pwalletdbDecryption->TxnCommit()) + exit(1); //We now have keys decrypted in memory, but no on disk...die to avoid confusion and let the user reload their encrypted wallet. + + delete pwalletdbDecryption; + pwalletdbDecryption = NULL; + } + + // Need to completely rewrite the wallet file; if we don't, bdb might keep + // encrypted private keys in the database file which can be a reason of consistency issues. + CDB::Rewrite(strWalletFile); + } + NotifyStatusChanged(this); + + return true; +} + +int64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb) +{ + int64_t nRet = nOrderPosNext++; + if (pwalletdb) { + pwalletdb->WriteOrderPosNext(nOrderPosNext); + } else { + CWalletDB(strWalletFile).WriteOrderPosNext(nOrderPosNext); + } + return nRet; +} + +CWallet::TxItems CWallet::OrderedTxItems(std::list& acentries, std::string strAccount) +{ + CWalletDB walletdb(strWalletFile); + + // First: get all CWalletTx and CAccountingEntry into a sorted-by-order multimap. + TxItems txOrdered; + + // Note: maintaining indices in the database of (account,time) --> txid and (account, time) --> acentry + // would make this much faster for applications that do this a lot. + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + CWalletTx* wtx = &((*it).second); + txOrdered.insert(make_pair(wtx->nOrderPos, TxPair(wtx, (CAccountingEntry*)0))); + } + acentries.clear(); + walletdb.ListAccountCreditDebit(strAccount, acentries); + BOOST_FOREACH(CAccountingEntry& entry, acentries) + { + txOrdered.insert(make_pair(entry.nOrderPos, TxPair((CWalletTx*)0, &entry))); + } + + return txOrdered; +} + +void CWallet::WalletUpdateSpent(const CTransaction &tx, bool fBlock) +{ + // 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. + { + LOCK(cs_wallet); + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + map::iterator mi = mapWallet.find(txin.prevout.hash); + if (mi != mapWallet.end()) + { + CWalletTx& wtx = (*mi).second; + if (txin.prevout.n >= wtx.vout.size()) + printf("WalletUpdateSpent: bad wtx %s\n", wtx.GetHash().ToString().c_str()); + else if (!wtx.IsSpent(txin.prevout.n) && IsMine(wtx.vout[txin.prevout.n])) + { + printf("WalletUpdateSpent found spent coin %sXP %s\n", FormatMoney(wtx.GetCredit(MINE_ALL)).c_str(), wtx.GetHash().ToString().c_str()); + wtx.MarkSpent(txin.prevout.n); + wtx.WriteToDisk(); + NotifyTransactionChanged(this, txin.prevout.hash, CT_UPDATED); + vMintingWalletUpdated.push_back(txin.prevout.hash); + } + } + } + + if (fBlock) + { + uint256 hash = tx.GetHash(); + map::iterator mi = mapWallet.find(hash); + CWalletTx& wtx = (*mi).second; + + BOOST_FOREACH(const CTxOut& txout, tx.vout) + { + if (IsMine(txout)) + { + wtx.MarkUnspent(&txout - &tx.vout[0]); + wtx.WriteToDisk(); + NotifyTransactionChanged(this, hash, CT_UPDATED); + vMintingWalletUpdated.push_back(hash); + } + } + } + + } +} + +void CWallet::MarkDirty() +{ + { + LOCK(cs_wallet); + BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) + item.second.MarkDirty(); + } +} + +bool CWallet::AddToWallet(const CWalletTx& wtxIn) +{ + uint256 hash = wtxIn.GetHash(); + { + LOCK(cs_wallet); + // 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.BindWallet(this); + bool fInsertedNew = ret.second; + if (fInsertedNew) + { + wtx.nTimeReceived = GetAdjustedTime(); + wtx.nOrderPos = IncOrderPosNext(); + + wtx.nTimeSmart = wtx.nTimeReceived; + if (wtxIn.hashBlock != 0) + { + if (mapBlockIndex.count(wtxIn.hashBlock)) + { + unsigned int latestNow = wtx.nTimeReceived; + unsigned int latestEntry = 0; + { + // Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future + int64_t latestTolerated = latestNow + 300; + std::list acentries; + TxItems txOrdered = OrderedTxItems(acentries); + for (TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) + { + CWalletTx *const pwtx = (*it).second.first; + if (pwtx == &wtx) + continue; + CAccountingEntry *const pacentry = (*it).second.second; + int64_t nSmartTime; + if (pwtx) + { + nSmartTime = pwtx->nTimeSmart; + if (!nSmartTime) + nSmartTime = pwtx->nTimeReceived; + } + else + nSmartTime = pacentry->nTime; + if (nSmartTime <= latestTolerated) + { + latestEntry = nSmartTime; + if (nSmartTime > latestNow) + latestNow = nSmartTime; + break; + } + } + } + + unsigned int& blocktime = mapBlockIndex[wtxIn.hashBlock]->nTime; + wtx.nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow)); + } + else + printf("AddToWallet() : found %s in block %s not in index\n", + wtxIn.GetHash().ToString().substr(0,10).c_str(), + wtxIn.hashBlock.ToString().c_str()); + } + } + + 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; +#ifndef QT_GUI + // If default receiving address gets used, replace it with a new one + CScript scriptDefaultKey; + scriptDefaultKey.SetDestination(vchDefaultKey.GetID()); + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + { + if (txout.scriptPubKey == scriptDefaultKey) + { + CPubKey newDefaultKey; + if (GetKeyFromPool(newDefaultKey, false)) + { + SetDefaultKey(newDefaultKey); + SetAddressBookName(vchDefaultKey.GetID(), ""); + } + } + } +#endif + // since AddToWallet is called directly for self-originating transactions, check for consumption of own coins + WalletUpdateSpent(wtx, (wtxIn.hashBlock != 0)); + + // Notify UI of new or updated transaction + NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED); + vMintingWalletUpdated.push_back(hash); + // notify an external script when a wallet transaction comes in or is updated + std::string strCmd = GetArg("-walletnotify", ""); + + if ( !strCmd.empty()) + { + boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); + boost::thread t(runCommand, strCmd); // thread runs free + } + + } + return true; +} + +// Add a transaction to the wallet, or update it. +// pblock is optional, but should be provided if the transaction is known to be in a block. +// If fUpdate is true, existing transactions will be updated. +bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fFindBlock) +{ + uint256 hash = tx.GetHash(); + { + LOCK(cs_wallet); + bool fExisted = mapWallet.count(hash) != 0; + 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; + { + LOCK(cs_wallet); + if (mapWallet.erase(hash)) + CWalletDB(strWalletFile).EraseTx(hash); + } + return true; +} + + +isminetype CWallet::IsMine(const CTxIn &txin) const +{ + { + LOCK(cs_wallet); + 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()) + return IsMine(prev.vout[txin.prevout.n]); + } + } + return MINE_NO; +} + +int64_t CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const +{ + { + LOCK(cs_wallet); + 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]) & filter) + return prev.vout[txin.prevout.n].nValue; + } + } + return 0; +} + +bool CWallet::IsChange(const CTxOut& txout) const +{ + // TODO: fix handling of 'change' outputs. The assumption is that any + // payment to a script that is ours, but isn't in the address book + // is change. That assumption is likely to break when we implement multisignature + // wallets that return change back into a multi-signature-protected address; + // a better way of identifying which outputs are 'the send' and which are + // 'the change' will need to be implemented (maybe extend CWalletTx to remember + // which output, if any, was change). + if (::IsMine(*this, txout.scriptPubKey)) + { + CTxDestination address; + if (!ExtractDestination(txout.scriptPubKey, address)) + return true; + + LOCK(cs_wallet); + if (!mapAddressBook.count(address)) + return true; + } + return false; +} + +int64_t CWalletTx::GetTxTime() const +{ + return nTime; +} + +int CWalletTx::GetRequestCount() const +{ + // Returns -1 if it wasn't being tracked + int nRequests = -1; + { + LOCK(pwallet->cs_wallet); + if (IsCoinBase() || IsCoinStake()) + { + // 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_t& nGeneratedImmature, int64_t& nGeneratedMature, list >& listReceived, + list >& listSent, int64_t& nFee, string& strSentAccount, const isminefilter& filter) const +{ + nGeneratedImmature = nGeneratedMature = nFee = 0; + listReceived.clear(); + listSent.clear(); + strSentAccount = strFromAccount; + + if (IsCoinBase() || IsCoinStake()) + { + if (GetBlocksToMaturity() > 0) + nGeneratedImmature = pwallet->GetCredit(*this, filter); + else + nGeneratedMature = GetCredit(filter); + return; + } + + // Compute fee: + int64_t nDebit = GetDebit(filter); + if (nDebit > 0) // debit>0 means we signed/sent this transaction + { + int64_t nValueOut = GetValueOut(); + nFee = nDebit - nValueOut; + } + + // Sent/received. + BOOST_FOREACH(const CTxOut& txout, vout) + { + isminetype fIsMine = pwallet->IsMine(txout); + // Only need to handle txouts if AT LEAST one of these is true: + // 1) they debit from us (sent) + // 2) the output is to us (received) + if (nDebit > 0) + { + // Don't report 'change' txouts + if (pwallet->IsChange(txout)) + continue; + } + else if (!(fIsMine & filter)) + continue; + + // In either case, we need to get the destination address + CTxDestination address; + if (!ExtractDestination(txout.scriptPubKey, address)) + { + printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", + this->GetHash().ToString().c_str()); + address = CNoDestination(); + } + + // If we are debited by the transaction, add the output as a "sent" entry + if (nDebit > 0) + listSent.push_back(make_pair(address, txout.nValue)); + + // If we are receiving the output, add it as a "received" entry + if (fIsMine & filter) + listReceived.push_back(make_pair(address, txout.nValue)); + } + +} + +void CWalletTx::GetAccountAmounts(const string& strAccount, int64_t& nGenerated, int64_t& nReceived, + int64_t& nSent, int64_t& nFee, const isminefilter& filter) const +{ + nGenerated = nReceived = nSent = nFee = 0; + + int64_t allGeneratedImmature, allGeneratedMature, allFee; + allGeneratedImmature = allGeneratedMature = allFee = 0; + string strSentAccount; + list > listReceived; + list > listSent; + GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount, filter); + + if (strAccount == "") + nGenerated = allGeneratedMature; + if (strAccount == strSentAccount) + { + BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& s, listSent) + nSent += s.second; + nFee = allFee; + } + { + LOCK(pwallet->cs_wallet); + BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& 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 + { + LOCK(pwallet->cs_wallet); + map mapWalletPrev; + set setAlreadyDone; + for (unsigned 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); +} + +// Scan the block chain (starting in pindexStart) for transactions +// from or to us. If fUpdate is true, found transactions that already +// exist in the wallet will be updated. +int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) +{ + int ret = 0; + + CBlockIndex* pindex = pindexStart; + { + LOCK(cs_wallet); + 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; +} + +int CWallet::ScanForWalletTransaction(const uint256& hashTx) +{ + CTransaction tx; + tx.ReadFromDisk(COutPoint(hashTx, 0)); + if (AddToWalletIfInvolvingMe(tx, NULL, true, true)) + return 1; + return 0; +} + +void CWallet::ReacceptWalletTransactions() +{ + CTxDB txdb("r"); + bool fRepeat = true; + while (fRepeat) + { + LOCK(cs_wallet); + fRepeat = false; + vector vMissingTx; + BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) + { + CWalletTx& wtx = item.second; + if ((wtx.IsCoinBase() && wtx.IsSpent(0)) || (wtx.IsCoinStake() && wtx.IsSpent(1))) + 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() %" PRIszu " != wtx.vout.size() %" PRIszu "\n", txindex.vSpent.size(), wtx.vout.size()); + continue; + } + for (unsigned 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 %sXP %s\n", FormatMoney(wtx.GetCredit(MINE_ALL)).c_str(), wtx.GetHash().ToString().c_str()); + wtx.MarkDirty(); + wtx.WriteToDisk(); + } + } + else + { + // Re-accept any txes of ours that aren't already in a block + if (!(wtx.IsCoinBase() || wtx.IsCoinStake())) + 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 re-accept. + } + } +} + +void CWalletTx::RelayWalletTransaction(CTxDB& txdb) +{ + BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) + { + if (!(tx.IsCoinBase() || tx.IsCoinStake())) + { + uint256 hash = tx.GetHash(); + if (!txdb.ContainsTx(hash)) + RelayTransaction((CTransaction)tx, hash); + } + } + if (!(IsCoinBase() || IsCoinStake())) + { + uint256 hash = GetHash(); + if (!txdb.ContainsTx(hash)) + { + printf("Relaying wtx %s\n", hash.ToString().substr(0,10).c_str()); + RelayTransaction((CTransaction)*this, hash); + } + } +} + +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_t nNextTime = GetRand(GetTime() + 30 * 60); + 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_t nLastTime = 0; + if (nTimeBestReceived < nLastTime) + return; + nLastTime = GetTime(); + + // Rebroadcast any of our txes that aren't in a block yet + printf("ResendWalletTransactions()\n"); + CTxDB txdb("r"); + { + LOCK(cs_wallet); + // 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_t)wtx.nTimeReceived > 5 * 60) + mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx)); + } + BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted) + { + CWalletTx& wtx = *item.second; + if (wtx.CheckTransaction()) + wtx.RelayWalletTransaction(txdb); + else + printf("ResendWalletTransactions() : CheckTransaction failed for transaction %s\n", wtx.GetHash().ToString().c_str()); + } + } +} + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// Actions +// + + +int64_t CWallet::GetBalance() const +{ + int64_t nTotal = 0; + { + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (pcoin->IsTrusted()) + nTotal += pcoin->GetAvailableCredit(); + } + } + + return nTotal; +} + +int64_t CWallet::GetWatchOnlyBalance() const +{ + int64_t nTotal = 0; + { + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (pcoin->IsTrusted()) + nTotal += pcoin->GetAvailableWatchCredit(); + } + } + + return nTotal; +} + +int64_t CWallet::GetUnconfirmedBalance() const +{ + int64_t nTotal = 0; + { + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (!pcoin->IsFinal() || !pcoin->IsTrusted()) + nTotal += pcoin->GetAvailableCredit(); + } + } + return nTotal; +} + +int64_t CWallet::GetUnconfirmedWatchOnlyBalance() const +{ + int64_t nTotal = 0; + { + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (!pcoin->IsFinal() || !pcoin->IsTrusted()) + nTotal += pcoin->GetAvailableWatchCredit(); + } + } + return nTotal; +} + +int64_t CWallet::GetImmatureBalance() const +{ + int64_t nTotal = 0; + { + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + nTotal += pcoin->GetImmatureCredit(); + } + } + return nTotal; +} + +int64_t CWallet::GetImmatureWatchOnlyBalance() const +{ + int64_t nTotal = 0; + { + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + nTotal += pcoin->GetImmatureWatchOnlyCredit(); + } + } + return nTotal; +} + +// populate vCoins with vector of spendable COutputs +void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl) const +{ + vCoins.clear(); + + { + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + + if (!pcoin->IsFinal()) + continue; + + if (fOnlyConfirmed && !pcoin->IsTrusted()) + continue; + + if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) + continue; + + if(pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0) + continue; + + for (unsigned int i = 0; i < pcoin->vout.size(); i++) { + isminetype mine = IsMine(pcoin->vout[i]); + if (!(pcoin->IsSpent(i)) && mine != MINE_NO && + pcoin->vout[i].nValue >= nMinimumInputValue && + (!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i))) + { + vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain(), mine == MINE_SPENDABLE)); + } + } + } + } +} + +void CWallet::AvailableCoinsMinConf(vector& vCoins, int nConf, int64_t nMinValue, int64_t nMaxValue) const +{ + vCoins.clear(); + + { + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + + if (!pcoin->IsFinal()) + continue; + + if(pcoin->GetDepthInMainChain() < nConf) + continue; + + for (unsigned int i = 0; i < pcoin->vout.size(); i++) { + isminetype mine = IsMine(pcoin->vout[i]); + + // ignore coin if it was already spent or we don't own it + if (pcoin->IsSpent(i) || mine == MINE_NO) + continue; + + // if coin value is between required limits then add new item to vector + if (pcoin->vout[i].nValue >= nMinValue && pcoin->vout[i].nValue < nMaxValue) + vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain(), mine == MINE_SPENDABLE)); + } + } + } +} + +static void ApproximateBestSubset(vector > >vValue, int64_t nTotalLower, int64_t nTargetValue, + vector& vfBest, int64_t& nBest, int iterations = 1000) +{ + vector vfIncluded; + + vfBest.assign(vValue.size(), true); + nBest = nTotalLower; + + for (int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++) + { + vfIncluded.assign(vValue.size(), false); + int64_t nTotal = 0; + bool fReachedTarget = false; + for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++) + { + for (unsigned 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; + } + } + } + } + } +} + +int64_t CWallet::GetStake() const +{ + int64_t nTotal = 0; + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0) + nTotal += CWallet::GetCredit(*pcoin, MINE_ALL); + } + return nTotal; +} + +int64_t CWallet::GetWatchOnlyStake() const +{ + int64_t nTotal = 0; + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0) + nTotal += CWallet::GetCredit(*pcoin, MINE_WATCH_ONLY); + } + return nTotal; +} + +int64_t CWallet::GetNewMint() const +{ + int64_t nTotal = 0; + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0) + nTotal += CWallet::GetCredit(*pcoin, MINE_ALL); + } + return nTotal; +} + +int64_t CWallet::GetWatchOnlyNewMint() const +{ + int64_t nTotal = 0; + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0) + nTotal += CWallet::GetCredit(*pcoin, MINE_WATCH_ONLY); + } + return nTotal; +} + +bool CWallet::SelectCoinsMinConf(int64_t nTargetValue, unsigned int nSpendTime, int nConfMine, int nConfTheirs, vector vCoins, set >& setCoinsRet, int64_t& nValueRet) const +{ + setCoinsRet.clear(); + nValueRet = 0; + + // List of values less than target + pair > coinLowestLarger; + coinLowestLarger.first = std::numeric_limits::max(); + coinLowestLarger.second.first = NULL; + vector > > vValue; + int64_t nTotalLower = 0; + + random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); + + BOOST_FOREACH(const COutput &output, vCoins) + { + if (!output.fSpendable) + continue; + + const CWalletTx *pcoin = output.tx; + + if (output.nDepth < (pcoin->IsFromMe(MINE_ALL) ? nConfMine : nConfTheirs)) + continue; + + int i = output.i; + + // Follow the timestamp rules + if (pcoin->nTime > nSpendTime) + continue; + + int64_t n = pcoin->vout[i].nValue; + + 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) + { + for (unsigned int i = 0; i < vValue.size(); ++i) + { + setCoinsRet.insert(vValue[i].second); + nValueRet += vValue[i].first; + } + return true; + } + + if (nTotalLower < nTargetValue) + { + if (coinLowestLarger.second.first == NULL) + return false; + setCoinsRet.insert(coinLowestLarger.second); + nValueRet += coinLowestLarger.first; + return true; + } + + // Solve subset sum by stochastic approximation + sort(vValue.rbegin(), vValue.rend(), CompareValueOnly()); + vector vfBest; + int64_t nBest; + + ApproximateBestSubset(vValue, nTotalLower, nTargetValue, vfBest, nBest, 1000); + if (nBest != nTargetValue && nTotalLower >= nTargetValue + CENT) + ApproximateBestSubset(vValue, nTotalLower, nTargetValue + CENT, vfBest, nBest, 1000); + + // If we have a bigger coin and (either the stochastic approximation didn't find a good solution, + // or the next bigger coin is closer), return the bigger coin + if (coinLowestLarger.second.first && + ((nBest != nTargetValue && nBest < nTargetValue + CENT) || coinLowestLarger.first <= nBest)) + { + setCoinsRet.insert(coinLowestLarger.second); + nValueRet += coinLowestLarger.first; + } + else { + for (unsigned int i = 0; i < vValue.size(); i++) + if (vfBest[i]) + { + setCoinsRet.insert(vValue[i].second); + nValueRet += vValue[i].first; + } + + if (fDebug && GetBoolArg("-printpriority")) + { + //// debug print + printf("SelectCoins() best subset: "); + for (unsigned 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_t nTargetValue, unsigned int nSpendTime, set >& setCoinsRet, int64_t& nValueRet, const CCoinControl* coinControl) const +{ + vector vCoins; + AvailableCoins(vCoins, true, coinControl); + + // coin control -> return all selected outputs (we want all selected to go into the transaction for sure) + if (coinControl && coinControl->HasSelected()) + { + BOOST_FOREACH(const COutput& out, vCoins) + { + if(!out.fSpendable) + continue; + nValueRet += out.tx->vout[out.i].nValue; + setCoinsRet.insert(make_pair(out.tx, out.i)); + } + return (nValueRet >= nTargetValue); + } + + return (SelectCoinsMinConf(nTargetValue, nSpendTime, 1, 6, vCoins, setCoinsRet, nValueRet) || + SelectCoinsMinConf(nTargetValue, nSpendTime, 1, 1, vCoins, setCoinsRet, nValueRet) || + SelectCoinsMinConf(nTargetValue, nSpendTime, 0, 1, vCoins, setCoinsRet, nValueRet)); +} + +// Select some coins without random shuffle or best subset approximation +bool CWallet::SelectCoinsSimple(int64_t nTargetValue, int64_t nMinValue, int64_t nMaxValue, unsigned int nSpendTime, int nMinConf, set >& setCoinsRet, int64_t& nValueRet) const +{ + vector vCoins; + AvailableCoinsMinConf(vCoins, nMinConf, nMinValue, nMaxValue); + + setCoinsRet.clear(); + nValueRet = 0; + + BOOST_FOREACH(COutput output, vCoins) + { + if(!output.fSpendable) + continue; + const CWalletTx *pcoin = output.tx; + int i = output.i; + + // Ignore immature coins + if (pcoin->GetBlocksToMaturity() > 0) + continue; + + // Stop if we've chosen enough inputs + if (nValueRet >= nTargetValue) + break; + + // Follow the timestamp rules + if (pcoin->nTime > nSpendTime) + continue; + + int64_t n = pcoin->vout[i].nValue; + + pair > coin = make_pair(n,make_pair(pcoin, i)); + + if (n >= nTargetValue) + { + // If input value is greater or equal to target then simply insert + // it into the current subset and exit + setCoinsRet.insert(coin.second); + nValueRet += coin.first; + break; + } + else if (n < nTargetValue + CENT) + { + setCoinsRet.insert(coin.second); + nValueRet += coin.first; + } + } + + return true; +} + +bool CWallet::CreateTransaction(const vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl* coinControl) +{ + int64_t nValue = 0; + BOOST_FOREACH (const PAIRTYPE(CScript, int64_t)& s, vecSend) + { + if (nValue < 0) + return false; + nValue += s.second; + } + if (vecSend.empty() || nValue < 0) + return false; + + wtxNew.BindWallet(this); + + { + LOCK2(cs_main, cs_wallet); + // txdb must be opened before the mapWallet lock + CTxDB txdb("r"); + { + nFeeRet = nTransactionFee; + while (true) + { + wtxNew.vin.clear(); + wtxNew.vout.clear(); + wtxNew.fFromMe = true; + + int64_t nTotalValue = nValue + nFeeRet; + double dPriority = 0; + // vouts to the payees + BOOST_FOREACH (const PAIRTYPE(CScript, int64_t)& s, vecSend) + wtxNew.vout.push_back(CTxOut(s.second, s.first)); + + // Choose coins to use + set > setCoins; + int64_t nValueIn = 0; + if (!SelectCoins(nTotalValue, wtxNew.nTime, setCoins, nValueIn, coinControl)) + return false; + BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) + { + int64_t nCredit = pcoin.first->vout[pcoin.second].nValue; + dPriority += (double)nCredit * pcoin.first->GetDepthInMainChain(); + } + + int64_t nChange = nValueIn - nValue - nFeeRet; + if (nChange > 0) + { + // Fill a vout to ourself + // TODO: pass in scriptChange instead of reservekey so + // change transaction isn't always pay-to-bitcoin-address + CScript scriptChange; + + // coin control: send change to custom address + if (coinControl && !boost::get(&coinControl->destChange)) + scriptChange.SetDestination(coinControl->destChange); + + // no coin control: send change to newly generated address + else + { + // 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 + CPubKey vchPubKey = reservekey.GetReservedKey(); + + scriptChange.SetDestination(vchPubKey.GetID()); + } + + // 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, PROTOCOL_VERSION); + if (nBytes >= MAX_BLOCK_SIZE_GEN/5) + return false; + dPriority /= nBytes; + + // Check that enough fee is included + bool fAllowFree = CTransaction::AllowFree(dPriority); + int64_t nPayFee = nTransactionFee * (1 + (int64_t)nBytes / 1000); + int64_t nMinFee = wtxNew.GetMinFee(1, fAllowFree, GMF_SEND, nBytes); + + 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_t nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl* coinControl) +{ + vector< pair > vecSend; + vecSend.push_back(make_pair(scriptPubKey, nValue)); + return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet, coinControl); +} + +void CWallet::GetStakeWeightFromValue(const int64_t& nTime, const int64_t& nValue, uint64_t& nWeight) +{ + int64_t nTimeWeight = GetWeight(nTime, (int64_t)GetTime()); + + // If time weight is lower or equal to zero then weight is zero. + if (nTimeWeight <= 0) + { + nWeight = 0; + return; + } + + CBigNum bnCoinDayWeight = CBigNum(nValue) * nTimeWeight / COIN / nOneDay; + nWeight = bnCoinDayWeight.getuint64(); +} + +bool CWallet::MergeCoins(const int64_t& nAmount, const int64_t& nMinValue, const int64_t& nOutputValue, list& listMerged) +{ + int64_t nBalance = GetBalance(); + + if (nAmount > nBalance) + return false; + + listMerged.clear(); + int64_t nValueIn = 0; + set > setCoins; + + // Simple coins selection - no randomization + if (!SelectCoinsSimple(nAmount, nMinValue, nOutputValue, GetTime(), 1, setCoins, nValueIn)) + return false; + + if (setCoins.empty()) + return false; + + CWalletTx wtxNew; + vector vwtxPrev; + + // Reserve a new key pair from key pool + CReserveKey reservekey(this); + CPubKey vchPubKey = reservekey.GetReservedKey(); + + // Output script + CScript scriptOutput; + scriptOutput.SetDestination(vchPubKey.GetID()); + + // Insert output + wtxNew.vout.push_back(CTxOut(0, scriptOutput)); + + double dWeight = 0; + BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) + { + int64_t nCredit = pcoin.first->vout[pcoin.second].nValue; + + // Add current coin to inputs list and add its credit to transaction output + wtxNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second)); + wtxNew.vout[0].nValue += nCredit; + vwtxPrev.push_back(pcoin.first); + +/* + // Replaced with estimation for performance purposes + + for (unsigned int i = 0; i < wtxNew.vin.size(); i++) { + const CWalletTx *txin = vwtxPrev[i]; + + // Sign scripts to get actual transaction size for fee calculation + if (!SignSignature(*this, *txin, wtxNew, i)) + return false; + } +*/ + + // Assuming that average scriptsig size is 110 bytes + int64_t nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK, PROTOCOL_VERSION) + wtxNew.vin.size() * 110; + dWeight += (double)nCredit * pcoin.first->GetDepthInMainChain(); + + double dFinalPriority = dWeight /= nBytes; + bool fAllowFree = CTransaction::AllowFree(dFinalPriority); + + // Get actual transaction fee according to its estimated size and priority + int64_t nMinFee = wtxNew.GetMinFee(1, fAllowFree, GMF_SEND, nBytes); + + // Prepare transaction for commit if sum is enough ot its size is too big + if (nBytes >= MAX_BLOCK_SIZE_GEN/6 || wtxNew.vout[0].nValue >= nOutputValue) + { + wtxNew.vout[0].nValue -= nMinFee; // Set actual fee + + for (unsigned int i = 0; i < wtxNew.vin.size(); i++) { + const CWalletTx *txin = vwtxPrev[i]; + + // Sign all scripts + if (!SignSignature(*this, *txin, wtxNew, i)) + return false; + } + + // Try to commit, return false on failure + if (!CommitTransaction(wtxNew, reservekey)) + return false; + + listMerged.push_back(wtxNew.GetHash()); // Add to hashes list + + dWeight = 0; // Reset all temporary values + vwtxPrev.clear(); + wtxNew.SetNull(); + wtxNew.vout.push_back(CTxOut(0, scriptOutput)); + } + } + + // Create transactions if there are some unhandled coins left + if (wtxNew.vout[0].nValue > 0) { + int64_t nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK, PROTOCOL_VERSION) + wtxNew.vin.size() * 110; + + double dFinalPriority = dWeight /= nBytes; + bool fAllowFree = CTransaction::AllowFree(dFinalPriority); + + // Get actual transaction fee according to its size and priority + int64_t nMinFee = wtxNew.GetMinFee(1, fAllowFree, GMF_SEND, nBytes); + + wtxNew.vout[0].nValue -= nMinFee; // Set actual fee + + if (wtxNew.vout[0].nValue <= 0) + return false; + + for (unsigned int i = 0; i < wtxNew.vin.size(); i++) { + const CWalletTx *txin = vwtxPrev[i]; + + // Sign all scripts again + if (!SignSignature(*this, *txin, wtxNew, i)) + return false; + } + + // Try to commit, return false on failure + if (!CommitTransaction(wtxNew, reservekey)) + return false; + + listMerged.push_back(wtxNew.GetHash()); // Add to hashes list + } + + return true; +} + +bool CWallet::CreateCoinStake(uint256 &hashTx, uint32_t nOut, uint32_t nGenerationTime, uint32_t nBits, CTransaction &txNew, CKey& key) +{ + CWalletTx wtx; + if (!GetTransaction(hashTx, wtx)) + return error("Transaction %s is not found\n", hashTx.GetHex().c_str()); + + vector vSolutions; + txnouttype whichType; + CScript scriptPubKeyOut; + CScript scriptPubKeyKernel = wtx.vout[nOut].scriptPubKey; + if (!Solver(scriptPubKeyKernel, whichType, vSolutions)) + return error("CreateCoinStake : failed to parse kernel\n"); + + if (fDebug && GetBoolArg("-printcoinstake")) + printf("CreateCoinStake : parsed kernel type=%d\n", whichType); + + if (whichType != TX_PUBKEY && whichType != TX_PUBKEYHASH) + return error("CreateCoinStake : no support for kernel type=%d\n", whichType); + + if (whichType == TX_PUBKEYHASH) // pay to address type + { + // convert to pay to public key type + if (!GetKey(uint160(vSolutions[0]), key)) + return error("CreateCoinStake : failed to get key for kernel type=%d\n", whichType); + + scriptPubKeyOut << key.GetPubKey() << OP_CHECKSIG; + } + if (whichType == TX_PUBKEY) + { + valtype& vchPubKey = vSolutions[0]; + if (!GetKey(Hash160(vchPubKey), key)) + return error("CreateCoinStake : failed to get key for kernel type=%d\n", whichType); + if (key.GetPubKey() != vchPubKey) + return error("CreateCoinStake : invalid key for kernel type=%d\n", whichType); // keys mismatch + scriptPubKeyOut = scriptPubKeyKernel; + } + + // The following combine threshold is important to security + // Should not be adjusted if you don't understand the consequences + int64_t nCombineThreshold = GetProofOfWorkReward(GetLastBlockIndex(pindexBest, false)->nBits, 0, pindexBest->nHeight) / 3; + + int64_t nBalance = GetBalance(); + int64_t nCredit = wtx.vout[nOut].nValue; + + txNew.vin.clear(); + txNew.vout.clear(); + + // List of constake dependencies + vector vwtxPrev; + vwtxPrev.push_back(&wtx); + + // Set generation time, and kernel input + txNew.nTime = nGenerationTime; + txNew.vin.push_back(CTxIn(hashTx, nOut)); + + // Mark coin stake transaction with empty vout[0] + CScript scriptEmpty; + scriptEmpty.clear(); + txNew.vout.push_back(CTxOut(0, scriptEmpty)); + + if (fDebug && GetBoolArg("-printcoinstake")) + printf("CreateCoinStake : added kernel type=%d\n", whichType); + + int64_t nValueIn = 0; + CoinsSet setCoins; + if (!SelectCoinsSimple(nBalance - nReserveBalance, MIN_TX_FEE, MAX_MONEY, nGenerationTime, nCoinbaseMaturity + 10, setCoins, nValueIn)) + return false; + + if (setCoins.empty()) + return false; + + bool fMaxTimeWeight = false; + if (GetWeight((int64_t)wtx.nTime, (int64_t)nGenerationTime) == nStakeMaxAge) + { + // Only one output for old kernel inputs + txNew.vout.push_back(CTxOut(0, scriptPubKeyOut)); + + // Iterate through set of (wtx*, nout) in order to find some additional inputs for our new coinstake transaction. + // + // * Value is higher than 0.01 XP; + // * Only add inputs of the same key/address as kernel; + // * Input hash and kernel parent hash should be different. + for(CoinsSet::iterator pcoin = setCoins.begin(); pcoin != setCoins.end(); pcoin++) + { + // Stop adding more inputs if already too many inputs + if (txNew.vin.size() >= 100) + break; + // Stop adding more inputs if value is already pretty significant + if (nCredit > nCombineThreshold) + break; + // Stop adding inputs if reached reserve limit + if (nCredit + pcoin->first->vout[pcoin->second].nValue > nBalance - nReserveBalance) + break; + + int64_t nTimeWeight = GetWeight((int64_t)pcoin->first->nTime, (int64_t)nGenerationTime); + + // Do not add input that is still too young + if (nTimeWeight < nStakeMaxAge) + continue; + // Do not add input if key/address is not the same as kernel + if (pcoin->first->vout[pcoin->second].scriptPubKey != scriptPubKeyKernel && pcoin->first->vout[pcoin->second].scriptPubKey != txNew.vout[1].scriptPubKey) + continue; + // Do not add input if parents are the same + if (pcoin->first->GetHash() != txNew.vin[0].prevout.hash) + continue; + // Do not add additional significant input + if (pcoin->first->vout[pcoin->second].nValue > nCombineThreshold) + continue; + + txNew.vin.push_back(CTxIn(pcoin->first->GetHash(), pcoin->second)); + nCredit += pcoin->first->vout[pcoin->second].nValue; + vwtxPrev.push_back(pcoin->first); + } + + fMaxTimeWeight = true; + } + else + { + // Split stake input if maximum weight isn't reached yet + txNew.vout.push_back(CTxOut(0, scriptPubKeyOut)); + txNew.vout.push_back(CTxOut(0, scriptPubKeyOut)); + + if (fDebug && GetBoolArg("-printcoinstake")) + printf("CreateCoinStake : maximum time weight isn't reached, splitting coinstake\n"); + } + + // Calculate coin age reward + uint64_t nCoinAge; + CTxDB txdb("r"); + if (!txNew.GetCoinAge(txdb, nCoinAge)) + return error("CreateCoinStake : failed to calculate coin age\n"); + nCredit += GetProofOfStakeReward(nCoinAge, nBits, nGenerationTime); + + int64_t nMinFee = 0; + while (true) + { + // Set output amount + if (fMaxTimeWeight) + txNew.vout[1].nValue = nCredit - nMinFee; + else + { + txNew.vout[1].nValue = ((nCredit - nMinFee) / 2 / CENT) * CENT; + txNew.vout[2].nValue = nCredit - nMinFee - txNew.vout[1].nValue; + } + + // Sign + int nIn = 0; + BOOST_FOREACH(const CWalletTx* pcoin, vwtxPrev) + { + if (!SignSignature(*this, *pcoin, txNew, nIn++)) + return error("CreateCoinStake : failed to sign coinstake\n"); + } + + // Limit size + unsigned int nBytes = ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION); + if (nBytes >= MAX_BLOCK_SIZE_GEN/5) + return error("CreateCoinStake : exceeded coinstake size limit\n"); + + // Check enough fee is paid + if (nMinFee < txNew.GetMinFee(1, false, GMF_BLOCK, nBytes) - CENT) + { + nMinFee = txNew.GetMinFee(1, false, GMF_BLOCK, nBytes) - CENT; + continue; // try signing again + } + else + { + if (fDebug && GetBoolArg("-printfee")) + printf("CreateCoinStake : fee for coinstake %s\n", FormatMoney(nMinFee).c_str()); + break; + } + } + + // Successfully created coinstake + return true; +} + +// Call after CreateTransaction unless you want to abort +bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) +{ + { + printf("CommitTransaction:\n%s", wtxNew.ToString().c_str()); + + // Track how many getdata requests our transaction gets + mapRequestCount[wtxNew.GetHash()] = 0; + + // Try to broadcast before saving + if (!wtxNew.AcceptToMemoryPool()) + { + // This must not fail. The transaction has already been signed. + printf("CommitTransaction() : Error: Transaction not valid"); + return false; + } + + wtxNew.RelayWalletTransaction(); + + { + LOCK2(cs_main, cs_wallet); + + // 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.BindWallet(this); + coin.MarkSpent(txin.prevout.n); + coin.WriteToDisk(); + NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED); + vMintingWalletUpdated.push_back(coin.GetHash()); + } + + if (fFileBacked) + delete pwalletdb; + } + } + return true; +} + + + + +string CWallet::SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, bool fAskFee) +{ + CReserveKey reservekey(this); + int64_t nFeeRequired; + + if (IsLocked()) + { + string strError = _("Error: Wallet locked, unable to create transaction "); + printf("SendMoney() : %s", strError.c_str()); + return strError; + } + if (fWalletUnlockMintOnly) + { + string strError = _("Error: Wallet unlocked for block minting only, unable to create transaction."); + printf("SendMoney() : %s", strError.c_str()); + return strError; + } + 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 && !uiInterface.ThreadSafeAskFee(nFeeRequired, _("Sending..."))) + 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."); + + return ""; +} + + + +string CWallet::SendMoneyToDestination(const CTxDestination& address, int64_t 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; + scriptPubKey.SetDestination(address); + + return SendMoney(scriptPubKey, nValue, wtxNew, fAskFee); +} + + + + +DBErrors CWallet::LoadWallet(bool& fFirstRunRet) +{ + if (!fFileBacked) + return DB_LOAD_OK; + fFirstRunRet = false; + DBErrors nLoadWalletRet = CWalletDB(strWalletFile,"cr+").LoadWallet(this); + if (nLoadWalletRet == DB_NEED_REWRITE) + { + if (CDB::Rewrite(strWalletFile, "\x04pool")) + { + setKeyPool.clear(); + // Note: can't top-up keypool here, because wallet is locked. + // User will be prompted to unlock wallet the next operation + // the requires a new key. + } + } + + if (nLoadWalletRet != DB_LOAD_OK) + return nLoadWalletRet; + fFirstRunRet = !vchDefaultKey.IsValid(); + + NewThread(ThreadFlushWalletDB, &strWalletFile); + return DB_LOAD_OK; +} + +DBErrors CWallet::ZapWalletTx() +{ + if (!fFileBacked) + return DB_LOAD_OK; + DBErrors nZapWalletTxRet = CWalletDB(strWalletFile,"cr+").ZapWalletTx(this); + if (nZapWalletTxRet == DB_NEED_REWRITE) + { + if (CDB::Rewrite(strWalletFile, "\x04pool")) + { + LOCK(cs_wallet); + setKeyPool.clear(); + // Note: can't top-up keypool here, because wallet is locked. + // User will be prompted to unlock wallet the next operation + // the requires a new key. + } + } + + if (nZapWalletTxRet != DB_LOAD_OK) + return nZapWalletTxRet; + + return DB_LOAD_OK; +} + +bool CWallet::SetAddressBookName(const CTxDestination& address, const string& strName) +{ + std::map::iterator mi = mapAddressBook.find(address); + mapAddressBook[address] = strName; + NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address) != MINE_NO, (mi == mapAddressBook.end()) ? CT_NEW : CT_UPDATED); + if (!fFileBacked) + return false; + return CWalletDB(strWalletFile).WriteName(CBitcoinAddress(address).ToString(), strName); +} + +bool CWallet::DelAddressBookName(const CTxDestination& address) +{ + mapAddressBook.erase(address); + NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address) != MINE_NO, CT_DELETED); + if (!fFileBacked) + return false; + return CWalletDB(strWalletFile).EraseName(CBitcoinAddress(address).ToString()); +} + + +void CWallet::PrintWallet(const CBlock& block) +{ + { + LOCK(cs_wallet); + if (block.IsProofOfStake() && mapWallet.count(block.vtx[1].GetHash())) + { + CWalletTx& wtx = mapWallet[block.vtx[1].GetHash()]; + printf(" PoS: %d %d %" PRId64 "", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit(MINE_ALL)); + } + else if (mapWallet.count(block.vtx[0].GetHash())) + { + CWalletTx& wtx = mapWallet[block.vtx[0].GetHash()]; + printf(" PoW: %d %d %" PRId64 "", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit(MINE_ALL)); + } + } + printf("\n"); +} + +bool CWallet::GetTransaction(const uint256 &hashTx, CWalletTx& wtx) +{ + { + LOCK(cs_wallet); + map::iterator mi = mapWallet.find(hashTx); + if (mi != mapWallet.end()) + { + wtx = (*mi).second; + return true; + } + } + return false; +} + +bool CWallet::SetDefaultKey(const CPubKey &vchPubKey) +{ + if (fFileBacked) + { + if (!CWalletDB(strWalletFile).WriteDefaultKey(vchPubKey)) + return false; + } + vchDefaultKey = vchPubKey; + return true; +} + +bool GetWalletFile(CWallet* pwallet, string &strWalletFileOut) +{ + if (!pwallet->fFileBacked) + return false; + strWalletFileOut = pwallet->strWalletFile; + return true; +} + +// +// Mark old keypool keys as used, +// and generate all new keys +// +bool CWallet::NewKeyPool(unsigned int nSize) +{ + { + LOCK(cs_wallet); + CWalletDB walletdb(strWalletFile); + BOOST_FOREACH(int64_t nIndex, setKeyPool) + walletdb.ErasePool(nIndex); + setKeyPool.clear(); + + if (IsLocked()) + return false; + + uint64_t nKeys; + if (nSize > 0) + nKeys = nSize; + else + nKeys = max(GetArg("-keypool", 100), 0); + + for (uint64_t i = 0; i < nKeys; i++) + { + uint64_t nIndex = i+1; + walletdb.WritePool(nIndex, CKeyPool(GenerateNewKey())); + setKeyPool.insert(nIndex); + } + printf("CWallet::NewKeyPool wrote %" PRIu64 " new keys\n", nKeys); + } + return true; +} + +bool CWallet::TopUpKeyPool(unsigned int nSize) +{ + { + LOCK(cs_wallet); + + if (IsLocked()) + return false; + + CWalletDB walletdb(strWalletFile); + + // Top up key pool + uint64_t nTargetSize; + if (nSize > 0) + nTargetSize = nSize; + else + nTargetSize = max(GetArg("-keypool", 100), 0); + + while (setKeyPool.size() < (nTargetSize + 1)) + { + uint64_t nEnd = 1; + if (!setKeyPool.empty()) + nEnd = *(--setKeyPool.end()) + 1; + if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey()))) + throw runtime_error("TopUpKeyPool() : writing generated key failed"); + setKeyPool.insert(nEnd); + printf("keypool added key %" PRIu64 ", size=%" PRIszu "\n", nEnd, setKeyPool.size()); + } + } + return true; +} + +void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool) +{ + nIndex = -1; + keypool.vchPubKey = CPubKey(); + { + LOCK(cs_wallet); + + if (!IsLocked()) + TopUpKeyPool(); + + // Get the oldest key + if(setKeyPool.empty()) + return; + + CWalletDB walletdb(strWalletFile); + + nIndex = *(setKeyPool.begin()); + setKeyPool.erase(setKeyPool.begin()); + if (!walletdb.ReadPool(nIndex, keypool)) + throw runtime_error("ReserveKeyFromKeyPool() : read failed"); + if (!HaveKey(keypool.vchPubKey.GetID())) + throw runtime_error("ReserveKeyFromKeyPool() : unknown key in key pool"); + assert(keypool.vchPubKey.IsValid()); + if (fDebug && GetBoolArg("-printkeypool")) + printf("keypool reserve %" PRId64 "\n", nIndex); + } +} + +int64_t CWallet::AddReserveKey(const CKeyPool& keypool) +{ + { + LOCK2(cs_main, cs_wallet); + CWalletDB walletdb(strWalletFile); + + int64_t nIndex = 1 + *(--setKeyPool.end()); + if (!walletdb.WritePool(nIndex, keypool)) + throw runtime_error("AddReserveKey() : writing added key failed"); + setKeyPool.insert(nIndex); + return nIndex; + } + return -1; +} + +void CWallet::KeepKey(int64_t nIndex) +{ + // Remove from key pool + if (fFileBacked) + { + CWalletDB walletdb(strWalletFile); + walletdb.ErasePool(nIndex); + } + if(fDebug) + printf("keypool keep %" PRId64 "\n", nIndex); +} + +void CWallet::ReturnKey(int64_t nIndex) +{ + // Return to key pool + { + LOCK(cs_wallet); + setKeyPool.insert(nIndex); + } + if(fDebug) + printf("keypool return %" PRId64 "\n", nIndex); +} + +bool CWallet::GetKeyFromPool(CPubKey& result, bool fAllowReuse) +{ + int64_t nIndex = 0; + CKeyPool keypool; + { + LOCK(cs_wallet); + ReserveKeyFromKeyPool(nIndex, keypool); + if (nIndex == -1) + { + if (fAllowReuse && vchDefaultKey.IsValid()) + { + result = vchDefaultKey; + return true; + } + if (IsLocked()) return false; + result = GenerateNewKey(); + return true; + } + KeepKey(nIndex); + result = keypool.vchPubKey; + } + return true; +} + +int64_t CWallet::GetOldestKeyPoolTime() +{ + int64_t nIndex = 0; + CKeyPool keypool; + ReserveKeyFromKeyPool(nIndex, keypool); + if (nIndex == -1) + return GetTime(); + ReturnKey(nIndex); + return keypool.nTime; +} + +std::map CWallet::GetAddressBalances() +{ + map balances; + + { + LOCK(cs_wallet); + BOOST_FOREACH(PAIRTYPE(uint256, CWalletTx) walletEntry, mapWallet) + { + CWalletTx *pcoin = &walletEntry.second; + + if (!pcoin->IsFinal() || !pcoin->IsTrusted()) + continue; + + if ((pcoin->IsCoinBase() || pcoin->IsCoinStake()) && pcoin->GetBlocksToMaturity() > 0) + continue; + + int nDepth = pcoin->GetDepthInMainChain(); + if (nDepth < (pcoin->IsFromMe(MINE_ALL) ? 0 : 1)) + continue; + + for (unsigned int i = 0; i < pcoin->vout.size(); i++) + { + CTxDestination addr; + if (!IsMine(pcoin->vout[i])) + continue; + if(!ExtractDestination(pcoin->vout[i].scriptPubKey, addr)) + continue; + + int64_t n = pcoin->IsSpent(i) ? 0 : pcoin->vout[i].nValue; + + if (!balances.count(addr)) + balances[addr] = 0; + balances[addr] += n; + } + } + } + + return balances; +} + +set< set > CWallet::GetAddressGroupings() +{ + set< set > groupings; + set grouping; + + BOOST_FOREACH(PAIRTYPE(uint256, CWalletTx) walletEntry, mapWallet) + { + CWalletTx *pcoin = &walletEntry.second; + + if (pcoin->vin.size() > 0 && IsMine(pcoin->vin[0])) + { + // group all input addresses with each other + BOOST_FOREACH(CTxIn txin, pcoin->vin) + { + CTxDestination address; + if(!ExtractDestination(mapWallet[txin.prevout.hash].vout[txin.prevout.n].scriptPubKey, address)) + continue; + grouping.insert(address); + } + + // group change with input addresses + BOOST_FOREACH(CTxOut txout, pcoin->vout) + if (IsChange(txout)) + { + CWalletTx tx = mapWallet[pcoin->vin[0].prevout.hash]; + CTxDestination txoutAddr; + if(!ExtractDestination(txout.scriptPubKey, txoutAddr)) + continue; + grouping.insert(txoutAddr); + } + groupings.insert(grouping); + grouping.clear(); + } + + // group lone addrs by themselves + for (unsigned int i = 0; i < pcoin->vout.size(); i++) + if (IsMine(pcoin->vout[i])) + { + CTxDestination address; + if(!ExtractDestination(pcoin->vout[i].scriptPubKey, address)) + continue; + grouping.insert(address); + groupings.insert(grouping); + grouping.clear(); + } + } + + set< set* > uniqueGroupings; // a set of pointers to groups of addresses + map< CTxDestination, set* > setmap; // map addresses to the unique group containing it + BOOST_FOREACH(set grouping, groupings) + { + // make a set of all the groups hit by this new group + set< set* > hits; + map< CTxDestination, set* >::iterator it; + BOOST_FOREACH(CTxDestination address, grouping) + if ((it = setmap.find(address)) != setmap.end()) + hits.insert((*it).second); + + // merge all hit groups into a new single group and delete old groups + set* merged = new set(grouping); + BOOST_FOREACH(set* hit, hits) + { + merged->insert(hit->begin(), hit->end()); + uniqueGroupings.erase(hit); + delete hit; + } + uniqueGroupings.insert(merged); + + // update setmap + BOOST_FOREACH(CTxDestination element, *merged) + setmap[element] = merged; + } + + set< set > ret; + BOOST_FOREACH(set* uniqueGrouping, uniqueGroupings) + { + ret.insert(*uniqueGrouping); + delete uniqueGrouping; + } + + return ret; +} + +// ppcoin: check 'spent' consistency between wallet and txindex +// ppcoin: fix wallet spent state according to txindex +void CWallet::FixSpentCoins(int& nMismatchFound, int64_t& nBalanceInQuestion, bool fCheckOnly) +{ + nMismatchFound = 0; + nBalanceInQuestion = 0; + + LOCK(cs_wallet); + vector vCoins; + vCoins.reserve(mapWallet.size()); + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + vCoins.push_back(&(*it).second); + + CTxDB txdb("r"); + BOOST_FOREACH(CWalletTx* pcoin, vCoins) + { + // Find the corresponding transaction index + CTxIndex txindex; + if (!txdb.ReadTxIndex(pcoin->GetHash(), txindex)) + continue; + for (unsigned int n=0; n < pcoin->vout.size(); n++) + { + if (IsMine(pcoin->vout[n]) && pcoin->IsSpent(n) && (txindex.vSpent.size() <= n || txindex.vSpent[n].IsNull())) + { + printf("FixSpentCoins found lost coin %sppc %s[%u], %s\n", + FormatMoney(pcoin->vout[n].nValue).c_str(), pcoin->GetHash().ToString().c_str(), n, fCheckOnly? "repair not attempted" : "repairing"); + nMismatchFound++; + nBalanceInQuestion += pcoin->vout[n].nValue; + if (!fCheckOnly) + { + pcoin->MarkUnspent(n); + pcoin->WriteToDisk(); + } + } + else if (IsMine(pcoin->vout[n]) && !pcoin->IsSpent(n) && (txindex.vSpent.size() > n && !txindex.vSpent[n].IsNull())) + { + printf("FixSpentCoins found spent coin %sppc %s[%u], %s\n", + FormatMoney(pcoin->vout[n].nValue).c_str(), pcoin->GetHash().ToString().c_str(), n, fCheckOnly? "repair not attempted" : "repairing"); + nMismatchFound++; + nBalanceInQuestion += pcoin->vout[n].nValue; + if (!fCheckOnly) + { + pcoin->MarkSpent(n); + pcoin->WriteToDisk(); + } + } + + } + + if(IsMine((CTransaction)*pcoin) && (pcoin->IsCoinBase() || pcoin->IsCoinStake()) && pcoin->GetDepthInMainChain() == 0) + { + printf("FixSpentCoins %s tx %s\n", fCheckOnly ? "found" : "removed", pcoin->GetHash().ToString().c_str()); + if (!fCheckOnly) + { + EraseFromWallet(pcoin->GetHash()); + } + } + } +} + +// ppcoin: disable transaction (only for coinstake) +void CWallet::DisableTransaction(const CTransaction &tx) +{ + if (!tx.IsCoinStake() || !IsFromMe(tx)) + return; // only disconnecting coinstake requires marking input unspent + + LOCK(cs_wallet); + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + map::iterator mi = mapWallet.find(txin.prevout.hash); + if (mi != mapWallet.end()) + { + CWalletTx& prev = (*mi).second; + if (txin.prevout.n < prev.vout.size() && IsMine(prev.vout[txin.prevout.n])) + { + prev.MarkUnspent(txin.prevout.n); + prev.WriteToDisk(); + } + } + } +} + +CPubKey CReserveKey::GetReservedKey() +{ + if (nIndex == -1) + { + CKeyPool keypool; + pwallet->ReserveKeyFromKeyPool(nIndex, keypool); + if (nIndex != -1) + vchPubKey = keypool.vchPubKey; + else + { + printf("CReserveKey::GetReservedKey(): Warning: Using default key instead of a new key, top up your keypool!"); + vchPubKey = pwallet->vchDefaultKey; + } + } + assert(vchPubKey.IsValid()); + return vchPubKey; +} + +void CReserveKey::KeepKey() +{ + if (nIndex != -1) + pwallet->KeepKey(nIndex); + nIndex = -1; + vchPubKey = CPubKey(); +} + +void CReserveKey::ReturnKey() +{ + if (nIndex != -1) + pwallet->ReturnKey(nIndex); + nIndex = -1; + vchPubKey = CPubKey(); +} + +void CWallet::GetAllReserveKeys(set& setAddress) const +{ + setAddress.clear(); + + CWalletDB walletdb(strWalletFile); + + LOCK2(cs_main, cs_wallet); + BOOST_FOREACH(const int64_t& id, setKeyPool) + { + CKeyPool keypool; + if (!walletdb.ReadPool(id, keypool)) + throw runtime_error("GetAllReserveKeyHashes() : read failed"); + assert(keypool.vchPubKey.IsValid()); + CKeyID keyID = keypool.vchPubKey.GetID(); + if (!HaveKey(keyID)) + throw runtime_error("GetAllReserveKeyHashes() : unknown key in key pool"); + setAddress.insert(keyID); + } +} + +void CWallet::UpdatedTransaction(const uint256 &hashTx) +{ + { + LOCK(cs_wallet); + // Only notify UI if this transaction is in this wallet + map::const_iterator mi = mapWallet.find(hashTx); + if (mi != mapWallet.end()) + { + NotifyTransactionChanged(this, hashTx, CT_UPDATED); + vMintingWalletUpdated.push_back(hashTx); + } + } +} + +void CWallet::GetKeyBirthTimes(std::map &mapKeyBirth) const { + mapKeyBirth.clear(); + + // get birth times for keys with metadata + for (std::map::const_iterator it = mapKeyMetadata.begin(); it != mapKeyMetadata.end(); it++) + if (it->second.nCreateTime) + mapKeyBirth[it->first] = it->second.nCreateTime; + + // map in which we'll infer heights of other keys + CBlockIndex *pindexMax = FindBlockByHeight(std::max(0, nBestHeight - 144)); // the tip can be reorganised; use a 144-block safety margin + std::map mapKeyFirstBlock; + std::set setKeys; + GetKeys(setKeys); + BOOST_FOREACH(const CKeyID &keyid, setKeys) { + if (mapKeyBirth.count(keyid) == 0) + mapKeyFirstBlock[keyid] = pindexMax; + } + setKeys.clear(); + + // if there are no such keys, we're done + if (mapKeyFirstBlock.empty()) + return; + + // find first block that affects those keys, if there are any left + std::vector vAffected; + for (std::map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); it++) { + // iterate over all wallet transactions... + const CWalletTx &wtx = (*it).second; + std::map::const_iterator blit = mapBlockIndex.find(wtx.hashBlock); + if (blit != mapBlockIndex.end() && blit->second->IsInMainChain()) { + // ... which are already in a block + int nHeight = blit->second->nHeight; + BOOST_FOREACH(const CTxOut &txout, wtx.vout) { + // iterate over all their outputs + ::ExtractAffectedKeys(*this, txout.scriptPubKey, vAffected); + BOOST_FOREACH(const CKeyID &keyid, vAffected) { + // ... and all their affected keys + std::map::iterator rit = mapKeyFirstBlock.find(keyid); + if (rit != mapKeyFirstBlock.end() && nHeight < rit->second->nHeight) + rit->second = blit->second; + } + vAffected.clear(); + } + } + } + + // Extract block timestamps for those keys + for (std::map::const_iterator it = mapKeyFirstBlock.begin(); it != mapKeyFirstBlock.end(); it++) + mapKeyBirth[it->first] = it->second->nTime - 7200; // block times can be 2h off +} + +void CWallet::ClearOrphans() +{ + list orphans; + + LOCK(cs_wallet); + for(map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx *wtx = &(*it).second; + if((wtx->IsCoinBase() || wtx->IsCoinStake()) && !wtx->IsInMainChain()) + { + orphans.push_back(wtx->GetHash()); + } + } + + for(list::const_iterator it = orphans.begin(); it != orphans.end(); ++it) + EraseFromWallet(*it); +} diff --git a/src/wallet.h b/src/wallet.h new file mode 100644 index 00000000..8480b3b8 --- /dev/null +++ b/src/wallet.h @@ -0,0 +1,1028 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_WALLET_H +#define BITCOIN_WALLET_H + +#include +#include + +#include + +#include "main.h" +#include "key.h" +#include "keystore.h" +#include "script.h" +#include "ui_interface.h" +#include "util.h" +#include "walletdb.h" + +extern unsigned int nStakeMaxAge; +extern bool fWalletUnlockMintOnly; +extern bool fConfChange; +class CAccountingEntry; +class CWalletTx; +class CReserveKey; +class COutput; +class CCoinControl; + +// Set of selected transactions +typedef std::set > CoinsSet; + +/** (client) version numbers for particular wallet features */ +enum WalletFeature +{ + FEATURE_BASE = 10500, // the earliest version new wallets supports (only useful for getinfo's clientversion output) + + FEATURE_WALLETCRYPT = 40000, // wallet encryption + FEATURE_COMPRPUBKEY = 60000, // compressed public keys + FEATURE_LATEST = 60000 +}; + +/** A key pool entry */ +class CKeyPool +{ +public: + int64_t nTime; + CPubKey vchPubKey; + + CKeyPool() + { + nTime = GetTime(); + } + + CKeyPool(const CPubKey& vchPubKeyIn) + { + nTime = GetTime(); + vchPubKey = vchPubKeyIn; + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(nTime); + READWRITE(vchPubKey); + ) +}; + +/** A CWallet is an extension of a keystore, which also maintains a set of transactions and balances, + * and provides the ability to create new transactions. + */ +class CWallet : public CCryptoKeyStore +{ +private: + bool SelectCoins(int64_t nTargetValue, unsigned int nSpendTime, std::set >& setCoinsRet, int64_t& nValueRet, const CCoinControl *coinControl=NULL) const; + + CWalletDB *pwalletdbEncryption, *pwalletdbDecryption; + + // the current wallet version: clients below this version are not able to load the wallet + int nWalletVersion; + + // the maximum wallet format version: memory-only variable that specifies to what version this wallet may be upgraded + int nWalletMaxVersion; + + // stake mining statistics + uint64_t nKernelsTried; + uint64_t nCoinDaysTried; + +public: + mutable CCriticalSection cs_wallet; + + bool fFileBacked; + std::string strWalletFile; + + std::set setKeyPool; + std::map mapKeyMetadata; + + typedef std::map MasterKeyMap; + MasterKeyMap mapMasterKeys; + unsigned int nMasterKeyMaxID; + + CWallet() + { + SetNull(); + } + CWallet(std::string strWalletFileIn) + { + SetNull(); + + strWalletFile = strWalletFileIn; + fFileBacked = true; + } + void SetNull() + { + nWalletVersion = FEATURE_BASE; + nWalletMaxVersion = FEATURE_BASE; + fFileBacked = false; + nMasterKeyMaxID = 0; + pwalletdbEncryption = NULL; + pwalletdbDecryption = NULL; + nOrderPosNext = 0; + nKernelsTried = 0; + nCoinDaysTried = 0; + nTimeFirstKey = 0; + } + + std::map mapWallet; + std::vector vMintingWalletUpdated; + int64_t nOrderPosNext; + std::map mapRequestCount; + + std::map mapAddressBook; + + CPubKey vchDefaultKey; + int64_t nTimeFirstKey; + + // check whether we are allowed to upgrade (or already support) to the named feature + bool CanSupportFeature(enum WalletFeature wf) { return nWalletMaxVersion >= wf; } + + void AvailableCoinsMinConf(std::vector& vCoins, int nConf, int64_t nMinValue, int64_t nMaxValue) const; + void AvailableCoins(std::vector& vCoins, bool fOnlyConfirmed=true, const CCoinControl *coinControl=NULL) const; + bool SelectCoinsMinConf(int64_t nTargetValue, unsigned int nSpendTime, int nConfMine, int nConfTheirs, std::vector vCoins, std::set >& setCoinsRet, int64_t& nValueRet) const; + + // Simple select (without randomization) + bool SelectCoinsSimple(int64_t nTargetValue, int64_t nMinValue, int64_t nMaxValue, unsigned int nSpendTime, int nMinConf, std::set >& setCoinsRet, int64_t& nValueRet) const; + + // keystore implementation + // Generate a new key + CPubKey GenerateNewKey(); + // Adds a key to the store, and saves it to disk. + bool AddKey(const CKey& key); + // Adds a key to the store, without saving it to disk (used by LoadWallet) + bool LoadKey(const CKey& key) { return CCryptoKeyStore::AddKey(key); } + // Load metadata (used by LoadWallet) + bool LoadKeyMetadata(const CPubKey &pubkey, const CKeyMetadata &metadata); + + bool LoadMinVersion(int nVersion) { nWalletVersion = nVersion; nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion); return true; } + + // Adds an encrypted key to the store, and saves it to disk. + bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret); + // Adds an encrypted key to the store, without saving it to disk (used by LoadWallet) + bool LoadCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret) { SetMinVersion(FEATURE_WALLETCRYPT); return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); } + bool AddCScript(const CScript& redeemScript); + bool LoadCScript(const CScript& redeemScript); + + // Adds a watch-only address to the store, and saves it to disk. + bool AddWatchOnly(const CScript &dest); + bool RemoveWatchOnly(const CScript &dest); + // Adds a watch-only address to the store, without saving it to disk (used by LoadWallet) + bool LoadWatchOnly(const CScript &dest); + + bool Unlock(const SecureString& strWalletPassphrase); + bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase); + bool EncryptWallet(const SecureString& strWalletPassphrase); + bool DecryptWallet(const SecureString& strWalletPassphrase); + + void GetKeyBirthTimes(std::map &mapKeyBirth) const; + + + /** Increment the next transaction order id + @return next transaction order id + */ + int64_t IncOrderPosNext(CWalletDB *pwalletdb = NULL); + + typedef std::pair TxPair; + typedef std::multimap TxItems; + + /** Get the wallet's activity log + @return multimap of ordered transactions and accounting entries + @warning Returned pointers are *only* valid within the scope of passed acentries + */ + TxItems OrderedTxItems(std::list& acentries, std::string strAccount = ""); + + void MarkDirty(); + bool AddToWallet(const CWalletTx& wtxIn); + bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate = false, bool fFindBlock = false); + bool EraseFromWallet(uint256 hash); + void ClearOrphans(); + void WalletUpdateSpent(const CTransaction& prevout, bool fBlock = false); + int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); + int ScanForWalletTransaction(const uint256& hashTx); + void ReacceptWalletTransactions(); + void ResendWalletTransactions(); + int64_t GetBalance() const; + int64_t GetWatchOnlyBalance() const; + int64_t GetUnconfirmedBalance() const; + int64_t GetUnconfirmedWatchOnlyBalance() const; + int64_t GetImmatureBalance() const; + int64_t GetImmatureWatchOnlyBalance() const; + int64_t GetStake() const; + int64_t GetNewMint() const; + int64_t GetWatchOnlyStake() const; + int64_t GetWatchOnlyNewMint() const; + bool CreateTransaction(const std::vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl *coinControl=NULL); + bool CreateTransaction(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl *coinControl=NULL); + bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); + + void GetStakeWeightFromValue(const int64_t& nTime, const int64_t& nValue, uint64_t& nWeight); + bool CreateCoinStake(uint256 &hashTx, uint32_t nOut, uint32_t nTime, uint32_t nBits, CTransaction &txNew, CKey& key); + bool MergeCoins(const int64_t& nAmount, const int64_t& nMinValue, const int64_t& nMaxValue, std::list& listMerged); + + std::string SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, bool fAskFee=false); + std::string SendMoneyToDestination(const CTxDestination &address, int64_t nValue, CWalletTx& wtxNew, bool fAskFee=false); + + bool NewKeyPool(unsigned int nSize = 0); + bool TopUpKeyPool(unsigned int nSize = 0); + int64_t AddReserveKey(const CKeyPool& keypool); + void ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool); + void KeepKey(int64_t nIndex); + void ReturnKey(int64_t nIndex); + bool GetKeyFromPool(CPubKey &key, bool fAllowReuse=true); + int64_t GetOldestKeyPoolTime(); + void GetAllReserveKeys(std::set& setAddress) const; + + std::set< std::set > GetAddressGroupings(); + std::map GetAddressBalances(); + + isminetype IsMine(const CTxIn& txin) const; + int64_t GetDebit(const CTxIn& txin, const isminefilter& filter) const; + isminetype IsMine(const CTxOut& txout) const + { + return ::IsMine(*this, txout.scriptPubKey); + } + int64_t GetCredit(const CTxOut& txout, const isminefilter& filter) const + { + if (!MoneyRange(txout.nValue)) + throw std::runtime_error("CWallet::GetCredit() : value out of range"); + return (IsMine(txout) & filter ? txout.nValue : 0); + } + bool IsChange(const CTxOut& txout) const; + int64_t 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) && txout.nValue >= nMinimumInputValue) + return true; + return false; + } + bool IsFromMe(const CTransaction& tx) const + { + return (GetDebit(tx, MINE_ALL) > 0); + } + int64_t GetDebit(const CTransaction& tx, const isminefilter& filter) const + { + int64_t nDebit = 0; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + nDebit += GetDebit(txin, filter); + if (!MoneyRange(nDebit)) + throw std::runtime_error("CWallet::GetDebit() : value out of range"); + } + return nDebit; + } + int64_t GetCredit(const CTransaction& tx, const isminefilter& filter) const + { + int64_t nCredit = 0; + BOOST_FOREACH(const CTxOut& txout, tx.vout) + { + nCredit += GetCredit(txout, filter); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWallet::GetCredit() : value out of range"); + } + return nCredit; + } + int64_t GetChange(const CTransaction& tx) const + { + int64_t 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); + + DBErrors LoadWallet(bool& fFirstRunRet); + + DBErrors ZapWalletTx(); + + bool SetAddressBookName(const CTxDestination& address, const std::string& strName); + + bool DelAddressBookName(const CTxDestination& address); + + void UpdatedTransaction(const uint256 &hashTx); + + void PrintWallet(const CBlock& block); + + void Inventory(const uint256 &hash) + { + { + LOCK(cs_wallet); + std::map::iterator mi = mapRequestCount.find(hash); + if (mi != mapRequestCount.end()) + (*mi).second++; + } + } + + unsigned int GetKeyPoolSize() + { + return (unsigned int)(setKeyPool.size()); + } + + bool GetTransaction(const uint256 &hashTx, CWalletTx& wtx); + + bool SetDefaultKey(const CPubKey &vchPubKey); + + // signify that a particular wallet feature is now used. this may change nWalletVersion and nWalletMaxVersion if those are lower + bool SetMinVersion(enum WalletFeature, CWalletDB* pwalletdbIn = NULL, bool fExplicit = false); + + // change which version we're allowed to upgrade to (note that this does not immediately imply upgrading to that format) + bool SetMaxVersion(int nVersion); + + // get the current wallet format (the oldest client version guaranteed to understand this wallet) + int GetVersion() { return nWalletVersion; } + + void FixSpentCoins(int& nMismatchSpent, int64_t& nBalanceInQuestion, bool fCheckOnly = false); + void DisableTransaction(const CTransaction &tx); + + /** Address book entry changed. + * @note called with lock cs_wallet held. + */ + boost::signals2::signal NotifyAddressBookChanged; + + /** Wallet transaction added, removed or updated. + * @note called with lock cs_wallet held. + */ + boost::signals2::signal NotifyTransactionChanged; + + /** Watch-only address added */ + boost::signals2::signal NotifyWatchonlyChanged; +}; + +/** A key allocated from the key pool. */ +class CReserveKey +{ +protected: + CWallet* pwallet; + int64_t nIndex; + CPubKey vchPubKey; +public: + CReserveKey(CWallet* pwalletIn) + { + nIndex = -1; + pwallet = pwalletIn; + } + + ~CReserveKey() + { + if (!fShutdown) + ReturnKey(); + } + + void ReturnKey(); + CPubKey GetReservedKey(); + void KeepKey(); +}; + + +typedef std::map mapValue_t; + + +static void ReadOrderPos(int64_t& nOrderPos, mapValue_t& mapValue) +{ + if (!mapValue.count("n")) + { + nOrderPos = -1; // TODO: calculate elsewhere + return; + } + nOrderPos = atoi64(mapValue["n"].c_str()); +} + + +static void WriteOrderPos(const int64_t& nOrderPos, mapValue_t& mapValue) +{ + if (nOrderPos == -1) + return; + mapValue["n"] = i64tostr(nOrderPos); +} + + +/** 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 +{ +private: + const CWallet* pwallet; + +public: + std::vector vtxPrev; + mapValue_t mapValue; + std::vector > vOrderForm; + unsigned int fTimeReceivedIsTxTime; + unsigned int nTimeReceived; // time received by this node + unsigned int nTimeSmart; + char fFromMe; + std::string strFromAccount; + std::vector vfSpent; // which outputs are already spent + int64_t nOrderPos; // position in ordered transaction list + + // memory only + mutable bool fDebitCached; + mutable bool fWatchDebitCached; + mutable bool fCreditCached; + mutable bool fWatchCreditCached; + mutable bool fAvailableCreditCached; + mutable bool fImmatureCreditCached; + mutable bool fImmatureWatchCreditCached; + mutable bool fAvailableWatchCreditCached; + mutable bool fChangeCached; + mutable int64_t nDebitCached; + mutable int64_t nWatchDebitCached; + mutable int64_t nCreditCached; + mutable int64_t nWatchCreditCached; + mutable int64_t nAvailableCreditCached; + mutable int64_t nImmatureCreditCached; + mutable int64_t nImmatureWatchCreditCached; + mutable int64_t nAvailableWatchCreditCached; + mutable int64_t nChangeCached; + + 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; + nTimeSmart = 0; + fFromMe = false; + strFromAccount.clear(); + vfSpent.clear(); + fDebitCached = false; + fWatchDebitCached = false; + fCreditCached = false; + fWatchCreditCached = false; + fAvailableCreditCached = false; + fAvailableWatchCreditCached = false; + fImmatureCreditCached = false; + fImmatureWatchCreditCached = false; + fChangeCached = false; + nDebitCached = 0; + nWatchDebitCached = 0; + nCreditCached = 0; + nWatchCreditCached = 0; + nAvailableCreditCached = 0; + nAvailableWatchCreditCached = 0; + nImmatureCreditCached = 0; + nImmatureWatchCreditCached = 0; + nChangeCached = 0; + nOrderPos = -1; + } + + 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; + + WriteOrderPos(pthis->nOrderPos, pthis->mapValue); + + if (nTimeSmart) + pthis->mapValue["timesmart"] = strprintf("%u", nTimeSmart); + } + + 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); + + ReadOrderPos(pthis->nOrderPos, pthis->mapValue); + + pthis->nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(pthis->mapValue["timesmart"]) : 0; + } + + pthis->mapValue.erase("fromaccount"); + pthis->mapValue.erase("version"); + pthis->mapValue.erase("spent"); + pthis->mapValue.erase("n"); + pthis->mapValue.erase("timesmart"); + ) + + // marks certain txout's as spent + // returns true if any update took place + bool UpdateSpent(const std::vector& vfNewSpent) + { + bool fReturn = false; + for (unsigned int i = 0; i < vfNewSpent.size(); i++) + { + if (i == vfSpent.size()) + break; + + if (vfNewSpent[i] && !vfSpent[i]) + { + vfSpent[i] = true; + fReturn = true; + fAvailableCreditCached = fAvailableWatchCreditCached = false; + } + } + return fReturn; + } + + // make sure balances are recalculated + void MarkDirty() + { + fCreditCached = false; + fAvailableCreditCached = fAvailableWatchCreditCached = false; + fDebitCached = fWatchDebitCached = false; + fChangeCached = false; + } + + void BindWallet(CWallet *pwalletIn) + { + pwallet = pwalletIn; + MarkDirty(); + } + + 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 = fAvailableWatchCreditCached = false; + } + } + + void MarkUnspent(unsigned int nOut) + { + if (nOut >= vout.size()) + throw std::runtime_error("CWalletTx::MarkUnspent() : nOut out of range"); + vfSpent.resize(vout.size()); + if (vfSpent[nOut]) + { + vfSpent[nOut] = false; + fAvailableCreditCached = fAvailableWatchCreditCached = 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_t GetDebit(const isminefilter& filter) const + { + if (vin.empty()) + return 0; + + int64_t nDebit = 0; + if (filter & MINE_SPENDABLE) + { + if (fDebitCached) + nDebit += nDebitCached; + else + { + nDebitCached = pwallet->GetDebit(*this, MINE_SPENDABLE); + fDebitCached = true; + nDebit += nDebitCached; + } + } + if (filter & MINE_WATCH_ONLY) + { + if (fWatchDebitCached) + nDebit += nWatchDebitCached; + else + { + nWatchDebitCached = pwallet->GetDebit(*this, MINE_WATCH_ONLY); + fWatchDebitCached = true; + nDebit += nWatchDebitCached; + } + } + + return nDebit; + } + + int64_t GetCredit(const isminefilter& filter) const + { + // Must wait until coinbase is safely deep enough in the chain before valuing it + if ((IsCoinBase() || IsCoinStake()) && GetBlocksToMaturity() > 0) + return 0; + + int64_t credit = 0; + if (filter & MINE_SPENDABLE) + { + // GetBalance can assume transactions in mapWallet won't change + if (fCreditCached) + credit += nCreditCached; + else + { + nCreditCached = pwallet->GetCredit(*this, MINE_SPENDABLE); + fCreditCached = true; + credit += nCreditCached; + } + } + if (filter & MINE_WATCH_ONLY) + { + if (fWatchCreditCached) + credit += nWatchCreditCached; + else + { + nWatchCreditCached = pwallet->GetCredit(*this, MINE_WATCH_ONLY); + fWatchCreditCached = true; + credit += nWatchCreditCached; + } + } + return credit; + } + + int64_t GetImmatureCredit(bool fUseCache=true) const + { + if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) + { + if (fUseCache && fImmatureCreditCached) + return nImmatureCreditCached; + nImmatureCreditCached = pwallet->GetCredit(*this, MINE_SPENDABLE); + fImmatureCreditCached = true; + return nImmatureCreditCached; + } + + return 0; + } + + int64_t GetImmatureWatchOnlyCredit(bool fUseCache=true) const + { + if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) + { + if (fUseCache && fImmatureWatchCreditCached) + return nImmatureWatchCreditCached; + nImmatureWatchCreditCached = pwallet->GetCredit(*this, MINE_WATCH_ONLY); + fImmatureWatchCreditCached = true; + return nImmatureWatchCreditCached; + } + + return 0; + } + + + int64_t GetAvailableCredit(bool fUseCache=true) const + { + // Must wait until coinbase is safely deep enough in the chain before valuing it + if ((IsCoinBase() || IsCoinStake()) && GetBlocksToMaturity() > 0) + return 0; + + if (fUseCache) { + if (fAvailableCreditCached) + return nAvailableCreditCached; + } + + int64_t nCredit = 0; + for (unsigned int i = 0; i < vout.size(); i++) + { + if (!IsSpent(i)) + { + const CTxOut &txout = vout[i]; + nCredit += pwallet->GetCredit(txout, MINE_SPENDABLE); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); + } + } + + nAvailableCreditCached = nCredit; + fAvailableCreditCached = true; + + return nCredit; + } + + int64_t GetAvailableWatchCredit(bool fUseCache=true) const + { + // Must wait until coinbase is safely deep enough in the chain before valuing it + if ((IsCoinBase() || IsCoinStake()) && GetBlocksToMaturity() > 0) + return 0; + + if (fUseCache) { + if (fAvailableWatchCreditCached) + return nAvailableWatchCreditCached; + } + + int64_t nCredit = 0; + for (unsigned int i = 0; i < vout.size(); i++) + { + if (!IsSpent(i)) + { + const CTxOut &txout = vout[i]; + nCredit += pwallet->GetCredit(txout, MINE_WATCH_ONLY); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); + } + } + + nAvailableWatchCreditCached = nCredit; + fAvailableWatchCreditCached = true; + + return nCredit; + } + + int64_t GetChange() const + { + if (fChangeCached) + return nChangeCached; + nChangeCached = pwallet->GetChange(*this); + fChangeCached = true; + return nChangeCached; + } + + void GetAmounts(int64_t& nGeneratedImmature, int64_t& nGeneratedMature, std::list >& listReceived, + std::list >& listSent, int64_t& nFee, std::string& strSentAccount, const isminefilter& filter) const; + + void GetAccountAmounts(const std::string& strAccount, int64_t& nGenerated, int64_t& nReceived, + int64_t& nSent, int64_t& nFee, const isminefilter& filter) const; + + bool IsFromMe(const isminefilter& filter) const + { + return (GetDebit(filter) > 0); + } + + bool IsTrusted() const + { + // Quick answer in most cases + if (!IsFinal()) + return false; + if (GetDepthInMainChain() >= 1) + return true; + if (fConfChange || !IsFromMe(MINE_ALL)) // 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 (unsigned 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_t GetTxTime() const; + int GetRequestCount() const; + + void AddSupportingTransactions(CTxDB& txdb); + + bool AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs=true); + bool AcceptWalletTransaction(); + + void RelayWalletTransaction(CTxDB& txdb); + void RelayWalletTransaction(); +}; + + + + +class COutput +{ +public: + const CWalletTx *tx; + int i; + int nDepth; + bool fSpendable; + + COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn) + { + tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; + } + + std::string ToString() const + { + return strprintf("COutput(%s, %d, %d, %d) [%s]", tx->GetHash().ToString().substr(0,10).c_str(), i, fSpendable, nDepth, FormatMoney(tx->vout[i].nValue).c_str()); + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + + + +/** Private key that includes an expiration date in case it never gets used. */ +class CWalletKey +{ +public: + CPrivKey vchPrivKey; + int64_t nTimeCreated; + int64_t nTimeExpires; + std::string strComment; + //// todo: add something to note what created it (user, getnewaddress, change) + //// maybe should have a map property map + + CWalletKey(int64_t 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: + CPubKey vchPubKey; + + CAccount() + { + SetNull(); + } + + void SetNull() + { + vchPubKey = CPubKey(); + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(vchPubKey); + ) +}; + + + +/** Internal transfers. + * Database key is acentry. + */ +class CAccountingEntry +{ +public: + std::string strAccount; + int64_t nCreditDebit; + int64_t nTime; + std::string strOtherAccount; + std::string strComment; + mapValue_t mapValue; + int64_t nOrderPos; // position in ordered transaction list + uint64_t nEntryNo; + + CAccountingEntry() + { + SetNull(); + } + + void SetNull() + { + nCreditDebit = 0; + nTime = 0; + strAccount.clear(); + strOtherAccount.clear(); + strComment.clear(); + nOrderPos = -1; + } + + IMPLEMENT_SERIALIZE + ( + CAccountingEntry& me = *const_cast(this); + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + // Note: strAccount is serialized as part of the key, not here. + READWRITE(nCreditDebit); + READWRITE(nTime); + READWRITE(strOtherAccount); + + if (!fRead) + { + WriteOrderPos(nOrderPos, me.mapValue); + + if (!(mapValue.empty() && _ssExtra.empty())) + { + CDataStream ss(nType, nVersion); + ss.insert(ss.begin(), '\0'); + ss << mapValue; + ss.insert(ss.end(), _ssExtra.begin(), _ssExtra.end()); + me.strComment.append(ss.str()); + } + } + + READWRITE(strComment); + + size_t nSepPos = strComment.find("\0", 0, 1); + if (fRead) + { + me.mapValue.clear(); + if (std::string::npos != nSepPos) + { + CDataStream ss(std::vector(strComment.begin() + nSepPos + 1, strComment.end()), nType, nVersion); + ss >> me.mapValue; + me._ssExtra = std::vector(ss.begin(), ss.end()); + } + ReadOrderPos(me.nOrderPos, me.mapValue); + } + if (std::string::npos != nSepPos) + me.strComment.erase(nSepPos); + + me.mapValue.erase("n"); + ) + +private: + std::vector _ssExtra; +}; + +bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut); + +#endif diff --git a/src/walletdb.cpp b/src/walletdb.cpp new file mode 100644 index 00000000..51a2ec9d --- /dev/null +++ b/src/walletdb.cpp @@ -0,0 +1,984 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "walletdb.h" +#include "wallet.h" +#include "base58.h" + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +using namespace std; +using namespace boost; + + +static uint64_t nAccountingEntryNumber = 0; +extern bool fWalletUnlockMintOnly; + +// +// CWalletDB +// + +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) +{ + 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 uint64_t nAccEntryNum, const CAccountingEntry& acentry) +{ + return Write(boost::make_tuple(string("acentry"), acentry.strAccount, nAccEntryNum), acentry); +} + +bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry) +{ + return WriteAccountingEntry(++nAccountingEntryNumber, acentry); +} + +int64_t CWalletDB::GetAccountCreditDebit(const string& strAccount) +{ + list entries; + ListAccountCreditDebit(strAccount, entries); + + int64_t nCreditDebit = 0; + BOOST_FOREACH (const CAccountingEntry& entry, entries) + nCreditDebit += entry.nCreditDebit; + + return nCreditDebit; +} + +void CWalletDB::ListAccountCreditDebit(const string& strAccount, list& entries) +{ + bool fAllAccounts = (strAccount == "*"); + + Dbc* pcursor = GetCursor(); + if (!pcursor) + throw runtime_error("CWalletDB::ListAccountCreditDebit() : cannot create DB cursor"); + unsigned int fFlags = DB_SET_RANGE; + while (true) + { + // Read next record + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + if (fFlags == DB_SET_RANGE) + ssKey << boost::make_tuple(string("acentry"), (fAllAccounts? string("") : strAccount), uint64_t(0)); + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + 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; + ssKey >> acentry.nEntryNo; + entries.push_back(acentry); + } + + pcursor->close(); +} + + +DBErrors +CWalletDB::ReorderTransactions(CWallet* pwallet) +{ + LOCK(pwallet->cs_wallet); + // Old wallets didn't have any defined order for transactions + // Probably a bad idea to change the output of this + + // First: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap. + typedef pair TxPair; + typedef multimap TxItems; + TxItems txByTime; + + for (map::iterator it = pwallet->mapWallet.begin(); it != pwallet->mapWallet.end(); ++it) + { + CWalletTx* wtx = &((*it).second); + txByTime.insert(make_pair(wtx->nTimeReceived, TxPair(wtx, (CAccountingEntry*)0))); + } + list acentries; + ListAccountCreditDebit("", acentries); + BOOST_FOREACH(CAccountingEntry& entry, acentries) + { + txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry))); + } + + int64_t& nOrderPosNext = pwallet->nOrderPosNext; + nOrderPosNext = 0; + std::vector nOrderPosOffsets; + for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it) + { + CWalletTx *const pwtx = (*it).second.first; + CAccountingEntry *const pacentry = (*it).second.second; + int64_t& nOrderPos = (pwtx != 0) ? pwtx->nOrderPos : pacentry->nOrderPos; + + if (nOrderPos == -1) + { + nOrderPos = nOrderPosNext++; + nOrderPosOffsets.push_back(nOrderPos); + + if (pacentry) + // Have to write accounting regardless, since we don't keep it in memory + if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) + return DB_LOAD_FAIL; + } + else + { + int64_t nOrderPosOff = 0; + BOOST_FOREACH(const int64_t& nOffsetStart, nOrderPosOffsets) + { + if (nOrderPos >= nOffsetStart) + ++nOrderPosOff; + } + nOrderPos += nOrderPosOff; + nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1); + + if (!nOrderPosOff) + continue; + + // Since we're changing the order, write it back + if (pwtx) + { + if (!WriteTx(pwtx->GetHash(), *pwtx)) + return DB_LOAD_FAIL; + } + else + if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) + return DB_LOAD_FAIL; + } + } + + return DB_LOAD_OK; +} + +class CWalletScanState { +public: + unsigned int nKeys; + unsigned int nCKeys; + unsigned int nKeyMeta; + bool fIsEncrypted; + bool fAnyUnordered; + int nFileVersion; + vector vWalletUpgrade; + + CWalletScanState() { + nKeys = nCKeys = nKeyMeta = 0; + fIsEncrypted = false; + fAnyUnordered = false; + nFileVersion = 0; + } +}; + +bool +ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, + CWalletScanState &wss, string& strType, string& strErr) +{ + try { + // Unserialize + // Taking advantage of the fact that pair serialization + // is just the two items serialized one after the other + ssKey >> strType; + if (strType == "name") + { + string strAddress; + ssKey >> strAddress; + ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()]; + } + else if (strType == "tx") + { + uint256 hash; + ssKey >> hash; + CWalletTx& wtx = pwallet->mapWallet[hash]; + ssValue >> wtx; + if (wtx.CheckTransaction() && (wtx.GetHash() == hash)) + wtx.BindWallet(pwallet); + else + { + pwallet->mapWallet.erase(hash); + return false; + } + + // Undo serialize changes in 31600 + if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703) + { + if (!ssValue.empty()) + { + char fTmp; + char fUnused; + ssValue >> fTmp >> fUnused >> wtx.strFromAccount; + strErr = strprintf("LoadWallet() upgrading tx ver=%d %d '%s' %s", + wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount.c_str(), hash.ToString().c_str()); + wtx.fTimeReceivedIsTxTime = fTmp; + } + else + { + strErr = strprintf("LoadWallet() repairing tx ver=%d %s", wtx.fTimeReceivedIsTxTime, hash.ToString().c_str()); + wtx.fTimeReceivedIsTxTime = 0; + } + wss.vWalletUpgrade.push_back(hash); + } + + if (wtx.nOrderPos == -1) + wss.fAnyUnordered = true; + + //// debug print + //printf("LoadWallet %s\n", wtx.GetHash().ToString().c_str()); + //printf(" %12"PRId64" %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_t nNumber; + ssKey >> nNumber; + if (nNumber > nAccountingEntryNumber) + nAccountingEntryNumber = nNumber; + + if (!wss.fAnyUnordered) + { + CAccountingEntry acentry; + ssValue >> acentry; + if (acentry.nOrderPos == -1) + wss.fAnyUnordered = true; + } + } + else if (strType == "watchs") + { + CScript script; + ssKey >> script; + char fYes; + ssValue >> fYes; + if (fYes == '1') + pwallet->LoadWatchOnly(script); + + // Watch-only addresses have no birthday information for now, + // so set the wallet birthday to the beginning of time. + pwallet->nTimeFirstKey = 1; + } + else if (strType == "key" || strType == "wkey") + { + vector vchPubKey; + ssKey >> vchPubKey; + CKey key; + if (strType == "key") + { + wss.nKeys++; + CPrivKey pkey; + ssValue >> pkey; + key.SetPubKey(vchPubKey); + if (!key.SetPrivKey(pkey)) + { + strErr = "Error reading wallet database: CPrivKey corrupt"; + return false; + } + if (key.GetPubKey() != vchPubKey) + { + strErr = "Error reading wallet database: CPrivKey pubkey inconsistency"; + return false; + } + if (!key.IsValid()) + { + strErr = "Error reading wallet database: invalid CPrivKey"; + return false; + } + } + else + { + CWalletKey wkey; + ssValue >> wkey; + key.SetPubKey(vchPubKey); + if (!key.SetPrivKey(wkey.vchPrivKey)) + { + strErr = "Error reading wallet database: CPrivKey corrupt"; + return false; + } + if (key.GetPubKey() != vchPubKey) + { + strErr = "Error reading wallet database: CWalletKey pubkey inconsistency"; + return false; + } + if (!key.IsValid()) + { + strErr = "Error reading wallet database: invalid CWalletKey"; + return false; + } + } + if (!pwallet->LoadKey(key)) + { + strErr = "Error reading wallet database: LoadKey failed"; + return false; + } + } + else if (strType == "mkey") + { + unsigned int nID; + ssKey >> nID; + CMasterKey kMasterKey; + ssValue >> kMasterKey; + + if(pwallet->mapMasterKeys.count(nID) != 0) + { + strErr = strprintf("Error reading wallet database: duplicate CMasterKey id %u", nID); + return false; + } + pwallet->mapMasterKeys[nID] = kMasterKey; + if (pwallet->nMasterKeyMaxID < nID) + pwallet->nMasterKeyMaxID = nID; + } + else if (strType == "ckey") + { + wss.nCKeys++; + vector vchPubKey; + ssKey >> vchPubKey; + vector vchPrivKey; + ssValue >> vchPrivKey; + if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey)) + { + strErr = "Error reading wallet database: LoadCryptedKey failed"; + return false; + } + wss.fIsEncrypted = true; + } + else if (strType == "keymeta") + { + CPubKey vchPubKey; + ssKey >> vchPubKey; + CKeyMetadata keyMeta; + ssValue >> keyMeta; + wss.nKeyMeta++; + + pwallet->LoadKeyMetadata(vchPubKey, keyMeta); + + // find earliest key creation time, as wallet birthday + if (!pwallet->nTimeFirstKey || + (keyMeta.nCreateTime < pwallet->nTimeFirstKey)) + pwallet->nTimeFirstKey = keyMeta.nCreateTime; + } + else if (strType == "defaultkey") + { + ssValue >> pwallet->vchDefaultKey; + } + else if (strType == "pool") + { + int64_t nIndex; + ssKey >> nIndex; + CKeyPool keypool; + ssValue >> keypool; + pwallet->setKeyPool.insert(nIndex); + + // If no metadata exists yet, create a default with the pool key's + // creation time. Note that this may be overwritten by actually + // stored metadata for that key later, which is fine. + CKeyID keyid = keypool.vchPubKey.GetID(); + if (pwallet->mapKeyMetadata.count(keyid) == 0) + pwallet->mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime); + + } + else if (strType == "version") + { + ssValue >> wss.nFileVersion; + if (wss.nFileVersion == 10300) + wss.nFileVersion = 300; + } + else if (strType == "cscript") + { + uint160 hash; + ssKey >> hash; + CScript script; + ssValue >> script; + if (!pwallet->LoadCScript(script)) + { + strErr = "Error reading wallet database: LoadCScript failed"; + return false; + } + } + else if (strType == "orderposnext") + { + ssValue >> pwallet->nOrderPosNext; + } + } catch (...) + { + return false; + } + return true; +} + +static bool IsKeyType(string strType) +{ + return (strType== "key" || strType == "wkey" || + strType == "mkey" || strType == "ckey"); +} + +DBErrors CWalletDB::LoadWallet(CWallet* pwallet) +{ + pwallet->vchDefaultKey = CPubKey(); + CWalletScanState wss; + bool fNoncriticalErrors = false; + DBErrors result = DB_LOAD_OK; + + try { + LOCK(pwallet->cs_wallet); + int nMinVersion = 0; + if (Read((string)"minversion", nMinVersion)) + { + if (nMinVersion > CLIENT_VERSION) + return DB_TOO_NEW; + pwallet->LoadMinVersion(nMinVersion); + } + + // Get cursor + Dbc* pcursor = GetCursor(); + if (!pcursor) + { + printf("Error getting wallet database cursor\n"); + return DB_CORRUPT; + } + + while (true) + { + // Read next record + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + int ret = ReadAtCursor(pcursor, ssKey, ssValue); + if (ret == DB_NOTFOUND) + break; + else if (ret != 0) + { + printf("Error reading next record from wallet database\n"); + return DB_CORRUPT; + } + + // Try to be tolerant of single corrupt records: + string strType, strErr; + if (!ReadKeyValue(pwallet, ssKey, ssValue, wss, strType, strErr)) + { + // losing keys is considered a catastrophic error, anything else + // we assume the user can live with: + if (IsKeyType(strType)) + result = DB_CORRUPT; + else + { + // Leave other errors alone, if we try to fix them we might make things worse. + fNoncriticalErrors = true; // ... but do warn the user there is something wrong. + if (strType == "tx") + // Rescan if there is a bad transaction record: + SoftSetBoolArg("-rescan", true); + } + } + if (!strErr.empty()) + printf("%s\n", strErr.c_str()); + } + pcursor->close(); + } + catch (...) + { + result = DB_CORRUPT; + } + + if (fNoncriticalErrors && result == DB_LOAD_OK) + result = DB_NONCRITICAL_ERROR; + + // Any wallet corruption at all: skip any rewriting or + // upgrading, we don't want to make it worse. + if (result != DB_LOAD_OK) + return result; + + printf("nFileVersion = %d\n", wss.nFileVersion); + + printf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total\n", + wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys); + + // nTimeFirstKey is only reliable if all keys have metadata + if ((wss.nKeys + wss.nCKeys) != wss.nKeyMeta) + pwallet->nTimeFirstKey = 1; // 0 would be considered 'no value' + + + BOOST_FOREACH(uint256 hash, wss.vWalletUpgrade) + WriteTx(hash, pwallet->mapWallet[hash]); + + // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc: + if (wss.fIsEncrypted && (wss.nFileVersion == 40000 || wss.nFileVersion == 50000)) + return DB_NEED_REWRITE; + + if (wss.nFileVersion < CLIENT_VERSION) // Update + WriteVersion(CLIENT_VERSION); + + if (wss.fAnyUnordered) + result = ReorderTransactions(pwallet); + + return result; +} + +DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector& vTxHash) +{ + pwallet->vchDefaultKey = CPubKey(); + CWalletScanState wss; + bool fNoncriticalErrors = false; + DBErrors result = DB_LOAD_OK; + + try { + LOCK(pwallet->cs_wallet); + int nMinVersion = 0; + if (Read((string)"minversion", nMinVersion)) + { + if (nMinVersion > CLIENT_VERSION) + return DB_TOO_NEW; + pwallet->LoadMinVersion(nMinVersion); + } + + // Get cursor + Dbc* pcursor = GetCursor(); + if (!pcursor) + { + printf("Error getting wallet database cursor\n"); + return DB_CORRUPT; + } + + while (true) + { + // Read next record + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + int ret = ReadAtCursor(pcursor, ssKey, ssValue); + if (ret == DB_NOTFOUND) + break; + else if (ret != 0) + { + printf("Error reading next record from wallet database\n"); + return DB_CORRUPT; + } + + string strType; + ssKey >> strType; + if (strType == "tx") { + uint256 hash; + ssKey >> hash; + + vTxHash.push_back(hash); + } + } + pcursor->close(); + } + catch (boost::thread_interrupted) { + throw; + } + catch (...) { + result = DB_CORRUPT; + } + + if (fNoncriticalErrors && result == DB_LOAD_OK) + result = DB_NONCRITICAL_ERROR; + + return result; +} + +DBErrors CWalletDB::ZapWalletTx(CWallet* pwallet) +{ + // build list of wallet TXs + vector vTxHash; + DBErrors err = FindWalletTx(pwallet, vTxHash); + if (err != DB_LOAD_OK) + return err; + + // erase each wallet TX + BOOST_FOREACH (uint256& hash, vTxHash) { + if (!EraseTx(hash)) + return DB_CORRUPT; + } + + return DB_LOAD_OK; +} + +void ThreadFlushWalletDB(void* parg) +{ + // Make this thread recognisable as the wallet flushing thread + RenameThread("XP-wallet"); + + const string& strFile = ((const string*)parg)[0]; + static bool fOneThread; + if (fOneThread) + return; + fOneThread = true; + if (!GetBoolArg("-flushwallet", true)) + return; + + unsigned int nLastSeen = nWalletDBUpdated; + unsigned int nLastFlushed = nWalletDBUpdated; + int64_t nLastWalletUpdate = GetTime(); + while (!fShutdown) + { + Sleep(500); + + if (nLastSeen != nWalletDBUpdated) + { + nLastSeen = nWalletDBUpdated; + nLastWalletUpdate = GetTime(); + } + + if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2) + { + TRY_LOCK(bitdb.cs_db,lockDb); + if (lockDb) + { + // Don't do this if any databases are in use + int nRefCount = 0; + map::iterator mi = bitdb.mapFileUseCount.begin(); + while (mi != bitdb.mapFileUseCount.end()) + { + nRefCount += (*mi).second; + mi++; + } + + if (nRefCount == 0 && !fShutdown) + { + map::iterator mi = bitdb.mapFileUseCount.find(strFile); + if (mi != bitdb.mapFileUseCount.end()) + { + printf("Flushing wallet.dat\n"); + nLastFlushed = nWalletDBUpdated; + int64_t nStart = GetTimeMillis(); + + // Flush wallet.dat so it's self contained + bitdb.CloseDb(strFile); + bitdb.CheckpointLSN(strFile); + + bitdb.mapFileUseCount.erase(mi++); + printf("Flushed wallet.dat %" PRId64 "ms\n", GetTimeMillis() - nStart); + } + } + } + } + } +} + +bool BackupWallet(const CWallet& wallet, const string& strDest) +{ + if (!wallet.fFileBacked) + return false; + while (!fShutdown) + { + { + LOCK(bitdb.cs_db); + if (!bitdb.mapFileUseCount.count(wallet.strWalletFile) || bitdb.mapFileUseCount[wallet.strWalletFile] == 0) + { + // Flush log data to the dat file + bitdb.CloseDb(wallet.strWalletFile); + bitdb.CheckpointLSN(wallet.strWalletFile); + bitdb.mapFileUseCount.erase(wallet.strWalletFile); + + // Copy wallet.dat + filesystem::path pathSrc = GetDataDir() / wallet.strWalletFile; + filesystem::path pathDest(strDest); + if (filesystem::is_directory(pathDest)) + pathDest /= wallet.strWalletFile; + + try { +#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 true; + } catch(const filesystem::filesystem_error &e) { + printf("error copying wallet.dat to %s - %s\n", pathDest.string().c_str(), e.what()); + return false; + } + } + } + Sleep(100); + } + return false; +} + +bool DumpWallet(CWallet* pwallet, const string& strDest) +{ + + if (!pwallet->fFileBacked) + return false; + while (!fShutdown) + { + // Populate maps + std::map mapKeyBirth; + std::set setKeyPool; + pwallet->GetKeyBirthTimes(mapKeyBirth); + pwallet->GetAllReserveKeys(setKeyPool); + + // sort time/key pairs + std::vector > vKeyBirth; + for (std::map::const_iterator it = mapKeyBirth.begin(); it != mapKeyBirth.end(); it++) { + vKeyBirth.push_back(std::make_pair(it->second, it->first)); + } + mapKeyBirth.clear(); + std::sort(vKeyBirth.begin(), vKeyBirth.end()); + + // open outputfile as a stream + ofstream file; + file.open(strDest.c_str()); + if (!file.is_open()) + return false; + + // produce output + file << strprintf("# Wallet dump created by XP %s (%s)\n", CLIENT_BUILD.c_str(), CLIENT_DATE.c_str()); + file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime()).c_str()); + file << strprintf("# * Best block at time of backup was %i (%s),\n", nBestHeight, hashBestChain.ToString().c_str()); + file << strprintf("# mined on %s\n", EncodeDumpTime(pindexBest->nTime).c_str()); + file << "\n"; + for (std::vector >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) { + const CKeyID &keyid = it->second; + std::string strTime = EncodeDumpTime(it->first); + std::string strAddr = CBitcoinAddress(keyid).ToString(); + bool IsCompressed; + + CKey key; + if (pwallet->GetKey(keyid, key)) { + if (pwallet->mapAddressBook.count(keyid)) { + CSecret secret = key.GetSecret(IsCompressed); + file << strprintf("%s %s label=%s # addr=%s\n", + CBitcoinSecret(secret, IsCompressed).ToString().c_str(), + strTime.c_str(), + EncodeDumpString(pwallet->mapAddressBook[keyid]).c_str(), + strAddr.c_str()); + } else if (setKeyPool.count(keyid)) { + CSecret secret = key.GetSecret(IsCompressed); + file << strprintf("%s %s reserve=1 # addr=%s\n", + CBitcoinSecret(secret, IsCompressed).ToString().c_str(), + strTime.c_str(), + strAddr.c_str()); + } else { + CSecret secret = key.GetSecret(IsCompressed); + file << strprintf("%s %s change=1 # addr=%s\n", + CBitcoinSecret(secret, IsCompressed).ToString().c_str(), + strTime.c_str(), + strAddr.c_str()); + } + } + } + file << "\n"; + file << "# End of dump\n"; + file.close(); + return true; + } + return false; +} + + +bool ImportWallet(CWallet *pwallet, const string& strLocation) +{ + + if (!pwallet->fFileBacked) + return false; + while (!fShutdown) + { + // open inputfile as stream + ifstream file; + file.open(strLocation.c_str()); + if (!file.is_open()) + return false; + + int64_t nTimeBegin = pindexBest->nTime; + + bool fGood = true; + + // read through input file checking and importing keys into wallet. + while (file.good()) { + std::string line; + std::getline(file, line); + if (line.empty() || line[0] == '#') + continue; + + std::vector vstr; + boost::split(vstr, line, boost::is_any_of(" ")); + if (vstr.size() < 2) + continue; + CBitcoinSecret vchSecret; + if (!vchSecret.SetString(vstr[0])) + continue; + + bool fCompressed; + CKey key; + CSecret secret = vchSecret.GetSecret(fCompressed); + key.SetSecret(secret, fCompressed); + CKeyID keyid = key.GetPubKey().GetID(); + + if (pwallet->HaveKey(keyid)) { + printf("Skipping import of %s (key already present)\n", CBitcoinAddress(keyid).ToString().c_str()); + continue; + } + int64_t nTime = DecodeDumpTime(vstr[1]); + std::string strLabel; + bool fLabel = true; + for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) { + if (boost::algorithm::starts_with(vstr[nStr], "#")) + break; + if (vstr[nStr] == "change=1") + fLabel = false; + if (vstr[nStr] == "reserve=1") + fLabel = false; + if (boost::algorithm::starts_with(vstr[nStr], "label=")) { + strLabel = DecodeDumpString(vstr[nStr].substr(6)); + fLabel = true; + } + } + printf("Importing %s...\n", CBitcoinAddress(keyid).ToString().c_str()); + if (!pwallet->AddKey(key)) { + fGood = false; + continue; + } + pwallet->mapKeyMetadata[keyid].nCreateTime = nTime; + if (fLabel) + pwallet->SetAddressBookName(keyid, strLabel); + nTimeBegin = std::min(nTimeBegin, nTime); + } + file.close(); + + // rescan block chain looking for coins from new keys + CBlockIndex *pindex = pindexBest; + while (pindex && pindex->pprev && pindex->nTime > nTimeBegin - 7200) + pindex = pindex->pprev; + + printf("Rescanning last %i blocks\n", pindexBest->nHeight - pindex->nHeight + 1); + pwallet->ScanForWalletTransactions(pindex); + pwallet->ReacceptWalletTransactions(); + pwallet->MarkDirty(); + + return fGood; + + } + + return false; + +} + + +// +// Try to (very carefully!) recover wallet.dat if there is a problem. +// +bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys) +{ + // Recovery procedure: + // move wallet.dat to wallet.timestamp.bak + // Call Salvage with fAggressive=true to + // get as much data as possible. + // Rewrite salvaged data to wallet.dat + // Set -rescan so any missing transactions will be + // found. + int64_t now = GetTime(); + std::string newFilename = strprintf("wallet.%" PRId64 ".bak", now); + + int result = dbenv.dbenv.dbrename(NULL, filename.c_str(), NULL, + newFilename.c_str(), DB_AUTO_COMMIT); + if (result == 0) + printf("Renamed %s to %s\n", filename.c_str(), newFilename.c_str()); + else + { + printf("Failed to rename %s to %s\n", filename.c_str(), newFilename.c_str()); + return false; + } + + std::vector salvagedData; + bool allOK = dbenv.Salvage(newFilename, true, salvagedData); + if (salvagedData.empty()) + { + printf("Salvage(aggressive) found no records in %s.\n", newFilename.c_str()); + return false; + } + printf("Salvage(aggressive) found %" PRIszu " records\n", salvagedData.size()); + + bool fSuccess = allOK; + Db* pdbCopy = new Db(&dbenv.dbenv, 0); + int ret = pdbCopy->open(NULL, // Txn pointer + filename.c_str(), // Filename + "main", // Logical db name + DB_BTREE, // Database type + DB_CREATE, // Flags + 0); + if (ret > 0) + { + printf("Cannot create database file %s\n", filename.c_str()); + return false; + } + CWallet dummyWallet; + CWalletScanState wss; + + DbTxn* ptxn = dbenv.TxnBegin(); + BOOST_FOREACH(CDBEnv::KeyValPair& row, salvagedData) + { + if (fOnlyKeys) + { + CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION); + CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION); + string strType, strErr; + bool fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue, + wss, strType, strErr); + if (!IsKeyType(strType)) + continue; + if (!fReadOK) + { + printf("WARNING: CWalletDB::Recover skipping %s: %s\n", strType.c_str(), strErr.c_str()); + continue; + } + } + Dbt datKey(&row.first[0], row.first.size()); + Dbt datValue(&row.second[0], row.second.size()); + int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE); + if (ret2 > 0) + fSuccess = false; + } + ptxn->commit(0); + pdbCopy->close(0); + delete pdbCopy; + + return fSuccess; +} + +bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename) +{ + return CWalletDB::Recover(dbenv, filename, false); +} diff --git a/src/walletdb.h b/src/walletdb.h new file mode 100644 index 00000000..6b6f55d8 --- /dev/null +++ b/src/walletdb.h @@ -0,0 +1,214 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_WALLETDB_H +#define BITCOIN_WALLETDB_H + +#include "db.h" +//#include "base58.h" +#include "keystore.h" + +class CKeyPool; +class CAccount; +class CAccountingEntry; + +/** Error statuses for the wallet database */ +enum DBErrors +{ + DB_LOAD_OK, + DB_CORRUPT, + DB_NONCRITICAL_ERROR, + DB_TOO_NEW, + DB_LOAD_FAIL, + DB_NEED_REWRITE +}; + +class CKeyMetadata +{ +public: + static const int CURRENT_VERSION=1; + int nVersion; + int64_t nCreateTime; // 0 means unknown + + CKeyMetadata() + { + SetNull(); + } + CKeyMetadata(int64_t nCreateTime_) + { + nVersion = CKeyMetadata::CURRENT_VERSION; + nCreateTime = nCreateTime_; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(nCreateTime); + ) + + void SetNull() + { + nVersion = CKeyMetadata::CURRENT_VERSION; + nCreateTime = 0; + } +}; + + +/** Access to the wallet database (wallet.dat) */ +class CWalletDB : public CDB +{ +public: + CWalletDB(std::string strFilename, const char* pszMode="r+") : CDB(strFilename.c_str(), pszMode) + { + } +private: + CWalletDB(const CWalletDB&); + void operator=(const CWalletDB&); +public: + bool WriteName(const std::string& strAddress, const std::string& strName); + + bool EraseName(const std::string& strAddress); + + 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 WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata &keyMeta) + { + nWalletDBUpdated++; + if(!Write(std::make_pair(std::string("keymeta"), vchPubKey), keyMeta)) + return false; + + if(!Write(std::make_pair(std::string("key"), vchPubKey.Raw()), vchPrivKey, false)) + return false; + + return true; + } + + bool WriteCryptedKey(const CPubKey& vchPubKey, const std::vector& vchCryptedSecret, const CKeyMetadata &keyMeta) + { + nWalletDBUpdated++; + bool fEraseUnencryptedKey = true; + + if(!Write(std::make_pair(std::string("keymeta"), vchPubKey), keyMeta)) + return false; + + if (!Write(std::make_pair(std::string("ckey"), vchPubKey.Raw()), vchCryptedSecret, false)) + return false; + if (fEraseUnencryptedKey) + { + Erase(std::make_pair(std::string("key"), vchPubKey.Raw())); + Erase(std::make_pair(std::string("wkey"), vchPubKey.Raw())); + } + return true; + } + + bool WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey) + { + nWalletDBUpdated++; + return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true); + } + + bool EraseMasterKey(unsigned int nID) + { + nWalletDBUpdated++; + return Erase(std::make_pair(std::string("mkey"), nID)); + } + + bool EraseCryptedKey(const CPubKey& vchPubKey) + { + return Erase(std::make_pair(std::string("ckey"), vchPubKey.Raw())); + } + + bool WriteCScript(const uint160& hash, const CScript& redeemScript) + { + nWalletDBUpdated++; + return Write(std::make_pair(std::string("cscript"), hash), redeemScript, false); + } + + bool WriteWatchOnly(const CScript &dest) + { + nWalletDBUpdated++; + return Write(std::make_pair(std::string("watchs"), dest), '1'); + } + + bool EraseWatchOnly(const CScript &dest) + { + nWalletDBUpdated++; + return Erase(std::make_pair(std::string("watchs"), dest)); + } + + bool WriteBestBlock(const CBlockLocator& locator) + { + nWalletDBUpdated++; + return Write(std::string("bestblock"), locator); + } + + bool ReadBestBlock(CBlockLocator& locator) + { + return Read(std::string("bestblock"), locator); + } + + bool WriteOrderPosNext(int64_t nOrderPosNext) + { + nWalletDBUpdated++; + return Write(std::string("orderposnext"), nOrderPosNext); + } + + bool WriteDefaultKey(const CPubKey& vchPubKey) + { + nWalletDBUpdated++; + return Write(std::string("defaultkey"), vchPubKey.Raw()); + } + + bool ReadPool(int64_t nPool, CKeyPool& keypool) + { + return Read(std::make_pair(std::string("pool"), nPool), keypool); + } + + bool WritePool(int64_t nPool, const CKeyPool& keypool) + { + nWalletDBUpdated++; + return Write(std::make_pair(std::string("pool"), nPool), keypool); + } + + bool ErasePool(int64_t nPool) + { + nWalletDBUpdated++; + return Erase(std::make_pair(std::string("pool"), nPool)); + } + + bool WriteMinVersion(int nVersion) + { + return Write(std::string("minversion"), nVersion); + } + + bool ReadAccount(const std::string& strAccount, CAccount& account); + bool WriteAccount(const std::string& strAccount, const CAccount& account); +private: + bool WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry); +public: + bool WriteAccountingEntry(const CAccountingEntry& acentry); + int64_t GetAccountCreditDebit(const std::string& strAccount); + void ListAccountCreditDebit(const std::string& strAccount, std::list& acentries); + + DBErrors ReorderTransactions(CWallet*); + DBErrors LoadWallet(CWallet* pwallet); + DBErrors FindWalletTx(CWallet* pwallet, std::vector& vTxHash); + DBErrors ZapWalletTx(CWallet* pwallet); + + static bool Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys); + static bool Recover(CDBEnv& dbenv, std::string filename); +}; + +#endif // BITCOIN_WALLETDB_H diff --git a/xp.pro b/xp.pro new file mode 100644 index 00000000..c29e3a6a --- /dev/null +++ b/xp.pro @@ -0,0 +1,499 @@ +TEMPLATE = app +TARGET = XP-qt +VERSION = 0.7.5 +INCLUDEPATH += src src/json src/qt +QT += core gui network +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets +DEFINES += QT_GUI BOOST_THREAD_USE_LIB BOOST_SPIRIT_THREADSAFE __STDC_FORMAT_MACROS __STDC_LIMIT_MACROS +CONFIG += no_include_pwd +CONFIG += thread +CONFIG += static + +freebsd-g++: QMAKE_TARGET.arch = $$QMAKE_HOST.arch +linux-g++: QMAKE_TARGET.arch = $$QMAKE_HOST.arch +linux-g++-32: QMAKE_TARGET.arch = i686 +linux-g++-64: QMAKE_TARGET.arch = x86_64 +win32-g++-cross: QMAKE_TARGET.arch = $$TARGET_PLATFORM + +# for boost 1.37, add -mt to the boost libraries +# use: qmake BOOST_LIB_SUFFIX=-mt +# for boost thread win32 with _win32 sufix +# use: BOOST_THREAD_LIB_SUFFIX=_win32-... +# or when linking against a specific BerkelyDB version: BDB_LIB_SUFFIX=-6.1 + +# Dependency library locations can be customized with: +# BOOST_INCLUDE_PATH, BOOST_LIB_PATH, BDB_INCLUDE_PATH, +# BDB_LIB_PATH, OPENSSL_INCLUDE_PATH and OPENSSL_LIB_PATH respectively + +windows:LIBS += -lshlwapi +LIBS += $$join(BOOST_LIB_PATH,,-L,) $$join(BDB_LIB_PATH,,-L,) $$join(OPENSSL_LIB_PATH,,-L,) $$join(QRENCODE_LIB_PATH,,-L,) +LIBS += -lssl -lcrypto -ldb_cxx$$BDB_LIB_SUFFIX +windows:LIBS += -lws2_32 -lole32 -loleaut32 -luuid -lgdi32 +LIBS += -lboost_system-mgw46-mt-sd-1_53 -lboost_filesystem-mgw46-mt-sd-1_53 -lboost_program_options-mgw46-mt-sd-1_53 -lboost_thread-mgw46-mt-sd-1_53 +BOOST_LIB_SUFFIX=-mgw46-mt-sd-1_53 +BOOST_INCLUDE_PATH=C:/deps/boost_1_53_0 +BOOST_LIB_PATH=C:/deps/boost_1_53_0/stage/lib +BDB_INCLUDE_PATH=c:/deps/db-4.8.30.NC/build_unix +BDB_LIB_PATH=c:/deps/db-4.8.30.NC/build_unix +OPENSSL_INCLUDE_PATH=c:/deps/openssl-1.0.2d/include +OPENSSL_LIB_PATH=c:/deps/openssl-1.0.2d +MINIUPNPC_INCLUDE_PATH=C:/deps/ +MINIUPNPC_LIB_PATH=C:/deps/miniupnpc +QRENCODE_INCLUDE_PATH=C:/deps/qrencode-3.4.4 +QRENCODE_LIB_PATH=C:/deps/qrencode-3.4.4/.libs + +OBJECTS_DIR = build +MOC_DIR = build +UI_DIR = build + +# use: qmake "RELEASE=1" +contains(RELEASE, 1) { + macx:QMAKE_CXXFLAGS += -isysroot /Applications/Xcode-beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk -mmacosx-version-min=10.7 + macx:QMAKE_CFLAGS += -isysroot /Applications/Xcode-beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk -mmacosx-version-min=10.7 + macx:QMAKE_OBJECTIVE_CFLAGS += -isysroot /Applications/Xcode-beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk -mmacosx-version-min=10.7 + + !windows:!macx { + # Linux: static link + LIBS += -Wl,-Bstatic + } +} + +!win32 { +# for extra security against potential buffer overflows: enable GCCs Stack Smashing Protection +QMAKE_CXXFLAGS *= -fstack-protector-all --param ssp-buffer-size=1 +QMAKE_LFLAGS *= -fstack-protector-all --param ssp-buffer-size=1 +# We need to exclude this for Windows cross compile with MinGW 4.2.x, as it will result in a non-working executable! +# This can be enabled for Windows, when we switch to MinGW >= 4.4.x. +} +# for extra security on Windows: enable ASLR and DEP via GCC linker flags + +win32:QMAKE_LFLAGS *= -Wl,--dynamicbase -Wl,--nxcompat +win32:QMAKE_LFLAGS += -static-libgcc -static-libstdc++ + +# use: qmake "USE_DBUS=1" +contains(USE_DBUS, 1) { + message(Building with DBUS (Freedesktop notifications) support) + DEFINES += USE_DBUS + QT += dbus +} + +# use: qmake "USE_IPV6=1" ( enabled by default; default) +# or: qmake "USE_IPV6=0" (disabled by default) +# or: qmake "USE_IPV6=-" (not supported) +contains(USE_IPV6, -) { + message(Building without IPv6 support) +} else { + count(USE_IPV6, 0) { + USE_IPV6=1 + } + DEFINES += USE_IPV6=$$USE_IPV6 +} + +contains(BITCOIN_NEED_QT_PLUGINS, 1) { + DEFINES += BITCOIN_NEED_QT_PLUGINS + QTPLUGIN += qcncodecs qjpcodecs qtwcodecs qkrcodecs qtaccessiblewidgets +} + +contains(USE_LEVELDB, 1) { + message(Building with LevelDB transaction index) + DEFINES += USE_LEVELDB + + INCLUDEPATH += src/leveldb/include src/leveldb/helpers + LIBS += $$PWD/src/leveldb/libleveldb.a $$PWD/src/leveldb/libmemenv.a + SOURCES += src/txdb-leveldb.cpp + !win32 { + # we use QMAKE_CXXFLAGS_RELEASE even without RELEASE=1 because we use RELEASE to indicate linking preferences not -O preferences + genleveldb.commands = cd $$PWD/src/leveldb && CC=$$QMAKE_CC CXX=$$QMAKE_CXX $(MAKE) OPT=\"$$QMAKE_CXXFLAGS $$QMAKE_CXXFLAGS_RELEASE\" libleveldb.a libmemenv.a + } else { + # make an educated guess about what the ranlib command is called + isEmpty(QMAKE_RANLIB) { + QMAKE_RANLIB = $$replace(QMAKE_STRIP, strip, ranlib) + } + LIBS += -lshlwapi + #genleveldb.commands = cd $$PWD/src/leveldb && CC=$$QMAKE_CC CXX=$$QMAKE_CXX TARGET_OS=OS_WINDOWS_CROSSCOMPILE $(MAKE) OPT=\"$$QMAKE_CXXFLAGS $$QMAKE_CXXFLAGS_RELEASE\" libleveldb.a libmemenv.a && $$QMAKE_RANLIB $$PWD/src/leveldb/libleveldb.a && $$QMAKE_RANLIB $$PWD/src/leveldb/libmemenv.a + } + genleveldb.target = $$PWD/src/leveldb/libleveldb.a + genleveldb.depends = FORCE + PRE_TARGETDEPS += $$PWD/src/leveldb/libleveldb.a + QMAKE_EXTRA_TARGETS += genleveldb + # Gross ugly hack that depends on qmake internals, unfortunately there is no other way to do it. + QMAKE_CLEAN += $$PWD/src/leveldb/libleveldb.a; cd $$PWD/src/leveldb ; $(MAKE) clean +} else { + message(Building with Berkeley DB transaction index) + SOURCES += src/txdb-bdb.cpp +} + + +# use: qmake "USE_ASM=1" +contains(USE_ASM, 1) { + message(Using assembler scrypt & sha256 implementations) + DEFINES += USE_ASM + + contains(QMAKE_TARGET.arch, i386) | contains(QMAKE_TARGET.arch, i586) | contains(QMAKE_TARGET.arch, i686) { + message("x86 platform, setting -msse2 & -mssse3 flags") + + QMAKE_CXXFLAGS += -msse2 -mssse3 + QMAKE_CFLAGS += -msse2 -mssse3 + } + + contains(QMAKE_TARGET.arch, x86_64) | contains(QMAKE_TARGET.arch, amd64) { + message("x86_64 platform, setting -mssse3 flag") + + QMAKE_CXXFLAGS += -mssse3 + QMAKE_CFLAGS += -mssse3 + } + + + SOURCES += src/crypto/scrypt/asm/scrypt-arm.S src/crypto/scrypt/asm/scrypt-x86.S src/crypto/scrypt/asm/scrypt-x86_64.S src/crypto/scrypt/asm/asm-wrapper.cpp + SOURCES += src/crypto/sha2/asm/sha2-arm.S src/crypto/sha2/asm/sha2-x86.S src/crypto/sha2/asm/sha2-x86_64.S +} else { + # use: qmake "USE_SSE2=1" + contains(USE_SSE2, 1) { + message(Using SSE2 intrinsic scrypt implementation & generic sha256 implementation) + SOURCES += src/crypto/scrypt/intrin/scrypt-sse2.cpp + DEFINES += USE_SSE2 + QMAKE_CXXFLAGS += -msse2 + QMAKE_CFLAGS += -msse2 + } else { + message(Using generic scrypt & sha256 implementations) + SOURCES += src/crypto/scrypt/generic/scrypt-generic.cpp + } +} + +# regenerate src/build.h +!windows|contains(USE_BUILD_INFO, 1) { + genbuild.depends = FORCE + genbuild.commands = cd $$PWD; /bin/sh share/genbuild.sh $$OUT_PWD/build/build.h + genbuild.target = $$OUT_PWD/build/build.h + PRE_TARGETDEPS += $$OUT_PWD/build/build.h + QMAKE_EXTRA_TARGETS += genbuild + DEFINES += HAVE_BUILD_INFO +} + +contains(USE_O3, 1) { + message(Building O3 optimization flag) + QMAKE_CXXFLAGS_RELEASE -= -O2 + QMAKE_CFLAGS_RELEASE -= -O2 + QMAKE_CXXFLAGS += -O3 + QMAKE_CFLAGS += -O3 +} + + +QMAKE_CXXFLAGS_WARN_ON = -fdiagnostics-show-option -Wall -Wextra -Wno-ignored-qualifiers -Wformat -Wformat-security -Wno-unused-parameter -Wno-unused-local-typedef -Wstack-protector + +# Input +DEPENDPATH += src src/json src/qt +HEADERS += src/qt/bitcoingui.h \ + src/qt/intro.h \ + src/qt/transactiontablemodel.h \ + src/qt/addresstablemodel.h \ + src/qt/optionsdialog.h \ + src/qt/coincontroldialog.h \ + src/qt/coincontroltreewidget.h \ + src/qt/sendcoinsdialog.h \ + src/qt/addressbookpage.h \ + src/qt/signverifymessagedialog.h \ + src/qt/aboutdialog.h \ + src/qt/editaddressdialog.h \ + src/qt/bitcoinaddressvalidator.h \ + src/qt/mintingfilterproxy.h \ + src/qt/mintingtablemodel.h \ + src/qt/mintingview.h \ + src/kernelrecord.h \ + src/alert.h \ + src/addrman.h \ + src/base58.h \ + src/bignum.h \ + src/checkpoints.h \ + src/compat.h \ + src/coincontrol.h \ + src/sync.h \ + src/util.h \ + src/timestamps.h \ + src/hash.h \ + src/uint256.h \ + src/kernel.h \ + src/kernel_worker.h \ + src/scrypt.h \ + src/serialize.h \ + src/main.h \ + src/miner.h \ + src/net.h \ + src/ministun.h \ + src/key.h \ + src/db.h \ + src/txdb.h \ + src/walletdb.h \ + src/script.h \ + src/init.h \ + src/irc.h \ + src/mruset.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/qt/clientmodel.h \ + src/qt/guiutil.h \ + src/qt/transactionrecord.h \ + src/qt/guiconstants.h \ + src/qt/optionsmodel.h \ + src/qt/monitoreddatamapper.h \ + src/qt/transactiondesc.h \ + src/qt/transactiondescdialog.h \ + src/qt/bitcoinamountfield.h \ + src/wallet.h \ + src/keystore.h \ + src/qt/transactionfilterproxy.h \ + src/qt/transactionview.h \ + src/qt/walletmodel.h \ + src/bitcoinrpc.h \ + src/qt/overviewpage.h \ + src/qt/csvmodelwriter.h \ + src/crypter.h \ + src/qt/sendcoinsentry.h \ + src/qt/qvalidatedlineedit.h \ + src/qt/bitcoinunits.h \ + src/qt/qvaluecombobox.h \ + src/qt/askpassphrasedialog.h \ + src/qt/trafficgraphwidget.h \ + src/protocol.h \ + src/qt/notificator.h \ + src/qt/qtipcserver.h \ + src/allocators.h \ + src/ui_interface.h \ + src/qt/rpcconsole.h \ + src/version.h \ + src/ntp.h \ + src/netbase.h \ + src/clientversion.h \ + src/qt/multisigaddressentry.h \ + src/qt/multisiginputentry.h \ + src/qt/multisigdialog.h \ + src/qt/secondauthdialog.h \ + src/qt/qrcodedialog.h + +SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ + src/qt/intro.cpp \ + src/qt/transactiontablemodel.cpp \ + src/qt/addresstablemodel.cpp \ + src/qt/optionsdialog.cpp \ + src/qt/sendcoinsdialog.cpp \ + src/qt/coincontroldialog.cpp \ + src/qt/coincontroltreewidget.cpp \ + src/qt/addressbookpage.cpp \ + src/qt/signverifymessagedialog.cpp \ + src/qt/aboutdialog.cpp \ + src/qt/editaddressdialog.cpp \ + src/qt/bitcoinaddressvalidator.cpp \ + src/qt/trafficgraphwidget.cpp \ + src/qt/mintingfilterproxy.cpp \ + src/qt/mintingtablemodel.cpp \ + src/qt/mintingview.cpp \ + src/kernelrecord.cpp \ + src/alert.cpp \ + src/version.cpp \ + src/sync.cpp \ + src/util.cpp \ + src/netbase.cpp \ + src/ntp.cpp \ + src/key.cpp \ + src/script.cpp \ + src/main.cpp \ + src/miner.cpp \ + src/init.cpp \ + src/net.cpp \ + src/stun.cpp \ + src/irc.cpp \ + src/checkpoints.cpp \ + src/addrman.cpp \ + src/db.cpp \ + src/walletdb.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 \ + src/qt/bitcoinstrings.cpp \ + src/qt/bitcoinamountfield.cpp \ + src/wallet.cpp \ + src/keystore.cpp \ + src/qt/transactionfilterproxy.cpp \ + src/qt/transactionview.cpp \ + src/qt/walletmodel.cpp \ + src/bitcoinrpc.cpp \ + src/rpcdump.cpp \ + src/rpcnet.cpp \ + src/rpcmining.cpp \ + src/rpcwallet.cpp \ + src/rpcblockchain.cpp \ + src/rpcrawtransaction.cpp \ + src/qt/overviewpage.cpp \ + src/qt/csvmodelwriter.cpp \ + src/crypter.cpp \ + src/qt/sendcoinsentry.cpp \ + src/qt/qvalidatedlineedit.cpp \ + src/qt/bitcoinunits.cpp \ + src/qt/qvaluecombobox.cpp \ + src/qt/askpassphrasedialog.cpp \ + src/protocol.cpp \ + src/qt/notificator.cpp \ + src/qt/qtipcserver.cpp \ + src/qt/rpcconsole.cpp \ + src/noui.cpp \ + src/kernel.cpp \ + src/kernel_worker.cpp \ + src/qt/multisigaddressentry.cpp \ + src/qt/multisiginputentry.cpp \ + src/qt/multisigdialog.cpp \ + src/qt/secondauthdialog.cpp \ + src/qt/qrcodedialog.cpp \ + src/base58.cpp + +RESOURCES += \ + src/qt/bitcoin.qrc + +FORMS += \ + src/qt/forms/intro.ui \ + src/qt/forms/coincontroldialog.ui \ + src/qt/forms/sendcoinsdialog.ui \ + src/qt/forms/addressbookpage.ui \ + src/qt/forms/signverifymessagedialog.ui \ + src/qt/forms/aboutdialog.ui \ + src/qt/forms/editaddressdialog.ui \ + src/qt/forms/transactiondescdialog.ui \ + src/qt/forms/overviewpage.ui \ + src/qt/forms/sendcoinsentry.ui \ + src/qt/forms/askpassphrasedialog.ui \ + src/qt/forms/rpcconsole.ui \ + src/qt/forms/optionsdialog.ui \ + src/qt/forms/multisigaddressentry.ui \ + src/qt/forms/multisiginputentry.ui \ + src/qt/forms/multisigdialog.ui \ + src/qt/forms/secondauthdialog.ui \ + src/qt/forms/qrcodedialog.ui + +CODECFORTR = UTF-8 + +# for lrelease/lupdate +# also add new translations to src/qt/bitcoin.qrc under translations/ +TRANSLATIONS = $$files(src/qt/locale/bitcoin_*.ts) + +isEmpty(QMAKE_LRELEASE) { + win32:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]\\lrelease.exe + else:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]/lrelease +} +isEmpty(QM_DIR):QM_DIR = $$PWD/src/qt/locale +# automatically build translations, so they can be included in resource file +TSQM.name = lrelease ${QMAKE_FILE_IN} +TSQM.input = TRANSLATIONS +TSQM.output = $$QM_DIR/${QMAKE_FILE_BASE}.qm +TSQM.commands = $$QMAKE_LRELEASE ${QMAKE_FILE_IN} -qm ${QMAKE_FILE_OUT} +TSQM.CONFIG = no_link +QMAKE_EXTRA_COMPILERS += TSQM + +# "Other files" to show in Qt Creator +OTHER_FILES += \ + doc/*.rst doc/*.txt doc/README README.md res/bitcoin-qt.rc + +# platform specific defaults, if not overridden on command line +isEmpty(BOOST_LIB_SUFFIX) { + windows:BOOST_LIB_SUFFIX = -mgw44-mt-1_53 +} + +isEmpty(BOOST_THREAD_LIB_SUFFIX) { + BOOST_THREAD_LIB_SUFFIX = $$BOOST_LIB_SUFFIX +} + +isEmpty(BDB_LIB_PATH) { + macx:BDB_LIB_PATH = /usr/local/BerkeleyDB.6.1/lib +} + +isEmpty(OPENSSL_LIB_PATH) { + macx:OPENSSL_LIB_PATH = /usr/local/ssl/lib +} + +isEmpty(BDB_LIB_SUFFIX) { + macx:BDB_LIB_SUFFIX = -6.1 +} + +isEmpty(BDB_INCLUDE_PATH) { + macx:BDB_INCLUDE_PATH = /usr/local/BerkeleyDB.6.1/include +} + +isEmpty(OPENSSL_INCLUDE_PATH) { + macx:OPENSSL_INCLUDE_PATH = /usr/local/ssl/include +} + +isEmpty(BOOST_LIB_PATH) { + macx:BOOST_LIB_PATH = /usr/local/lib +} + +isEmpty(BOOST_INCLUDE_PATH) { + macx:BOOST_INCLUDE_PATH = /usr/local/include +} + +windows:DEFINES += WIN32 +windows:RC_FILE = src/qt/res/bitcoin-qt.rc + +windows:!contains(MINGW_THREAD_BUGFIX, 0) { + # At least qmake's win32-g++-cross profile is missing the -lmingwthrd + # thread-safety flag. GCC has -mthreads to enable this, but it doesn't + # work with static linking. -lmingwthrd must come BEFORE -lmingw, so + # it is prepended to QMAKE_LIBS_QT_ENTRY. + # It can be turned off with MINGW_THREAD_BUGFIX=0, just in case it causes + # any problems on some untested qmake profile now or in the future. + DEFINES += _MT BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN + QMAKE_LIBS_QT_ENTRY = -lmingwthrd $$QMAKE_LIBS_QT_ENTRY +} + +!windows:!macx { + DEFINES += LINUX + LIBS += -lrt +} + +macx:HEADERS += src/qt/macdockiconhandler.h \ + src/qt/macnotificationhandler.h +macx:OBJECTIVE_SOURCES += src/qt/macdockiconhandler.mm \ + src/qt/macnotificationhandler.mm +macx:LIBS += -framework Foundation -framework ApplicationServices -framework AppKit +macx:DEFINES += MAC_OSX MSG_NOSIGNAL=0 +macx:ICON = src/qt/res/icons/bitcoin.icns +macx:TARGET = "XP-Qt" +macx:QMAKE_CFLAGS_THREAD += -pthread +macx:QMAKE_LFLAGS_THREAD += -pthread +macx:QMAKE_CXXFLAGS_THREAD += -pthread + +# Set libraries and includes at end, to use platform-defined defaults if not overridden +INCLUDEPATH += $$BOOST_INCLUDE_PATH $$BDB_INCLUDE_PATH $$OPENSSL_INCLUDE_PATH $$QRENCODE_INCLUDE_PATH +LIBS += $$join(BOOST_LIB_PATH,,-L,) $$join(BDB_LIB_PATH,,-L,) $$join(OPENSSL_LIB_PATH,,-L,) $$join(QRENCODE_LIB_PATH,,-L,) +LIBS += -lqrencode -lssl -lcrypto -ldb_cxx$$BDB_LIB_SUFFIX +# -lgdi32 has to happen after -lcrypto (see #681) +windows:LIBS += -lws2_32 -lshlwapi -lmswsock -lole32 -loleaut32 -luuid -lgdi32 +LIBS += -lboost_system$$BOOST_LIB_SUFFIX -lboost_filesystem$$BOOST_LIB_SUFFIX -lboost_program_options$$BOOST_LIB_SUFFIX -lboost_thread$$BOOST_THREAD_LIB_SUFFIX +windows:LIBS += -lboost_chrono$$BOOST_LIB_SUFFIX -Wl,-Bstatic -lpthread -Wl,-Bdynamic + +contains(RELEASE, 1) { + !windows:!macx { + # Linux: turn dynamic linking back on for c/c++ runtime libraries + LIBS += -Wl,-Bdynamic + } +} + +linux-* { + # We may need some linuxism here + LIBS += -ldl +} + +netbsd-*|freebsd-*|openbsd-* { + # libexecinfo is required for back trace + LIBS += -lexecinfo +} + +system($$QMAKE_LRELEASE -silent $$PWD/src/qt/locale/translations.pro)