From 487843278b5d92c728bb00c2f367808756bd2736 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 15 Aug 2018 21:37:14 +0800 Subject: [PATCH] greenchain src --- COPYING | 20 + INSTALL | 9 + README.md | 1 + contrib/bitcoind.bash-completion | 115 + contrib/bitrpc/bitrpc.py | 324 ++ contrib/debian/bitcoin-qt.desktop | 12 + contrib/debian/bitcoin-qt.install | 6 + contrib/debian/bitcoin-qt.lintian-overrides | 2 + contrib/debian/bitcoin-qt.protocol | 11 + contrib/debian/bitcoind.bash-completion | 1 + contrib/debian/bitcoind.examples | 1 + contrib/debian/bitcoind.install | 1 + contrib/debian/bitcoind.lintian-overrides | 2 + contrib/debian/bitcoind.manpages | 2 + contrib/debian/changelog | 367 ++ contrib/debian/compat | 1 + contrib/debian/control | 55 + contrib/debian/copyright | 166 + contrib/debian/examples/bitcoin.conf | 84 + contrib/debian/gbp.conf | 5 + contrib/debian/manpages/bitcoin-qt.1 | 206 + contrib/debian/manpages/bitcoin.conf.5 | 92 + contrib/debian/manpages/bitcoind.1 | 209 + contrib/debian/patches/README | 3 + contrib/debian/patches/series | 1 + contrib/debian/rules | 33 + contrib/debian/source/format | 1 + contrib/debian/watch | 7 + contrib/gitian-descriptors/README | 86 + contrib/gitian-descriptors/boost-win32.yml | 38 + contrib/gitian-descriptors/deps-win32.yml | 72 + contrib/gitian-descriptors/gitian-win32.yml | 65 + contrib/gitian-descriptors/gitian.yml | 55 + contrib/gitian-descriptors/qt-win32.yml | 54 + contrib/gitian-downloader/bluematt-key.pgp | Bin 0 -> 4113 bytes contrib/gitian-downloader/devrandom-key.pgp | Bin 0 -> 2213 bytes .../gitian-downloader/gavinandresen-key.pgp | Bin 0 -> 1176 bytes contrib/gitian-downloader/laanwj-key.pgp | 28 + .../gitian-downloader/linux-download-config | 38 + contrib/gitian-downloader/luke-jr-key.pgp | Bin 0 -> 7322 bytes contrib/gitian-downloader/sipa-key.pgp | Bin 0 -> 109468 bytes contrib/gitian-downloader/tcatm-key.pgp | Bin 0 -> 1554 bytes .../gitian-downloader/win32-download-config | 38 + contrib/installericons/info | 1 + contrib/installericons/qrk-installer-side.bmp | Bin 0 -> 132776 bytes contrib/installericons/qrk-installer-top.bmp | Bin 0 -> 9860 bytes contrib/installericons/qrk-installer.ico | Bin 0 -> 353118 bytes contrib/installericons/qrk-uninstaller.ico | Bin 0 -> 353118 bytes contrib/installericons/qrk.ico | Bin 0 -> 353118 bytes contrib/macdeploy/LICENSE | 674 +++ contrib/macdeploy/background.png | Bin 0 -> 12073 bytes contrib/macdeploy/background.psd | Bin 0 -> 163518 bytes contrib/macdeploy/fancy.plist | 32 + contrib/macdeploy/macdeployqtplus | 808 +++ contrib/macdeploy/notes.txt | 26 + contrib/pyminer/README | 6 + contrib/pyminer/example-config.cfg | 32 + contrib/pyminer/pyminer.py | 252 + contrib/qt_translations.py | 22 + contrib/seeds/README | 9 + contrib/seeds/makeseeds.py | 32 + contrib/spendfrom/README | 32 + contrib/spendfrom/setup.py | 9 + contrib/spendfrom/spendfrom.py | 267 + contrib/test-patches/README | 4 + contrib/testgen/README | 1 + contrib/testgen/base58.py | 104 + contrib/testgen/gen_base58_test_vectors.py | 126 + contrib/tidy_datadir.sh | 59 + contrib/verifysfbinaries/verify.sh | 119 + contrib/wallettools/walletchangepass.py | 5 + contrib/wallettools/walletunlock.py | 4 + doc/Doxyfile | 1752 ++++++ doc/README.md | 47 + doc/README_windows.txt | 32 + doc/Tor.txt | 92 + doc/assets-attribution.txt | 58 + doc/bitcoin_logo_doxygen.png | Bin 0 -> 24215 bytes doc/build-msw.md | 90 + doc/build-osx.md | 185 + doc/build-unix.md | 170 + doc/coding.md | 94 + doc/files.txt | 19 + doc/multiwallet-qt.md | 52 + doc/readme-qt.rst | 157 + doc/release-notes.md | 80 + doc/release-process.md | 164 + doc/translation_process.md | 105 + doc/unit-tests.md | 35 + favicon.png | Bin 0 -> 85640 bytes share/certs/BitcoinFoundation_Apple_Cert.pem | 37 + share/certs/BitcoinFoundation_Comodo_Cert.pem | 37 + share/certs/PrivateKeyNotes.md | 46 + 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/bitcoin-bc.ico | Bin 0 -> 9662 bytes share/pixmaps/bitcoin.ico | Bin 0 -> 9662 bytes share/pixmaps/bitcoin128.png | Bin 0 -> 39323 bytes share/pixmaps/bitcoin128.xpm | 384 ++ share/pixmaps/bitcoin256.png | Bin 0 -> 87796 bytes share/pixmaps/bitcoin256.xpm | 465 ++ share/pixmaps/bitcoin32.png | Bin 0 -> 20418 bytes share/pixmaps/bitcoin32.xpm | 140 + share/pixmaps/bitcoin64.png | Bin 0 -> 26310 bytes share/pixmaps/bitcoin64.xpm | 242 + share/pixmaps/check.ico | Bin 0 -> 766 bytes share/pixmaps/favicon.ico | Bin 0 -> 9662 bytes share/pixmaps/nsis-header.bmp | Bin 0 -> 9740 bytes share/pixmaps/nsis-wizard.bmp | Bin 0 -> 52572 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/Info.plist | 35 + share/qt/clean_mac_info_plist.py | 29 + share/qt/extract_strings_qt.py | 72 + share/qt/img/reload.xcf | Bin 0 -> 25292 bytes share/qt/make_spinner.py | 43 + share/qt/make_windows_icon.sh | 9 + share/setup.nsi | 158 + share/ui.rc | 15 + src/addrman.cpp | 528 ++ src/addrman.h | 503 ++ src/alert.cpp | 268 + src/alert.h | 102 + src/allocators.h | 258 + src/base58.h | 467 ++ src/bignum.h | 590 ++ src/bitcoinrpc.cpp | 1454 +++++ src/bitcoinrpc.h | 209 + src/blake.c | 1120 ++++ src/bloom.cpp | 160 + src/bloom.h | 88 + src/bmw.c | 965 ++++ src/checkpoints.cpp | 544 ++ src/checkpoints.h | 152 + src/checkqueue.h | 192 + src/clientversion.h | 26 + src/compat.h | 64 + src/crypter.cpp | 121 + src/crypter.h | 107 + src/db.cpp | 585 ++ src/db.h | 328 ++ src/groestl.c | 3123 +++++++++++ src/hash.cpp | 58 + src/hash.h | 120 + src/hashblock.h | 150 + src/init.cpp | 1114 ++++ src/init.h | 18 + src/jh.c | 1116 ++++ 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 | 534 ++ src/json/json_spirit_writer.cpp | 95 + src/json/json_spirit_writer.h | 50 + src/json/json_spirit_writer_template.h | 248 + src/keccak.c | 1824 ++++++ src/key.cpp | 406 ++ src/key.h | 161 + src/keystore.cpp | 221 + src/keystore.h | 184 + src/leveldb.cpp | 81 + src/leveldb.h | 153 + src/limitedmap.h | 100 + src/main.cpp | 4883 +++++++++++++++++ src/main.h | 2316 ++++++++ src/makefile.linux-mingw | 140 + src/makefile.mingw | 155 + src/makefile.mingw.bak | 155 + src/makefile.osx | 190 + src/makefile.unix | 218 + src/mruset.h | 64 + src/net.cpp | 1884 +++++++ src/net.cpp.bak | 1885 +++++++ src/net.h | 639 +++ src/netbase.cpp | 1139 ++++ src/netbase.h | 151 + src/noui.cpp | 51 + src/obj-test/.gitignore | 2 + src/protocol.cpp | 152 + src/protocol.h | 147 + src/qt/aboutdialog.cpp | 34 + src/qt/aboutdialog.h | 29 + src/qt/addressbookpage.cpp | 390 ++ src/qt/addressbookpage.h | 94 + src/qt/addresstablemodel.cpp | 426 ++ src/qt/addresstablemodel.h | 93 + src/qt/askpassphrasedialog.cpp | 248 + src/qt/askpassphrasedialog.h | 44 + src/qt/bitcoin.cpp | 297 + src/qt/bitcoin.qrc | 55 + src/qt/bitcoinaddressvalidator.cpp | 77 + src/qt/bitcoinaddressvalidator.h | 21 + src/qt/bitcoinamountfield.cpp | 169 + src/qt/bitcoinamountfield.h | 61 + src/qt/bitcoingui.cpp | 818 +++ src/qt/bitcoingui.h | 198 + src/qt/bitcoinstrings.cpp | 210 + src/qt/bitcoinunits.cpp | 181 + src/qt/bitcoinunits.h | 69 + src/qt/clientmodel.cpp | 209 + src/qt/clientmodel.h | 87 + src/qt/csvmodelwriter.cpp | 88 + src/qt/csvmodelwriter.h | 42 + src/qt/editaddressdialog.cpp | 137 + src/qt/editaddressdialog.h | 52 + src/qt/forms/Noname1.txt | 19 + src/qt/forms/aboutdialog.ui | 187 + src/qt/forms/addressbookpage.ui | 186 + src/qt/forms/askpassphrasedialog.ui | 151 + src/qt/forms/editaddressdialog.ui | 105 + src/qt/forms/optionsdialog.ui | 480 ++ src/qt/forms/overviewpage.ui | 365 ++ src/qt/forms/qrcodedialog.ui | 212 + src/qt/forms/rpcconsole.ui | 456 ++ src/qt/forms/sendcoinsdialog.ui | 175 + src/qt/forms/sendcoinsentry.ui | 159 + src/qt/forms/signverifymessagedialog.ui | 396 ++ src/qt/forms/transactiondescdialog.ui | 74 + src/qt/guiconstants.h | 34 + src/qt/guiutil.cpp | 461 ++ src/qt/guiutil.h | 121 + src/qt/locale/bitcoin_en.qm | Bin 0 -> 79898 bytes src/qt/locale/bitcoin_en.ts | 3040 ++++++++++ src/qt/locale/bitcoin_zh_CN.qm | Bin 0 -> 62020 bytes src/qt/locale/bitcoin_zh_CN.ts | 2952 ++++++++++ src/qt/macdockiconhandler.h | 47 + src/qt/macdockiconhandler.mm | 107 + src/qt/monitoreddatamapper.cpp | 35 + src/qt/monitoreddatamapper.h | 30 + src/qt/notificator.cpp | 302 + src/qt/notificator.h | 69 + src/qt/optionsdialog.cpp | 280 + src/qt/optionsdialog.h | 61 + src/qt/optionsmodel.cpp | 292 + src/qt/optionsmodel.h | 64 + src/qt/overviewpage.cpp | 213 + src/qt/overviewpage.h | 54 + src/qt/paymentserver.cpp | 160 + src/qt/paymentserver.h | 67 + src/qt/qrcodedialog.cpp | 171 + src/qt/qrcodedialog.h | 41 + src/qt/qvalidatedlineedit.cpp | 45 + src/qt/qvalidatedlineedit.h | 30 + src/qt/qvaluecombobox.cpp | 27 + src/qt/qvaluecombobox.h | 33 + src/qt/res/bitcoin-qt.rc | 38 + src/qt/res/icons/add.png | Bin 0 -> 1312 bytes src/qt/res/icons/address-book.png | Bin 0 -> 1380 bytes src/qt/res/icons/bitcoin.icns | Bin 0 -> 40975 bytes src/qt/res/icons/bitcoin.ico | Bin 0 -> 16958 bytes src/qt/res/icons/bitcoin.png | Bin 0 -> 39323 bytes src/qt/res/icons/clock1.png | Bin 0 -> 349 bytes src/qt/res/icons/clock2.png | Bin 0 -> 559 bytes src/qt/res/icons/clock3.png | Bin 0 -> 610 bytes src/qt/res/icons/clock4.png | Bin 0 -> 619 bytes src/qt/res/icons/clock5.png | Bin 0 -> 647 bytes src/qt/res/icons/coin.png | Bin 0 -> 1418 bytes src/qt/res/icons/configure.png | Bin 0 -> 1351 bytes src/qt/res/icons/connect0_16.png | Bin 0 -> 702 bytes src/qt/res/icons/connect1_16.png | Bin 0 -> 3129 bytes src/qt/res/icons/connect2_16.png | Bin 0 -> 3100 bytes src/qt/res/icons/connect3_16.png | Bin 0 -> 3017 bytes src/qt/res/icons/connect4_16.png | Bin 0 -> 2932 bytes src/qt/res/icons/debugwindow.png | Bin 0 -> 1126 bytes src/qt/res/icons/edit.png | Bin 0 -> 1835 bytes src/qt/res/icons/editcopy.png | Bin 0 -> 1429 bytes src/qt/res/icons/editpaste.png | Bin 0 -> 1624 bytes src/qt/res/icons/export.png | Bin 0 -> 1615 bytes src/qt/res/icons/favicon.ico | Bin 0 -> 9662 bytes src/qt/res/icons/filesave.png | Bin 0 -> 1549 bytes src/qt/res/icons/history.png | Bin 0 -> 1076 bytes src/qt/res/icons/key.png | Bin 0 -> 1806 bytes src/qt/res/icons/lock_closed.png | Bin 0 -> 1441 bytes src/qt/res/icons/lock_open.png | Bin 0 -> 1413 bytes src/qt/res/icons/mining.png | Bin 0 -> 895 bytes src/qt/res/icons/mining_active.png | Bin 0 -> 895 bytes src/qt/res/icons/mining_inactive.png | Bin 0 -> 1314 bytes src/qt/res/icons/notsynced.png | Bin 0 -> 1270 bytes src/qt/res/icons/overview.png | Bin 0 -> 1438 bytes src/qt/res/icons/qrcode.png | Bin 0 -> 237 bytes src/qt/res/icons/quit.png | Bin 0 -> 1727 bytes src/qt/res/icons/receive.png | Bin 0 -> 1380 bytes src/qt/res/icons/remove.png | Bin 0 -> 1100 bytes src/qt/res/icons/send.png | Bin 0 -> 1311 bytes src/qt/res/icons/synced.png | Bin 0 -> 2882 bytes src/qt/res/icons/toolbar.png | Bin 0 -> 20418 bytes src/qt/res/icons/transaction0.png | Bin 0 -> 3023 bytes src/qt/res/icons/transaction2.png | Bin 0 -> 2882 bytes src/qt/res/icons/tx_inout.png | Bin 0 -> 2629 bytes src/qt/res/icons/tx_input.png | Bin 0 -> 2343 bytes src/qt/res/icons/tx_mined.png | Bin 0 -> 19235 bytes src/qt/res/icons/tx_output.png | Bin 0 -> 2315 bytes src/qt/res/images/about.png | Bin 0 -> 3488 bytes src/qt/res/images/splash.png | Bin 0 -> 85640 bytes src/qt/res/images/wallet.png | Bin 0 -> 85640 bytes src/qt/res/images/wallet_bgcoin.png | Bin 0 -> 85640 bytes src/qt/res/movies/update_spinner.mng | Bin 0 -> 27817 bytes src/qt/res/src/bitcoin.svg | 58 + 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/res/src/tongyongcoin.svg | 58 + src/qt/rpcconsole.cpp | 427 ++ src/qt/rpcconsole.h | 66 + src/qt/sendcoinsdialog.cpp | 325 ++ src/qt/sendcoinsdialog.h | 55 + src/qt/sendcoinsentry.cpp | 179 + src/qt/sendcoinsentry.h | 56 + src/qt/signverifymessagedialog.cpp | 274 + src/qt/signverifymessagedialog.h | 46 + src/qt/splashscreen.cpp | 84 + src/qt/splashscreen.h | 16 + src/qt/test/test_main.cpp | 16 + src/qt/test/uritests.cpp | 62 + src/qt/test/uritests.h | 15 + src/qt/transactiondesc.cpp | 274 + src/qt/transactiondesc.h | 25 + src/qt/transactiondescdialog.cpp | 20 + src/qt/transactiondescdialog.h | 27 + src/qt/transactionfilterproxy.cpp | 87 + src/qt/transactionfilterproxy.h | 49 + src/qt/transactionrecord.cpp | 231 + src/qt/transactionrecord.h | 131 + src/qt/transactiontablemodel.cpp | 632 +++ src/qt/transactiontablemodel.h | 86 + src/qt/transactionview.cpp | 431 ++ src/qt/transactionview.h | 84 + src/qt/walletframe.cpp | 132 + src/qt/walletframe.h | 73 + src/qt/walletmodel.cpp | 381 ++ src/qt/walletmodel.h | 165 + src/qt/walletstack.cpp | 156 + src/qt/walletstack.h | 102 + src/qt/walletview.cpp | 266 + src/qt/walletview.h | 104 + src/rpcblockchain.cpp | 237 + src/rpcdump.cpp | 99 + src/rpcmining.cpp | 472 ++ src/rpcnet.cpp | 204 + src/rpcrawtransaction.cpp | 580 ++ src/rpcwallet.cpp | 1602 ++++++ src/script.cpp | 1906 +++++++ src/script.h | 685 +++ src/serialize.h | 1374 +++++ src/skein.c | 1254 +++++ src/sph_blake.h | 327 ++ src/sph_bmw.h | 328 ++ src/sph_groestl.h | 329 ++ src/sph_jh.h | 298 + src/sph_keccak.h | 293 + src/sph_skein.h | 298 + src/sph_types.h | 1976 +++++++ src/sync.cpp | 128 + src/sync.h | 213 + src/test/Checkpoints_tests.cpp | 34 + src/test/DoS_tests.cpp | 314 ++ src/test/README | 21 + src/test/accounting_tests.cpp | 123 + src/test/alert_tests.cpp | 185 + src/test/allocator_tests.cpp | 115 + src/test/base32_tests.cpp | 20 + src/test/base58_tests.cpp | 259 + src/test/base64_tests.cpp | 22 + src/test/bignum_tests.cpp | 178 + src/test/bloom_tests.cpp | 447 ++ src/test/canonical_tests.cpp | 87 + src/test/checkblock_tests.cpp | 66 + src/test/compress_tests.cpp | 62 + src/test/data/alertTests | Bin 0 -> 1283 bytes src/test/data/base58_encode_decode.json | 14 + src/test/data/base58_keys_invalid.json | 152 + src/test/data/base58_keys_valid.json | 452 ++ src/test/data/script_invalid.json | 314 ++ src/test/data/script_valid.json | 379 ++ src/test/data/sig_canonical.json | 7 + src/test/data/sig_noncanonical.json | 22 + src/test/data/tx_invalid.json | 69 + src/test/data/tx_valid.json | 81 + src/test/getarg_tests.cpp | 167 + src/test/key_tests.cpp | 147 + src/test/miner_tests.cpp | 230 + src/test/mruset_tests.cpp | 90 + src/test/multisig_tests.cpp | 296 + src/test/netbase_tests.cpp | 102 + src/test/pmt_tests.cpp | 98 + src/test/rpc_tests.cpp | 150 + src/test/script_P2SH_tests.cpp | 339 ++ src/test/script_tests.cpp | 445 ++ src/test/serialize_tests.cpp | 45 + src/test/sigopcount_tests.cpp | 60 + src/test/test_bitcoin.cpp | 66 + src/test/transaction_tests.cpp | 275 + src/test/uint160_tests.cpp | 18 + src/test/uint256_tests.cpp | 18 + src/test/util_tests.cpp | 326 ++ src/test/wallet_tests.cpp | 294 + src/threadsafety.h | 53 + src/txdb.cpp | 263 + src/txdb.h | 60 + src/ui_interface.h | 110 + src/uint256.h | 902 +++ src/util.cpp | 1490 +++++ src/util.h | 587 ++ src/version.cpp | 67 + src/version.h | 47 + src/wallet.cpp | 1882 +++++++ src/wallet.h | 862 +++ src/walletdb.cpp | 678 +++ src/walletdb.h | 163 + 426 files changed, 92785 insertions(+) create mode 100644 COPYING create mode 100644 INSTALL create mode 100644 README.md create mode 100644 contrib/bitcoind.bash-completion create mode 100644 contrib/bitrpc/bitrpc.py create mode 100644 contrib/debian/bitcoin-qt.desktop create mode 100644 contrib/debian/bitcoin-qt.install create mode 100644 contrib/debian/bitcoin-qt.lintian-overrides create mode 100644 contrib/debian/bitcoin-qt.protocol create mode 100644 contrib/debian/bitcoind.bash-completion create mode 100644 contrib/debian/bitcoind.examples create mode 100644 contrib/debian/bitcoind.install create mode 100644 contrib/debian/bitcoind.lintian-overrides create mode 100644 contrib/debian/bitcoind.manpages 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/bitcoin.conf create mode 100644 contrib/debian/gbp.conf create mode 100644 contrib/debian/manpages/bitcoin-qt.1 create mode 100644 contrib/debian/manpages/bitcoin.conf.5 create mode 100644 contrib/debian/manpages/bitcoind.1 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/gitian-descriptors/README create mode 100644 contrib/gitian-descriptors/boost-win32.yml create mode 100644 contrib/gitian-descriptors/deps-win32.yml create mode 100644 contrib/gitian-descriptors/gitian-win32.yml create mode 100644 contrib/gitian-descriptors/gitian.yml create mode 100644 contrib/gitian-descriptors/qt-win32.yml create mode 100644 contrib/gitian-downloader/bluematt-key.pgp create mode 100644 contrib/gitian-downloader/devrandom-key.pgp create mode 100644 contrib/gitian-downloader/gavinandresen-key.pgp create mode 100644 contrib/gitian-downloader/laanwj-key.pgp create mode 100644 contrib/gitian-downloader/linux-download-config create mode 100644 contrib/gitian-downloader/luke-jr-key.pgp create mode 100644 contrib/gitian-downloader/sipa-key.pgp create mode 100644 contrib/gitian-downloader/tcatm-key.pgp create mode 100644 contrib/gitian-downloader/win32-download-config create mode 100644 contrib/installericons/info create mode 100644 contrib/installericons/qrk-installer-side.bmp create mode 100644 contrib/installericons/qrk-installer-top.bmp create mode 100644 contrib/installericons/qrk-installer.ico create mode 100644 contrib/installericons/qrk-uninstaller.ico create mode 100644 contrib/installericons/qrk.ico 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/pyminer/README create mode 100644 contrib/pyminer/example-config.cfg create mode 100644 contrib/pyminer/pyminer.py create mode 100644 contrib/qt_translations.py create mode 100644 contrib/seeds/README create mode 100644 contrib/seeds/makeseeds.py create mode 100644 contrib/spendfrom/README create mode 100644 contrib/spendfrom/setup.py create mode 100644 contrib/spendfrom/spendfrom.py create mode 100644 contrib/test-patches/README create mode 100644 contrib/testgen/README create mode 100644 contrib/testgen/base58.py create mode 100644 contrib/testgen/gen_base58_test_vectors.py create mode 100644 contrib/tidy_datadir.sh create mode 100644 contrib/verifysfbinaries/verify.sh create mode 100644 contrib/wallettools/walletchangepass.py create mode 100644 contrib/wallettools/walletunlock.py create mode 100644 doc/Doxyfile create mode 100644 doc/README.md create mode 100644 doc/README_windows.txt create mode 100644 doc/Tor.txt create mode 100644 doc/assets-attribution.txt create mode 100644 doc/bitcoin_logo_doxygen.png create mode 100644 doc/build-msw.md create mode 100644 doc/build-osx.md create mode 100644 doc/build-unix.md create mode 100644 doc/coding.md create mode 100644 doc/files.txt create mode 100644 doc/multiwallet-qt.md create mode 100644 doc/readme-qt.rst create mode 100644 doc/release-notes.md create mode 100644 doc/release-process.md create mode 100644 doc/translation_process.md create mode 100644 doc/unit-tests.md create mode 100644 favicon.png create mode 100644 share/certs/BitcoinFoundation_Apple_Cert.pem create mode 100644 share/certs/BitcoinFoundation_Comodo_Cert.pem create mode 100644 share/certs/PrivateKeyNotes.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/bitcoin-bc.ico create mode 100644 share/pixmaps/bitcoin.ico create mode 100644 share/pixmaps/bitcoin128.png create mode 100644 share/pixmaps/bitcoin128.xpm create mode 100644 share/pixmaps/bitcoin256.png create mode 100644 share/pixmaps/bitcoin256.xpm create mode 100644 share/pixmaps/bitcoin32.png create mode 100644 share/pixmaps/bitcoin32.xpm create mode 100644 share/pixmaps/bitcoin64.png create mode 100644 share/pixmaps/bitcoin64.xpm create mode 100644 share/pixmaps/check.ico create mode 100644 share/pixmaps/favicon.ico 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/Info.plist create mode 100644 share/qt/clean_mac_info_plist.py 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.h create mode 100644 src/bignum.h create mode 100644 src/bitcoinrpc.cpp create mode 100644 src/bitcoinrpc.h create mode 100644 src/blake.c create mode 100644 src/bloom.cpp create mode 100644 src/bloom.h create mode 100644 src/bmw.c 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/compat.h create mode 100644 src/crypter.cpp create mode 100644 src/crypter.h create mode 100644 src/db.cpp create mode 100644 src/db.h create mode 100644 src/groestl.c create mode 100644 src/hash.cpp create mode 100644 src/hash.h create mode 100644 src/hashblock.h create mode 100644 src/init.cpp create mode 100644 src/init.h create mode 100644 src/jh.c 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/keccak.c 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.cpp create mode 100644 src/leveldb.h create mode 100644 src/limitedmap.h create mode 100644 src/main.cpp create mode 100644 src/main.h create mode 100644 src/makefile.linux-mingw create mode 100644 src/makefile.mingw create mode 100644 src/makefile.mingw.bak create mode 100644 src/makefile.osx create mode 100644 src/makefile.unix create mode 100644 src/mruset.h create mode 100644 src/net.cpp create mode 100644 src/net.cpp.bak 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/obj-test/.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/csvmodelwriter.cpp create mode 100644 src/qt/csvmodelwriter.h create mode 100644 src/qt/editaddressdialog.cpp create mode 100644 src/qt/editaddressdialog.h create mode 100644 src/qt/forms/Noname1.txt 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/editaddressdialog.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/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/locale/bitcoin_en.qm create mode 100644 src/qt/locale/bitcoin_en.ts create mode 100644 src/qt/locale/bitcoin_zh_CN.qm create mode 100644 src/qt/locale/bitcoin_zh_CN.ts create mode 100644 src/qt/macdockiconhandler.h create mode 100644 src/qt/macdockiconhandler.mm create mode 100644 src/qt/monitoreddatamapper.cpp create mode 100644 src/qt/monitoreddatamapper.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/paymentserver.cpp create mode 100644 src/qt/paymentserver.h create mode 100644 src/qt/qrcodedialog.cpp create mode 100644 src/qt/qrcodedialog.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/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/coin.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/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/favicon.ico 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/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.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/toolbar.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/splash.png create mode 100644 src/qt/res/images/wallet.png create mode 100644 src/qt/res/images/wallet_bgcoin.png 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/res/src/tongyongcoin.svg create mode 100644 src/qt/rpcconsole.cpp create mode 100644 src/qt/rpcconsole.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/splashscreen.cpp create mode 100644 src/qt/splashscreen.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/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/walletframe.cpp create mode 100644 src/qt/walletframe.h create mode 100644 src/qt/walletmodel.cpp create mode 100644 src/qt/walletmodel.h create mode 100644 src/qt/walletstack.cpp create mode 100644 src/qt/walletstack.h create mode 100644 src/qt/walletview.cpp create mode 100644 src/qt/walletview.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/serialize.h create mode 100644 src/skein.c create mode 100644 src/sph_blake.h create mode 100644 src/sph_bmw.h create mode 100644 src/sph_groestl.h create mode 100644 src/sph_jh.h create mode 100644 src/sph_keccak.h create mode 100644 src/sph_skein.h create mode 100644 src/sph_types.h create mode 100644 src/sync.cpp create mode 100644 src/sync.h create mode 100644 src/test/Checkpoints_tests.cpp create mode 100644 src/test/DoS_tests.cpp create mode 100644 src/test/README create mode 100644 src/test/accounting_tests.cpp create mode 100644 src/test/alert_tests.cpp create mode 100644 src/test/allocator_tests.cpp create mode 100644 src/test/base32_tests.cpp create mode 100644 src/test/base58_tests.cpp create mode 100644 src/test/base64_tests.cpp create mode 100644 src/test/bignum_tests.cpp create mode 100644 src/test/bloom_tests.cpp create mode 100644 src/test/canonical_tests.cpp create mode 100644 src/test/checkblock_tests.cpp create mode 100644 src/test/compress_tests.cpp create mode 100644 src/test/data/alertTests create mode 100644 src/test/data/base58_encode_decode.json create mode 100644 src/test/data/base58_keys_invalid.json create mode 100644 src/test/data/base58_keys_valid.json create mode 100644 src/test/data/script_invalid.json create mode 100644 src/test/data/script_valid.json create mode 100644 src/test/data/sig_canonical.json create mode 100644 src/test/data/sig_noncanonical.json create mode 100644 src/test/data/tx_invalid.json create mode 100644 src/test/data/tx_valid.json create mode 100644 src/test/getarg_tests.cpp create mode 100644 src/test/key_tests.cpp create mode 100644 src/test/miner_tests.cpp create mode 100644 src/test/mruset_tests.cpp create mode 100644 src/test/multisig_tests.cpp create mode 100644 src/test/netbase_tests.cpp create mode 100644 src/test/pmt_tests.cpp create mode 100644 src/test/rpc_tests.cpp create mode 100644 src/test/script_P2SH_tests.cpp create mode 100644 src/test/script_tests.cpp create mode 100644 src/test/serialize_tests.cpp create mode 100644 src/test/sigopcount_tests.cpp create mode 100644 src/test/test_bitcoin.cpp create mode 100644 src/test/transaction_tests.cpp create mode 100644 src/test/uint160_tests.cpp create mode 100644 src/test/uint256_tests.cpp create mode 100644 src/test/util_tests.cpp create mode 100644 src/test/wallet_tests.cpp create mode 100644 src/threadsafety.h create mode 100644 src/txdb.cpp 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 diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..82b7b0d --- /dev/null +++ b/COPYING @@ -0,0 +1,20 @@ +Copyright (c) 2009-2013 Bitcoin Developers +Copyright (c) 2018 Greenchain 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 0000000..61cae2d --- /dev/null +++ b/INSTALL @@ -0,0 +1,9 @@ +Building Greenchain 1.0.0 + +See doc/readme-qt.rst for instructions on building Greenchain 1.0.0-Qt, +the intended-for-end-users, nice-graphical-interface, reference +implementation of Greenchain 1.0.0. + +See doc/build-*.txt for instructions on building bitcoind, +the intended-for-services, no-graphical-interface, reference +implementation of Greenchain 1.0.0. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..233e13a --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +Greenchain 1.0.0 \ No newline at end of file diff --git a/contrib/bitcoind.bash-completion b/contrib/bitcoind.bash-completion new file mode 100644 index 0000000..dd6c1ce --- /dev/null +++ b/contrib/bitcoind.bash-completion @@ -0,0 +1,115 @@ +# bash programmable completion for bitcoind(1) +# Copyright (c) 2012 Christian von Roques +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +have bitcoind && { + +# call $bitcoind for RPC +_bitcoin_rpc() { + # determine already specified args necessary for RPC + local rpcargs=() + for i in ${COMP_LINE}; do + case "$i" in + -conf=*|-proxy*|-rpc*) + rpcargs=( "${rpcargs[@]}" "$i" ) + ;; + esac + done + $bitcoind "${rpcargs[@]}" "$@" +} + +# Add bitcoin accounts to COMPREPLY +_bitcoin_accounts() { + local accounts + accounts=$(_bitcoin_rpc listaccounts | awk '/".*"/ { a=$1; gsub(/"/, "", a); print a}') + COMPREPLY=( "${COMPREPLY[@]}" $( compgen -W "$accounts" -- "$cur" ) ) +} + +_bitcoind() { + local cur prev words=() cword + local bitcoind + + # save and use original argument to invoke bitcoind + # bitcoind might not be in $PATH + bitcoind="$1" + + COMPREPLY=() + _get_comp_words_by_ref -n = cur prev words cword + + if ((cword > 2)); then + case ${words[cword-2]} in + listreceivedbyaccount|listreceivedbyaddress) + COMPREPLY=( $( compgen -W "true false" -- "$cur" ) ) + return 0 + ;; + move|setaccount) + _bitcoin_accounts + return 0 + ;; + esac + fi + + case "$prev" in + backupwallet) + _filedir + return 0 + ;; + setgenerate) + COMPREPLY=( $( compgen -W "true false" -- "$cur" ) ) + return 0 + ;; + getaccountaddress|getaddressesbyaccount|getbalance|getnewaddress|getreceivedbyaccount|listtransactions|move|sendfrom|sendmany) + _bitcoin_accounts + return 0 + ;; + esac + + case "$cur" in + -conf=*|-pid=*|-rpcsslcertificatechainfile=*|-rpcsslprivatekeyfile=*) + cur="${cur#*=}" + _filedir + return 0 + ;; + -datadir=*) + cur="${cur#*=}" + _filedir -d + return 0 + ;; + -*=*) # prevent nonsense completions + return 0 + ;; + *) + local helpopts commands + + # only parse --help if senseful + if [[ -z "$cur" || "$cur" =~ ^- ]]; then + helpopts=$($bitcoind --help 2>&1 | awk '$1 ~ /^-/ { sub(/=.*/, "="); print $1 }' ) + fi + + # only parse help if senseful + if [[ -z "$cur" || "$cur" =~ ^[a-z] ]]; then + commands=$(_bitcoin_rpc help 2>/dev/null | awk '{ print $1; }') + fi + + COMPREPLY=( $( compgen -W "$helpopts $commands" -- "$cur" ) ) + + # Prevent space if an argument is desired + if [[ $COMPREPLY == *= ]]; then + compopt -o nospace + fi + return 0 + ;; + esac +} + +complete -F _bitcoind bitcoind +} + +# Local variables: +# mode: shell-script +# sh-basic-offset: 4 +# sh-indent-comment: t +# indent-tabs-mode: nil +# End: +# ex: ts=4 sw=4 et filetype=sh diff --git a/contrib/bitrpc/bitrpc.py b/contrib/bitrpc/bitrpc.py new file mode 100644 index 0000000..b02b299 --- /dev/null +++ b/contrib/bitrpc/bitrpc.py @@ -0,0 +1,324 @@ +from jsonrpc import ServiceProxy +import sys +import string + +# ===== BEGIN USER SETTINGS ===== +# if you do not set these you will be prompted for a password for every command +rpcuser = "" +rpcpass = "" +# ====== END USER SETTINGS ====== + + +if rpcpass == "": + access = ServiceProxy("http://127.0.0.1:8332") +else: + access = ServiceProxy("http://"+rpcuser+":"+rpcpass+"@127.0.0.1:8332") +cmd = sys.argv[1].lower() + +if cmd == "backupwallet": + try: + path = raw_input("Enter destination path/filename: ") + print access.backupwallet(path) + except: + print "\n---An error occurred---\n" + +elif cmd == "getaccount": + try: + addr = raw_input("Enter a Bitcoin address: ") + print access.getaccount(addr) + except: + print "\n---An error occurred---\n" + +elif cmd == "getaccountaddress": + try: + acct = raw_input("Enter an account name: ") + print access.getaccountaddress(acct) + except: + print "\n---An error occurred---\n" + +elif cmd == "getaddressesbyaccount": + try: + acct = raw_input("Enter an account name: ") + print access.getaddressesbyaccount(acct) + except: + print "\n---An error occurred---\n" + +elif cmd == "getbalance": + try: + acct = raw_input("Enter an account (optional): ") + mc = raw_input("Minimum confirmations (optional): ") + try: + print access.getbalance(acct, mc) + except: + print access.getbalance() + except: + print "\n---An error occurred---\n" + +elif cmd == "getblockbycount": + try: + height = raw_input("Height: ") + print access.getblockbycount(height) + except: + print "\n---An error occurred---\n" + +elif cmd == "getblockcount": + try: + print access.getblockcount() + except: + print "\n---An error occurred---\n" + +elif cmd == "getblocknumber": + try: + print access.getblocknumber() + except: + print "\n---An error occurred---\n" + +elif cmd == "getconnectioncount": + try: + print access.getconnectioncount() + except: + print "\n---An error occurred---\n" + +elif cmd == "getdifficulty": + try: + print access.getdifficulty() + except: + print "\n---An error occurred---\n" + +elif cmd == "getgenerate": + try: + print access.getgenerate() + except: + print "\n---An error occurred---\n" + +elif cmd == "gethashespersec": + try: + print access.gethashespersec() + except: + print "\n---An error occurred---\n" + +elif cmd == "getinfo": + try: + print access.getinfo() + except: + print "\n---An error occurred---\n" + +elif cmd == "getnewaddress": + try: + acct = raw_input("Enter an account name: ") + try: + print access.getnewaddress(acct) + except: + print access.getnewaddress() + except: + print "\n---An error occurred---\n" + +elif cmd == "getreceivedbyaccount": + try: + acct = raw_input("Enter an account (optional): ") + mc = raw_input("Minimum confirmations (optional): ") + try: + print access.getreceivedbyaccount(acct, mc) + except: + print access.getreceivedbyaccount() + except: + print "\n---An error occurred---\n" + +elif cmd == "getreceivedbyaddress": + try: + addr = raw_input("Enter a Bitcoin address (optional): ") + mc = raw_input("Minimum confirmations (optional): ") + try: + print access.getreceivedbyaddress(addr, mc) + except: + print access.getreceivedbyaddress() + except: + print "\n---An error occurred---\n" + +elif cmd == "gettransaction": + try: + txid = raw_input("Enter a transaction ID: ") + print access.gettransaction(txid) + except: + print "\n---An error occurred---\n" + +elif cmd == "getwork": + try: + data = raw_input("Data (optional): ") + try: + print access.gettransaction(data) + except: + print access.gettransaction() + except: + print "\n---An error occurred---\n" + +elif cmd == "help": + try: + cmd = raw_input("Command (optional): ") + try: + print access.help(cmd) + except: + print access.help() + except: + print "\n---An error occurred---\n" + +elif cmd == "listaccounts": + try: + mc = raw_input("Minimum confirmations (optional): ") + try: + print access.listaccounts(mc) + except: + print access.listaccounts() + except: + print "\n---An error occurred---\n" + +elif cmd == "listreceivedbyaccount": + try: + mc = raw_input("Minimum confirmations (optional): ") + incemp = raw_input("Include empty? (true/false, optional): ") + try: + print access.listreceivedbyaccount(mc, incemp) + except: + print access.listreceivedbyaccount() + except: + print "\n---An error occurred---\n" + +elif cmd == "listreceivedbyaddress": + try: + mc = raw_input("Minimum confirmations (optional): ") + incemp = raw_input("Include empty? (true/false, optional): ") + try: + print access.listreceivedbyaddress(mc, incemp) + except: + print access.listreceivedbyaddress() + except: + print "\n---An error occurred---\n" + +elif cmd == "listtransactions": + try: + acct = raw_input("Account (optional): ") + count = raw_input("Number of transactions (optional): ") + frm = raw_input("Skip (optional):") + try: + print access.listtransactions(acct, count, frm) + except: + print access.listtransactions() + except: + print "\n---An error occurred---\n" + +elif cmd == "move": + try: + frm = raw_input("From: ") + to = raw_input("To: ") + amt = raw_input("Amount:") + mc = raw_input("Minimum confirmations (optional): ") + comment = raw_input("Comment (optional): ") + try: + print access.move(frm, to, amt, mc, comment) + except: + print access.move(frm, to, amt) + except: + print "\n---An error occurred---\n" + +elif cmd == "sendfrom": + try: + frm = raw_input("From: ") + to = raw_input("To: ") + amt = raw_input("Amount:") + mc = raw_input("Minimum confirmations (optional): ") + comment = raw_input("Comment (optional): ") + commentto = raw_input("Comment-to (optional): ") + try: + print access.sendfrom(frm, to, amt, mc, comment, commentto) + except: + print access.sendfrom(frm, to, amt) + except: + print "\n---An error occurred---\n" + +elif cmd == "sendmany": + try: + frm = raw_input("From: ") + to = raw_input("To (in format address1:amount1,address2:amount2,...): ") + mc = raw_input("Minimum confirmations (optional): ") + comment = raw_input("Comment (optional): ") + try: + print access.sendmany(frm,to,mc,comment) + except: + print access.sendmany(frm,to) + except: + print "\n---An error occurred---\n" + +elif cmd == "sendtoaddress": + try: + to = raw_input("To (in format address1:amount1,address2:amount2,...): ") + amt = raw_input("Amount:") + comment = raw_input("Comment (optional): ") + commentto = raw_input("Comment-to (optional): ") + try: + print access.sendtoaddress(to,amt,comment,commentto) + except: + print access.sendtoaddress(to,amt) + except: + print "\n---An error occurred---\n" + +elif cmd == "setaccount": + try: + addr = raw_input("Address: ") + acct = raw_input("Account:") + print access.setaccount(addr,acct) + except: + print "\n---An error occurred---\n" + +elif cmd == "setgenerate": + try: + gen= raw_input("Generate? (true/false): ") + cpus = raw_input("Max processors/cores (-1 for unlimited, optional):") + try: + print access.setgenerate(gen, cpus) + except: + print access.setgenerate(gen) + except: + print "\n---An error occurred---\n" + +elif cmd == "settxfee": + try: + amt = raw_input("Amount:") + print access.settxfee(amt) + except: + print "\n---An error occurred---\n" + +elif cmd == "stop": + try: + print access.stop() + except: + print "\n---An error occurred---\n" + +elif cmd == "validateaddress": + try: + addr = raw_input("Address: ") + print access.validateaddress(addr) + except: + print "\n---An error occurred---\n" + +elif cmd == "walletpassphrase": + try: + pwd = raw_input("Enter wallet passphrase: ") + access.walletpassphrase(pwd, 60) + print "\n---Wallet unlocked---\n" + except: + print "\n---An error occurred---\n" + +elif cmd == "walletpassphrasechange": + try: + pwd = raw_input("Enter old wallet passphrase: ") + pwd2 = raw_input("Enter new wallet passphrase: ") + access.walletpassphrasechange(pwd, pwd2) + print + print "\n---Passphrase changed---\n" + except: + print + print "\n---An error occurred---\n" + print + +else: + print "Command not found or not supported" \ No newline at end of file diff --git a/contrib/debian/bitcoin-qt.desktop b/contrib/debian/bitcoin-qt.desktop new file mode 100644 index 0000000..5d6f781 --- /dev/null +++ b/contrib/debian/bitcoin-qt.desktop @@ -0,0 +1,12 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=Bitcoin +Comment=Bitcoin P2P Cryptocurrency +Comment[fr]=Bitcoin, monnaie virtuelle cryptographique pair à pair +Comment[tr]=Bitcoin, eşten eşe kriptografik sanal para birimi +Exec=/usr/bin/bitcoin-qt +Terminal=false +Type=Application +Icon=/usr/share/pixmaps/bitcoin128.png +MimeType=x-scheme-handler/bitcoin; +Categories=Office; diff --git a/contrib/debian/bitcoin-qt.install b/contrib/debian/bitcoin-qt.install new file mode 100644 index 0000000..59cacb0 --- /dev/null +++ b/contrib/debian/bitcoin-qt.install @@ -0,0 +1,6 @@ +bitcoin-qt usr/bin +share/pixmaps/bitcoin32.xpm usr/share/pixmaps +share/pixmaps/bitcoin16.xpm usr/share/pixmaps +share/pixmaps/bitcoin128.png usr/share/pixmaps +debian/bitcoin-qt.desktop usr/share/applications +debian/bitcoin-qt.protocol usr/share/kde4/services/ diff --git a/contrib/debian/bitcoin-qt.lintian-overrides b/contrib/debian/bitcoin-qt.lintian-overrides new file mode 100644 index 0000000..7fb230e --- /dev/null +++ b/contrib/debian/bitcoin-qt.lintian-overrides @@ -0,0 +1,2 @@ +# Linked code is Expat - only Debian packaging is GPL-2+ +bitcoin-qt: possible-gpl-code-linked-with-openssl diff --git a/contrib/debian/bitcoin-qt.protocol b/contrib/debian/bitcoin-qt.protocol new file mode 100644 index 0000000..014588d --- /dev/null +++ b/contrib/debian/bitcoin-qt.protocol @@ -0,0 +1,11 @@ +[Protocol] +exec=bitcoin-qt '%u' +protocol=bitcoin +input=none +output=none +helper=true +listing= +reading=false +writing=false +makedir=false +deleting=false diff --git a/contrib/debian/bitcoind.bash-completion b/contrib/debian/bitcoind.bash-completion new file mode 100644 index 0000000..0f84707 --- /dev/null +++ b/contrib/debian/bitcoind.bash-completion @@ -0,0 +1 @@ +contrib/bitcoind.bash-completion bitcoind diff --git a/contrib/debian/bitcoind.examples b/contrib/debian/bitcoind.examples new file mode 100644 index 0000000..4ded67d --- /dev/null +++ b/contrib/debian/bitcoind.examples @@ -0,0 +1 @@ +debian/examples/bitcoin.conf diff --git a/contrib/debian/bitcoind.install b/contrib/debian/bitcoind.install new file mode 100644 index 0000000..7bf7460 --- /dev/null +++ b/contrib/debian/bitcoind.install @@ -0,0 +1 @@ +src/bitcoind usr/bin diff --git a/contrib/debian/bitcoind.lintian-overrides b/contrib/debian/bitcoind.lintian-overrides new file mode 100644 index 0000000..3f9f140 --- /dev/null +++ b/contrib/debian/bitcoind.lintian-overrides @@ -0,0 +1,2 @@ +# Linked code is Expat - only Debian packaging is GPL-2+ +bitcoind: possible-gpl-code-linked-with-openssl diff --git a/contrib/debian/bitcoind.manpages b/contrib/debian/bitcoind.manpages new file mode 100644 index 0000000..3e4ca63 --- /dev/null +++ b/contrib/debian/bitcoind.manpages @@ -0,0 +1,2 @@ +debian/manpages/bitcoind.1 +debian/manpages/bitcoin.conf.5 diff --git a/contrib/debian/changelog b/contrib/debian/changelog new file mode 100644 index 0000000..e600e46 --- /dev/null +++ b/contrib/debian/changelog @@ -0,0 +1,367 @@ +bitcoin (0.8.1-natty3) natty; urgency=low + + * New pixmaps + + -- Jonas Schnelli Mon, 13 May 2013 16:14:00 +0100 + +bitcoin (0.8.1-natty2) natty; urgency=low + + * Remove dumb broken launcher script + + -- Matt Corallo Sun, 24 Mar 2013 20:01:00 -0400 + +bitcoin (0.8.1-natty1) natty; urgency=low + + * New upstream release. + + -- Matt Corallo Tue, 19 Mar 2013 13:03:00 -0400 + +bitcoin (0.8.0-natty1) natty; urgency=low + + * New upstream release. + + -- Matt Corallo Sat, 23 Feb 2013 16:01:00 -0500 + +bitcoin (0.7.2-natty1) natty; urgency=low + + * New upstream release. + + -- Matt Corallo Sat, 15 Dec 2012 10:59:00 -0400 + +bitcoin (0.7.1-natty1) natty; urgency=low + + * New upstream release. + + -- Matt Corallo Wed, 24 Oct 2012 15:06:00 -0400 + +bitcoin (0.7.0-natty1) natty; urgency=low + + * New upstream release. + + -- Matt Corallo Mon, 17 Sep 2012 13:45:00 +0200 + +bitcoin (0.6.3-natty1) natty; urgency=low + + * New upstream release. + + -- Matt Corallo Mon, 25 Jun 2012 23:47:00 +0200 + +bitcoin (0.6.2-natty1) natty; urgency=low + + * Update package description and launch scripts. + + -- Matt Corallo Sat, 2 Jun 2012 16:41:00 +0200 + +bitcoin (0.6.2-natty0) natty; urgency=low + + * New upstream release. + + -- Matt Corallo Tue, 8 May 2012 16:27:00 -0500 + +bitcoin (0.6.1-natty0) natty; urgency=low + + * New upstream release. + + -- Matt Corallo Sun, 6 May 2012 20:09:00 -0500 + +bitcoin (0.6.0-natty0) natty; urgency=low + + * New upstream release. + * Add GNOME/KDE support for bitcoin-qt's bitcoin: URI support. + Thanks to luke-jr for the KDE .protocol file. + + -- Matt Corallo Sat, 31 Mar 2012 15:35:00 -0500 + +bitcoin (0.5.3-natty1) natty; urgency=low + + * Mark for upload to PPA. + + -- Matt Corallo Wed, 14 Mar 2012 23:06:00 -0400 + +bitcoin (0.5.3-natty0) natty; urgency=low + + * New upstream release. + + -- Luke Dashjr Tue, 10 Jan 2012 15:57:00 -0500 + +bitcoin (0.5.2-natty1) natty; urgency=low + + * Remove mentions on anonymity in package descriptions and manpage. + These should never have been there, bitcoin isnt anonymous without + a ton of work that virtually no users will ever be willing and + capable of doing + + -- Matt Corallo Sat, 7 Jan 2012 13:37:00 -0500 + +bitcoin (0.5.2-natty0) natty; urgency=low + + * New upstream release. + + -- Luke Dashjr Fri, 16 Dec 2011 17:57:00 -0500 + +bitcoin (0.5.1-natty0) natty; urgency=low + + * New upstream release. + + -- Matt Corallo Fri, 16 Dec 2011 13:27:00 -0500 + +bitcoin (0.5.0-natty0) natty; urgency=low + + * New upstream release. + + -- Matt Corallo Mon, 21 Nov 2011 11:32:00 -0500 + +bitcoin (0.5.0~rc7-natty0) natty; urgency=low + + * New upstream release candidate. + + -- Matt Corallo Sun, 20 Nov 2011 17:08:00 -0500 + +bitcoin (0.5.0~rc3-natty0) natty; urgency=low + + * New upstream release candidate. + * Don't set rpcpassword for bitcoin-qt. + + -- Matt Corallo Tue, 8 Nov 2011 11:56:00 -0400 + +bitcoin (0.5.0~rc1-natty1) natty; urgency=low + + * Add test_bitcoin to build test + * Fix clean + * Remove uneccessary build-dependancies + + -- Matt Corallo Wed, 26 Oct 2011 14:37:18 -0400 + +bitcoin (0.5.0~rc1-natty0) natty; urgency=low + + * Mark for natty + * Fix broken build + * Fix copyright listing + * Remove bitcoin: URL handler until bitcoin actually has support for it (Oops) + + -- Matt Corallo Wed, 26 Oct 2011 14:37:18 -0400 + +bitcoin (0.5.0~rc1-2) experimental; urgency=low + + * Add bitcoin-qt + + -- Matt Corallo Tue, 25 Oct 2011 15:24:18 -0400 + +bitcoin (0.5.0~rc1-1) experimental; urgency=low + + * New upstream prerelease. + * Add Github as alternate upstream source in watch file. + * Stop build-depending on libcrypto++-dev, and drop patch 1000: + Upstream no longer use crypto++. + * Drop patch 1003: Upstream builds dynamic by default now. + * Update copyright file: Drop notes on longer included sources. + + -- Jonas Smedegaard Fri, 14 Oct 2011 00:16:18 +0200 + +bitcoin (0.4.0-1) unstable; urgency=low + + * New upstream release. + * Stop repackaging source tarballs: No DFSG-violating stripping left. + * Update copyright file: + + Add Github URL to Source. + * Drop dpkg-source local-options hint: Declared options are default + since dpkg-source 1.16.1. + + Add irc URL to Upstream-Contact. + + Add comment on Bitcoin Developers to catch-all Files section. + + Add Files sections for newly readded src/cryptopp/* (new custom + BSD-like license), and newly added doc/build-osx.txt and + src/makefile.osx (Expat). + * Bump debhelper compatibility level to 7. + * Suppress binary icns and gpg files. + * Enable regression tests: + + Build-depend on libboost-test-dev. + + Extend patch 1003 to also dynamically link test binary. + + Build and invoke test binary unless tests are disabled. + * Tighten build-dependency on cdbs: Recent version needed to support + debhelper 7. + * Relax build-depend unversioned on debhelper: needed version + satisfied even in oldstable. + * Stop suppress optional build-dependencies: Satisfied in stable. + Build-depend on devscripts (enabling copyright-check). + + -- Jonas Smedegaard Wed, 05 Oct 2011 01:48:53 +0200 + +bitcoin (0.3.24~dfsg-1) unstable; urgency=low + + * New upstream release. + + [ Jonas Smedegaard ] + * Improve various usage hints: + + Explicitly mention in long description that bitcoind contains + daemon and command-line interface. + + Extend README.Debian with section on lack of GUI, and add primary + headline. + + Avoid installing upstream README: contains no parts relevant for + Debian usage. + Thanks to richard for suggestions (see bug#629443). + * Favor final releases over prereleases in rules and watch file. + Thanks to Jan Dittberner. + * Track -src (not -linux) tarballs in rules and watch file. + Thanks to Jan Dittberner. + * Drop patches 1004 and 1005 (integrated upstream) and simplify + CXXFLAGS in rules file. + * Stop stripping no longer included source-less binaries from upstream + tarballs. + + [ Jan Dittberner ] + * refresh debian/patches/1000_use_system_crypto++.patch + + -- Jonas Smedegaard Tue, 19 Jul 2011 15:08:54 +0200 + +bitcoin (0.3.21~dfsg-2) unstable; urgency=low + + * Enable UPNP support: + + Drop patch 1006. + + Build-depend on libminiupnpc-dev. + Thanks to Matt Corallo. + + -- Jonas Smedegaard Sat, 28 May 2011 15:52:44 +0200 + +bitcoin (0.3.21~dfsg-1) unstable; urgency=low + + * New upstream release. + * Refresh patches. + * Drop patch 1002: no longer needed, as upstream use pkgconfig now. + * Add patch 1006 to really unset USE_UPNP as aparently intended. + * Adjust cleanup rule to preserve .gitignore files. + * Update copyright file: + + Bump format to draft 174 of DEP-5. + + Shorten comments. + * Bump policy compliance to standards-version 3.9.2. + * Shorten Vcs-Browser paragraph in control file. + * Fix mention daemon (not CLI tools) in short description. + * Stop conflicting with or replace bitcoin-cli: Only transitional, no + longer needed. + * Link against unversioned berkeleydb. Update NEWS and README.Debian + accordingly (and improve wording while at it). + Closes: Bug#621425. Thanks to Ondřej Surý. + * This release also implicitly updates linkage against libcrypto++, + which closes: bug#626953, #627024. + * Disable linkage against not yet Debian packaged MiniUPnP. + * Silence seemingly harmless noise about unused variables. + + -- Jonas Smedegaard Tue, 17 May 2011 15:31:24 +0200 + +bitcoin (0.3.20.2~dfsg-2) unstable; urgency=medium + + * Fix have wrapper script execute real binary (not loop executing + itself). + Closes: bug#617290. Thanks to Philippe Gauthier and Etienne Laurin. + * Set urgency=medium as the only (user-exposed) binary is useless + without this fix and has been for some time. + + -- Jonas Smedegaard Wed, 16 Mar 2011 09:11:06 +0100 + +bitcoin (0.3.20.2~dfsg-1) unstable; urgency=low + + * New upstream release. + * Fix provide and replace former package name bitcoin-cli. + Closes: bug#618439. Thanks to Shane Wegner. + + -- Jonas Smedegaard Tue, 15 Mar 2011 11:41:43 +0100 + +bitcoin (0.3.20.01~dfsg-1) unstable; urgency=low + + * New upstream release. + + [ Micah Anderson ] + * Add myself as uploader. + + [ Jonas Smedegaard ] + * Add wrapper for bitcoind to ease initial startup. + * Update patches: + + Drop patch 2002: Applied upstream. + + Add patch 1005 to add phtread linker option. + Closes: bug#615619. Thanks to Shane Wegner. + + Refresh patches. + * Extend copyright years in rules file header. + * Rewrite copyright file using draft svn166 of DEP5 format. + * Rename binary package to bitcoind (from bincoin-cli). + Closes: bug#614025. Thanks to Luke-Jr. + + -- Jonas Smedegaard Tue, 01 Mar 2011 15:55:04 +0100 + +bitcoin (0.3.19~dfsg-6) unstable; urgency=low + + * Fix override agressive optimizations. + * Fix tighten build-dependencies to really fit backporting to Lenny: + + Add fallback build-dependency on libdb4.6++-dev. + + Tighten unversioned Boost build-dependencies to recent versions, + To force use of versioned Boost when backporting to Lenny. + ...needs more love, though: actual build fails. + + -- Jonas Smedegaard Mon, 17 Jan 2011 19:48:35 +0100 + +bitcoin (0.3.19~dfsg-5) unstable; urgency=low + + * Fix lower Boost fallback-build-dependencies to 1.35, really + available in Lenny. + * Correct comment in rules file regarding reason for versioned Boost + fallback-build-dependency. + * Add patch 2002 adding -mt decoration to Boost flags, to ease + backporting to Lenny. + * Respect DEB_BUILD_OPTIONS, and suppress arch-specific optimizations: + + Add patch 1004 to allow overriding optimization flags. + + Set optimization flags conditionally at build time. + + Drop patch 2002 unconditionally suppressing arch-optimizations. + + -- Jonas Smedegaard Mon, 17 Jan 2011 16:04:48 +0100 + +bitcoin (0.3.19~dfsg-4) unstable; urgency=low + + [ Micah Anderson ] + * Provide example bitcoin.conf. + * Add bitcoind(1) and bitcoin.conf(5) man pages. + + [ Jonas Smedegaard ] + * Ease backporting: + + Suppress optional build-dependencies. + + Add fallback build-dependencies on the most recent Boost libs + available in Lenny (where unversioned Boost libs are missing). + * Add Micah as copyright holder for manpages, licensed as GPL-3+. + * Bump copyright format to Subversion candidate draft 162 of DEP5. + + -- Jonas Smedegaard Mon, 17 Jan 2011 14:00:48 +0100 + +bitcoin (0.3.19~dfsg-3) unstable; urgency=low + + * Document in copyright file files excluded from repackaged source. + * Update copyright file: + + Bump DEP5 format hint to Subversion draft rev. 153. + + Consistently wrap at 72 chars. + + Refer to GPL-2 file (not GPL symlink). + * Link against Berkeley DB 4.8 (not 4.7): + + Build-depend on libdb4.8++-dev (and on on libdb4.7++-dev). + + Suggest libdb4.8-util and db4.7-util. + + Add README.Debian note on (untested) upgrade routine. + + Add NEWS entry on changed db version, referring to README.Debian. + + -- Jonas Smedegaard Fri, 07 Jan 2011 22:50:57 +0100 + +bitcoin (0.3.19~dfsg-2) unstable; urgency=low + + * Adjust build options to use optimized miner only for amd64. Fixes + FTBFS on i386 (and other archs, if compiling anywhere else at all). + * Avoid static linking. + * Adjust patch 2001 to avoid only arch-specific optimizations (keep + -O3). + * Extend long description to mention disk consumption and initial use + of IRC. + All of above changes thanks to Helmuth Grohne. + * Add lintian override regarding OpenSSL and GPL: Linked code is Expat + - only Debian packaging is GPL-2+. + + -- Jonas Smedegaard Wed, 29 Dec 2010 00:27:54 +0100 + +bitcoin (0.3.19~dfsg-1) unstable; urgency=low + + [ Jonas Smedegaard ] + * Initial release. + Closes: bug#578157. + + -- Jonas Smedegaard Tue, 28 Dec 2010 15:49:22 +0100 diff --git a/contrib/debian/compat b/contrib/debian/compat new file mode 100644 index 0000000..7f8f011 --- /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 0000000..dd167ef --- /dev/null +++ b/contrib/debian/control @@ -0,0 +1,55 @@ +Source: bitcoin +Section: utils +Priority: optional +Maintainer: Jonas Smedegaard +Uploaders: Micah Anderson +Build-Depends: debhelper, + devscripts, + bash-completion, + libboost-system-dev (>> 1.35) | libboost-system1.35-dev, + libdb4.8++-dev, + libssl-dev, + pkg-config, + libminiupnpc8-dev, + libboost-filesystem-dev (>> 1.35) | libboost-filesystem1.35-dev, + libboost-program-options-dev (>> 1.35) | libboost-program-options1.35-dev, + libboost-thread-dev (>> 1.35) | libboost-thread1.35-dev, + libboost-test-dev (>> 1.35) | libboost-test1.35-dev, + qt4-qmake, + libqt4-dev, + libqrencode-dev +Standards-Version: 3.9.2 +Homepage: http://www.bitcoin.org/ +Vcs-Git: git://github.com/bitcoin/bitcoin.git +Vcs-Browser: http://github.com/bitcoin/bitcoin + +Package: bitcoind +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: peer-to-peer network based digital currency - daemon + Bitcoin 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. + . + Full transaction history is stored locally at each client. This + requires 2+ GB of space, slowly growing. + . + This package provides bitcoind, a combined daemon and CLI tool to + interact with the daemon. + +Package: bitcoin-qt +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: peer-to-peer network based digital currency - Qt GUI + Bitcoin 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. + . + Full transaction history is stored locally at each client. This + requires 2+ GB of space, slowly growing. + . + This package provides Bitcoin-Qt, a GUI for Bitcoin based on Qt. diff --git a/contrib/debian/copyright b/contrib/debian/copyright new file mode 100644 index 0000000..b996049 --- /dev/null +++ b/contrib/debian/copyright @@ -0,0 +1,166 @@ +Format: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?rev=174 +Upstream-Name: Bitcoin +Upstream-Contact: Satoshi Nakamoto + irc://#bitcoin@freenode.net +Source: http://sourceforge.net/projects/bitcoin/files/ + https://github.com/bitcoin/bitcoin + +Files: * +Copyright: 2009-2012, 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/bitcoin.conf b/contrib/debian/examples/bitcoin.conf new file mode 100644 index 0000000..10ec36a --- /dev/null +++ b/contrib/debian/examples/bitcoin.conf @@ -0,0 +1,84 @@ +# bitcoin.conf configuration file. Lines beginning with # are comments. + + +# Network-related settings: + +# Run on the test network instead of the real bitcoin 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=69.164.218.197 +#addnode=10.0.0.2:8333 + +# ... or use as many connect= settings as you like to connect ONLY +# to specific peers: +#connect=69.164.218.197 +#connect=10.0.0.1:8333 + +# Maximum number of inbound+outbound connections. +#maxconnections= + + +# JSON-RPC options (for controlling a running Bitcoin/bitcoind process) + +# server=1 tells Bitcoin to accept JSON-RPC commands. +#server=1 + +# You must set rpcuser and rpcpassword to secure the JSON-RPC api +#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=8332 + +# You can use Bitcoin or bitcoind to send commands to Bitcoin/bitcoind +# 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 Bitcoin -server or bitcoind +#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 + +# Set gen=1 to attempt to generate bitcoins +gen=0 + +# Use SSE instructions to try to generate bitcoins faster. +#4way=1 + +# 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 bitcoins. Transactions with fees +# are more likely than free transactions to be included in generated blocks, so may +# be validated sooner. +paytxfee=0.00 + +# Allow direct connections for the 'pay via IP address' feature. +#allowreceivebyip=1 + + +# User interface options + +# Start Bitcoin 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 0000000..a7281f9 --- /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/bitcoin-qt.1 b/contrib/debian/manpages/bitcoin-qt.1 new file mode 100644 index 0000000..4d5febb --- /dev/null +++ b/contrib/debian/manpages/bitcoin-qt.1 @@ -0,0 +1,206 @@ +.TH BITCOIN-QT "1" "April 2013" "bitcoin-qt 1" +.SH NAME +bitcoin-qt \- peer-to-peer network based digital currency +.SH DESCRIPTION +.SS "Usage:" +.IP +bitcoin\-qt [command\-line options] +.SH OPTIONS +.TP +\-? +This help message +.TP +\fB\-conf=\fR +Specify configuration file (default: bitcoin.conf) +.TP +\fB\-pid=\fR +Specify pid file (default: bitcoind.pid) +.TP +\fB\-gen\fR +Generate coins +.TP +\fB\-gen\fR=\fI0\fR +Don't generate coins +.TP +\fB\-datadir=\fR +Specify data directory +.TP +\fB\-dbcache=\fR +Set database cache size in megabytes (default: 25) +.TP +\fB\-timeout=\fR +Specify connection timeout in milliseconds (default: 5000) +.TP +\fB\-proxy=\fR +Connect through socks proxy +.TP +\fB\-socks=\fR +Select the version of socks proxy to use (4\-5, default: 5) +.TP +\fB\-tor=\fR +Use proxy to reach tor hidden services (default: same as \fB\-proxy\fR) +.TP +\fB\-dns\fR +Allow DNS lookups for \fB\-addnode\fR, \fB\-seednode\fR and \fB\-connect\fR +.TP +\fB\-port=\fR +Listen for connections on (default: 31743 or testnet: 41743) +.TP +\fB\-maxconnections=\fR +Maintain at most connections to peers (default: 125) +.TP +\fB\-addnode=\fR +Add a node to connect to and attempt to keep the connection open +.TP +\fB\-connect=\fR +Connect only to the specified node(s) +.TP +\fB\-seednode=\fR +Connect to a node to retrieve peer addresses, and disconnect +.TP +\fB\-externalip=\fR +Specify your own public address +.TP +\fB\-onlynet=\fR +Only connect to nodes in network (IPv4, IPv6 or Tor) +.TP +\fB\-discover\fR +Discover own IP address (default: 1 when listening and no \fB\-externalip\fR) +.TP +\fB\-checkpoints\fR +Only accept block chain matching built\-in checkpoints (default: 1) +.TP +\fB\-listen\fR +Accept connections from outside (default: 1 if no \fB\-proxy\fR or \fB\-connect\fR) +.TP +\fB\-bind=\fR +Bind to given address and always listen on it. Use [host]:port notation for IPv6 +.TP +\fB\-dnsseed\fR +Find peers using DNS lookup (default: 1 unless \fB\-connect\fR) +.TP +\fB\-banscore=\fR +Threshold for disconnecting misbehaving peers (default: 100) +.TP +\fB\-bantime=\fR +Number of seconds to keep misbehaving peers from reconnecting (default: 86400) +.TP +\fB\-maxreceivebuffer=\fR +Maximum per\-connection receive buffer, *1000 bytes (default: 5000) +.TP +\fB\-maxsendbuffer=\fR +Maximum per\-connection send buffer, *1000 bytes (default: 1000) +.TP +\fB\-upnp\fR +Use UPnP to map the listening port (default: 1 when listening) +.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\-testnet\fR +Use the test network +.TP +\fB\-debug\fR +Output extra debugging information. Implies all other \fB\-debug\fR* options +.TP +\fB\-debugnet\fR +Output extra network debugging information +.TP +\fB\-logtimestamps\fR +Prepend debug output with timestamp +.TP +\fB\-shrinkdebugfile\fR +Shrink debug.log file on client startup (default: 1 when no \fB\-debug\fR) +.TP +\fB\-printtoconsole\fR +Send trace/debug info to console instead of debug.log file +.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 (default: 8332 or testnet: 18332) +.TP +\fB\-rpcallowip=\fR +Allow JSON\-RPC connections from specified IP address +.TP +\fB\-rpcthreads=\fR +Set the number of threads to service RPC calls (default: 4) +.TP +\fB\-blocknotify=\fR +Execute command when the best block changes (%s in cmd is replaced by block hash) +.TP +\fB\-walletnotify=\fR +Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) +.TP +\fB\-alertnotify=\fR +Execute command when a relevant alert is received (%s in cmd is replaced by message) +.TP +\fB\-upgradewallet\fR +Upgrade wallet to latest format +.TP +\fB\-keypool=\fR +Set key pool size to (default: 100) +.TP +\fB\-rescan\fR +Rescan the block chain for missing wallet transactions +.TP +\fB\-salvagewallet\fR +Attempt to recover private keys from a corrupt wallet.dat +.TP +\fB\-checkblocks=\fR +How many blocks to check at startup (default: 288, 0 = all) +.TP +\fB\-checklevel=\fR +How thorough the block verification is (0\-4, default: 3) +.TP +\fB\-txindex\fR +Maintain a full transaction index (default: 0) +.TP +\fB\-loadblock=\fR +Imports blocks from external blk000??.dat file +.TP +\fB\-reindex\fR +Rebuild block chain index from current blk000??.dat files +.TP +\fB\-par=\fR +Set the number of script verification threads (1\-16, 0=auto, default: 0) +.SS "Block creation options:" +.TP +\fB\-blockminsize=\fR +Set minimum block size in bytes (default: 0) +.TP +\fB\-blockmaxsize=\fR +Set maximum block size in bytes (default: 250000) +.HP +\fB\-blockprioritysize=\fR Set maximum size of high\-priority/low\-fee transactions in bytes (default: 27000) +.PP +SSL options: (see the Bitcoin Wiki for SSL setup instructions) +.TP +\fB\-rpcssl\fR +Use OpenSSL (https) for JSON\-RPC connections +.TP +\fB\-rpcsslcertificatechainfile=\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) +.SS "UI options:" +.TP +\fB\-lang=\fR +Set language, for example "de_DE" (default: system locale) +.TP +\fB\-min\fR +Start minimized +.TP +\fB\-splash\fR +Show splash screen on startup (default: 1) diff --git a/contrib/debian/manpages/bitcoin.conf.5 b/contrib/debian/manpages/bitcoin.conf.5 new file mode 100644 index 0000000..eef2131 --- /dev/null +++ b/contrib/debian/manpages/bitcoin.conf.5 @@ -0,0 +1,92 @@ +.TH BITCOIN.CONF "5" "January 2011" "bitcoin.conf 3.19" +.SH NAME +bitcoin.conf \- bitcoin 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, bitcoind(1) will look for a file named bitcoin.conf(5) in the bitcoin 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 +bitcoin.conf should be located in $HOME/.bitcoin +.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 *bitcoin* 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 +\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 *bitcoin* 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 *bitcoin* 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 *bitcoin* or *bitcoind(1)* to send commands to *bitcoin*/*bitcoind(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 *bitcoin* '\-server' or *bitcoind(1)*. Example of OpenSSL settings used when *rpcssl*='1': +.TP +\fB\-rpcsslciphers=\fR +Acceptable ciphers (default: TLSv1+HIGH:\:!SSLv2:\:!aNULL:\:!eNULL:\:!AH:\:!3DES:\:@STRENGTH) +.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 bitcoins. +.TP +\fB4way=\fR[\fI'0'\fR|\fI'1'\fR] +Enable or disable use SSE instructions to try to generate bitcoins faster. +.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 bitcoins. 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 bitcoind minimized. +.TP +\fBminimizetotray=\fR[\fI'0'\fR|\fI'1'\fR] +Enable or disable minimize to the system tray. +.SH "SEE ALSO" +bitcoind(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/bitcoind.1 b/contrib/debian/manpages/bitcoind.1 new file mode 100644 index 0000000..e99d2c8 --- /dev/null +++ b/contrib/debian/manpages/bitcoind.1 @@ -0,0 +1,209 @@ +.TH BITCOIND "1" "January 2011" "bitcoind 3.19" +.SH NAME +bitcoind \- peer-to-peer network based digital currency +.SH SYNOPSIS +bitcoin [options] [params] +.TP +bitcoin [options] help \- Get help for a command +.SH DESCRIPTION +This manual page documents the bitcoind program. Bitcoin 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: + +Bitcoins 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 Greenchain and central banks. The limited inflation of the Bitcoin 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: bitcoin.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 'bitcoinaddress'\fR +Returns the account associated with the given address. +.TP +\fBsetaccount 'bitcoinaddress' ['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 bitcoin 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 bitcoins, 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 bitcoin address for receiving payments. If 'account' is specified (recommended), it is added to the address book so payments received with the address will be credited to 'account'. +.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 'bitcoinaddress' ['minconf=1']\fR +Returns the total amount received by 'bitcoinaddress' 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. + *note: requires bitcoin 0.3.20 or later. +.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). + + *note: requires bitcoin 0.3.20 or later. +.TP +\fBmove <'fromaccount'> <'toaccount'> <'amount'> ['minconf=1'] ['comment']\fR +Moves funds between accounts. +.TP +\fBsendfrom* <'account'> <'bitcoinaddress'> <'amount'> ['minconf=1'] ['comment'] ['comment-to']\fR +Sends amount from account's balance to 'bitcoinaddress'. This method will fail if there is less than amount bitcoins 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 'bitcoinaddress' 'amount' ['comment'] ['comment-to']\fR +Sends amount from the server's available balance to 'bitcoinaddress'. amount is a real and is rounded to the nearest 0.01. Returns transaction id on success. +.TP +\fBstop\fR +Stops the bitcoin server. +.TP +\fBvalidateaddress 'bitcoinaddress'\fR +Checks that 'bitcoinaddress' looks like a proper bitcoin address. Returns an object containing: + + "isvalid" : true or false. + "ismine" : true if the address is in the server's wallet. + "address" : bitcoinaddress. + + *note: ismine and address are only returned if the address is valid. + +.SH "SEE ALSO" +bitcoin.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/patches/README b/contrib/debian/patches/README new file mode 100644 index 0000000..80c1584 --- /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 0000000..8b13789 --- /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 0000000..98bb2bb --- /dev/null +++ b/contrib/debian/rules @@ -0,0 +1,33 @@ +#!/usr/bin/make -f +# -*- mode: makefile; coding: utf-8 -*- + +#DEB_MAKE_CHECK_TARGET = test_bitcoin +#build/bitcoind:: +# $(if $(filter nocheck,$(DEB_BUILD_OPTIONS)),,src/test_bitcoin) + +DEB_INSTALL_EXAMPLES_bitcoind += debian/examples/* +DEB_INSTALL_MANPAGES_bitcoind += debian/manpages/* + +%: + dh --with bash-completion $@ + +override_dh_auto_build: + cd src; $(MAKE) -f makefile.unix bitcoind + $(MAKE) + +override_dh_auto_clean: + if [ -f Makefile ]; then $(MAKE) clean; else rm -rf build/; rm -f bitcoin-qt; fi + cd src; $(MAKE) -f makefile.unix clean + +override_dh_auto_configure: + qmake bitcoin-qt.pro USE_QRCODE=1 + +override_dh_auto_test: + cd src; $(MAKE) -f makefile.unix test_bitcoin + src/test_bitcoin + +# Ensure wrapper is set executable +binary-post-install/bitcoind: + chmod +x $(cdbs_curdestdir)usr/bin/bitcoind +binary-post-install/bitcoin-qt: + chmod +x $(cdbs_curdestdir)usr/bin/bitcoin-qt diff --git a/contrib/debian/source/format b/contrib/debian/source/format new file mode 100644 index 0000000..163aaf8 --- /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 0000000..c96d2f8 --- /dev/null +++ b/contrib/debian/watch @@ -0,0 +1,7 @@ +# Run the "uscan" command to check for upstream updates and more. +version=3 +# use qa.debian.org redirector; see man uscan +opts=uversionmangle=s/(\d)(alpha|beta|rc)/$1~$2/;s/\-src//,dversionmangle=s/~dfsg\d*// \ + http://sf.net/bitcoin/bitcoin-(\d.*)-linux\.tar\.gz debian +opts=uversionmangle=s/(\d)(alpha|beta|rc)/$1~$2/,dversionmangle=s/~dfsg\d*// \ + http://githubredir.debian.net/github/bitcoin/bitcoin v(.*).tar.gz diff --git a/contrib/gitian-descriptors/README b/contrib/gitian-descriptors/README new file mode 100644 index 0000000..46c7668 --- /dev/null +++ b/contrib/gitian-descriptors/README @@ -0,0 +1,86 @@ +Gavin's notes on getting gitian builds up and running using KVM: + +These instructions distilled from: + https://help.ubuntu.com/community/KVM/Installation +... see there for complete details. + +You need the right hardware: you need a 64-bit-capable CPU with hardware virtualization support (Intel VT-x or AMD-V). Not all modern CPUs support hardware virtualization. + +You probably need to enable hardware virtualization in your machine's BIOS. + +You need to be running a recent version of 64-bit-Ubuntu, and you need to install several prerequisites: + sudo apt-get install ruby apache2 git apt-cacher-ng python-vm-builder qemu-kvm + +Sanity checks: + sudo service apt-cacher-ng status # Should return apt-cacher-ng is running + ls -l /dev/kvm # Should show a /dev/kvm device + + +Once you've got the right hardware and software: + + git clone git://github.com/bitcoin/bitcoin.git + git clone git://github.com/devrandom/gitian-builder.git + mkdir gitian-builder/inputs + cd gitian-builder/inputs + # Inputs for Linux and Win32: + wget -O miniupnpc-1.6.tar.gz 'http://miniupnp.tuxfamily.org/files/download.php?file=miniupnpc-1.6.tar.gz' + wget 'http://fukuchi.org/works/qrencode/qrencode-3.2.0.tar.bz2' + # Inputs for Win32: (Linux has packages for these) + wget 'https://downloads.sourceforge.net/project/boost/boost/1.50.0/boost_1_50_0.tar.bz2' + wget 'http://www.openssl.org/source/openssl-1.0.1c.tar.gz' + wget 'http://download.oracle.com/berkeley-db/db-4.8.30.NC.tar.gz' + wget 'https://downloads.sourceforge.net/project/libpng/zlib/1.2.6/zlib-1.2.6.tar.gz' + wget 'https://downloads.sourceforge.net/project/libpng/libpng15/older-releases/1.5.9/libpng-1.5.9.tar.gz' + wget 'http://releases.qt-project.org/qt4/source/qt-everywhere-opensource-src-4.8.3.tar.gz' + cd ../.. + + cd gitian-builder + bin/make-base-vm --arch i386 + bin/make-base-vm --arch amd64 + cd .. + + # Build Linux release: + cd bitcoin + git pull + cd ../gitian-builder + git pull + ./bin/gbuild --commit bitcoin=HEAD ../bitcoin/contrib/gitian-descriptors/gitian.yml + + # Build Win32 dependencies: (only needs to be done once, or when dependency versions change) + ./bin/gbuild --commit bitcoin=HEAD ../bitcoin/contrib/gitian-descriptors/boost-win32.yml + ./bin/gbuild --commit bitcoin=HEAD ../bitcoin/contrib/gitian-descriptors/deps-win32.yml + ./bin/gbuild --commit bitcoin=HEAD ../bitcoin/contrib/gitian-descriptors/qt-win32.yml + + # Build Win32 release: + ./bin/gbuild --commit bitcoin=HEAD ../bitcoin/contrib/gitian-descriptors/gitian-win32.yml + +--------------------- + +gitian-builder now also supports building using LXC. See + https://help.ubuntu.com/12.04/serverguide/lxc.html +... for how to get LXC up and running under Ubuntu. + +If your main machine is a 64-bit Mac or PC with a few gigabytes of memory +and at least 10 gigabytes of free disk space, you can gitian-build using +LXC running inside a virtual machine. + +Here's a description of Gavin's setup on OSX 10.6: + +1. Download and install VirtualBox from https://www.virtualbox.org/ + +2. Download the 64-bit Ubuntu Desktop 12.04 LTS .iso CD image from + http://www.ubuntu.com/ + +3. Run VirtualBox and create a new virtual machine, using the + Ubuntu .iso (see the VirtualBox documentation for details). + Create it with at least 2 gigabytes of memory and a disk + that is at least 20 gigabytes big. + +4. Inside the running Ubuntu desktop, install: + sudo apt-get install debootstrap lxc ruby apache2 git apt-cacher-ng python-vm-builder + +5. Still inside Ubuntu, tell gitian-builder to use LXC, then follow the "Once you've got the right + hardware and software" instructions above: + export USE_LXC=1 + git clone git://github.com/bitcoin/bitcoin.git + ... etc diff --git a/contrib/gitian-descriptors/boost-win32.yml b/contrib/gitian-descriptors/boost-win32.yml new file mode 100644 index 0000000..1eeb9ea --- /dev/null +++ b/contrib/gitian-descriptors/boost-win32.yml @@ -0,0 +1,38 @@ +--- +name: "boost" +suites: +- "lucid" +architectures: +- "i386" +packages: +- "mingw32" +- "faketime" +- "zip" +reference_datetime: "2011-01-30 00:00:00" +remotes: [] +files: +- "boost_1_50_0.tar.bz2" +script: | + TMPDIR="$HOME/tmpdir" + mkdir -p $TMPDIR/bin/$GBUILD_BITS $TMPDIR/include + tar xjf boost_1_50_0.tar.bz2 + cd boost_1_50_0 + echo "using gcc : 4.4 : i586-mingw32msvc-g++ + : + i586-mingw32msvc-windres + i586-mingw32msvc-ar + -frandom-seed=boost1 + ;" > user-config.jam + ./bootstrap.sh --without-icu + ./bjam toolset=gcc target-os=windows threadapi=win32 threading=multi variant=release link=static --user-config=user-config.jam --without-mpi --without-python -sNO_BZIP2=1 -sNO_ZLIB=1 --layout=tagged --build-type=complete $MAKEOPTS stage + for lib in chrono date_time exception filesystem graph iostreams math_c99f math_c99l math_c99 math_tr1f math_tr1l math_tr1 prg_exec_monitor program_options random regex serialization signals system test_exec_monitor thread_win32 unit_test_framework wave wserialization; do + mkdir $lib + (cd $lib ; ar xf ../stage/lib/libboost_${lib}-mt-s.a) + mv $lib $TMPDIR/bin/$GBUILD_BITS + done + cp -a boost $TMPDIR/include + cd $TMPDIR + export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1 + export FAKETIME=$REFERENCE_DATETIME + zip -r boost-win32-1.50.0-gitian2.zip * + cp boost-win32-1.50.0-gitian2.zip $OUTDIR diff --git a/contrib/gitian-descriptors/deps-win32.yml b/contrib/gitian-descriptors/deps-win32.yml new file mode 100644 index 0000000..90f4c6c --- /dev/null +++ b/contrib/gitian-descriptors/deps-win32.yml @@ -0,0 +1,72 @@ +--- +name: "bitcoin-deps" +suites: +- "lucid" +architectures: +- "i386" +packages: +- "mingw32" +- "git-core" +- "zip" +- "faketime" +- "wine" +- "psmisc" +reference_datetime: "2011-01-30 00:00:00" +remotes: [] +files: +- "openssl-1.0.1c.tar.gz" +- "db-4.8.30.NC.tar.gz" +- "miniupnpc-1.6.tar.gz" +- "zlib-1.2.6.tar.gz" +- "libpng-1.5.9.tar.gz" +- "qrencode-3.2.0.tar.bz2" +script: | + # + export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1 + export FAKETIME=$REFERENCE_DATETIME + export TZ=UTC + # + tar xzf openssl-1.0.1c.tar.gz + cd openssl-1.0.1c + ./Configure --cross-compile-prefix=i586-mingw32msvc- mingw + make + cd .. + # + tar xzf db-4.8.30.NC.tar.gz + cd db-4.8.30.NC/build_unix + ../dist/configure --enable-mingw --enable-cxx --host=i586-mingw32msvc CFLAGS="-I/usr/i586-mingw32msvc/include" + make $MAKEOPTS + cd ../.. + # + tar xzf miniupnpc-1.6.tar.gz + cd miniupnpc-1.6 + sed 's/dllwrap -k --driver-name gcc/$(DLLWRAP) -k --driver-name $(CC)/' -i Makefile.mingw + sed 's|wingenminiupnpcstrings $< $@|./wingenminiupnpcstrings $< $@|' -i Makefile.mingw + make -f Makefile.mingw DLLWRAP=i586-mingw32msvc-dllwrap CC=i586-mingw32msvc-gcc AR=i586-mingw32msvc-ar + cd .. + mv miniupnpc-1.6 miniupnpc + # + tar xzf zlib-1.2.6.tar.gz + cd zlib-1.2.6 + make -f win32/Makefile.gcc PREFIX=i586-mingw32msvc- $MAKEOPTS + cd .. + # + tar xzf libpng-1.5.9.tar.gz + cd libpng-1.5.9 + ./configure -disable-shared CC=i586-mingw32msvc-cc AR=i586-mingw32msvc-ar STRIP=i586-mingw32msvc-strip RANLIB=i586-mingw32msvc-ranlib OBJDUMP=i586-mingw32msvc-objdump LD=i586-mingw32msvc-ld LDFLAGS="-L../zlib-1.2.6/" CFLAGS="-I../zlib-1.2.6/" + make $MAKEOPTS + cd .. + # + tar xjf qrencode-3.2.0.tar.bz2 + cd qrencode-3.2.0 + ./configure CC=i586-mingw32msvc-cc AR=i586-mingw32msvc-ar STRIP=i586-mingw32msvc-strip RANLIB=i586-mingw32msvc-ranlib OBJDUMP=i586-mingw32msvc-objdump LD=i586-mingw32msvc-ld png_LIBS="../libpng-1.5.9/.libs/libpng15.a ../zlib-1.2.6/libz.a" png_CFLAGS="-I../libpng-1.5.9" + make $MAKEOPTS + cd .. + # + zip -r $OUTDIR/bitcoin-deps-0.0.5.zip \ + $(ls qrencode-*/{qrencode.h,.libs/libqrencode.{,l}a} | sort) \ + $(ls db-*/build_unix/{libdb_cxx.a,db.h,db_cxx.h,libdb.a,.libs/libdb_cxx-?.?.a} | sort) \ + $(find openssl-* -name '*.a' -o -name '*.h' | sort) \ + $(find miniupnpc -name '*.h' -o -name 'libminiupnpc.a' | sort) + # Kill wine processes as gitian won't figure out we are done otherwise + killall wineserver services.exe explorer.exe winedevice.exe diff --git a/contrib/gitian-descriptors/gitian-win32.yml b/contrib/gitian-descriptors/gitian-win32.yml new file mode 100644 index 0000000..59b4cf2 --- /dev/null +++ b/contrib/gitian-descriptors/gitian-win32.yml @@ -0,0 +1,65 @@ +--- +name: "Greenchain" +suites: +- "lucid" +architectures: +- "i386" +packages: +- "mingw32" +- "git-core" +- "unzip" +- "nsis" +- "faketime" +reference_datetime: "2011-01-30 00:00:00" +remotes: +- "url": "https://github.com/moonmooncoin/Greenchain.git" + "dir": "Greenchain" +files: +- "qt-win32-4.8.3-gitian-r1.zip" +- "boost-win32-1.50.0-gitian2.zip" +- "bitcoin-deps-0.0.5.zip" +script: | + # + mkdir $HOME/qt + cd $HOME/qt + unzip ../build/qt-win32-4.8.3-gitian-r1.zip + cd $HOME/build/ + export PATH=$HOME/qt/bin/:$PATH + # + mkdir boost_1_50_0 + cd boost_1_50_0 + mkdir -p stage/lib + unzip ../boost-win32-1.50.0-gitian2.zip + cd bin/$GBUILD_BITS + for lib in *; do + i586-mingw32msvc-ar rc ../../stage/lib/libboost_${lib}-mt-s.a $lib/*.o + i586-mingw32msvc-ranlib ../../stage/lib/libboost_${lib}-mt-s.a + done + cd ../.. + mv include/boost . + cd .. + # + unzip bitcoin-deps-0.0.5.zip + # + find -type f | xargs touch --date="$REFERENCE_DATETIME" + # + cd Greenchain + mkdir -p $OUTDIR/src + git archive HEAD | tar -x -C $OUTDIR/src + cp $OUTDIR/src/doc/README_windows.txt $OUTDIR/readme.txt + cp $OUTDIR/src/COPYING $OUTDIR/COPYING.txt + export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1 + export FAKETIME=$REFERENCE_DATETIME + export TZ=UTC + $HOME/qt/src/bin/qmake -spec unsupported/win32-g++-cross MINIUPNPC_LIB_PATH=$HOME/build/miniupnpc MINIUPNPC_INCLUDE_PATH=$HOME/build/ BDB_LIB_PATH=$HOME/build/db-4.8.30.NC/build_unix BDB_INCLUDE_PATH=$HOME/build/db-4.8.30.NC/build_unix BOOST_LIB_PATH=$HOME/build/boost_1_50_0/stage/lib BOOST_INCLUDE_PATH=$HOME/build/boost_1_50_0 BOOST_LIB_SUFFIX=-mt-s BOOST_THREAD_LIB_SUFFIX=_win32-mt-s OPENSSL_LIB_PATH=$HOME/build/openssl-1.0.1c OPENSSL_INCLUDE_PATH=$HOME/build/openssl-1.0.1c/include QRENCODE_LIB_PATH=$HOME/build/qrencode-3.2.0/.libs QRENCODE_INCLUDE_PATH=$HOME/build/qrencode-3.2.0 USE_QRCODE=1 INCLUDEPATH=$HOME/build DEFINES=BOOST_THREAD_USE_LIB BITCOIN_NEED_QT_PLUGINS=1 QMAKE_LRELEASE=lrelease QMAKE_CXXFLAGS=-frandom-seed=bitcoin USE_BUILD_INFO=1 + make $MAKEOPTS + cp release/Greenchain-qt.exe $OUTDIR/ + # + cd src + export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1 + export FAKETIME=$REFERENCE_DATETIME + export TZ=UTC + make -f makefile.linux-mingw $MAKEOPTS DEPSDIR=$HOME/build Greenchaind.exe USE_UPNP=0 DEBUGFLAGS="-frandom-seed=bitcoin" + i586-mingw32msvc-strip Greenchaind.exe + mkdir $OUTDIR/daemon + cp Greenchaind.exe $OUTDIR/daemon diff --git a/contrib/gitian-descriptors/gitian.yml b/contrib/gitian-descriptors/gitian.yml new file mode 100644 index 0000000..195d0e3 --- /dev/null +++ b/contrib/gitian-descriptors/gitian.yml @@ -0,0 +1,55 @@ +--- +name: "bitcoin" +suites: +- "lucid" +architectures: +- "i386" +- "amd64" +packages: +- "libdb4.8++-dev" +- "qt4-qmake" +- "libqt4-dev" +- "libboost-system-dev" +- "libboost-filesystem-dev" +- "libboost-program-options-dev" +- "libboost-thread-dev" +- "libssl-dev" +- "git-core" +- "unzip" +- "pkg-config" +- "libpng12-dev" +reference_datetime: "2011-01-30 00:00:00" +remotes: +- "url": "https://github.com/bitcoin/bitcoin.git" + "dir": "bitcoin" +files: +- "miniupnpc-1.6.tar.gz" +- "qrencode-3.2.0.tar.bz2" +script: | + INSTDIR="$HOME/install" + export LIBRARY_PATH="$INSTDIR/lib" + # + tar xzf miniupnpc-1.6.tar.gz + cd miniupnpc-1.6 + INSTALLPREFIX=$INSTDIR make $MAKEOPTS install + cd .. + # + tar xjf qrencode-3.2.0.tar.bz2 + cd qrencode-3.2.0 + ./configure --prefix=$INSTDIR --enable-static --disable-shared + make $MAKEOPTS install + cd .. + # + cd bitcoin + mkdir -p $OUTDIR/src + git archive HEAD | tar -x -C $OUTDIR/src + cp $OUTDIR/src/doc/README.md $OUTDIR + cp $OUTDIR/src/COPYING $OUTDIR + cd src + make -f makefile.unix STATIC=1 OPENSSL_INCLUDE_PATH="$INSTDIR/include" OPENSSL_LIB_PATH="$INSTDIR/lib" $MAKEOPTS bitcoind USE_UPNP=0 DEBUGFLAGS= + mkdir -p $OUTDIR/bin/$GBUILD_BITS + install -s bitcoind $OUTDIR/bin/$GBUILD_BITS + cd .. + qmake INCLUDEPATH="$INSTDIR/include" LIBS="-L$INSTDIR/lib" RELEASE=1 USE_QRCODE=1 + make $MAKEOPTS + install bitcoin-qt $OUTDIR/bin/$GBUILD_BITS diff --git a/contrib/gitian-descriptors/qt-win32.yml b/contrib/gitian-descriptors/qt-win32.yml new file mode 100644 index 0000000..15daf06 --- /dev/null +++ b/contrib/gitian-descriptors/qt-win32.yml @@ -0,0 +1,54 @@ +--- +name: "qt" +suites: +- "lucid" +architectures: +- "i386" +packages: +- "mingw32" +- "zip" +- "faketime" +reference_datetime: "2011-01-30 00:00:00" +remotes: [] +files: +- "qt-everywhere-opensource-src-4.8.3.tar.gz" +script: | + INSTDIR="$HOME/qt/" + mkdir $INSTDIR + SRCDIR="$INSTDIR/src/" + mkdir $SRCDIR + # + tar xzf qt-everywhere-opensource-src-4.8.3.tar.gz + cd qt-everywhere-opensource-src-4.8.3 + sed 's/$TODAY/2011-01-30/' -i configure + sed 's/i686-pc-mingw32-/i586-mingw32msvc-/' -i mkspecs/unsupported/win32-g++-cross/qmake.conf + sed --posix 's|QMAKE_CFLAGS\t\t= -pipe|QMAKE_CFLAGS\t\t= -pipe -isystem /usr/i586-mingw32msvc/include/ -frandom-seed=qtbuild|' -i mkspecs/unsupported/win32-g++-cross/qmake.conf + sed 's/QMAKE_CXXFLAGS_EXCEPTIONS_ON = -fexceptions -mthreads/QMAKE_CXXFLAGS_EXCEPTIONS_ON = -fexceptions/' -i mkspecs/unsupported/win32-g++-cross/qmake.conf + sed 's/QMAKE_LFLAGS_EXCEPTIONS_ON = -mthreads/QMAKE_LFLAGS_EXCEPTIONS_ON = -lmingwthrd/' -i mkspecs/unsupported/win32-g++-cross/qmake.conf + sed --posix 's/QMAKE_MOC\t\t= i586-mingw32msvc-moc/QMAKE_MOC\t\t= moc/' -i mkspecs/unsupported/win32-g++-cross/qmake.conf + sed --posix 's/QMAKE_RCC\t\t= i586-mingw32msvc-rcc/QMAKE_RCC\t\t= rcc/' -i mkspecs/unsupported/win32-g++-cross/qmake.conf + sed --posix 's/QMAKE_UIC\t\t= i586-mingw32msvc-uic/QMAKE_UIC\t\t= uic/' -i mkspecs/unsupported/win32-g++-cross/qmake.conf + # ar adds timestamps to every object file included in the static library + # providing -D as ar argument is supposed to solve it, but doesn't work as qmake strips off the arguments and adds -M to pass a script... + # which somehow cannot be combined with other flags. + # use faketime only for ar, as it confuses make/qmake into hanging sometimes + sed --posix "s|QMAKE_LIB\t\t= i586-mingw32msvc-ar -ru|QMAKE_LIB\t\t= $HOME/ar -Dr|" -i mkspecs/unsupported/win32-g++-cross/qmake.conf + echo '#!/bin/bash' > $HOME/ar + echo 'export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1' >> $HOME/ar + echo 'i586-mingw32msvc-ar "$@"' >> $HOME/ar + chmod +x $HOME/ar + #export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1 + export FAKETIME=$REFERENCE_DATETIME + export TZ=UTC + ./configure -prefix $INSTDIR -confirm-license -release -opensource -static -no-qt3support -xplatform unsupported/win32-g++-cross -no-multimedia -no-audio-backend -no-phonon -no-phonon-backend -no-declarative -no-script -no-scripttools -no-javascript-jit -no-webkit -no-svg -no-xmlpatterns -no-sql-sqlite -no-nis -no-cups -no-iconv -no-dbus -no-gif -no-libtiff -no-opengl -nomake examples -nomake demos -nomake docs -no-feature-style-plastique -no-feature-style-cleanlooks -no-feature-style-motif -no-feature-style-cde -no-feature-style-windowsce -no-feature-style-windowsmobile -no-feature-style-s60 + find . -name *.prl | xargs -l sed 's|/\.||' -i + find . -name *.prl | xargs -l sed 's|/$||' -i + make $MAKEOPTS install + cp -a bin $SRCDIR/ + cd $INSTDIR + find . -name *.prl | xargs -l sed 's|/$||' -i + #sed 's|QMAKE_PRL_LIBS.*|QMAKE_PRL_LIBS = -lQtDeclarative -lQtScript -lQtSvg -lQtSql -lQtXmlPatterns -lQtGui -lgdi32 -lcomdlg32 -loleaut32 -limm32 -lwinmm -lwinspool -lmsimg32 -lQtNetwork -lQtCore -lole32 -luuid -lws2_32 -ladvapi32 -lshell32 -luser32 -lkernel32|' -i imports/Qt/labs/Greenchains/qmlGreenchainsplugin.prl + + # as zip stores file timestamps, use faketime to intercept stat calls to set dates for all files to reference date + export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1 + zip -r $OUTDIR/qt-win32-4.8.3-gitian-r1.zip * diff --git a/contrib/gitian-downloader/bluematt-key.pgp b/contrib/gitian-downloader/bluematt-key.pgp new file mode 100644 index 0000000000000000000000000000000000000000..fb6d9eb28423d2c8ece89179f8e67ae06fa6dc13 GIT binary patch literal 4113 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 literal 0 HcmV?d00001 diff --git a/contrib/gitian-downloader/gavinandresen-key.pgp b/contrib/gitian-downloader/gavinandresen-key.pgp new file mode 100644 index 0000000000000000000000000000000000000000..f81f44e874707837c13446858db2fed14ad5c501 GIT binary patch literal 1176 zcmV;J1ZVr10ipyd+BPOXG1ZrDpv%iXyY*~=|+SlmVRJ2@d|5G=3lw%C&g;j0KvRW zG0RVD!3nRuh%R)pia&~L1FEOGx;eXR*K` zu*WO05N?XC!C+-X<8ovFvIpC&fxEyfX(2{j!G#JZ7?CU%mrw)$TSGNqq(L>S_EMhm z>C$tB193UkB)z)vhMWgbGCTvPxP}Kfdx0GIl8t06ZPII(&3kQCzEXv7#M)%vPj{EY zcen(gIU`UxU-nd2`Gon+|B_0Ff*@;+F`bIwREZv?xbmc4=-PL2hJnWpib2AUtPbc4=;5Ze((0b7gKoXKi6=Y%XJO zZ9a%*1QQVg044HzfCdW*2nPcK1Qh}Z0|XWV0|5da0Rk6*0162Z zb%-P{zBtpUe6IkY$jlON2g&-~!PtabhH%KRx;RqM0G%3)n>ot_(%$#C+gXU@atMHu zy_C5E4FpWQvb7Kh0Lyf?4-i~4^f3$`ga{Ay5*wreKf>2lV6ywp8d}7ENW=D%ux6^* zfe_#szOQ3V{y*tz7?DrDyfq@LWFCHCr%@-(h(6PCP!Kw9x74URPC$@TblGE?^C!T- zq8)?0%b0~_Ytjnul$QnT9RS<*D5dmu1n$x{uRAV*zh*N+8p9}Ho%Pe)*0 zMnEdZ`oH?%L4~7A1F_aQ|8Rcjri-TI9EL4W#QMcIXrNTVrH)rvOHg%{!T`G$1?>8K zRy|m)M}%VOrUsN$>#AQvZnPq$teX`yY;D;>tsyDHK5Qtj#S?asN{)W{exHw@bhzOU z9^1V{J3(+Kj{pM&2mgPBEamX!SrHwdYa^4uCaz(-f7?+0M)V2mw#6{WkHkn8b1R-^ zYDL+eN?p#i&&iBy&Go@UU6MyD*c7YSQ3wLLX`?CdU~}FpEb`VEVP6+=?&FSbCm=k@ z&d1&vM(f=F5d18_%WVyD>j@@JhkmOlWQELtPqF$CFI z6&Wk2#X|#%!_kOO1Q-zl01pKMOuVwS0vikk2`>HzfB*^!5Os(oFTOa_s4)oupKEmm q7ky6Gg$kN{t|V0xiYY!|8vvlVg5(~9gx$G(luB9`Z;BnEL2B3d)*1@{ literal 0 HcmV?d00001 diff --git a/contrib/gitian-downloader/laanwj-key.pgp b/contrib/gitian-downloader/laanwj-key.pgp new file mode 100644 index 0000000..5592951 --- /dev/null +++ b/contrib/gitian-downloader/laanwj-key.pgp @@ -0,0 +1,28 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: SKS 1.1.0 + +mQENBE5UtMEBCADOUz2i9l/D8xYINCmfUDnxi+DXvX5LmZ39ZdvsoE+ugO0SRRGdIHEFO2is +0xezX50wXu9aneb+tEqM0BuiLo6VxaXpxrkxHpr6c4jf37SkE/H0qsi/txEUp7337y3+4HMG +lUjiuh802I72p1qusjsKBnmnnR0rwNouTcoDmGUDh7jpKCtzFv+2TR2dRthJn7vmmjq3+bG6 +PYfqoFY1yHrAGT1lrDBULZsQ/NBLI2+J4oo2LYv3GCq8GNnzrovqvTvui50VSROhLrOe58o2 +shE+sjQShAy5wYkPt1R1fQnpfx+5vf+TPnkxVwRb3h5GhCp0YL8XC/BXsd5vM4KlVH2rABEB +AAG0K1dsYWRpbWlyIEouIHZhbiBkZXIgTGFhbiA8bGFhbndqQGdtYWlsLmNvbT6JATgEEwEC +ACIFAk5UtMECGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEHSBCwEjRsmmy6YIAK09 +buNXyYQrJBsX16sXxEhx5QPKyF3uHJDFJv66SdnpvIkNoznsaPiRJkbTANop93FZmaGa6wVn +zGDiz7jPA8Dpxx5aAYPhIT+zPJAdXWM3wJ/Gio9besRNzniai8Lwi5MZ9R/5yFGBobm6/AcN +4sUoqA3NSV2U3I29R0Vwlzo8GVtmyi9ENSi6Oo7AcXNTRt69cxW4nAHkB+amwwDJlcAb31ex +bogYXPhScwqQZixRr+JBkKxBjkTXXnQypT4KI5SegYwQVYfyiZmDP7UHKe/u6pSKKbVphLg8 +xLB5spcXse8/a2+onrbNlw6y8TXiJ++Z54PE7zztWTXf2huakeG5AQ0ETlS0wQEIAMNO3OkP +xoPRKWzBLcI7JRITAW+HNaLTq3uN2+4WxA57DEjbL9EDoAv+7wTkDAL40f0T+xiu6GJcLFjw +GJZu/tYu7+mErHjrdo+K4suCQt7w5EXCBvOLjhW4tyYMzNx8hP+oqzOW9iEC+6VV91+DYeqt +EkJuyVXOI4vzBlTw8uGow8aMMsCq8XVvKUZFTPsjGl197Q5B3A+ZOFCR8xqiqdPjuz6MglVV +oFdDNu3EZn8zkGsQlovXoE9ndVeVzx/XMNmsxFaMYsReUs253RIf1FEfgExID0fg2OnyLCjS +2iFW1RgajS+/saIkKl+N1iuMzJA7wMAM0plhRueOG0MtZSsAEQEAAYkBHwQYAQIACQUCTlS0 +wQIbDAAKCRB0gQsBI0bJpmsDB/4waenn2CvSHXyomykfpwf5lMte1V5LvH3z5R2LY+1NopRv +LSz3iC39x69XWiTbhywDfgafnGPW4pWBOff2/bu5/A6z1Hnan1vyrRRD/hx1uMJ7S6q+bIvZ +iVIg1p0jH6tdIIhwX3cydhdRZHo7e9oSMgOUWsr6Ar59NRo9CENwGPE4U61HXfOnxWdrFWoA +XdwZczBeLxmUy6Vo6sKqv+gE4bqrtAM0sY/MsQ9cU95x+52ox/sq44lQMwd3ZBYUP7B1qbHI +hZSZuch6MLi5scLPeau0ZvCaljiaMeivP5+x0gWPRs0kI+9sZxInbqvrsJ6oOBJM3xYGhtn1 +zZ7qmZR7 +=si/k +-----END PGP PUBLIC KEY BLOCK----- diff --git a/contrib/gitian-downloader/linux-download-config b/contrib/gitian-downloader/linux-download-config new file mode 100644 index 0000000..aef614d --- /dev/null +++ b/contrib/gitian-downloader/linux-download-config @@ -0,0 +1,38 @@ +--- +name: bitcoin +urls: +- http://bitcoin.org/bitcoin-latest-linux-gitian.zip +rss: +- url: http://sourceforge.net/api/file/index/project-id/244765/mtime/desc/limit/100/rss + xpath: //item/link/text() + pattern: bitcoin-\d+.\d+.\d+-linux-gitian.zip +signers: + 0A82509767C7D4A5D14DA2301AE1D35043E08E54: + weight: 40 + name: BlueMatt + key: bluematt + BF6273FAEF7CC0BA1F562E50989F6B3048A116B5: + weight: 40 + name: Devrandom + key: devrandom + E463A93F5F3117EEDE6C7316BD02942421F4889F: + weight: 40 + name: Luke-Jr + key: luke-jr + D762373D24904A3E42F33B08B9A408E71DAAC974: + weight: 40 + name: "Pieter Wuille" + key: sipa + 77E72E69DA7EE0A148C06B21B34821D4944DE5F7: + weight: 40 + name: tcatm + key: tcatm + 01CDF4627A3B88AAE4A571C87588242FBE38D3A8: + weight: 40 + name: "Gavin Andresen" + key: gavinandresen + 71A3B16735405025D447E8F274810B012346C9A6: + weight: 40 + name: "Wladimir J. van der Laan" + key: laanwj +minimum_weight: 120 diff --git a/contrib/gitian-downloader/luke-jr-key.pgp b/contrib/gitian-downloader/luke-jr-key.pgp new file mode 100644 index 0000000000000000000000000000000000000000..275b041d2059ece19e1a4ade74b32654ab3a2c56 GIT binary patch literal 7322 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'pMIr=Y0zm9x*Gy{hu97sznPEpu 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 aIuiUKpC;|giAK$4lG@Yh1egw&=>8v(vgiE( literal 0 HcmV?d00001 diff --git a/contrib/gitian-downloader/tcatm-key.pgp b/contrib/gitian-downloader/tcatm-key.pgp new file mode 100644 index 0000000000000000000000000000000000000000..baaec76b8c656a04beb4a91155dff40658c87bd7 GIT binary patch literal 1554 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 Mzx|I};H8)T3A&5+761SM literal 0 HcmV?d00001 diff --git a/contrib/installericons/qrk-installer-top.bmp b/contrib/installericons/qrk-installer-top.bmp new file mode 100644 index 0000000000000000000000000000000000000000..dc7128087a5d5b68062e9fa0831411ab001bc74a GIT binary patch literal 9860 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 VCe+>l19;FN2@(f@wfGmm_zx#3n05dF literal 0 HcmV?d00001 diff --git a/contrib/installericons/qrk-installer.ico b/contrib/installericons/qrk-installer.ico new file mode 100644 index 0000000000000000000000000000000000000000..f54ea7eafc87dee32d111b48362c299e5980b097 GIT binary patch literal 353118 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 literal 0 HcmV?d00001 diff --git a/contrib/installericons/qrk-uninstaller.ico b/contrib/installericons/qrk-uninstaller.ico new file mode 100644 index 0000000000000000000000000000000000000000..cc22ae1a43d5de8d7cbb175415cafd9492c24c2f GIT binary patch literal 353118 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%0A$&9}#wKWun0{WRsr!Lz?M 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 J`W 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 literal 0 HcmV?d00001 diff --git a/contrib/installericons/qrk.ico b/contrib/installericons/qrk.ico new file mode 100644 index 0000000000000000000000000000000000000000..554104148e3071cf700847fbc89c3d7e44260aff GIT binary patch literal 353118 zcmeFaXLJ?UmM!f5M)!N&@7gyQ2W+sxB$IQ_ATlC~49Yp@2ojP2p@a}w*WOj<)LL_{9jZ?0z4!j+y}x^p ze}D5{;odcf%crw5!KaC*S$0jCF? z9&mcV=>ew)oE~s`!07>}2b>;odcf%crw5!KaC*S$0jCF?9&mcV=>ew)oE~s`!07>} z2b>;odcf%crw5!KaC*S$0jCF?9&mcV=>ew)oE~s`!07>}2b>;odcf%crw5!KaC*S$ z0jCF?9&mcV>4CqB2iC4#`^Tc9qCc)$wd#+{mM#0^k|j(2xOnm6KNb`e{Gco;Tw?WE zzI?gWf6baTe_Xe2-L1dM7k7TB(*sTqywU^0&u7BbXZiX0&oVMH{xKyb1yNB^2nYzk z%$YMWY0@N&9Xl4IMvcO-VZ$(V=uoQ+88QScE-o&$MQ!ZEzAU3hkH&-v6EJ1+6nJ}k zBRDu1v9Yn3J9jR!va+5@4tcg>#foS0J%+#Xi#i*f9&mc#ojoA@ikGqbj&SZyYHF&* zL-L#49zJ~d54ddd+SCUP8U!r^2M&b2Xd9(@TwOYI#>kN);pXNBPft%oM@KU^{F~&X zJ2Kz6^UfFSJkaTZU$F;-I~TLFvoFq>Gshas$*&P3MmXfT$>IL}`(wa>0ahW02M-=x zTTBk?Sno(`Kl51XJRd(x&##}$Ha$iq=MbDbOrJg-;o;$!H*enmD|3a5lE>Enio@!> zbf*U#dO+6ar8zk{$2foY@$s?7b#jP&Qhq7_Ob%)r`;p7q-|VYAHIE~QO}o8~dBon% zI?t=0rJu(-?e@GtyM7<`W&YxNBs4S>^XJe1Z`p@9=Fk~VqtgSwa1X3rz4{};f5q(C zvnxYFLag~S*W1b&^b0spfO_xH+hX$2#T?81Q1xj{R6n4!^`PV;?}dNckqk{9fkw;jygqbIi}BO+Q1A z)jpK^nQUWS^MTqac|Nvr-7{s%6ze`iZeH#QxqsR37X%0ArThv!AoKfSGL98;Kb(7a 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 + 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..fce12e380793b5ba9e893d7e1ed4ac75e69fd02d GIT binary patch literal 12073 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 g?`IaTgcUD+l_|TP+hyhZbLIPU<@ + + + + window_bounds + + 300 + 300 + 800 + 620 + + background_picture + background.png + icon_size + 96 + applications_symlink + + items_position + + Applications + + 370 + 156 + + Bitcoin-Qt.app + + 128 + 156 + + + + diff --git a/contrib/macdeploy/macdeployqtplus b/contrib/macdeploy/macdeployqtplus new file mode 100644 index 0000000..11140d3 --- /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" : "Bitcoin-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 0000000..3d74901 --- /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". + +This script should be invoked in the target directory like this: +$source_dir/contrib/macdeploy/macdeployqtplus Bitcoin-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: Bitcoin-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 "bitcoin-qt" + diff --git a/contrib/pyminer/README b/contrib/pyminer/README new file mode 100644 index 0000000..d159657 --- /dev/null +++ b/contrib/pyminer/README @@ -0,0 +1,6 @@ + +This is a 'getwork' CPU mining client for bitcoin. + +It is pure-python, and therefore very, very slow. The purpose is to +provide a reference implementation of a miner, for study. + diff --git a/contrib/pyminer/example-config.cfg b/contrib/pyminer/example-config.cfg new file mode 100644 index 0000000..e4b6145 --- /dev/null +++ b/contrib/pyminer/example-config.cfg @@ -0,0 +1,32 @@ + +# +# RPC login details +# +host=127.0.0.1 +port=8372 + +rpcuser=myusername +rpcpass=mypass + + +# +# mining details +# + +threads=4 + +# periodic rate for requesting new work, if solution not found +scantime=60 + + +# +# misc. +# + +# not really used right now +logdir=/tmp/pyminer + +# set to 1, to enable hashmeter output +hashmeter=0 + + diff --git a/contrib/pyminer/pyminer.py b/contrib/pyminer/pyminer.py new file mode 100644 index 0000000..0a2932d --- /dev/null +++ b/contrib/pyminer/pyminer.py @@ -0,0 +1,252 @@ +#!/usr/bin/python +# +# Copyright (c) 2011 The Bitcoin developers +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# + +import time +import json +import pprint +import hashlib +import struct +import re +import base64 +import httplib +import sys +from multiprocessing import Process + +ERR_SLEEP = 15 +MAX_NONCE = 1000000L + +settings = {} +pp = pprint.PrettyPrinter(indent=4) + +class BitcoinRPC: + OBJID = 1 + + def __init__(self, host, port, username, password): + authpair = "%s:%s" % (username, password) + self.authhdr = "Basic %s" % (base64.b64encode(authpair)) + self.conn = httplib.HTTPConnection(host, port, False, 30) + def rpc(self, method, params=None): + self.OBJID += 1 + obj = { 'version' : '1.1', + 'method' : method, + 'id' : self.OBJID } + if params is None: + obj['params'] = [] + else: + obj['params'] = params + self.conn.request('POST', '/', json.dumps(obj), + { 'Authorization' : self.authhdr, + 'Content-type' : 'application/json' }) + + resp = self.conn.getresponse() + if resp is None: + print "JSON-RPC: no response" + return None + + body = resp.read() + resp_obj = json.loads(body) + if resp_obj is None: + print "JSON-RPC: cannot JSON-decode body" + return None + if 'error' in resp_obj and resp_obj['error'] != None: + return resp_obj['error'] + if 'result' not in resp_obj: + print "JSON-RPC: no result in object" + return None + + return resp_obj['result'] + def getblockcount(self): + return self.rpc('getblockcount') + def getwork(self, data=None): + return self.rpc('getwork', data) + +def uint32(x): + return x & 0xffffffffL + +def bytereverse(x): + return uint32(( ((x) << 24) | (((x) << 8) & 0x00ff0000) | + (((x) >> 8) & 0x0000ff00) | ((x) >> 24) )) + +def bufreverse(in_buf): + out_words = [] + for i in range(0, len(in_buf), 4): + word = struct.unpack('@I', in_buf[i:i+4])[0] + out_words.append(struct.pack('@I', bytereverse(word))) + return ''.join(out_words) + +def wordreverse(in_buf): + out_words = [] + for i in range(0, len(in_buf), 4): + out_words.append(in_buf[i:i+4]) + out_words.reverse() + return ''.join(out_words) + +class Miner: + def __init__(self, id): + self.id = id + self.max_nonce = MAX_NONCE + + def work(self, datastr, targetstr): + # decode work data hex string to binary + static_data = datastr.decode('hex') + static_data = bufreverse(static_data) + + # the first 76b of 80b do not change + blk_hdr = static_data[:76] + + # decode 256-bit target value + targetbin = targetstr.decode('hex') + targetbin = targetbin[::-1] # byte-swap and dword-swap + targetbin_str = targetbin.encode('hex') + target = long(targetbin_str, 16) + + # pre-hash first 76b of block header + static_hash = hashlib.sha256() + static_hash.update(blk_hdr) + + for nonce in xrange(self.max_nonce): + + # encode 32-bit nonce value + nonce_bin = struct.pack(" Upstream RPC result:", result + + def iterate(self, rpc): + work = rpc.getwork() + if work is None: + time.sleep(ERR_SLEEP) + return + if 'data' not in work or 'target' not in work: + time.sleep(ERR_SLEEP) + return + + time_start = time.time() + + (hashes_done, nonce_bin) = self.work(work['data'], + work['target']) + + time_end = time.time() + time_diff = time_end - time_start + + self.max_nonce = long( + (hashes_done * settings['scantime']) / time_diff) + if self.max_nonce > 0xfffffffaL: + self.max_nonce = 0xfffffffaL + + if settings['hashmeter']: + print "HashMeter(%d): %d hashes, %.2f Khash/sec" % ( + self.id, hashes_done, + (hashes_done / 1000.0) / time_diff) + + if nonce_bin is not None: + self.submit_work(rpc, work['data'], nonce_bin) + + def loop(self): + rpc = BitcoinRPC(settings['host'], settings['port'], + settings['rpcuser'], settings['rpcpass']) + if rpc is None: + return + + while True: + self.iterate(rpc) + +def miner_thread(id): + miner = Miner(id) + miner.loop() + +if __name__ == '__main__': + if len(sys.argv) != 2: + print "Usage: pyminer.py CONFIG-FILE" + sys.exit(1) + + f = open(sys.argv[1]) + for line in f: + # skip comment lines + m = re.search('^\s*#', line) + if m: + continue + + # parse key=value lines + m = re.search('^(\w+)\s*=\s*(\S.*)$', line) + if m is None: + continue + settings[m.group(1)] = m.group(2) + f.close() + + if 'host' not in settings: + settings['host'] = '127.0.0.1' + if 'port' not in settings: + settings['port'] = 8332 + if 'threads' not in settings: + settings['threads'] = 1 + if 'hashmeter' not in settings: + settings['hashmeter'] = 0 + if 'scantime' not in settings: + settings['scantime'] = 30L + if 'rpcuser' not in settings or 'rpcpass' not in settings: + print "Missing username and/or password in cfg file" + sys.exit(1) + + settings['port'] = int(settings['port']) + settings['threads'] = int(settings['threads']) + settings['hashmeter'] = int(settings['hashmeter']) + settings['scantime'] = long(settings['scantime']) + + thr_list = [] + for thr_id in range(settings['threads']): + p = Process(target=miner_thread, args=(thr_id,)) + p.start() + thr_list.append(p) + time.sleep(1) # stagger threads + + print settings['threads'], "mining threads started" + + print time.asctime(), "Miner Starts - %s:%s" % (settings['host'], settings['port']) + try: + for thr_proc in thr_list: + thr_proc.join() + except KeyboardInterrupt: + pass + print time.asctime(), "Miner Stops - %s:%s" % (settings['host'], settings['port']) + diff --git a/contrib/qt_translations.py b/contrib/qt_translations.py new file mode 100644 index 0000000..fd8a8b7 --- /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/README b/contrib/seeds/README new file mode 100644 index 0000000..97e3e1e --- /dev/null +++ b/contrib/seeds/README @@ -0,0 +1,9 @@ +Utility to generate the pnSeed[] array that is compiled into the client +(see src/net.cpp). + +The 600 seeds compiled into the 0.8 release were created from sipa's DNS seed data, like this: + +curl -s http://bitcoin.sipa.be/seeds.txt | head -1000 | makeseeds.py + +The input to makeseeds.py is assumed to be approximately sorted from most-reliable to least-reliable, +with IP:port first on each line (lines that don't match IPv4:port are ignored). diff --git a/contrib/seeds/makeseeds.py b/contrib/seeds/makeseeds.py new file mode 100644 index 0000000..1d01fd7 --- /dev/null +++ b/contrib/seeds/makeseeds.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +# +# Generate pnSeed[] from Pieter's DNS seeder +# + +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}):8333") + 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/spendfrom/README b/contrib/spendfrom/README new file mode 100644 index 0000000..8a087a0 --- /dev/null +++ b/contrib/spendfrom/README @@ -0,0 +1,32 @@ +Use the raw transactions API to send coins received on a particular +address (or addresses). + +Depends on jsonrpc + +Usage: + +spendfrom.py --from=FROMADDRESS1[,FROMADDRESS2] --to=TOADDRESS --amount=amount \ + --fee=fee --datadir=/path/to/.bitcoin --testnet --dry_run + +With no arguments, outputs a list of amounts associated with addresses. + +With arguments, sends coins received by the FROMADDRESS addresses to the TOADDRESS. + +You may explictly specify how much fee to pay (a fee more than 1% of the amount +will fail, though, to prevent bitcoin-losing accidents). Spendfrom may fail if +it thinks the transaction would never be confirmed (if the amount being sent is +too small, or if the transaction is too many bytes for the fee). + +If a change output needs to be created, the change will be sent to the last +FROMADDRESS (if you specify just one FROMADDRESS, change will go back to it). + +If --datadir is not specified, the default datadir is used. + +The --dry_run option will just create and sign the the transaction and print +the transaction data (as hexadecimal), instead of broadcasting it. + +If the transaction is created and broadcast successfully, a transaction id +is printed. + +If this was a tool for end-users and not programmers, it would have much friendlier +error-handling. diff --git a/contrib/spendfrom/setup.py b/contrib/spendfrom/setup.py new file mode 100644 index 0000000..01b9768 --- /dev/null +++ b/contrib/spendfrom/setup.py @@ -0,0 +1,9 @@ +from distutils.core import setup +setup(name='btcspendfrom', + version='1.0', + description='Command-line utility for bitcoin "coin control"', + author='Gavin Andresen', + author_email='gavin@bitcoinfoundation.org', + requires=['jsonrpc'], + scripts=['spendfrom.py'], + ) diff --git a/contrib/spendfrom/spendfrom.py b/contrib/spendfrom/spendfrom.py new file mode 100644 index 0000000..72ee042 --- /dev/null +++ b/contrib/spendfrom/spendfrom.py @@ -0,0 +1,267 @@ +#!/usr/bin/env python +# +# Use the raw transactions API to spend bitcoins received on particular addresses, +# and send any change back to that same address. +# +# Example usage: +# spendfrom.py # Lists available funds +# spendfrom.py --from=ADDRESS --to=ADDRESS --amount=11.00 +# +# Assumes it will talk to a bitcoind or Bitcoin-Qt running +# on localhost. +# +# Depends on jsonrpc +# + +from decimal import * +import getpass +import math +import os +import os.path +import platform +import sys +import time +from jsonrpc import ServiceProxy, json + +BASE_FEE=Decimal("0.001") + +def check_json_precision(): + """Make sure json library being used does not lose precision converting BTC values""" + n = Decimal("20000000.00000003") + satoshis = int(json.loads(json.dumps(float(n)))*1.0e8) + if satoshis != 2000000000000003: + raise RuntimeError("JSON encode/decode loses precision") + +def determine_db_dir(): + """Return the default location of the bitcoin data directory""" + if platform.system() == "Darwin": + return os.path.expanduser("~/Library/Application Support/Bitcoin/") + elif platform.system() == "Windows": + return os.path.join(os.environ['APPDATA'], "Bitcoin") + return os.path.expanduser("~/.bitcoin") + +def read_bitcoin_config(dbdir): + """Read the bitcoin.conf file from dbdir, returns dictionary of settings""" + from ConfigParser import SafeConfigParser + + class FakeSecHead(object): + def __init__(self, fp): + self.fp = fp + self.sechead = '[all]\n' + def readline(self): + if self.sechead: + try: return self.sechead + finally: self.sechead = None + else: + s = self.fp.readline() + if s.find('#') != -1: + s = s[0:s.find('#')].strip() +"\n" + return s + + config_parser = SafeConfigParser() + config_parser.readfp(FakeSecHead(open(os.path.join(dbdir, "bitcoin.conf")))) + return dict(config_parser.items("all")) + +def connect_JSON(config): + """Connect to a bitcoin JSON-RPC server""" + testnet = config.get('testnet', '0') + testnet = (int(testnet) > 0) # 0/1 in config file, convert to True/False + if not 'rpcport' in config: + config['rpcport'] = 18332 if testnet else 8332 + connect = "http://%s:%s@127.0.0.1:%s"%(config['rpcuser'], config['rpcpassword'], config['rpcport']) + try: + result = ServiceProxy(connect) + # ServiceProxy is lazy-connect, so send an RPC command mostly to catch connection errors, + # but also make sure the bitcoind we're talking to is/isn't testnet: + if result.getmininginfo()['testnet'] != testnet: + sys.stderr.write("RPC server at "+connect+" testnet setting mismatch\n") + sys.exit(1) + return result + except: + sys.stderr.write("Error connecting to RPC server at "+connect+"\n") + sys.exit(1) + +def unlock_wallet(bitcoind): + info = bitcoind.getinfo() + if 'unlocked_until' not in info: + return True # wallet is not encrypted + t = int(info['unlocked_until']) + if t <= time.time(): + try: + passphrase = getpass.getpass("Wallet is locked; enter passphrase: ") + bitcoind.walletpassphrase(passphrase, 5) + except: + sys.stderr.write("Wrong passphrase\n") + + info = bitcoind.getinfo() + return int(info['unlocked_until']) > time.time() + +def list_available(bitcoind): + address_summary = dict() + + address_to_account = dict() + for info in bitcoind.listreceivedbyaddress(0): + address_to_account[info["address"]] = info["account"] + + unspent = bitcoind.listunspent(0) + for output in unspent: + # listunspent doesn't give addresses, so: + rawtx = bitcoind.getrawtransaction(output['txid'], 1) + vout = rawtx["vout"][output['vout']] + pk = vout["scriptPubKey"] + + # This code only deals with ordinary pay-to-bitcoin-address + # or pay-to-script-hash outputs right now; anything exotic is ignored. + if pk["type"] != "pubkeyhash" and pk["type"] != "scripthash": + continue + + address = pk["addresses"][0] + if address in address_summary: + address_summary[address]["total"] += vout["value"] + address_summary[address]["outputs"].append(output) + else: + address_summary[address] = { + "total" : vout["value"], + "outputs" : [output], + "account" : address_to_account.get(address, "") + } + + return address_summary + +def select_coins(needed, inputs): + # Feel free to improve this, this is good enough for my simple needs: + outputs = [] + have = Decimal("0.0") + n = 0 + while have < needed and n < len(inputs): + outputs.append({ "txid":inputs[n]["txid"], "vout":inputs[n]["vout"]}) + have += inputs[n]["amount"] + n += 1 + return (outputs, have-needed) + +def create_tx(bitcoind, fromaddresses, toaddress, amount, fee): + all_coins = list_available(bitcoind) + + total_available = Decimal("0.0") + needed = amount+fee + potential_inputs = [] + for addr in fromaddresses: + if addr not in all_coins: + continue + potential_inputs.extend(all_coins[addr]["outputs"]) + total_available += all_coins[addr]["total"] + + if total_available < needed: + sys.stderr.write("Error, only %f BTC available, need %f\n"%(total_available, needed)); + sys.exit(1) + + # + # Note: + # Python's json/jsonrpc modules have inconsistent support for Decimal numbers. + # Instead of wrestling with getting json.dumps() (used by jsonrpc) to encode + # Decimals, I'm casting amounts to float before sending them to bitcoind. + # + outputs = { toaddress : float(amount) } + (inputs, change_amount) = select_coins(needed, potential_inputs) + if change_amount > BASE_FEE: # don't bother with zero or tiny change + change_address = fromaddresses[-1] + if change_address in outputs: + outputs[change_address] += float(change_amount) + else: + outputs[change_address] = float(change_amount) + + rawtx = bitcoind.createrawtransaction(inputs, outputs) + signed_rawtx = bitcoind.signrawtransaction(rawtx) + if not signed_rawtx["complete"]: + sys.stderr.write("signrawtransaction failed\n") + sys.exit(1) + txdata = signed_rawtx["hex"] + + return txdata + +def compute_amount_in(bitcoind, txinfo): + result = Decimal("0.0") + for vin in txinfo['vin']: + in_info = bitcoind.getrawtransaction(vin['txid'], 1) + vout = in_info['vout'][vin['vout']] + result = result + vout['value'] + return result + +def compute_amount_out(txinfo): + result = Decimal("0.0") + for vout in txinfo['vout']: + result = result + vout['value'] + return result + +def sanity_test_fee(bitcoind, txdata_hex, max_fee): + class FeeError(RuntimeError): + pass + try: + txinfo = bitcoind.decoderawtransaction(txdata_hex) + total_in = compute_amount_in(bitcoind, txinfo) + total_out = compute_amount_out(txinfo) + if total_in-total_out > max_fee: + raise FeeError("Rejecting transaction, unreasonable fee of "+str(total_in-total_out)) + + tx_size = len(txdata_hex)/2 + kb = tx_size/1000 # integer division rounds down + if kb > 1 and fee < BASE_FEE: + raise FeeError("Rejecting no-fee transaction, larger than 1000 bytes") + if total_in < 0.01 and fee < BASE_FEE: + raise FeeError("Rejecting no-fee, tiny-amount transaction") + # Exercise for the reader: compute transaction priority, and + # warn if this is a very-low-priority transaction + + except FeeError as err: + sys.stderr.write((str(err)+"\n")) + sys.exit(1) + +def main(): + import optparse + + parser = optparse.OptionParser(usage="%prog [options]") + parser.add_option("--from", dest="fromaddresses", default=None, + help="addresses to get bitcoins from") + parser.add_option("--to", dest="to", default=None, + help="address to get send bitcoins to") + parser.add_option("--amount", dest="amount", default=None, + help="amount to send") + parser.add_option("--fee", dest="fee", default="0.0", + help="fee to include") + parser.add_option("--datadir", dest="datadir", default=determine_db_dir(), + help="location of bitcoin.conf file with RPC username/password (default: %default)") + parser.add_option("--testnet", dest="testnet", default=False, action="store_true", + help="Use the test network") + parser.add_option("--dry_run", dest="dry_run", default=False, action="store_true", + help="Don't broadcast the transaction, just create and print the transaction data") + + (options, args) = parser.parse_args() + + check_json_precision() + config = read_bitcoin_config(options.datadir) + if options.testnet: config['testnet'] = True + bitcoind = connect_JSON(config) + + if options.amount is None: + address_summary = list_available(bitcoind) + for address,info in address_summary.iteritems(): + n_transactions = len(info['outputs']) + if n_transactions > 1: + print("%s %.8f %s (%d transactions)"%(address, info['total'], info['account'], n_transactions)) + else: + print("%s %.8f %s"%(address, info['total'], info['account'])) + else: + fee = Decimal(options.fee) + amount = Decimal(options.amount) + while unlock_wallet(bitcoind) == False: + pass # Keep asking for passphrase until they get it right + txdata = create_tx(bitcoind, options.fromaddresses.split(","), options.to, amount, fee) + sanity_test_fee(bitcoind, txdata, amount*Decimal("0.01")) + if options.dry_run: + print(txdata) + else: + txid = bitcoind.sendrawtransaction(txdata) + print(txid) + +if __name__ == '__main__': + main() diff --git a/contrib/test-patches/README b/contrib/test-patches/README new file mode 100644 index 0000000..ed754ce --- /dev/null +++ b/contrib/test-patches/README @@ -0,0 +1,4 @@ +These patches are applied when the automated pull-tester +tests each pull and when master is tested using jenkins. +You can find more information about the tests run at +http://jenkins.bluematt.me/pull-tester/files/ diff --git a/contrib/testgen/README b/contrib/testgen/README new file mode 100644 index 0000000..02d6c4c --- /dev/null +++ b/contrib/testgen/README @@ -0,0 +1 @@ +Utilities to generate test vectors for the data-driven Bitcoin tests diff --git a/contrib/testgen/base58.py b/contrib/testgen/base58.py new file mode 100644 index 0000000..b716495 --- /dev/null +++ b/contrib/testgen/base58.py @@ -0,0 +1,104 @@ +''' +Bitcoin base58 encoding and decoding. + +Based on https://bitcointalk.org/index.php?topic=1026.0 (public domain) +''' +import hashlib + +# for compatibility with following code... +class SHA256: + new = hashlib.sha256 + +if str != bytes: + # Python 3.x + def ord(c): + return c + def chr(n): + return bytes( (n,) ) + +__b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' +__b58base = len(__b58chars) +b58chars = __b58chars + +def b58encode(v): + """ encode v, which is a string of bytes, to base58. + """ + long_value = 0 + for (i, c) in enumerate(v[::-1]): + long_value += (256**i) * ord(c) + + result = '' + while long_value >= __b58base: + div, mod = divmod(long_value, __b58base) + result = __b58chars[mod] + result + long_value = div + result = __b58chars[long_value] + result + + # Bitcoin does a little leading-zero-compression: + # leading 0-bytes in the input become leading-1s + nPad = 0 + for c in v: + if c == '\0': nPad += 1 + else: break + + return (__b58chars[0]*nPad) + result + +def b58decode(v, length = None): + """ decode v into a string of len bytes + """ + long_value = 0 + for (i, c) in enumerate(v[::-1]): + long_value += __b58chars.find(c) * (__b58base**i) + + result = bytes() + while long_value >= 256: + div, mod = divmod(long_value, 256) + result = chr(mod) + result + long_value = div + result = chr(long_value) + result + + nPad = 0 + for c in v: + if c == __b58chars[0]: nPad += 1 + else: break + + result = chr(0)*nPad + result + if length is not None and len(result) != length: + return None + + return result + +def checksum(v): + """Return 32-bit checksum based on SHA256""" + return SHA256.new(SHA256.new(v).digest()).digest()[0:4] + +def b58encode_chk(v): + """b58encode a string, with 32-bit checksum""" + return b58encode(v + checksum(v)) + +def b58decode_chk(v): + """decode a base58 string, check and remove checksum""" + result = b58decode(v) + if result is None: + return None + h3 = checksum(result[:-4]) + if result[-4:] == checksum(result[:-4]): + return result[:-4] + else: + return None + +def get_bcaddress_version(strAddress): + """ Returns None if strAddress is invalid. Otherwise returns integer version of address. """ + addr = b58decode_chk(strAddress) + if addr is None or len(addr)!=21: return None + version = addr[0] + return ord(version) + +if __name__ == '__main__': + # Test case (from http://gitorious.org/bitcoin/python-base58.git) + assert get_bcaddress_version('15VjRaDX9zpbA8LVnbrCAFzrVzN7ixHNsC') is 0 + _ohai = 'o hai'.encode('ascii') + _tmp = b58encode(_ohai) + assert _tmp == 'DYB3oMS' + assert b58decode(_tmp, 5) == _ohai + print("Tests passed") diff --git a/contrib/testgen/gen_base58_test_vectors.py b/contrib/testgen/gen_base58_test_vectors.py new file mode 100644 index 0000000..1813436 --- /dev/null +++ b/contrib/testgen/gen_base58_test_vectors.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python +''' +Generate valid and invalid base58 address and private key test vectors. + +Usage: + gen_base58_test_vectors.py valid 50 > ../../src/test/data/base58_keys_valid.json + gen_base58_test_vectors.py invalid 50 > ../../src/test/data/base58_keys_invalid.json +''' +# 2012 Wladimir J. van der Laan +# Released under MIT License +import os +from itertools import islice +from base58 import b58encode, b58decode, b58encode_chk, b58decode_chk, b58chars +import random +from binascii import b2a_hex + +# key types +PUBKEY_ADDRESS = 0 +SCRIPT_ADDRESS = 5 +PUBKEY_ADDRESS_TEST = 111 +SCRIPT_ADDRESS_TEST = 196 +PRIVKEY = 128 +PRIVKEY_TEST = 239 + +metadata_keys = ['isPrivkey', 'isTestnet', 'addrType', 'isCompressed'] +# templates for valid sequences +templates = [ + # prefix, payload_size, suffix, metadata + # None = N/A + ((PUBKEY_ADDRESS,), 20, (), (False, False, 'pubkey', None)), + ((SCRIPT_ADDRESS,), 20, (), (False, False, 'script', None)), + ((PUBKEY_ADDRESS_TEST,), 20, (), (False, True, 'pubkey', None)), + ((SCRIPT_ADDRESS_TEST,), 20, (), (False, True, 'script', None)), + ((PRIVKEY,), 32, (), (True, False, None, False)), + ((PRIVKEY,), 32, (1,), (True, False, None, True)), + ((PRIVKEY_TEST,), 32, (), (True, True, None, False)), + ((PRIVKEY_TEST,), 32, (1,), (True, True, None, True)) +] + +def is_valid(v): + '''Check vector v for validity''' + result = b58decode_chk(v) + if result is None: + return False + valid = False + for template in templates: + prefix = str(bytearray(template[0])) + suffix = str(bytearray(template[2])) + if result.startswith(prefix) and result.endswith(suffix): + if (len(result) - len(prefix) - len(suffix)) == template[1]: + return True + return False + +def gen_valid_vectors(): + '''Generate valid test vectors''' + while True: + for template in templates: + prefix = str(bytearray(template[0])) + payload = os.urandom(template[1]) + suffix = str(bytearray(template[2])) + rv = b58encode_chk(prefix + payload + suffix) + assert is_valid(rv) + metadata = dict([(x,y) for (x,y) in zip(metadata_keys,template[3]) if y is not None]) + yield (rv, b2a_hex(payload), metadata) + +def gen_invalid_vector(template, corrupt_prefix, randomize_payload_size, corrupt_suffix): + '''Generate possibly invalid vector''' + if corrupt_prefix: + prefix = os.urandom(1) + else: + prefix = str(bytearray(template[0])) + + if randomize_payload_size: + payload = os.urandom(max(int(random.expovariate(0.5)), 50)) + else: + payload = os.urandom(template[1]) + + if corrupt_suffix: + suffix = os.urandom(len(template[2])) + else: + suffix = str(bytearray(template[2])) + + return b58encode_chk(prefix + payload + suffix) + +def randbool(p = 0.5): + '''Return True with P(p)''' + return random.random() < p + +def gen_invalid_vectors(): + '''Generate invalid test vectors''' + # start with some manual edge-cases + yield "", + yield "x", + while True: + # kinds of invalid vectors: + # invalid prefix + # invalid payload length + # invalid (randomized) suffix (add random data) + # corrupt checksum + for template in templates: + val = gen_invalid_vector(template, randbool(0.2), randbool(0.2), randbool(0.2)) + if random.randint(0,10)<1: # line corruption + if randbool(): # add random character to end + val += random.choice(b58chars) + else: # replace random character in the middle + n = random.randint(0, len(val)) + val = val[0:n] + random.choice(b58chars) + val[n+1:] + if not is_valid(val): + yield val, + +if __name__ == '__main__': + import sys, json + iters = {'valid':gen_valid_vectors, 'invalid':gen_invalid_vectors} + try: + uiter = iters[sys.argv[1]] + except IndexError: + uiter = gen_valid_vectors + try: + count = int(sys.argv[2]) + except IndexError: + count = 0 + + data = list(islice(uiter(), count)) + json.dump(data, sys.stdout, sort_keys=True, indent=4) + sys.stdout.write('\n') + diff --git a/contrib/tidy_datadir.sh b/contrib/tidy_datadir.sh new file mode 100644 index 0000000..5d6d826 --- /dev/null +++ b/contrib/tidy_datadir.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +if [ -d "$1" ]; then + cd "$1" +else + echo "Usage: $0 " >&2 + echo "Removes obsolete Bitcoin database files" >&2 + exit 1 +fi + +LEVEL=0 +if [ -f wallet.dat -a -f addr.dat -a -f blkindex.dat -a -f blk0001.dat ]; then LEVEL=1; fi +if [ -f wallet.dat -a -f peers.dat -a -f blkindex.dat -a -f blk0001.dat ]; then LEVEL=2; fi +if [ -f wallet.dat -a -f peers.dat -a -f coins/CURRENT -a -f blktree/CURRENT -a -f blocks/blk00000.dat ]; then LEVEL=3; fi +if [ -f wallet.dat -a -f peers.dat -a -f chainstate/CURRENT -a -f blocks/index/CURRENT -a -f blocks/blk00000.dat ]; then LEVEL=4; fi + +case $LEVEL in + 0) + echo "Error: no Bitcoin datadir detected." + exit 1 + ;; + 1) + echo "Detected old Bitcoin datadir (before 0.7)." + echo "Nothing to do." + exit 0 + ;; + 2) + echo "Detected Bitcoin 0.7 datadir." + ;; + 3) + echo "Detected Bitcoin pre-0.8 datadir." + ;; + 4) + echo "Detected Bitcoin 0.8 datadir." + ;; +esac + +FILES="" +DIRS="" + +if [ $LEVEL -ge 3 ]; then FILES=$(echo $FILES blk????.dat blkindex.dat); fi +if [ $LEVEL -ge 2 ]; then FILES=$(echo $FILES addr.dat); fi +if [ $LEVEL -ge 4 ]; then DIRS=$(echo $DIRS coins blktree); fi + +for FILE in $FILES; do + if [ -f $FILE ]; then + echo "Deleting: $FILE" + rm -f $FILE + fi +done + +for DIR in $DIRS; do + if [ -d $DIR ]; then + echo "Deleting: $DIR/" + rm -rf $DIR + fi +done + +echo "Done." diff --git a/contrib/verifysfbinaries/verify.sh b/contrib/verifysfbinaries/verify.sh new file mode 100644 index 0000000..768be86 --- /dev/null +++ b/contrib/verifysfbinaries/verify.sh @@ -0,0 +1,119 @@ +#!/bin/bash + +### This script attempts to download the signature file SHA256SUMS.asc from SourceForge +### It first checks if the signature passes, and then downloads the files specified in +### the file, and checks if the hashes of these files match those that are specified +### in the signature file. +### The script returns 0 if everything passes the checks. It returns 1 if either the +### signature check or the hash check doesn't pass. If an error occurs the return value is 2 + +function clean_up { + for file in $* + do + rm "$file" 2> /dev/null + done +} + +WORKINGDIR="/tmp/bitcoin" +TMPFILE="hashes.tmp" + +#this URL is used if a version number is not specified as an argument to the script +SIGNATUREFILE="http://downloads.sourceforge.net/project/bitcoin/Bitcoin/bitcoin-0.8.2/SHA256SUMS.asc" + +SIGNATUREFILENAME="SHA256SUMS.asc" +RCSUBDIR="test/" +BASEDIR="http://downloads.sourceforge.net/project/bitcoin/Bitcoin/" +VERSIONPREFIX="bitcoin-" +RCVERSIONSTRING="rc" + +if [ ! -d "$WORKINGDIR" ]; then + mkdir "$WORKINGDIR" +fi + +cd "$WORKINGDIR" + +#test if a version number has been passed as an argument +if [ -n "$1" ]; then + #let's also check if the version number includes the prefix 'bitcoin-', + # and add this prefix if it doesn't + if [[ $1 == "$VERSIONPREFIX"* ]]; then + VERSION="$1" + else + VERSION="$VERSIONPREFIX$1" + fi + + #now let's see if the version string contains "rc", and strip it off if it does + # and simultaneously add RCSUBDIR to BASEDIR, where we will look for SIGNATUREFILENAME + if [[ $VERSION == *"$RCVERSIONSTRING"* ]]; then + BASEDIR="$BASEDIR${VERSION/%-$RCVERSIONSTRING*}/" + BASEDIR="$BASEDIR$RCSUBDIR" + else + BASEDIR="$BASEDIR$VERSION/" + fi + + SIGNATUREFILE="$BASEDIR$SIGNATUREFILENAME" +else + BASEDIR="${SIGNATUREFILE%/*}/" +fi + +#first we fetch the file containing the signature +WGETOUT=$(wget -N "$BASEDIR$SIGNATUREFILENAME" 2>&1) + +#and then see if wget completed successfully +if [ $? -ne 0 ]; then + echo "Error: couldn't fetch signature file. Have you specified the version number in the following format?" + echo "[bitcoin-]-[rc[0-9]] (example: bitcoin-0.7.1-rc1)" + echo "wget output:" + echo "$WGETOUT"|sed 's/^/\t/g' + exit 2 +fi + +#then we check it +GPGOUT=$(gpg --yes --decrypt --output "$TMPFILE" "$SIGNATUREFILENAME" 2>&1) + +#return value 0: good signature +#return value 1: bad signature +#return value 2: gpg error + +RET="$?" +if [ $RET -ne 0 ]; then + if [ $RET -eq 1 ]; then + #and notify the user if it's bad + echo "Bad signature." + elif [ $RET -eq 2 ]; then + #or if a gpg error has occurred + echo "gpg error. Do you have Gavin's code signing key installed?" + fi + + echo "gpg output:" + echo "$GPGOUT"|sed 's/^/\t/g' + clean_up $SIGNATUREFILENAME $TMPFILE + exit "$RET" +fi + +#here we extract the filenames from the signature file +FILES=$(awk '{print $2}' "$TMPFILE") + +#and download these one by one +for file in in $FILES +do + wget --quiet -N "$BASEDIR$file" +done + +#check hashes +DIFF=$(diff <(sha256sum $FILES) "$TMPFILE") + +if [ $? -eq 1 ]; then + echo "Hashes don't match." + echo "Offending files:" + echo "$DIFF"|grep "^<"|awk '{print "\t"$3}' + exit 1 +elif [ $? -gt 1 ]; then + echo "Error executing 'diff'" + exit 2 +fi + +#everything matches! clean up the mess +clean_up $FILES $SIGNATUREFILENAME $TMPFILE + +exit 0 diff --git a/contrib/wallettools/walletchangepass.py b/contrib/wallettools/walletchangepass.py new file mode 100644 index 0000000..32b8f6e --- /dev/null +++ b/contrib/wallettools/walletchangepass.py @@ -0,0 +1,5 @@ +from jsonrpc import ServiceProxy +access = ServiceProxy("http://127.0.0.1:8372") +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/walletunlock.py b/contrib/wallettools/walletunlock.py new file mode 100644 index 0000000..b47fa6c --- /dev/null +++ b/contrib/wallettools/walletunlock.py @@ -0,0 +1,4 @@ +from jsonrpc import ServiceProxy +access = ServiceProxy("http://127.0.0.1:8372") +pwd = raw_input("Enter wallet passphrase: ") +access.walletpassphrase(pwd, 60) \ No newline at end of file diff --git a/doc/Doxyfile b/doc/Doxyfile new file mode 100644 index 0000000..c32d0f8 --- /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 = Bitcoin + +# 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/bitcoin_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 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.md b/doc/README.md new file mode 100644 index 0000000..dd8fa35 --- /dev/null +++ b/doc/README.md @@ -0,0 +1,47 @@ +Greenchain 0.8.2 BETA +==================== + +Copyright (c) 2003-2018 Greenchain Developers + +Distributed under the MIT/X11 software license, see the accompanying +file COPYING 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](mailto:eay@cryptsoft.com)), and UPnP software written by Thomas Bernard, +and sphlib 3.0 by Thomas Pornin. + + +Intro +--------------------- +Greenchain is a free open source peer-to-peer electronic cash system based of Greenchain,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. + + +Setup +--------------------- +You need the Qt4 run-time libraries to run Greenchain-Qt. On Debian or Ubuntu: + `sudo apt-get install libqtgui4` + +Unpack the files into a directory and run: + +- bin/32/Greenchain-qt (GUI, 32-bit) +- bin/32/bitcoind (headless, 32-bit) +- bin/64/Greenchain-qt (GUI, 64-bit) +- bin/64/bitcoind (headless, 64-bit) + +See the documentation at the [Bitcoin Wiki](https://en.bitcoin.it/wiki/Main_Page) +for help and more information. + + +Other Pages +--------------------- +- [Unix Build Notes](build-unix.md) +- [OSX Build Notes](build-osx.md) +- [Windows Build Notes](build-msw.md) +- [Coding Guidelines](coding.md) +- [Release Process](release-process.md) +- [Release Notes](release-notes.md) +- [Multiwallet Qt Development](multiwallet-qt.md) +- [Unit Tests](unit-tests.md) +- [Translation Process](translation_process.md) diff --git a/doc/README_windows.txt b/doc/README_windows.txt new file mode 100644 index 0000000..fcf7e48 --- /dev/null +++ b/doc/README_windows.txt @@ -0,0 +1,32 @@ +Greenchain 0.8.2 BETA + +Copyright (c) 2009-2013 Greenchain Developers +Distributed under the MIT/X11 software license, see the accompanying +file COPYING 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), and +sphlib 3.0 by Thomas Pornin. + + +Intro +----- + +Sifcoin is a free open source peer-to-peer electronic cash system based on Greenchain 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. + + +Setup +----- +Unpack the files into a directory and run Greenchain-qt.exe. + +Greenchain-Qt is the original Greenchain client and it builds the backbone of the network. +However, it downloads and stores the entire history of Greenchain transactions; +depending on the speed of your computer and network connection, the synchronization +process can take anywhere from a few hours to a day or more. + +See the bitcoin wiki at: + https://en.bitcoin.it/wiki/Main_Page +for more help and information. diff --git a/doc/Tor.txt b/doc/Tor.txt new file mode 100644 index 0000000..386e3b5 --- /dev/null +++ b/doc/Tor.txt @@ -0,0 +1,92 @@ +TOR SUPPORT IN BITCOIN +====================== + +It is possible to run Bitcoin 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 +https://www.torproject.org/docs/faq.html.en#TBBSocksPort for how to properly +configure Tor. + + +1. Run bitcoin behind a Tor proxy +--------------------------------- + +The first step is running Bitcoin behind a Tor proxy. This will already make all +outgoing connections be anonimized, but more is possible. + +-socks=5 SOCKS5 supports connecting-to-hostname, which can be used instead + of doing a (leaking) local DNS lookup. SOCKS5 is the default, + but SOCKS4 does not support this. (SOCKS4a does, but isn't + implemented). + +-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. + +-tor=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 -notor + 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: + + ./bitcoin -proxy=127.0.0.1:9050 + + +2. Run a bitcoin 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/bitcoin-service/ + HiddenServicePort 8333 127.0.0.1:8333 + +The directory can be different of course, but (both) port numbers should be equal to +your bitcoind's P2P listen port (8333 by default). + +-externalip=X You can tell bitcoin 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/bitcoin-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: + + ./bitcoind -proxy=127.0.0.1:9050 -externalip=57qr3yd1nyntf5k.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: + + ./bitcoind ... -discover + +and open port 8333 on your firewall (or use -upnp). + +If you only want to use Tor to reach onion addresses, but not use it as a proxy +for normal IPv4/IPv6 communication, use: + + ./bitcoin -tor=127.0.0.1:9050 -externalip=57qr3yd1nyntf5k.onion -discover + diff --git a/doc/assets-attribution.txt b/doc/assets-attribution.txt new file mode 100644 index 0000000..2069c5d --- /dev/null +++ b/doc/assets-attribution.txt @@ -0,0 +1,58 @@ +Icon: src/qt/res/icons/clock*.png, src/qt/res/icons/tx*.png, + src/qt/res/src/clock_green.svg, src/qt/res/src/clock1.svg, + src/qt/res/src/clock2.svg, src/qt/res/src/clock3.svg, + src/qt/res/src/clock4.svg, src/qt/res/src/clock5.svg, + src/qt/res/src/inout.svg, src/qt/res/src/questionmark.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://bitcointalk.org/index.php?topic=15276.0 +License: You are free to do with these icons as you wish, including selling, + copying, modifying etc. +License: MIT + +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: 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 + +Icon: src/qt/res/icons/debugwindow.png +Designer: Vignoni David +Site: http://www.oxygen-icons.org/ +License: Oxygen icon theme is dual licensed. You may copy it under the Creative Common Attribution-ShareAlike 3.0 License or the GNU Library General Public License. + +Icon: src/qt/res/icons/bitcoin.icns, src/qt/res/src/bitcoin.svg, + src/qt/res/src/bitcoin.ico, src/qt/res/src/bitcoin.png, + src/qt/res/src/bitcoin_testnet.png, docs/bitcoin_logo_doxygen.png, + src/qt/res/icons/toolbar.png, src/qt/res/icons/toolbar_testnet.png, + src/qt/res/images/splash.png, src/qt/res/images/splash_testnet.png +Designer: Jonas Schnelli (based on the original bitcoin logo from Bitboy) +License: MIT diff --git a/doc/bitcoin_logo_doxygen.png b/doc/bitcoin_logo_doxygen.png new file mode 100644 index 0000000000000000000000000000000000000000..483d9336cd99fe518d671c4fb463235e66d43ea2 GIT binary patch literal 24215 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 +* Douglas Huff +* Colin Dean +* Gavin Andresen + +License +------- + +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. + +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) and UPnP software written by Thomas Bernard. + +Notes +----- + +See `doc/readme-qt.rst` for instructions on building Bitcoin-Qt, the +graphical user interface. + +Tested on OS X 10.5 through 10.8 on Intel processors only. PPC is not +supported because it is big-endian. + +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 an assumption that you already have `git` installed, as well. 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 or MacPorts. + +You will also need to install [Homebrew](http://mxcl.github.io/homebrew/) +or [MacPorts](https://www.macports.org/) in order to install library +dependencies. It's largely a religious decision which to choose, but, as of +December 2012, MacPorts is a little easier because you can just install the +dependencies immediately - no other work required. If you're unsure, read +the instructions through first in order to assess what you want to do. +Homebrew is a little more popular among those newer to OS X. + +The installation of the actual dependencies is covered in the Instructions +sections below. + +Instructions: MacPorts +---------------------- + +### Install dependencies + +Installing the dependencies using MacPorts is very straightforward. + + sudo port install boost db48@+no_java openssl miniupnpc + +### Building `bitcoind` + +1. Clone the github tree to get the source code and go into the directory. + + git clone git@github.com:bitcoin/bitcoin.git bitcoin + cd bitcoin + +2. Build bitcoind: + + cd src + make -f makefile.osx + +3. It is a good idea to build and run the unit tests, too: + + make -f makefile.osx test + +Instructions: HomeBrew +---------------------- + +#### Install dependencies using Homebrew + + brew install boost miniupnpc openssl berkeley-db4 + +Note: After you have installed the dependencies, you should check that the Brew installed version of OpenSSL is the one available for compilation. You can check this by typing + + openssl version + +into Terminal. You should see OpenSSL 1.0.1e 11 Feb 2013. + +If not, you can ensure that the Brew OpenSSL is correctly linked by running + + brew link openssl --force + +Rerunning "openssl version" should now return the correct version. + +### Building `bitcoind` + +1. Clone the github tree to get the source code and go into the directory. + + git clone git@github.com:bitcoin/bitcoin.git bitcoin + cd bitcoin + +2. Modify source in order to pick up the `openssl` library. + + Edit `makefile.osx` to account for library location differences. There's a + diff in `contrib/homebrew/makefile.osx.patch` that shows what you need to + change, or you can just patch by doing + + patch -p1 < contrib/homebrew/makefile.osx.patch + +3. Build bitcoind: + + cd src + make -f makefile.osx + +4. It is a good idea to build and run the unit tests, too: + + make -f makefile.osx test + +Creating a release build +------------------------ + +A bitcoind binary is not included in the Bitcoin-Qt.app bundle. You can ignore +this section if you are building `bitcoind` for your own use. + +If you are building `bitcoind` 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.5 -arch i386 -isysroot /Developer/SDKs/MacOSX10.5.sdk + +For MacPorts, that means editing your macports.conf and setting +`macosx_deployment_target` and `build_arch`: + + macosx_deployment_target=10.5 + build_arch=i386 + +... and then uninstalling and re-installing, or simply rebuilding, all ports. + +As of December 2012, the `boost` port does not obey `macosx_deployment_target`. +Download `http://gavinandresen-bitcoin.s3.amazonaws.com/boost_macports_fix.zip` +for a fix. Some ports also seem to obey either `build_arch` or +`macosx_deployment_target`, but not both at the same time. For example, building +on an OS X 10.6 64-bit machine fails. Official release builds of Bitcoin-Qt are +compiled on an OS X 10.6 32-bit machine to workaround that problem. + +Once dependencies are compiled, creating `Bitcoin-Qt.app` is easy: + + make -f Makefile.osx RELEASE=1 + +Running +------- + +It's now available at `./bitcoind`, provided that you are still in the `src` +directory. We have to first create the RPC configuration file, though. + +Run `./bitcoind` to get the filename where it should be put, or just try these +commands: + + echo -e "rpcuser=sifcoinrpc\nrpcpassword=$(xxd -l 16 -p /dev/urandom)" > "/Users/${USER}/Library/Application Support/Sifcoin/sifcoin.conf" + chmod 600 "/Users/${USER}/Library/Application Support/Sifcoin/sifcoin.conf" + +When next 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. + +Other commands: + + ./bitcoind --help # for a list of command-line options. + ./bitcoind -daemon # to start the bitcoin daemon. + ./bitcoind help # When the daemon is running, to get a list of RPC commands diff --git a/doc/build-unix.md b/doc/build-unix.md new file mode 100644 index 0000000..9728bef --- /dev/null +++ b/doc/build-unix.md @@ -0,0 +1,170 @@ +Copyright (c) 2009-2013 Bitcoin Developers + +Distributed under the MIT/X11 software license, see the accompanying +file COPYING 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](mailto:eay@cryptsoft.com)), and UPnP software written by Thomas Bernard. + +UNIX BUILD NOTES +==================== + +To Build +--------------------- + + cd src/ + make -f makefile.unix # Headless Greenchain + +See readme-qt.rst for instructions on building Greenchain-Qt, the graphical user interface. + +Dependencies +--------------------- + + Library Purpose Description + ------- ------- ----------- + libssl SSL Support Secure communications + libdb4.8 Berkeley DB Blockchain & wallet storage + libboost Boost C++ Library + miniupnpc UPnP Support Optional firewall-jumping support + +[miniupnpc](http://miniupnp.free.fr/) may be used for UPnP port mapping. It can be downloaded from [here]( +http://miniupnp.tuxfamily.org/files/). UPnP support is compiled in and +turned off by default. Set USE_UPNP to a different value to control this: + + USE_UPNP= No UPnP support miniupnp not required + USE_UPNP=0 (the default) UPnP support turned off by default at runtime + USE_UPNP=1 UPnP support turned on by default at runtime + +IPv6 support may be disabled by setting: + + USE_IPV6=0 Disable IPv6 support + +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 + miniupnpc New (3-clause) BSD license + +- Versions used in this release: +- GCC 4.3.3 +- OpenSSL 1.0.1c +- Berkeley DB 4.8.30.NC +- Boost 1.37 +- miniupnpc 1.6 + +Dependency Build Instructions: Ubuntu & Debian +---------------------------------------------- +Build requirements: + + sudo apt-get install build-essential + sudo apt-get install libssl-dev + +for Ubuntu 12.04: + + sudo apt-get install libboost-all-dev + + db4.8 packages are available [here](https://launchpad.net/~bitcoin/+archive/bitcoin). + + Ubuntu precise has packages for libdb5.1-dev and libdb5.1++-dev, + but using these will break binary wallet compatibility, and is not recommended. + +for other Ubuntu & Debian: + + sudo apt-get install libdb4.8-dev + sudo apt-get install libdb4.8++-dev + sudo apt-get install libboost1.37-dev + (If using Boost 1.37, append -mt to the boost libraries in the makefile) + +Optional: + + sudo apt-get install libminiupnpc-dev (see USE_UPNP compile flag) + + +Dependency Build Instructions: Gentoo +------------------------------------- + +Note: If you just want to install bitcoind on Gentoo, you can add the Bitcoin overlay and use your package manager: + + layman -a bitcoin && emerge bitcoind + emerge -av1 --noreplace boost glib openssl sys-libs/db:4.8 + +Take the following steps to build (no UPnP support): + + cd ${BITCOIN_DIR}/src + make -f makefile.unix USE_UPNP= USE_IPV6=1 BDB_INCLUDE_PATH='/usr/include/db4.8' + strip bitcoind + + +Notes +----- +The release is built with GCC and then "strip bitcoind" to strip the debug +symbols, which reduces the executable size by about 90%. + + +miniupnpc +--------- + tar -xzvf miniupnpc-1.6.tar.gz + cd miniupnpc-1.6 + make + sudo su + make install + + +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 bitcoin 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 ./bitcoin + + 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, bitcoin 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 ./bitcoin` + + 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/coding.md b/doc/coding.md new file mode 100644 index 0000000..3581d7d --- /dev/null +++ b/doc/coding.md @@ -0,0 +1,94 @@ +Coding +==================== + +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 +LOCK/TRY_LOCK 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. + +- 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/files.txt b/doc/files.txt new file mode 100644 index 0000000..5d4cdab --- /dev/null +++ b/doc/files.txt @@ -0,0 +1,19 @@ +Used in 0.8.0: +* wallet.dat: personal wallet (BDB) with keys and transactions +* peers.dat: peer IP address database (custom format); since 0.7.0 +* blocks/blk000??.dat: block data (custom, 128 MiB per file); since 0.8.0 +* blocks/rev000??.dat; block undo data (custom); since 0.8.0 (format changed since pre-0.8) +* blocks/index/*; block index (LevelDB); since 0.8.0 +* chainstate/*; block chain state database (LevelDB); since 0.8.0 +* database/*: BDB database environment; only used for wallet since 0.8.0 + +Only used in pre-0.8.0: +* blktree/*; block chain index (LevelDB); since pre-0.8, replaced by blocks/index/* in 0.8.0 +* coins/*; unspent transaction output database (LevelDB); since pre-0.8, replaced by chainstate/* in 0.8.0 + +Only used before 0.8.0: +* blkindex.dat: block chain index database (BDB); replaced by {chainstate/*,blocks/index/*,blocks/rev000??.dat} in 0.8.0 +* blk000?.dat: block data (custom, 2 GiB per file); replaced by blocks/blk000??.dat in 0.8.0 + +Only used before 0.7.0: +* addr.dat: peer IP address database (BDB); replaced by peers.dat in 0.7.0 diff --git a/doc/multiwallet-qt.md b/doc/multiwallet-qt.md new file mode 100644 index 0000000..8d69555 --- /dev/null +++ b/doc/multiwallet-qt.md @@ -0,0 +1,52 @@ +Multiwallet Qt Development and Integration Strategy +=================================================== + +In order to support loading of multiple wallets in bitcoin-qt, a few changes in the UI architecture will be needed. +Fortunately, only four of the files in the existing project are affected by this change. + +Three new classes have been implemented in three new .h/.cpp file pairs, with much of the functionality that was previously +implemented in the BitcoinGUI class moved over to these new classes. + +The two existing files most affected, by far, are bitcoingui.h and bitcoingui.cpp, as the BitcoinGUI class will require +some major retrofitting. + +Only requiring some minor changes is bitcoin.cpp. + +Finally, three new headers and source files will have to be added to bitcoin-qt.pro. + +Changes to class BitcoinGUI +--------------------------- +The principal change to the BitcoinGUI class concerns the QStackedWidget instance called centralWidget. +This widget owns five page views: overviewPage, transactionsPage, addressBookPage, receiveCoinsPage, and sendCoinsPage. + +A new class called *WalletView* inheriting from QStackedWidget has been written to handle all renderings and updates of +these page views. In addition to owning these five page views, a WalletView also has a pointer to a WalletModel instance. +This allows the construction of multiple WalletView objects, each rendering a distinct wallet. + +A second class called *WalletStack*, also inheriting from QStackedWidget, has been written to handle switching focus between +different loaded wallets. In its current implementation, as a QStackedWidget, only one wallet can be viewed at a time - +but this can be changed later. + +A third class called *WalletFrame* inheriting from QFrame has been written as a container for embedding all wallet-related +controls into BitcoinGUI. At present it just contains a WalletStack instance and does little more than passing on messages +from BitcoinGUI to the WalletStack, which in turn passes them to the individual WalletViews. It is a WalletFrame instance +that takes the place of what used to be centralWidget in BitcoinGUI. The purpose of this class is to allow future +refinements of the wallet controls with minimal need for further modifications to BitcoinGUI, thus greatly simplifying +merges while reducing the risk of breaking top-level stuff. + +Changes to bitcoin.cpp +---------------------- +bitcoin.cpp is the entry point into bitcoin-qt, and as such, will require some minor modifications to provide hooks for +multiple wallet support. Most importantly will be the way it instantiates WalletModels and passes them to the +singleton BitcoinGUI instance called window. Formerly, BitcoinGUI kept a pointer to a single instance of a WalletModel. +The initial change required is very simple: rather than calling `window.setWalletModel(&walletModel);` we perform the +following two steps: + + window.addWallet("~Default", &walletModel); + window.setCurrentWallet("~Default"); + +The string parameter is just an arbitrary name given to the default wallet. It's been prepended with a tilde to avoid name collisions in the future with additional wallets. + +The shutdown call `window.setWalletModel(0)` has also been removed. In its place is now: + +window.removeAllWallets(); diff --git a/doc/readme-qt.rst b/doc/readme-qt.rst new file mode 100644 index 0000000..1bc3d70 --- /dev/null +++ b/doc/readme-qt.rst @@ -0,0 +1,157 @@ +Bitcoin-Qt: Qt4 GUI for Bitcoin +=============================== + +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 libminiupnpc-dev + +then execute the following: + +:: + + qmake + make + +Alternatively, install `Qt Creator`_ and open the `bitcoin-qt.pro` file. + +An executable named `bitcoin-qt` will be built. + +.. _`Qt Creator`: http://qt-project.org/downloads/ + +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 either `MacPorts`_ or `HomeBrew`_. + +- Execute the following commands in a terminal to get the dependencies using MacPorts: + +:: + + sudo port selfupdate + sudo port install boost db48 miniupnpc + +- Execute the following commands in a terminal to get the dependencies using HomeBrew: + +:: + + brew update + brew install boost miniupnpc openssl berkeley-db4 + +- If using HomeBrew, edit `bitcoin-qt.pro` to account for library location differences. There's a diff in `contrib/homebrew/bitcoin-qt-pro.patch` that shows what you need to change, or you can just patch by doing + + patch -p1 < contrib/homebrew/bitcoin.qt.pro.patch + +- Open the bitcoin-qt.pro file in Qt Creator and build as normal (cmd-B) + +.. _`Qt Mac OS X SDK`: http://qt-project.org/downloads/ +.. _`MacPorts`: http://www.macports.org/install.php +.. _`HomeBrew`: http://mxcl.github.io/homebrew/ + + +Build configuration options +============================ + +UPnP port forwarding +--------------------- + +To use UPnP for port forwarding behind a NAT router (recommended, as more connections overall allow for a faster and more stable bitcoin experience), pass the following argument to qmake: + +:: + + qmake "USE_UPNP=1" + +(in **Qt Creator**, you can find the setting for additional qmake arguments under "Projects" -> "Build Settings" -> "Build Steps", then click "Details" next to **qmake**) + +This requires miniupnpc for UPnP port mapping. It can be downloaded from +http://miniupnp.tuxfamily.org/files/. UPnP support is not compiled in by default. + +Set USE_UPNP to a different value to control this: + ++------------+--------------------------------------------------------------------------+ +| USE_UPNP=- | no UPnP support, miniupnpc not required; | ++------------+--------------------------------------------------------------------------+ +| USE_UPNP=0 | (the default) built with UPnP, support turned off by default at runtime; | ++------------+--------------------------------------------------------------------------+ +| USE_UPNP=1 | build with UPnP support turned on by default at runtime. | ++------------+--------------------------------------------------------------------------+ + +Notification support for recent (k)ubuntu versions +--------------------------------------------------- + +To see desktop notifications on (k)ubuntu versions starting from 10.04, enable usage of the +FreeDesktop notification interface through DBUS using the following qmake option: + +:: + + qmake "USE_DBUS=1" + +Generation of QR codes +----------------------- + +libqrencode may be 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. Pass the USE_QRCODE +flag to qmake to control this: + ++--------------+--------------------------------------------------------------------------+ +| USE_QRCODE=0 | (the default) No QRCode support - libarcode not required | ++--------------+--------------------------------------------------------------------------+ +| USE_QRCODE=1 | QRCode support enabled | ++--------------+--------------------------------------------------------------------------+ + + +Berkely DB version warning +========================== + +A warning for people using the *static binary* version of Bitcoin on a Linux/UNIX-ish system (tl;dr: **Berkely DB databases are not forward compatible**). + +The static binary version of Bitcoin is linked against libdb4.8 (see also `this Debian issue`_). + +Now the nasty thing is that databases from 5.X are not compatible with 4.X. + +If the globally installed development package of Berkely DB installed on your system is 5.X, any source you +build yourself will be linked against that. The first time you run with a 5.X version the database will be upgraded, +and 4.X cannot open the new format. This means that you cannot go back to the old statically linked version without +significant hassle! + +.. _`this Debian issue`: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=621425 + +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 bitcoin-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/release-notes.md b/doc/release-notes.md new file mode 100644 index 0000000..0e0b04b --- /dev/null +++ b/doc/release-notes.md @@ -0,0 +1,80 @@ +(note: this is a temporary file, to be added-to by anybody, and deleted at +release time) + +Fee Policy changes +------------------ + +The default fee for low-priority transactions is lowered from 0.0005 BTC +(for each 1,000 bytes in the transaction; an average transaction is +about 500 bytes) to 0.0001 BTC. + +Payments (transaction outputs) of 0.543 times the minimum relay fee +(0.00005430 BTC) are now considered 'non-standard', because storing them +costs the network more than they are worth and spending them will usually +cost their owner more in transaction fees than they are worth. + +Non-standard transactions are not relayed across the network, are not included +in blocks by most miners, and will not show up in your wallet until they are +included in a block. + +The default fee policy can be overridden using the -mintxfee and -minrelaytxfee +command-line options, but note that we intend to replace the hard-coded fees +with code that automatically calculates and suggests appropriate fees in the +0.9 release and note that if you set a fee policy significantly different from +the rest of the network your transactions may never confirm. + +Bitcoin-Qt changes +------------------ + +- New icon and splash screen +- Improve reporting of synchronization process +- Remove hardcoded fee recommendations +- Improve metadata of executable on MacOSX and Windows +- Move export button to individual tabs instead of toolbar +- Add "send coins" command to context menu in address book +- Add "copy txid" command to copy transaction IDs from transaction overview +- Save & restore window size and position when showing & hiding window +- New translations: Arabic (ar), Bosnian (bs), Catalan (ca), Welsh (cy), Esperanto (eo), Interlingua (la), Latvian (lv) and many improvements to current translations + +MacOSX: + +- OSX support for click-to-pay (bitcoin:) links +- Fix GUI disappearing problem on MacOSX (issue #1522) + +Linux/Unix: + +- Copy addresses to middle-mouse-button clipboard + + +Command-line options +-------------------- + +* `-walletnotify` will call a command on receiving transactions that affect the wallet. +* `-alertnotify` will call a command on receiving an alert from the network. +* `-par` now takes a negative number, to leave a certain amount of cores free. + +JSON-RPC API changes +-------------------- + +* `listunspent` now lists account and address infromation. +* `getinfo` now also returns the time adjustment estimated from your peers. +* `getpeerinfo` now returns bytessent, bytesrecv and syncnode. +* `gettxoutsetinfo` returns statistics about the unspent transaction output database. +* `gettxout` returns information about a specific unspent transaction output. + + +Networking changes +------------------ + +* Significant changes to the networking code, reducing latency and memory consumption. +* Avoid initial block download stalling. +* Remove IRC seeding support. +* Performance tweaks. +* Added testnet DNS seeds. + +Wallet compatibility/rescuing +----------------------------- + +* Cases where wallets cannot be opened in another version/installation should be reduced. +* `-salvagewallet` now works for encrypted wallets. + diff --git a/doc/release-process.md b/doc/release-process.md new file mode 100644 index 0000000..ec706f5 --- /dev/null +++ b/doc/release-process.md @@ -0,0 +1,164 @@ +Release Process +==================== + +* update translations (ping wumpus, Diapolo or tcatm on IRC) +* see https://github.com/bitcoin/bitcoin/blob/master/doc/translation_process.md#syncing-with-transifex + +* * * + +###update (commit) version in sources + + + Greenchain-qt.pro + contrib/verifysfbinaries/verify.sh + doc/README* + share/setup.nsi + src/clientversion.h (change CLIENT_VERSION_IS_RELEASE to true) + +###tag version in git + + git tag -a v0.8.0 + +###write release notes. git shortlog helps a lot, for example: + + git shortlog --no-merges v0.7.2..v0.8.0 + +* * * + +##perform gitian builds + + From a directory containing the Greenchain source, gitian-builder and gitian.sigs + + export SIGNER=(your gitian key, ie bluematt, sipa, etc) + export VERSION=0.8.0 + cd ./gitian-builder + + Fetch and build inputs: (first time, or when dependency versions change) + + mkdir -p inputs; cd inputs/ + wget 'http://miniupnp.free.fr/files/download.php?file=miniupnpc-1.6.tar.gz' -O miniupnpc-1.6.tar.gz + wget 'http://www.openssl.org/source/openssl-1.0.1c.tar.gz' + wget 'http://download.oracle.com/berkeley-db/db-4.8.30.NC.tar.gz' + wget 'http://zlib.net/zlib-1.2.6.tar.gz' + wget 'ftp://ftp.simplesystems.org/pub/libpng/png/src/libpng-1.5.9.tar.gz' + wget 'http://fukuchi.org/works/qrencode/qrencode-3.2.0.tar.bz2' + wget 'http://downloads.sourceforge.net/project/boost/boost/1.50.0/boost_1_50_0.tar.bz2' + wget 'http://releases.qt-project.org/qt4/source/qt-everywhere-opensource-src-4.8.3.tar.gz' + cd .. + ./bin/gbuild ../bitcoin/contrib/gitian-descriptors/boost-win32.yml + mv build/out/boost-win32-1.50.0-gitian2.zip inputs/ + ./bin/gbuild ../bitcoin/contrib/gitian-descriptors/qt-win32.yml + mv build/out/qt-win32-4.8.3-gitian-r1.zip inputs/ + ./bin/gbuild ../bitcoin/contrib/gitian-descriptors/deps-win32.yml + mv build/out/bitcoin-deps-0.0.5.zip inputs/ + + Build bitcoind and bitcoin-qt on Linux32, Linux64, and Win32: + + ./bin/gbuild --commit bitcoin=v${VERSION} ../bitcoin/contrib/gitian-descriptors/gitian.yml + ./bin/gsign --signer $SIGNER --release ${VERSION} --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian.yml + pushd build/out + zip -r bitcoin-${VERSION}-linux-gitian.zip * + mv bitcoin-${VERSION}-linux-gitian.zip ../../ + popd + ./bin/gbuild --commit bitcoin=v${VERSION} ../bitcoin/contrib/gitian-descriptors/gitian-win32.yml + ./bin/gsign --signer $SIGNER --release ${VERSION}-win32 --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-win32.yml + pushd build/out + zip -r bitcoin-${VERSION}-win32-gitian.zip * + mv bitcoin-${VERSION}-win32-gitian.zip ../../ + popd + + Build output expected: + + 1. linux 32-bit and 64-bit binaries + source (bitcoin-${VERSION}-linux-gitian.zip) + 2. windows 32-bit binary, installer + source (bitcoin-${VERSION}-win32-gitian.zip) + 3. Gitian signatures (in gitian.sigs/${VERSION}[-win32]/(your gitian key)/ + +repackage gitian builds for release as stand-alone zip/tar/installer exe + +**Linux .tar.gz:** + + unzip bitcoin-${VERSION}-linux-gitian.zip -d bitcoin-${VERSION}-linux + tar czvf bitcoin-${VERSION}-linux.tar.gz bitcoin-${VERSION}-linux + rm -rf bitcoin-${VERSION}-linux + +**Windows .zip and setup.exe:** + + unzip bitcoin-${VERSION}-win32-gitian.zip -d bitcoin-${VERSION}-win32 + mv bitcoin-${VERSION}-win32/bitcoin-*-setup.exe . + zip -r bitcoin-${VERSION}-win32.zip bitcoin-${VERSION}-win32 + rm -rf bitcoin-${VERSION}-win32 + +**Perform Mac build:** + + OSX binaries are created by Gavin Andresen on a 32-bit, OSX 10.6 machine. + + qmake RELEASE=1 USE_UPNP=1 USE_QRCODE=1 bitcoin-qt.pro + make + export QTDIR=/opt/local/share/qt4 # needed to find translations/qt_*.qm files + T=$(contrib/qt_translations.py $QTDIR/translations src/qt/locale) + python2.7 share/qt/clean_mac_info_plist.py + python2.7 contrib/macdeploy/macdeployqtplus Bitcoin-Qt.app -add-qt-tr $T -dmg -fancy contrib/macdeploy/fancy.plist + + Build output expected: Bitcoin-Qt.dmg + +###Next steps: + +* Code-sign Windows -setup.exe (in a Windows virtual machine) and + OSX Bitcoin-Qt.app (Note: only Gavin has the code-signing keys currently) + +* upload builds to SourceForge + +* create SHA256SUMS for builds, and PGP-sign it + +* update bitcoin.org version + make sure all OS download links go to the right versions + +* update forum version + +* update wiki download links + +* update wiki changelog: [https://en.bitcoin.it/wiki/Changelog](https://en.bitcoin.it/wiki/Changelog) + +Commit your signature to gitian.sigs: + + pushd gitian.sigs + git add ${VERSION}/${SIGNER} + git add ${VERSION}-win32/${SIGNER} + git commit -a + git push # Assuming you can push to the gitian.sigs tree + popd + +------------------------------------------------------------------------- + +### After 3 or more people have gitian-built, repackage gitian-signed zips: + +From a directory containing bitcoin source, gitian.sigs and gitian zips + + export VERSION=0.5.1 + mkdir bitcoin-${VERSION}-linux-gitian + pushd bitcoin-${VERSION}-linux-gitian + unzip ../bitcoin-${VERSION}-linux-gitian.zip + mkdir gitian + cp ../bitcoin/contrib/gitian-downloader/*.pgp ./gitian/ + for signer in $(ls ../gitian.sigs/${VERSION}/); do + cp ../gitian.sigs/${VERSION}/${signer}/bitcoin-build.assert ./gitian/${signer}-build.assert + cp ../gitian.sigs/${VERSION}/${signer}/bitcoin-build.assert.sig ./gitian/${signer}-build.assert.sig + done + zip -r bitcoin-${VERSION}-linux-gitian.zip * + cp bitcoin-${VERSION}-linux-gitian.zip ../ + popd + mkdir bitcoin-${VERSION}-win32-gitian + pushd bitcoin-${VERSION}-win32-gitian + unzip ../bitcoin-${VERSION}-win32-gitian.zip + mkdir gitian + cp ../bitcoin/contrib/gitian-downloader/*.pgp ./gitian/ + for signer in $(ls ../gitian.sigs/${VERSION}-win32/); do + cp ../gitian.sigs/${VERSION}-win32/${signer}/bitcoin-build.assert ./gitian/${signer}-build.assert + cp ../gitian.sigs/${VERSION}-win32/${signer}/bitcoin-build.assert.sig ./gitian/${signer}-build.assert.sig + done + zip -r bitcoin-${VERSION}-win32-gitian.zip * + cp bitcoin-${VERSION}-win32-gitian.zip ../ + popd + +- Upload gitian zips to SourceForge +- Celebrate \ No newline at end of file diff --git a/doc/translation_process.md b/doc/translation_process.md new file mode 100644 index 0000000..97f57f1 --- /dev/null +++ b/doc/translation_process.md @@ -0,0 +1,105 @@ +Translations +============ + +The Qt GUI can be easily translated into other languages. Here's how we +handle those translations. + +Files and Folders +----------------- + +### Greenchain-qt.pro + +This file takes care of generating `.qm` files from `.ts` files. It is mostly +automated. + +### src/qt/Greenchain.qrc + +This file must be updated whenever a new translation is added. Please note that +files must end with `.qm`, not `.ts`. + + + locale/bitcoin_en.qm + ... + + +### src/qt/locale/ + +This directory contains all translations. Filenames must adhere to this format: + + bitcoin_xx_YY.ts or bitcoin_xx.ts + +#### bitcoin_en.ts (Source file) + +`src/qt/locale/bitcoin_en.ts` is treated in a special way. It is used as the +source for all other translations. Whenever a string in the code is changed +this file must be updated to reflect those changes. This can be accomplished +by running `lupdate` (included in the Qt SDK). Also, a custom script is used +to extract strings from the non-Qt parts. This script makes use of `gettext`, +so make sure that utility is installed (ie, `apt-get install gettext` on +Ubuntu/Debian): + + python share/qt/extract_strings_qt.py + lupdate bitcoin-qt.pro -no-obsolete -locations relative -ts src/qt/locale/bitcoin_en.ts + +##### Handling of plurals in the source file + +When new plurals are added to the source file, it's important to do the following steps: + +1. Open bitcoin_en.ts in Qt Linguist (also included in the Qt SDK) +2. Search for `%n`, which will take you to the parts in the translation that use plurals +3. Look for empty `English Translation (Singular)` and `English Translation (Plural)` fields +4. Add the appropriate strings for the singular and plural form of the base string +5. Mark the item as done (via the green arrow symbol in the toolbar) +6. Repeat from step 2. until all singular and plural forms are in the source file +7. Save the source file + +##### Creating the pull-request + +An updated source file should be merged to github and Transifex will pick it +up from there (can take some hours). Afterwards the new strings show up as "Remaining" +in Transifex and can be translated. + +To create the pull-request you have to do: + + git add src/qt/bitcoinstrings.cpp src/qt/locale/bitcoin_en.ts + git commit + +Syncing with Transifex +---------------------- + +We are using https://transifex.com as a frontend for translating the client. + +https://www.transifex.com/projects/p/bitcoin/resource/tx/ + +The "Transifex client" (see: http://help.transifex.com/features/client/) +will help with fetching new translations from Transifex. Use the following +config to be able to connect with the client: + +### .tx/config + + [main] + host = https://www.transifex.com + + [bitcoin.tx] + file_filter = src/qt/locale/bitcoin_.ts + source_file = src/qt/locale/bitcoin_en.ts + source_lang = en + +### .tx/config (for Windows) + + [main] + host = https://www.transifex.com + + [bitcoin.tx] + file_filter = src\qt\locale\bitcoin_.ts + source_file = src\qt\locale\bitcoin_en.ts + source_lang = en + +It is also possible to directly download new translations one by one from the Transifex website. + +### Fetching new translations + +1. `tx pull -a` +2. update `src/qt/bitcoin.qrc` manually or via + `ls src/qt/locale/*ts|xargs -n1 basename|sed 's/\(bitcoin_\(.*\)\).ts/locale/\1.qm<\/file>/'` +3. `git add` new translations from `src/qt/locale/` diff --git a/doc/unit-tests.md b/doc/unit-tests.md new file mode 100644 index 0000000..1f47e94 --- /dev/null +++ b/doc/unit-tests.md @@ -0,0 +1,35 @@ +Compiling/running Greenchaind unit tests +------------------------------------ + +Greenchaind unit tests are in the `src/test/` directory; they +use the Boost::Test unit-testing framework. + +To compile and run the tests: + + cd src + make -f makefile.unix test_Greenchain # Replace makefile.unix if you're not on unix + ./test_Greenchain # Runs the unit tests + +If all tests succeed the last line of output will be: +`*** No errors detected` + +To add more tests, add `BOOST_AUTO_TEST_CASE` functions to the existing +.cpp files in the test/ directory or add new .cpp files that +implement new BOOST_AUTO_TEST_SUITE sections (the makefiles are +set up to add test/*.cpp to test_Greenchain automatically). + + +Compiling/running Greenchain-Qt unit tests +--------------------------------------- + +Greenchain-Qt unit tests are in the src/qt/test/ directory; they +use the Qt unit-testing framework. + +To compile and run the tests: + + qmake Greenchain-qt.pro Greenchain_QT_TEST=1 + make + ./Greenchain-qt_test + +To add more tests, add them to the `src/qt/test/` directory, +the `src/qt/test/test_main.cpp` file, and Greenchain-qt.pro. diff --git a/favicon.png b/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..7cfe023b576f8ad16bb194d111ce6ef8f4393193 GIT binary patch literal 85640 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∓66Mo!6W&CZ0 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

WnS8?>oOOdjA(!U&fz)`#UCYz6o8k8t7Lpy`5dO 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 literal 0 HcmV?d00001 diff --git a/share/certs/BitcoinFoundation_Apple_Cert.pem b/share/certs/BitcoinFoundation_Apple_Cert.pem new file mode 100644 index 0000000..028d55f --- /dev/null +++ b/share/certs/BitcoinFoundation_Apple_Cert.pem @@ -0,0 +1,37 @@ +Bag Attributes + friendlyName: Developer ID Application: BITCOIN FOUNDATION, INC., THE + localKeyID: 6B 9C 6C A8 A5 73 70 70 E2 57 A3 49 D8 62 FB 97 C7 A5 5D 5E +subject=/UID=PBV4GLS9J4/CN=Developer ID Application: BITCOIN FOUNDATION, INC., THE/OU=PBV4GLS9J4/O=BITCOIN FOUNDATION, INC., THE/C=US +issuer=/CN=Developer ID Certification Authority/OU=Apple Certification Authority/O=Apple Inc./C=US +-----BEGIN CERTIFICATE----- +MIIFhzCCBG+gAwIBAgIIJ0r1rumyfZAwDQYJKoZIhvcNAQELBQAweTEtMCsGA1UE +AwwkRGV2ZWxvcGVyIElEIEKFCnRpZmljYXRpb24gQXV0aG9yaXR5MSYwJAYDVQQL +DB1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUg +SW5jLjELMAkGA1UEBhMCVVMwHhcNMTMwMTEwMjIzOTAxWhcNMTgwMTExMjIzOTAx +WjCBqDEaMBgGCgmSJomT8ixkAQEMClBCVjRHTFM5SjQxQDA+BgNVBAMMN0RldmVs +b3BlciBJRCBBcHBsaWNhdGlvbjogQklUQ09JTiBGT1VOREFUSU9OLCBJTkMuLCBU +SEUxEzARBgNVBAsMClBCVjRHTFM5SjQxJjAkBgNVBAoMHUJJVENPSU4gRk9VTkRB +VElPTiwgSU5DLiwgVEhFMQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBALTd5zURuZVoJviusr119aktXksenb9IN9vq6kBbq38vxEk7 +9wkKMES2XfBRh0HxcEizGzhMNy5OCXuTLMaNMihYdfwYSoBoR2foEU+6kjPUnyJ4 +dQBFLJZJr5/QeQmALmYHEgZ6lwXFD2lU8t92340zeJ4y5LZw5pcEHtH9IummYDut +OGCkCGXDcjL+5nHhNScJiXHhswM+62o6XXsQiP6EWbM1CsgrGTNLtaa0U/UvVDwE +79YKklSC5Bog2LD0jBcTuveI66mFzqu++L9X9u+ZArtebwCl7BPNQ+uboYy5uV2d +zf8lpNNZLfXCFjoLe9bLICKfZ7ub9V5aC8+GhckCAwEAAaOCAeEwggHdMD4GCCsG +AQUFBwEBBDIwMDAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuYXBwbGUuY29tL29j +c3AtZGV2aWQwMTAdBgNVHQ4EFgQUa5xsqKVzcHDiV6NJ2GL7l8elXV4wDAYDVR0T +AQH/BAIwADAfBgNVHSMEGDAWgBRXF+2iz9x8mKEQ4Py+hy0s8uMXVDCCAQ4GA1Ud +IASCAQUwggEBMIH+BgkqhkiG92NkBQEwgfAwKAYIKwYBBQUHAgEWHGh0dHA6Ly93 +d3cuYXBwbGUuY29tL2FwcGxlY2EwgcMGCCsGAQUFBwICMIG2DIGzUmVsaWFuY2Ug +b24gdGhpcyBjZXJ0aWZpY2F0ZSBieSBhbnkgcGFydHkgYXNzdW1lcyBhY2KFCHRh +bmNlIG9mIHRoZSB0aGVuIGFwcGxpY2FibGUgc3RhbmRhcmQgdGVybXMgYW5kIGNv +bmRpdGlvbnMgb2YgdXNlLCBjZXJ0aWZpY2F0ZSBwb2xpY3kgYW5kIGKFCnRpZmlj +YXRpb24gcHJhY3RpY2Ugc3RhdGVtZW50cy4wDgYDVR0PAQH/BAQDAgeAMBYGA1Ud +JQEB/wQMMAoGCCsGAQUFBwMDMBMGCiqGSIb3Y2QGAQ0BAf8EAgUAMA0GCSqGSIb3 +DQEBCwUAA4IBAQAfJ0BjID/1dS2aEeVyhAzPzCBjG8vm0gDf+/qfwRn3+yWeL9vS +nMdbilwM48IyQWTagjGGcojbsAd/vE4N7NhQyHInoCllNoeor1I5xx+blTaGRBK+ +dDhJbbdlGCjsLnH/BczGZi5fyEJds9lUIrp1hJidRcUKO76qb/9gc6qNZpl1vH5k +lDUuJYt7YhAs+L6rTXDyqcK9maeQr0gaOPsRRAQLLwiQCorPeMTUNsbVMdMwZYJs +R+PxiAnk+nyi7rfiFvPoASAYUuI6OzYL/Fa6QU4/gYyPgic944QYVkaQBnc0vEP1 +nXq6LGKwgVGcqJnkr/E2kui5gJoV5C3qll3e +-----END CERTIFICATE----- diff --git a/share/certs/BitcoinFoundation_Comodo_Cert.pem b/share/certs/BitcoinFoundation_Comodo_Cert.pem new file mode 100644 index 0000000..dc752d4 --- /dev/null +++ b/share/certs/BitcoinFoundation_Comodo_Cert.pem @@ -0,0 +1,37 @@ +Bag Attributes + friendlyName: The Bitcoin Foundation, Inc.'s COMODO CA Limited ID + localKeyID: 8C 94 64 E3 B5 B0 41 89 5B 89 B0 57 CC 74 B9 44 E5 B2 92 66 +subject=/C=US/postalCode=98104-1444/ST=WA/L=Seattle/street=Suite 300/street=71 Columbia St/O=The Bitcoin Foundation, Inc./CN=The Bitcoin Foundation, Inc. +issuer=/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO Code Signing CA 2 +-----BEGIN CERTIFICATE----- +MIIFeDCCBGCgAwIBAgIRAJVYMd+waOER7lUqtiz3M2IwDQYJKoZIhvcNAQEFBQAw +ezELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxITAfBgNV +BAMTGENPTU9ETyBDb2RlIFNpZ25pbmcgQ0EgMjAeFw0xMzAxMTYwMDAwMDBaFw0x +NDAxMTYyMzU5NTlaMIG8MQswCQYDVQQGEwJVUzETMBEGA1UEEQwKOTgxMDQtMTQ0 +NDELMAkGA1UECAwCV0ExEDAOBgNVBAcMB1NlYXR0bGUxEjAQBgNVBAkMCVN1aXRl +IDMwMDEXMBUGA1UECQwONzEgQ29sdW1iaWEgU3QxJTAjBgNVBAoMHFRoZSBCaXRj +b2luIEZvdW5kYXRpb24sIEluYy4xJTAjBgNVBAMMHFRoZSBCaXRjb2luIEZvdW5k +YXRpb24sIEluYy4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQChUwLD +u/hu5aFZ/n11B27awONaaDrmHm0pamiWHb01yL4JmTBtaLCrSftF8RhCscQ8jpI0 +UG1Cchmay0e3zH5o5XRs0H9C3x+SM5ozms0TWDmAYiB8aQEghsGovDk0D2nyTQeK +Q0xqyCh0m8ZPOnMnYrakHEmF6WvhLdJvI6Od4KIwbKxgN17cPFIfLVsZ7GrzmmbU +Gdi4wSQCHy5rxzvBxho8Qq/SfBl93uOMUrqOHjOUAPhNuTJG3t/MdhU8Zp24s29M +abHtYkT9W86hMjIiI8RTAR+WHKVglx9SB0cjDabXN8SZ3gME0+H++LyzlySHT8sI +ykepojZ7UBRgp9w3AgMBAAGjggGzMIIBrzAfBgNVHSMEGDAWgBQexbEsfYfaAmh8 +JbwMB4Q/ts/e8TAdBgNVHQ4EFgQUfPf+ZyDWl/4LH0Y5BuJTelkRd/EwDgYDVR0P +AQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEQYJ +YIZIAYb4QgEBBAQDAgQQMEYGA1UdIAQ/MD0wOwYMKwYBBAGyMQECAQMCMCswKQYI +KwYBBQUHAgEWHWh0dHBzOi8vc2VjdXJlLmNvbW9kby5uZXQvQ1BTMEEGA1UdHwQ6 +MDgwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0NPTU9ET0NvZGVTaWdu +aW5nQ0EyLmNybDByBggrBgEFBQcBAQRmMGQwPAYIKwYBBQUHMAKGMGh0dHA6Ly9j +cnQuY29tb2RvY2EuY29tL0NPTU9ET0NvZGVTaWduaW5nQ0EyLmNydDAkBggrBgEF +BQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2EuY29tMCgGA1UdEQQhMB+BHWxpbmRz +YXlAYml0Y29pbmZvdW5kYXRpb24ub3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAqibjo +D4HG5XSIIMCmYE5RgQBSEAJfI+EZERk1G9F83ZUWr0yNRZCw4O+RaM7xQhvJhEoD +G2kpk/q2bNOc71/VyZ6SrE1JRVUON41/Flhz4M6cP0BclTicXvh+efVwqZhIz+ws +UxF2hvC/1Xx6rqI7NYAlOYXk2MSUq3HREo+gWUPKM8em4MZZV/7XCH4QbsfxOl1J +xS6EOQmV8hfUN4KRXI5WfGUmedBxq7dM0RSJOSQl8fq2f+JjRLfjQwQucy7LDY+y +pRTsL2TdQV/DuDuI3s0NHRGznQNddoX5jqpXhSQFAAdgrhN1gGkWaaTPzr9IF2TG +qgr6PEp9tIYC+MbM +-----END CERTIFICATE----- diff --git a/share/certs/PrivateKeyNotes.md b/share/certs/PrivateKeyNotes.md new file mode 100644 index 0000000..da299d1 --- /dev/null +++ b/share/certs/PrivateKeyNotes.md @@ -0,0 +1,46 @@ +Code-signing private key notes +== + +The private keys for these certificates were generated on Gavin's main work machine, +following the certificate authoritys' recommendations for generating certificate +signing requests. + +For OSX, the private key was generated by Keychain.app on Gavin's main work machine. +The key and certificate is in a separate, passphrase-protected keychain file that is +unlocked to sign the Bitcoin-Qt.app bundle. + +For Windows, the private key was generated by Firefox running on Gavin's main work machine. +The key and certificate were exported into a separate, passphrase-protected PKCS#12 file, and +then deleted from Firefox's keystore. The exported file is used to sign the Windows setup.exe. + +Threat analysis +-- + +Gavin is a single point of failure. He could be coerced to divulge the secret signing keys, +allowing somebody to distribute a Bitcoin-Qt.app or bitcoin-qt-setup.exe with a valid +signature but containing a malicious binary. + +Or the machine Gavin uses to sign the binaries could be compromised, either remotely or +by breaking in to his office, allowing the attacker to get the private key files and then +install a keylogger to get the passphrase that protects them. + +Threat Mitigation +-- + +"Air gapping" the machine used to do the signing will not work, because the signing +process needs to access a timestamp server over the network. And it would not +prevent the "rubber hose cryptography" threat (coercing Gavin to sign a bad binary +or divulge the private keys). + +Windows binaries are reproducibly 'gitian-built', and the setup.exe file created +by the NSIS installer system is a 7zip archive, so you could check to make sure +that the bitcoin-qt.exe file inside the installer had not been tampered with. +However, an attacker could modify the installer's code, so when the setup.exe +was run it compromised users' systems. A volunteer to write an auditing tool +that checks the setup.exe for tampering, and checks the files in it against +the list of gitian signatures, is needed. + +The long-term solution is something like the 'gitian downloader' system, which +uses signatures from multiple developers to determine whether or not a binary +should be trusted. However, that just pushes the problem to "how will +non-technical users securely get the gitian downloader code to start?" diff --git a/share/genbuild.sh b/share/genbuild.sh new file mode 100644 index 0000000..d959877 --- /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/bitcoin-bc.ico b/share/pixmaps/bitcoin-bc.ico new file mode 100644 index 0000000000000000000000000000000000000000..c6bcb0251fca6055218d467b7cd398d58455571a GIT binary patch literal 9662 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 literal 0 HcmV?d00001 diff --git a/share/pixmaps/bitcoin.ico b/share/pixmaps/bitcoin.ico new file mode 100644 index 0000000000000000000000000000000000000000..c6bcb0251fca6055218d467b7cd398d58455571a GIT binary patch literal 9662 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 literal 0 HcmV?d00001 diff --git a/share/pixmaps/bitcoin128.png b/share/pixmaps/bitcoin128.png new file mode 100644 index 0000000000000000000000000000000000000000..bfc48f424b6e15878f9474006cff396492ac1d76 GIT binary patch literal 39323 zcmeFabyQqS7U|E{!wjra>3fyE0`R5g+5qpD;ZKLC)t6s`|VGZ*bGz%UREL#R6_6hRFO8~vS>YM1{6 zf>cK6O7@EebK&q%tq4^et^<;}f?6++!~Ol6shj4_cbm5(;89dxbPTGvja(}Lw^9%v z&ooJlyi`|zi!UU$)Dko)+Qwj~IZQVIa0CT*_Xbr8-a=H^10Vqo6ATm>y~_^)O$t=% z0Ma>WW000~C8xbI&7#XnlLs|d^FkKWshYOg_ zB`SggL_q*jUr16w3Vi|?sQM|fRqaoAo?707>ZN|Dx2wR+ftFfLr{Yh1fYE|aX9()S5~Zs#4iMHCr3*wWGkIE zRU(GCFVW24*MRSm)5$TVHlbujkN^OJtFexIF2UH%jkVWRRExQ3s0vo z0avC64ge6#_M@p3hDGYYDs4x3*FoIejtgn{OiT!gqMZ*-zHhS7J+Q$xNF67N63?08+2i#`{c$uAe&B5@{!hLXDXV`C*XWHdwzDfh@fVl=_2 z!v_R1cTxl63*|pjdSG02xf}6R#cD{DQXP;zC(ESNqej8hkC&oA>3_Q>hd@Rd%OZ^^ zZ7nq`MgLy;y}(yA%XczVyRo+Y3u|!IzNMtearLpNvGQ+k`aiGLWASu{uE@aWQYgJo zz<9-OD!2~)A&W&MHM>}GxG3`*TyZE*mX@?_wyOf8a!3&-n@bwya5|3Y;CuSKi7#oE z=9U*0jN70l`1ye6;>llZf|Y}{BT1;4n_{>l`?vPZ;kJXUw~JS&zLHc4>O*e z+NZ)a#X^{Bo`RZUuc|?FPV)vkZU7^ZCs8fYJ8`pEMb%kVZ~|P6s8XVeTB4`sl`o-M zqp(^8{|%>PqKH`aQxQk`h?1L9jKZj*eqO8oo&U8la&>T!M0T)Lo{DjO4}KIGP7jOe zv4+T`lN!`6opp+=k#_afo{THZ4WTQer;M_wbFo>eS)5tMhT|{A0&5%pHW6N{l0^h{Zq}UYH zH0v_DBQS2f3*Lr58#qJRo*$Duv;C2GhP!h(c{oPG$;1Z75sPob%w>Eu6l=U?onqNA zb`-c=h(2i16xU^uHzu7&-9U8^V!q1mjw@hRYENWT%C=xSXU%75WXUr7zUzJWTIIXprN5Yy1CNn(*C_@?umESnTD-1&9k>rK_A55b-#+>i0DoFp7drACP_Bw zqBKQKKn<*RrZ!b7J*l%xw`)78Zlge`o{p5xlRj8iSe9Pa<(h80h_!+h^YZJUEHC|2`drzJ?YH?M{%)_58(D^Q z!hYS){D;CnjxID-9EVmj3epQYPh&S3^<4Ed^=?|8%h$Z4%A91f?VIh=UTPWiG~f~7 zH@zO&h7#xzfXwp93Te%61upWpl6y~jr+UNQX5R2#58lOGwk?BCaE|3*3?Mon)*)K_ z2>fcGtf5GtQ=vb=vcp8de0s)+B!De~1dpsq;uLS+j(b9QWz0HfPM0=@Jph-1twngv zG(;uyd=fo6coc3C&D%czN@jbg4lZ;_(k8Tl#1cOh_sR?FB@Ih4!;a!dWsoE2f^M>O zP4mXQCHgp~D6)}v)~e3Q)x6ZoM#;u_FTKIHp}4`%J>*jSI2i+kNddnfw$?S>#UHln z47q8O#`$+ zBrrWlUc5CoDp#?pzTp+$+l>sh#B;8BUN6yWi(q*PmmXGlc_mtK9RAxz@>5wgvB`zf z(HX}XdhUdG3GechkS&uw(oQkezo?ftqos{UUITB~4iLtN3|y>{Upb%7V%r4CjOBe) zDpx?kkzp*Pb=Stzc5jesk<$9cx@dO4b48BmkMpJ_wo;*?vSzx$UGsPe*e3o7hde?V z*NuT$SExU1bwEpUBiA4$hgLdUVv>J`E z4HHdN53Q0XwAFlQa1-@Q9ZCz!FDoS)HdfT-^e5h&fo;EkUwBnNu)iy{`+nE1^g^?( z;#G^4@WhwkBjhr|7!fwLZFtW&NV>^) zXU2Gb@Ps`VKistf+1t!jw6#^k+bVHHJ(uEfSWox0|DL{|=2q*VMT62Qyxu{*nE%Fe zRbeY<t;oC#Vm)HX*7^tUL*`XumtOW5 z^~~&y7yQRpYyR=81ttZV8FGUB-%{tm7c#f0*1C$iE#{)5nS!{W1DSgLi~iGv%^2OI zW``fZ3!_=e`qyRr<@}P~4tL8^725W@rWb?z>U`y(AF{i=V~68D*H-Nl&Qq6D47yq1 zmcyPs!g@QZd7=i&rn5`e^L<=5gaes5U5^;|ird!jcRR4xs5?F%@2gDPZI><$+6_iO zYoZ>az=vesCp?UYEPs_8lAIuyAdia4AR!?{CTKY^8Hryms4l=99q?9r0Pi}NY%bod z+`JyuxSc*iyowlYD+d>yIvti2Rq*tZ_mVV^fccN}Z_7qzy$%}=n-+UpNK#+i3f(K5 zj$Iq~7JQMv5ZHWkf6I6gczQ$mHH#Gh;9;>;(Ew@4%J3N5+cFrM*c+KLxZ65BE<*tT zK0$W}Lt|@G5UG)=xuqRH@bFU`kkrzIAE?eQ%OvX{YHDFA<>6$i>>;OO>|t%pZ2}Y& zK;U!dd30cF3Nj>hx3#fz=5gl-{_dCOasRhsMj+|$RY2DKK;hpSB-M~rAQiQDG9_ha z;Gj2V;@~9Zp-CjZ09 zKmCZAIvYD#I)E(g?MQ#~YiMNe0^$b(e{1N^*FXAY>+okocFzA`_ehb^-Oz!NnSqJ% zzZl8N{@l#g_CH!WgT!4QkK`Xc`;QGetAHI$8I??(?OmLVO~qYJ?Lg%J=^#vu|McVF z;$-tX9}{CnQyWv;M-%7AgJ=Gahv4wf>yJOp|K%|LRR7oBgDlPd8k~Qz{Y~><%}m`b z|BKmgnm^2b4~0KQi0?7}cto8{4MFx!D)#m^0)Ir=Usq3$7f-)M73m9gOFI*LH|Ljp zj89Ge8t=cnnTi>LOa&e-x#*d==$TnmnAmvOICxl?XqmWpn3(?N2Ymr zXK3@cI=}VBZzUrYa~u(A9-gE$yEIh+1AG5LZ2oSA>7oIf+lZ#KVA za2`YB-*S(D@o(X4YQo3(FQtDS)}NODFthyk;r!na{Nev^=KsA7Hw#m{pN0P)u70oe z-<>$yn}OU6olJ$zA0y;{nVElh{a3T6?)eyh%hfiP{}%?r+0ga>-!S}dv+DneVfZVG zjV%oA%uP)M82=f-|19|5M(&UC{MVWF&xG*rGm+24n8(cC$<`1gU}m~wW^N7+dKL!e zzq|Qcy}$YS?Ig$Zm~9>--|+WY{%G@WWl!sC{FXIrTz;?5$-?ybVr2g9^B<;vS3fn? z{JH7h)lW_TvQ)LR1PQSGqqQf+f3y1SOw98;ROQD-qp84S*Pa$Vt*v0`Zfc_;X8Cxo zaQ-dlF|%;}!|18zKU!-3SIfU?{$cq?K>clT_j{)Jqx5m<__%6i{Bzm*Z%O3OT>F1= z=dUjPpY-~~*RMvN0QtH23)j#2JkkEb^#sVz#b3C7&gY5t7p^BjelGsP^>aQ?w7+mY z0rGS47p|Z4d7}M=>j{vbi@$LFoX->OFI-Q6{9OEn>*su)Xn)~)0_5l7FI+$8^F;d# z*ApN=7k}aUIiDxmU$~wC`MLND*U$Mp(f-2q1jx_DU$}nG=ZW?gt|vf#F8;#xb3RYB zzi>SP@^kSQuAlRHqWy*I36P(Qzi|DW&lBx0Tu*@fT>ORW=X{=Mf8lxp3BB0?V@5hXR2Qj!G#JSZRkhDiVbaP{!G z-39<$m;itsgU6>!Qvd*5`?vbt5&(coxU`tCiu?Dy47UWLiLrAZmrvBT3llV(0628< z5d)`@{uC1%f97Y#mCPx)4(&7E@Emd!gf>igO!$HDKyzpj(wui6hAoZU7O(HOGG}Hc z5Gc?(4zCWloz2`_T}9OA5#;$*)d@y#Fdks1@C^8vAn=eQA)i477&p=;6NbycKhDzHz^!E_#HA4O$#*HLdih%oMRp|mF{fSa$Lo3(m^c1ETk*CwvEg7~SXHPl*iNzMSrp+6YQ+oB%3 ztt@dd^PFF9VU_8mB$*6Ic+DFLV2E4^@`3WaeZX7Fn}TQ;Z}%qrHbk~oDk%&JIGK4) zLnv82b}0>V$FEFsP4DjTrNPGd%#Kn_NVg;iY~n|}rE)@lScFU{p`SlIFavC! z-J&{l7MwjpcwaMbGsYE7D0e9^8=ckJypD*^ZN-LNI|2Ausw10U5W*ZF=`i~m^>qHG zB2Usw>3FT+?(PuE8?yPT@q-FBZ{$NW-RML+ar;JAaWK@ehCjIMHQ42h$ThPGGd*fC zMT14rLr&ZUyEcH}a+Y`ea)iI_18Z_lQ|?Yp14=riIn-G@tM3CCZly+#SKMIWL)Lma zN*PJdEJ^d=w~GCtiswxqMi)y-9KJnxr;nHOBp%pAU#A5lZ$C_#>H>x!&ac|a0m%@I z5QILH`fPA-f>H2cG9{Irodspu4adSiC}@g$nIa1>`JmkRGq?Ky2QPi(AUGg1A4=PF z9wy>;JMj6Eva&#AfX(?^(Q>cbL%{7x7b00khx!e;*ECtYXjSb&{cHQG@2ubDrkCmI zzO0V>7ftuvy_<1X_l=@YWUAc*2Ws9P)11>zgY)2eFc~s-ZPtb4JP%~d2*B>j9tF|| zNk=|((dVAK!Vynrv2Ox(zRTKPus1fUU`1`>WDODZxbp=3{_RV(kDPI#-BO#qW7Er46834R*Lq5FB-`j~}mvG#25#T%^e^o^yUW+BHcbRiSK{e>q3KmvB_Pnr z6V(av=zVe0t?0X-x8$I1u#T$;u|rOup@F~@nZr6RI^!v@2p)hool3jXNy^Fp-Wd*M=G^o*g1QQbZa2!UIim~dT8Gm(sn4(msW2|fy zK0^zAieBfm5yQU+GdFk^y*ja#i6NUOT#Gs(@UWrOPJ1}}yYM$vwehbL!b3AhRoQUv zessGdsg}Z|<1`rOSHg++Cpi-WqSkMospDD{{wR{ww8D5rQ%+&5(i5qo-ZjAvDVz-< zQg2?{drFpp%j5LrYrKi7xqr7UL&U6(8mP&*IkE<}{6Jq7e=jS1Tj7WEa%fpJOOUEQ zG>n69YG9?ArHh*zlS>|b<7%_M!=C);YLqxD9*@o3Gx$oeb{gI7=0;j|rVCZdfZiTt znrZkb@Z*N*V^3Zy5Kk}hOPR5pjkBncu|9sm4ic^-EH_4pYh*kIa}R(S);e;SyJy!m zYAOxnqAD(e!<)YeMRs8)uV-F*z%htZ-|g;W`ZkqTsreHh{^3p}F;LnQB>KT85?v{a z-n~z$Q|r~mW*K07TYa~#_);%vo^vLXm1BmmpviyLt_6A&5s$~>vRC4}SUakTs^sLH zLEvIk>DhNqf;97iBP8RU({E~hdfrRVw`MOPg4b}ux!^g)J7o=WAKan>jFF<5@u14N z@Qj&yNHGL9@B#uvvn^Fv8&GK2hF^7i=2On?oJ5UD|x);5Ge zm(dSz-GX&I&J&3$KQ9n@-NAgUt!DvkZJjCkOC3exrB%L(tjxdtGy#MEI||vtG5fyQGxxE zBf{v|pFw3dS)EFYM6O5cP;ac;M^95>P`QQ>3>G%6#Tf$E)-Kdzl%wdAh&x1z@+9XM zxM0uPwPM2BG}0PNMy_NeB3_>6xh<`qAlc&ad~r#aV3a&&RrMu-N>0AH&9p(?F6VI2 zspM$B?&(|s6M22nBx*hNe&=>G@xz1_$(HCi?iDHI+ClkmKh?7%{9VyvP^fA?Ok4-FT{xBNW=u z)8S&&Kox|JD~@IqDh(+zHgqGLCh{e0wKP*XUF&QLl(WfL!B}h!HrYVS_fd#f7}c3m zP;Z!Hrx1LF4xO`vyJe%k+76Ajivo-N!A{&xi{zXc;ie78hp>i*C7N3=02QqyCi?s_%_q^?T|#e;{so0 zmMc~hQIXfLyt#4gbp2}-?51`dNxWem+0`@h5jT(Ku11MDk6vWA658-k4Ay7r?d<-m zUD+>8@=8>l56L=+ruyEHH%%{9a7gfq+r*IX;0N#+=}sw_Myl{qO}cc0pb^b2K-o{}MmCM8h`El#!6Wl#Pk9TE$61 z=C~xMGWqQb#|<$9-wNbTiPhB@xlb;KW%5`5U%%-Od8TpB|PYdv1l z#l=iv#B~CH!>8CLVd+R0B8a>OBjz5FwOQk6gCoV02lV6nl~4i0t~%c64(tGQkwwNF zb*DjZJ!J?s4;>&(rADo9w-#l4uT~6TQIY+IOT~+O14ZB^J|TWgsunaQ$$NOv{Z7Zk z>4^1C_`|N7eDv}$=WdCS>U|k<;ytt9J|bf+sE)~HC8-&_zJGf?)wcF&N>AkOCG6Y% zty~eKk1lM^U!reLzNEGp&AyIVZ7DbXrh=kTsf84I!xkaD2-~W3aKZaEqYFd2=_`Np z?fov@j#d{Y;=;zQaofnJtHkE(1Gc1zE=?RzrOYP{quJC{J&9P5bV4c%`c_{W;wnnA zx200dyw{r;N8*d7a*_2~;4u%mucBDzCeB^+l4;q-eQp{*;jzzl=n#vMQ#5nR+e6f0 zo8!&a80B*=uS}9jcr5YHp7b6zuyH8}W~~L=H>bj<`Ya14_-X+ymYLq+ZE0VO=u zF1_lMJiU8uGvSs|wI+{Q%|+cj*}8;gFL{8hry04hsC@lf+4=g7&o#^-^_0Rkl>?N8 zyc>E%_l(7!m>J&{pM2{*Bug$zg#zhvPu{4SmJ2!+>;AZcFTJnl_4OSU2R?gP0LOw~ zvRWgIyXGJ&Yg!a=2N^TFM9ilXtk8ZRD|;` z@KX+!(Dv?NR2N#<2dH!sJvgma8(XpiDgP8@@vmjQry;V#SX|)bun4mQE`ge(ee8)* z25oE~>cnbltTOknQ8l0mkvanQ)J&12dNY1$jzh?QMd;T9Mi%H(~+O?p?e*@3u7eD2`KIDF?Wjx67Ng*8t+9En$p9)Uaj zo)>#_AfvTY-|kbG8K)#&stDV_K7EDTMusj2Nt$-+ViyD2CSz*$&P}uKuda4|HPS!>*K$g42YIG0zx?rQ;wm%@Y+ z7+v!oZ>xksn<0CN;kEJt?h%O>r33kDt^AxW1@T`#8%1-dyf4=LXv09lM!)J-a2v~b z=!&)-&Cq&V8*edi_5}+RtKAaW;>zVaJ9@``BQpcBcmZA!e0cBFC$(h_vl1Bf64fr0 z)Pc=Voo*driHEsMOReP;rFS39=H~U_7V89dyyty)+&h@Ux+z;o+?7vgE8)GNgyY6J zR@p7arXCu|$m(%oHd_{Kx-XaKdBk>KSbz=NB!|}@;cEPjbd4ve_aHb%0;YR1nC zg)qia9}vpg@`-4xkVo4h=ynv9Cz(c@08tFFKti%XHbK^E{CE~*pDiw5su)R%9vmm! zrGT9!BkP0^9Z#XWMS9SDuOn)Ds;4fPwRHhQU> zI&)>4a`f{SfmKN4s$}oG-YeKZvg!T$#bCHw?+!M-Ox}PF7tu=V{NQv}&T9%rw z+w*NOX1$5`0kU0p^DcOt`apcMlTu}3%Em6&U-sHFeZYaQY^$|E%1aGJs`~W&BIMD zHd9v^_XO6h>BoYs&!_5d!%-nzr$12gU#1SC=nUlWg31moqV}z@OOXq^y75(dM?M{l zyiQ&i5yhQvKiU!Ib3gRb@fjz%oZY?ntS5aw<0+UQoj#=YnQAum;g zcgTvm&hYWq)XsCO2k%cCqx{6TL>{1Y)M z!aQPOyzTqh^vYB=71ip_vxb{{ zPYt5i%7T_M7T3ri+#yavrH&TQ=KnZhc4kA`MI1>=1H{g6B@-|(un=|6b9L!d*&pIaMTAL zL@Of2Cy|zylW3Gv925PduWK;sW|M< z8f^|2bIaUld~K;)n|7$@2tQip9rOK|9L=XayA9o)IOS8T@X}zQ?RNm^{U>}C&EewR zEE@it`PZi%)j@RD+QV-r6NfbGd}XO+H}ppZMo#Tz&5s)3Efyr9o&4skmP+&qro?r> z>ZR08v_)}0D_A>)=~bkVdeD$@5UlwzrS1C;x97-FJ(PQi zOJoSH9A1Ah$}U=wNRkQop!Ne!Rr^`1MM8jeMNnvhX!w@edti<@6C6sWEO{EmK{UlC z`&|s~nmwKt=118q?T&R+Bp`MTstWZP6FUm=Hp2>xlv~Bp`Jmkz?JRLSrMmtIL9Vl1stYAZXD*lD^Bfk2}MW1^=nx5 z-ESOwwvgK67}^W$IPJ$KJ@7XUdP3;MDnf8j zsGegDDPr!PL{{{VX-WzB!w+_=kyS2*LUCNY$8|T?ay)|U!_Lhx7D&yC;f}p9P{l1h zP(u(_B%8c4Jy0)!)wU+j3F&0RSb-EDm*1YeNr~G6dj(cxpIe2>s+PT5hatrfEazbd zFYFvt9g?)!rr&%h`})zXp%;;$b6zg8{o>X3V)y|i8w#OZkhz8Kyx6$ECPln-Iq$L& zM>(;uWRn*u%WlOK`!hVKTgU86YV?Ts3#iP@JA?vaX9V1gHz?|Qr^?Sgm!B`Zq+4Ca zuc+pl^-pDkjK4r9T^*EFb-eq0{@GK(@qX*I)nRM~ridT&>dGr`OR_$@JUkT`m^X;6 zj1)%UyXR^Mk8MabklD>kO{WqsVVtt()7Krj+52y(!CYDg+F(bVLk1*vH@}foop%pwyp@h zU;45IH(-DL0_IX!j6b>^En?HNO9!_kX+VXNcw5eFD67Ab_xa~RXEP?+8sFmM^wz(X1!G@j(z0k29xx;`I)d1`KFZg(OYIu`#N%I>Kp|MuUIE`bi;M@gC(0$uYI43& zgH&R_#_Q>m`i4RB^ed``rJjTv$8aTCp5|C{;Njd^+wI0yL`p&lFup$lxF)2zPH@lC zL=)28IP@yF&V8aZSO``4UcVLj9)W7^CDfb_EJAB#?nL*+OHKd#@D-j{zDF;ZiVKozW& zSoV1;S|^}KA)B5RRj?1!t+z)<`K%^@bGO~Gk>_w6%D;ACi1KvWFt5x)e|~6tkt@Kf z%>zq*#3P83EfWSJC%qMVBU2aL z!OH=X=%jR(y3)M^;!8am`)5Zz5sa+Hle}E3!3K~~Z`!WTkHR~(jjd0Ln)9EtQ*EH_ zdmAH_UuKf5xvIVsVa|NMWtK;*f0V;(UieCr2183^hbRw^G~RiuKgbKVT(n3_9ol8G z1M+=j-ynukned0j;A8=2A!aq<56}i@S^zNavU|xbI~;Tb5&cnYr*QY?bl-#~{Rxus z+ARZg^3K|e@6|II!4suT-j!=xiz&%B*Nn59PHl+x7iaUK*X95>?L&ARWK8VRME2 zWcQK1VBec-k!wk;2Z=bsJdvKtu1C9MV|>k|NQ#1N4uTkx+D0dfa(w`mU&#|YKmcMz z0?vOAJb404IWqF;lVyjtILZ8y$f2SRtwK=?BC7$1G;by-wK)w|${`rM?+MhEO%H%> zNvgu#Aj4?D@p{wGjzvNaj2%#Ihi_$%C`&Y?nS(naFZpKO_~o_Qhl_j!?uv+nS)n;w zfNi;(?5Jo3T@iT3<;U9w<2jHUY?v4&a#VmjJ7+hn;Ayu#QRwZ#6&+jXx-lyR-IG#Qz3h*<}^`7 zq15+z$|Qq>Vwl?>2@%uoYgh~Vt+AGj0zIDdgHrLI^LY&v=*E`+Q>2@tC$8f1! z$xR&O9`Ld&?9oKE%?V#Or57z2@tyLTv5adEPjKprPPDFat)Dn+wmL9Z2wXrmZm3ek z_Zw9e%(lBkNux$^_o`Eu*Wqr$b|WHeY9R@OASSQOI$RRVOLJ-myPqiyTnOS&63lBD zN*JV?kS~_Knkz;@PPl+tKWGoW=Tm^cYYSHG6bfp8g+=Y4YA~~an6eC8T~gd!SUd%* zMiap-nyqwB(6ZQHY*ZFJS{2R=-{e%X-Cr9U9z}s&(rS^;tV?>^MdNMCC+=C7uFvnl zS|M;obgJitkDFjQ4MFa=bYf#c}>i(04As%Sqv&_@u9jPv^f=;v`4b4^aRhe>T-l7fzH zqetctf>rFrJ@c2-<3kv(vm65mSZI0XJ*||_1H(;_8R@_={JpjvFVnrFMC;}wMC)ea zY3kid=ffK#rVFj`F2i{Yk-bSZaj~=gs#Dpx7s=xXk3ztDTUqVA8bGb}1e=}ret(Pl|k_ea6*OV1NM zy!{vgBCH!ILA#rCXjMH1gvCJQ7t0ZvQNBb>N9+;zQi*xuYkI=y^iJ?=RwX*mkMG({ z0tIPVie7wiQrE(2mJq4`u1@wURrE(q9Q3vb@8da4;@K2xnI(_G$x&u6E<5Mkt04Xk zi0?dUm&ek$mD`ni7)Nhm5F6L;dEIZX3R32){OyUn5R4^d^Aq{{Th*=T3=57sb0PP> z7gW{c1!d&#wM%^NsT`|h-fL66+{9h*NOa@HIqlrxzEqnr`q4wD(-=tFnE_?0*;!RF z*~5>pJaeWyGm9$*AYo#7Ii3#|s3o*+KfZb>h~GLE6jF(qwL< zL(zuK32KXeh9KdI$OU)E-i85t{Y%*X?__rspY7HWDwVJFm9SMQFdR{ux3Ge#thih+ z0=O(b@z1#Ipv(!nz_RE?qpC4%Gz;E_W|lL&jS924NcXhH6o5DGw_eepW=UC_NYaM^nKQ5l=WuT~U=L~r~#;iATo z8H2n)ibFt!g68nv2IcM5hl_^05pBZ|9**y(ga`ZwgGBeU&V}W+*$YJm{dEQl(Um7UtYi+V;E_^w=YK_1+{9W;MqoFTP0xIrH+`v=Rrf1lM>w z!Y8IjQkaSQY!>Nozt|qG2)p6Mp>S+9a0riO#1ZVvB1=g+!}hKtRiQ|TlDtv`Q0|&s ze~7%G4xabL^-U;+*h0wt>b9A527%C=?+{UY5{=xl4GVmi|4IawW`@Id}x4gU|agWOJ=UYcy*$&^z`mkBr8 zt~mzN(;Wz{!CN!b?k>x`Dp5poGLv}T`%s*Xrtcm?QE}0fO$&zIC=`{Yd9qdmun{c> zOs}Sz3ofTbIh|IuW2$VOD9jWn``t0BpBJwg<&IJjlgMWnGoggjLxFBDN_Y0oW||-` z(Jic3P-EB(i31o2`5TIyCBBO7$7QceGTP%k30h`HXcSQCh=YR z6ej!>TY6ZsCb&VAp1zFiRcz*B8~GHhAKxRKvuRdxC8R~0Bs-OiVp9}@e|D{tyP9bcDJI4)x^nmWp6_Q2;=JyYYXFo`+V zX8vG@CD?SsiAI7!wF#-VUfWQ`C1Ol!Ry}rP-c@!9{}(74wN{NeTUs!jgu0{G{tRD-i*MIY(Bc( z?5=fPak2bNZ-2(d=v*TWAtU?iI)}}HgGM*be3P-AaxJZKlmMClb!;kgCvGZaeDUF) z+991<-~HfdbE$pO;BOpX#>RRQmNBo+I|`0=tSdw~kiD=025(=_=#T&evrKQ8)17*a zFRBZ36MKr3#}4=;5;cm$7v&#t1s6`)f~TVMdp#&}-{>uuHDChvmP$lr$mjHlnn~;Z zN!{hU45o~4a3NE^ZNtdqvP8vlN zj$F>70j8>MIZ}G17H|4uTqsubIt^MMm6x=r7ZK>7VJv2hdyJ(;ga?%$;;I2-(8QWq ziZ0v|Qw0_Wu8YMo)5>1E;~AqzU$&=?=XMz@t1Ypk!6;0ingDIJz5*euWg8)HrlUh6{{75Cm1;Vrz;|Fgc@qT!z(}mmE(dN<2j+4(Z2kpi&iFJ>X?g>KV4B^FC=2;aliY zHe86+B7$IU90m<8ybfJw+>6txaMmOv06lgzU5lk*QEN69+q-&)kj}QxGY z8by~Ug-t)=XQM`Lw_Dw;%vGtffteJCG(Jri_I&KN1cx&?O=@(3Z-PVY;X)!7+*kG8 z_kySk=dDkxfewt%6(S)#l9PMg0!vyw$S!p5>10pML`>PCAwHI`RkPX|*O0AV8Y~mN z)sBK;>>74uxp?$Lqx#0n2QF3~Zz1 zn2%-&ZCTR5O74IkspL(agM~}8p@!(XMzg`pQn>p~7L5@*2{evT^y}D*%VAcxC6(^u zVp>w}KuI4EEUeu3=*@ij%^{L3$v16fp%DQ)1O!Z-lzh@P^+lz1af^H)*2T7EklNxc z%M{-abv^wb&od7l=b~>;qvoU6>!6x~BrM4UZ zqOhzu4}A3o<$j&hea+KnX=ioxa1h(aiYIyD2O^DYYUJ^&XQ0F|pizmL#j(*1j+GGu z3^O8s?QSB(r5Y2)eN62eIv9mcba6Fc7vlgVMQJcGuviv&iwYF%^q07U=lFii5pGPe zLV;D&A_dU{l_z!yNKx-ny#B;YzeC1{-_455+bDy!yo8;Hzwj$R&c1B=DibiQ=Zi!b0!({8&GSKIB=hLYtE7kB;|2XWsNfL2bnkA-)kQiAmxpveJRRl0T;S75lQ(Mad0qRy$!}@8TW3LK>E^XooyRv;-H1;jEL0 zbVYio5r92Z6~X)Q8#jX0Xz4EEVOTXSQU9nM@H!A>?nc9ocb6J4=dhcFSF>+|!oQ|N zMM1#rdSXX(d+NN6a`G)zfE-+`+gXI+#p45G*cTesJi(D=Kj)3TmJ$0l?|Bh1_19_j zOJ7j1I7u;`k}(_GJn8$ETLeklnDWgTEMfFg^ss?^5>W^?mNP@dX;z?9DO{Pv51hbp za%(hfh+2(}7%Vb`B?=|tZAo-^*{{#h8&{%pAww%QmIQ)k6&GH>V^W1WVRb6DmlLGr zEgJM)E-X5pdn_I9>i;S5?_!BS&1s8 zq3UPfL1yU5x_&@ByTDhs+1O@uXVODS27|kmb`=V}$aQG!1B`Vk=36KV)krv%4+#$; z-g>Y7^rcb7URMdhZ^6s98w%+dkadpw)t9Zyh1JE;d{$_UbYQDYml(;GkIIfY`Vfeo zP%4Z?a_QGygELY11{vSigig4*DHziS3~{;$&iZ)lU`=zBH5x>+fL&N-%hvPXH>Fch2@Tm&M4dj^=Y9KvA4@KsP1`Ae&<{Izzt`9mdC}n zkM^2Lw#1ihI8Ou75FNU!@E@vytz$k#cq&T40$UYZn9I{NLXF?FAaVpSt`+_FO1uz} ztl?Tx2+mm|ubUuhTQa1qkfRF;S;HrebXMHygte^^Un6p_C`&34Mg$=j#|cFuz%1s) z`{Kzxq>5A_bI|JyI@siiH;W7RSJ)rx!_TzA=(}5oeGcq+{88RYnLfu6MQ1$ZX7;Uc z?sitbeykYnhhNLPWw<$ol!6wCUN^a}xH+6B_ zzB?h&;`p}K%NB`y!Gjkts5tdp-&6yK$-wfH6KRpy>k!7%Uz4&Ij07q^WZ1nH*42+7 z6<;LM3J_8y!CEs@A)&hggcP&Li&wz(rX=nEaKZ&xBedc&BWWT|(x}k~ z3Elc*6Oq+HtZk&`GG0-<&gJS9iBTP%?Sq6ibc?h$^xX)9u#01S868rE5Ty-cyxczaD2wD;fb3%v5Ps&_0k= z_9~tR75IjdZg~KS@XDId5eCZo1;1L)OS%9r^5~o&=Y-SuE$CjM1<*+#HXRpHA~!t; z^7Gv<@>>9_Jk*)8SIHt@69(GfvXvSW;HJt>ZGSEy-4IgmVXBZ$Vd83MH_TWfr+XcP zmgd0RlHvpAQ4e&5q5t3ti74Jk@4GAhzE5{rEAa7KVy=NLj3B1De!Y)D>QK)U_J@p? zrVGL}uGL@@Arv|nj1w+#H=}dAx(6$Q{~-b^WdU#g7efqiYJh% z2@}C(=ch#OO0Mns1vOxbvabffHl6^;Eh{2%JgAhGEu%b!}!kRU!z2ITix)D zl-rM2^^5?0Aqi+NcUcZc4HrpI9jX8skYJRaD1>Tg#UXX$1xJ5#vM#(tP?}TdU+l#a zu;q+NZf0X$m)$5riZ_niev?u%i=5Lh-yD2?05^-(juk+PLnPBuE=pE_ht){(HlD(0 z*&$@Ttg}y;%#a*zGSd*Qh3Q?GYo-O4GO5Ug9)!1j;w-T81w&^y(4!76eqdaEoBDQz zL0l@C*``^5f>oClE|A^rjbbAR5P{|@gpY=c?&C74++6u!bone++k6c2M+e{yAsVeU z)sd~x$YA`S8R=|*GH+-Hd%)4%!?}1@I=K^AQHuh z9n0I%ZuGm_9P&#e+F3)XGMtdt6&V<6&4o%sZl?H2f;88X*jnTf4m6#qz&)H5;jgdj-(z{*0*5H;8(k0ppd$J8zE>LtPn%VNqac^Nb`oyS9^t z87gyiF2lsaAFmgZ@=ceqL#gouKoqqqV@x(gH+C(wuV>YrAAW=smfF7Phnaq%a2j0I zK@^E0)?T7oCWN1tbRj_JQIAknXia?yXRWB>zaf{C!=vPH03ekL-zLIengrYfnaFA2 zNnQ>#0-Lg=^d;1pMZaLI&8DL$Wd;VORdRXIp}hZce;i$!NXiq-jt57?LM71}O9{s# z*yrKcEW{zrMY#4R4V%GKlvFiQ$Box~56q^gQy5BV;Xq@d*ZPjX;9vlqH#kK5(5)wK zjXwr~Je1x^&qO%_EIN@@MBvNyLZo1G6`Dse@M5yACW3yE*?gY)VP!35(k>?-XajM$ zX{J`<6jMBB5-ISKjM^C$9=&5V{0=}c_DyWi6%3w`AFOjpr47Z(12m&c*G5+>Hr=%? z3aMrLe5fQMq6zu`0^lMa-G#kC8B|e2I^Q+3BWH`XgblG48(={ReKUr(=A}#-Gl_F2 z>_ht-^LXt3`*`NX=h$Q8Z2x6+|N4MblQi`~vH0woGPEp7NR&KObva0f=T8=BGVS$#ndH1Gi zrF5)3EwdC0Ii=Ay)-ualx}xU|w-)LDzd_mmXsOL?fajD_L@9d@i-=2rArn}#p1jnZ zcQL}$4Ws}I+^CeCo-kgz=Zr>Ci}ujj%0@8_lmZlvbeFNM)q zUp?PWaZoF(Rf2hHm;{!jNsuHywZkjmI%|*A6Yj@Dq#P4o#JmT_i8_43wH?CK2C7W4 zi>R!^C)(?^%gYR<)~`OUAes$So|}gH_GVeOH}1oB|C)$kzZhVE)X=ITYDhRKI(nK~ zJG0q&8LyVO(d{>o0?c;_j~`nJVN_X{s$#~HIhY|XXf5X)djSVd-jTLd&75)Nw{Ujd z3=@qctEs3e9r+^Yc8ZAyI0ZC#*kUhm5b{AlV1u0<>1+B*K(~1I|vNl%GhbhB=u7ENa7{PTC#3lFG z!d9iHVCr2*Hyf~k2kS$-O9OL@N!QHM3)yfEPoN6Jt^ z46aF#&1cAZJ^$<~B+2=*uZc7=Ob>;Y)|IrbT1;EhQd(9lq@}w9V;nwW!s;v_Tg>&| z0`@OzW+s=WvZ986UwAzLXJ2?O&(E9Amgk(ts8}^$-{*L$(#rlnKAnbfV==ZQh=Ud9 zDYg271VM*&-8xZ=uLi5;2*rQDe9}g7`bWa~_`AP6<@18j4M?w9E+KwJqKEY2`=!6@ z0A`=^pm4wpfN4N+9-9m7E`0&OHR756yT12lSrZYoiWnE1I7UpNJ6m|7En9;-+^>X*k~z>(Kn!2K^gNaKi!paY)P`bxP1Jl{96a&V)Z>}%iqCaWaF zE6>eg-txsX=Q<(Z39&d;Lq?Kv>cE!H{~HHps+HJVMsqid4bHIQfGWN?6Tob3l^OlX zpMJ4)+V`p*E5cc&i)4yWdS8tWv3@XcmI(U=mj6V8PZSFPC2J7X_KUl-gcE2#WuUJW zDHmE4C#(uNex>y-8GsD>kpUrV`u$#i?qD?@Hm+#?<`q40GQQcTI&K>Tp?%dNjy~W- zes=5y{PWH`xcjLGsUA9ry*AsP(@s2;HHiH{bI|9%-Jz+retQ@pU983}MXnJ8Ud zNzCYJA^RrGNv}Nqn>_vCqnvooN!+vOX>NVxe6~D!FA603MnCC|R*2 zN_B5N6`TvLxM00&G4Y=nHROE7G0y3($hU2o-}W`UIcu8Dglfp@MS4M*&_5QV`Veby zq8>jJT7#ER_Ewl~AQmX5m;p$+kEe|38*;oN!iV&-`K51+5xwOoV^~ycDEXj#&g)b^|stBl*@0ldJYaoi>}&0E6v{`^xodMb^B zhah<$8+VvF>kZDn`h#QAYI!%Yu!{}k7sH?1D z)v|^B`m2|t1)t0R{wpqf`a!;N(q){u-67mG<9?F0!-|ZP2H@*R{6-DZzr*@hs}nat zVDTkZ8x!yJ!1}kEXoR}8kntd~CX)9%gqKxIOt4cG4%aelU!wt!A^kg~Z<(+i;TaL` zv&zb1s)_XV`_aiIeNsM&f`IaXu%sc15!69A%h=+f7GMmjx^5?Qtq&v05v4j|S>6?6 zPD_%Qqn8L_JGv2r?-yulY6^#Qr7V&zgx^1>FlYpGJ6G_^2k(GNv;+EF+=m8IfTyE! zW{p)#CZA*537c`nA!qTEzyFR*PY+g=ns%T3toL9-Ba=7Zj7^=v-1MJYu?@qK5{PfF zHpaa%2;3h0{B>$#-$tNd1;@m$BJggtW!%4XA;bZy;-RT8!j#di=!fsI@HgNTl`_8> zSpyX*4PJ;~-lL>E+SjrGPkMb{xkXS}GX|iH76vjGCFX-yR90g)?3wWjRJh@pNpo5g z%xX!Zf+StNwD_Je$E9WUa%SgObL1We@YrXZ`5yG{9l1n0n+&_6UR>A(k+hU8-Mx{U8zp!>fn1PT|qLHV*l-SW7mn{SfNp8 zeu-6dQ=hCUGnwY3An@+6e!50t747OKjI{w3MsRKB1kVO{6%SGmLyBNVkBK9;p+LSTwAhK#?ao~ffwW$Mm_C?ZW0aERJkd3+hi-w zKKLu>icSu>^c0d)ClTu#c0Clanb)uY0RJp1Ky|W$cW1uJ%rV4AeD0tM<$Ji(~xDd7Z^WrTrj;eiw8nnAwyh7YNk}TP$Z^ zNrdtRsKGm@p1YUm`vo;H0 zfHt5OpryT;$zwO+l}m4-rYbCS`CS?Q`M|%pclu*I`p$D?ld#*)d+=u4LXxQzro2ET z<}22E|M0zZT%2SFv1BU1Qeg~^pFY>O!5_*GgYqm?#X?Kq+g?*$v;h5J)B8m@yuVP( ztww407kmB6cDs>&!5#hCKBb-SOL$!FUFG!$-$J?&4yde%X`cXA`YDjmFNmct5*kFA zO2#wHY^_9qRLmB?=LNtuM74poRZIBJ3E$-B$6ZJ)`qa&tPJVI!jr`{RKZabnEHu_M z;ta3Fw?R3X#RKXi7{ktV~26j zPW$oWeZIyu)Beuw&;FAk!zV_EEd$UYV(NV_jRxK!#H$`sOzLmqP1!~7s{|1nxJ*&#FCf-1`v5r+mK8ApN5V#xbdjB`rMkcX z45z*fW~=2q68aRDVkT@RncI;fZ^N^b2!}g&0&ydTrj?60@!%8r!Okb~ryFk|Ik1uDauw)Qp}GPP`1(D6jq)ky)C zgzFFsvX2zJ{2`{SVbMn5(4Z7Fqh zBW}`akULbYAGR!6!8)nmf=Yz#izkmU zNkr?*W~koN=V^gQI^DxJjy;W$+wVg2jMwlBK5-9*P27^6j)naGi94zE1=JacE`9NU zrS!FYs<#PG3lIC!0!w4VV793p%c2kF)7-V1AKd(XhEE>P(|?@KwC~?Ubu2~e;uY*O zeh1Rst$nQkOi=V%CziyB^NRSHt3)kT7xHm-n*E)?cE1**uz-%wg4%xllWpq!Z_nnJ*G= zZ0zrE7DhPFnhO3Q=LYUwgba-snwl1K@ge8X(%QoGg|G0F6E5PUT@C?Y?z`{tqw9ak z1Mj`Sq|G;FMtUig)zze&czHkwVot>dw*~ob)VTlBSmiN+LeeqBH2YM}%kLxQ@qq0O znDRkL85>mtwaGBp4Yc0@ksBxmaH3Sw3&Kc0q*eSykz1ZhmL3XUhDp>O$6cE=W4|8jVW6>0E|DmD1oYfcV18V|hn3=h2VG)D-M7tul;`#1#qIj z6)a?ZA?Jrm=9*qbG{W8@+$1oL4{KT1(vYyS=+&ZPP$x+NL$wmRmUerYneV zIhu+@svJmJL^#c4U|!`%Si!T24jZhgs-|iFyF9b_ZT6e8GZ$TXF*iK?4|1_Ishu`w zNPQhmGv8##F;h7Ips(=q;#u7B^xZTL8Qt%~*y-ZLCP^&SLLsw<2-9&=yW|2TZc?Yj z9qsn4_s#x?NhqO92!N)Za9%}Sis(eJ-s9jz8E72ZNBa3#)NYpcgj~?upz2DW?(Y4p zGHs3XBG-P-a7htF15{$Dlb~Rs(uonX`8l0l=eDad6?7D0=)_g=%jFaC#JhHgRyF0nQnmeLQq@&x)+_%^=YFW0 z`-tP?ml2*MGC@@_)pgA8T*=FiKgo^v--6j=G7Xz;j2l3AM>kHYlAFH%Luyl1JoU&l zUYq|KPF+3mzDJoRL5~q9_D95c__><}-4`b|##g~{V%xi-lLG7C7ftqCLs+hXtQQ{o z+>;M$3F$%@BX~gstVGXqolv-OC*mGP_iogJmT;p3qzgiOUdRW1fgIb*UT#pBS)6rQ zx=DH7Usg6Quk&LGB#v*A6s(YPVkE?0n)AB0ZOT=2x8#y_31er7P(xId>d9xQuOGy= zjT1@rShm}8DrfF>3_(1_vx{cZnOVuZohx{2=`5C2TLx{tF)`zYAVu#*djt61hbsR^ zxB!5O#oDoB9J&ixG@YlWz7~zUWV*WPXY*XY=&;{>c1y-)EB(cO|*^mgF-T zD&tAwZmd5w=|#lsYW+O6kU^Zp3qdTki>e`E6k~Ju6#V?Llvx59fNf#RxPC=~sEKm) zL?n185_TBWX(_lXDv2QUcu|nI_Umv1@Qw01LO+PQ4XBFB!qGvIVPRKIykMPFBG7;; zH%3D9i*jD~PR+UG>egI^agBY)NJ#XQXdYv0PX}X0j%CSvv!ElxPfod*ADnSvSj&C? zRnGY1Wh|NZHkA`Mr8-fCpUn+qiMEZ>zmIJJ0NM!$N$sGj#Kd5dF$5u>$zW~3RVQ4? zNe7+8>H8hW@TvyB`n_*5d-fX~_2rW}Ve5T(^o0jV)im_~e4|146eo5k;sp4)n?-}o zjBz*fRB*(d;u`h~toMM5WQ2hm%Zjb!#kMkIdWjnaAJQIldtpl1k%x|)(2^5cv*B%J zI;4KpCL%=|xOc$;Zq{hbyh=YhcX)eR!^Y4`-v)?rh#99nu=<*35+kiRZjGBDZgbD} z6uNeA&Q-Lv=TnWYvCkU`nLmAiK@~OJbN5|T% zWJ4<5kJuxBvYFp?y zsw0;>uPIkej~CxN?${aqg%40!QB6-a&DP&OjJ#jq=8Jw$I@3v8TQeg@ZA@%tg|m>!-k_#a~)hE#|uK{F?2$YuV-SeUQ_) z=9bfc!f88&_`vxWeT(ZKxCzG8Qaf=HorP|&%Fu?P1c5)mr!i-s_LjiQL6EP;#D8zy zPX{JYvUA}JBUvh+CS(2bCkWU8`!lDF+hJV*{M-U5jR!E z<8M5}xY6U;Y>%njb@A`nbHY}vShAGEPCAOaUb!Ep4nanZ;Q1fi%=w3$%IiyJ@xh$8 zsjR6Vz^CgGh}1ex{Bgu7*r0Hqv4wd8dj-Z#1t#9;824mH>?pOt%Zg^!Xnv3ZejMO2 z>BspiVg_E&J7=l*SmY&MQ{|f_@`e*9?zs5YKSMF^p^VOYnd3Yw2Ua9%?}_EG};C1&&o{RM7}LvzbYGHt8Kbhnewc0-{6*5~p6+)iEnXwKf}L>`&> zEKVZ97GpPNY4dX2LDk{OpuJk!93hhHZVA`Z0zi1azz4;wVD;)H3>`e2Ro6cQz+HF$ zlXHK3F|9Q@s(0L+U59VNrH7rx9-C}K$BJeidGTp(n)f(Qy)~U7!zW+|vIH;#G2cY( zi-DIzgF?NCbGdaA=i?+?BZ36^9<_yEV}ri{D&h5x>m1LGTW!gsy`yH2KV>=ZgyEN9flyR*k8 z+pv1!QbzB;1@Vc4x&3E1lJz}apZ5-R)eQrA^{r9+0wTY0;uV)`5No%7?jqx7uG1iY zv2~K?h!d~E#IF+dt}xcSRjq%!)gU~DT;O9LC|5=t2^0b!JSmoc ze)Y#&nEt|(H0-)H(swe(`RgnEk81&n>ssJ^Aw=iWHpj3Iht8M}L#pUl-9lqSBQ1;O zaMs~xqBEBBm$&YN(J3xH_PZRh(a!92#j!Mu96JEq(-({qF;^;jvkg4d&!aXNE#jQ1 zPW-Dl@lhfsdV0@Qy9>{DQ0MOmqY$G?@BzUyECh{nT~M#e>rmx4t^20M;>pkv%~ z4YHGns?p%jeo(lwpk{SI5>H)J=-MWy5?Ki;`Y0?9u~_7;=7|yzfyNPf_sl zd~?4q^V$dRFn#{p1b&X{AVF2*AXd(Qi=SQaYren7Db$~~J=umh)h4kn@AZy|xmMAe zHSn=P0jy_04ntzcsS`U2<5u*J?Ys&<|TOAM@1v<)S2$~uyZ=Jj}y{%Fm69IxZMEz^?fp?n{!xASTW~_3KBlZH;WAK(B;5sxf znZq{Yx8eH-eVr>F{5`W@eH@04fp`Mqad2G*H4MV|iWccoAymK1$Iga6p#nhIWVm$^ zmFetmW4lRP^5Vrev+ws#=E;s(45}W4jX5D@*$-&H3dGs&u_(9?lgp z*C_l&t?y~zqt-)hP$gpaL7c-d?*5JwA6-yzL~sN=wOG~ApF@vlZ7bMr5pqBv;386m zP?s>yP!S;;1h^tpxh_3ko|y20!N0`|{CmK3sKiiW1UMDIHw^Y$^gn@ReO!;@(zIee zn~j>nU(fvoKYi>*o?HAbRq+bi+S`b^vHl>%TwqfMIt%18RDeq0J;Hu`R4v^-om{y0 z$;9*U;|G34Y{Ue-)|KEDxbyPAa>Dlea_61@;LE=~kGeg!#m#@L&%YdzI|aE<)xMzC zqSi;PkA!CvOb(Q(DrU2U>u&0+jPq5-ijhHJdJl+_zLgtANf}w`3iC3)_l_ZWBO&s- zkIoFs&n`#|iv!5sphpRMJTPQ~a1b1q)vFdUs%`}D{qMuv_spZ5f9p@MjWr}ppOCGU za3(?1C&EKEfC4}`X!7k4F%xoLf$nS?7*LRFtvwLKxr=i9N? zo1@*iPZF>Xv^o)a3*;?<84B;CIv=#HIFY&hOuxQuz)`Rmrj(6gAxx%(7hAUhof=uZ zPkqJWaavY4ff4G54#s8+#p~1!>0)b;G4Ih-e*HA08zo{Fa$&bmVCMgFw``J`~b54hTi%A*Vin@i`0M7(?DG;06AAooDztt>=KL8(R3E zmI4&l!NB!>>WQjfm@Dc9L3C8R;(xJ)e+yxcfcL3vgiraypAVe*MZ2ORLVNRazI)&) z9J$pVw70JMqI#nXfipf;OZXXli5m%v6z_gfu4>K?r%GOa#F-qs-M(m7D}1rsz*%vuj^omi?jqmS7P_J4<&55TFC61Ab?juo(7bpyBr3pl zsB0KX+=*jt@OcRT_k@#LJ`>NhzI=dBx}G2$Pqq(h8La$itZcTMZN^ODgguX7%&@Tx zX&A=22b|2d<2I$XvYO_=qq??{J*Mu)m$uxM#mkq_o$tYM+|Tmbv=dfYf5XQ+C-Vz! z0m|27gePqMf{11NX|F(_wRsf>>^_Bkk3E0QEqBWIOF#eb?|w^1YYSSF_$;roJq3I%^7Wa|LlB>r0uChm!87w%U-5&`yGgRlt$frO6%F?At_G9w20UPK1FadrX@rHGyT1q* z{)?gj#dTMt2nR&&VZ%mQovm!L>2@5w)n3ea=~c!bw>{fja2T2{@Y^qc2jUgvf=>l+ zc@{XF5Y7AB7wLk3-Y($7^5ENmX;C^f0Z4t)7tN&GIcLw~$$qesy{|rl+?YyM^{nQe z=l{d*BR6MWx{cYZ7ElpSZHVvC4g4cY>3&E=r2V3;Poo(0X{>6(*>*=n*5PBFm=!oM z(pK^Q{5j~@ItHiez{2X5rBwMTCJrCN!h9=n^RbhobfmQR6VBsn{i0vk{}u%(Uv&|V zusNV@&~%m4rY07*qoM6N<$ Ef^e6^j{pDw literal 0 HcmV?d00001 diff --git a/share/pixmaps/bitcoin128.xpm b/share/pixmaps/bitcoin128.xpm new file mode 100644 index 0000000..d8e41e9 --- /dev/null +++ b/share/pixmaps/bitcoin128.xpm @@ -0,0 +1,384 @@ +/* XPM */ +static char *bitcoin___[] = { +/* columns rows colors chars-per-pixel */ +"128 128 250 2", +" c #845415", +". c #895616", +"X c #84581E", +"o c #8D5C18", +"O c #925A15", +"+ c #925E1C", +"@ c #98621C", +"# c #9E711C", +"$ c #A36E1A", +"% c #A96F1B", +"& c #A6711C", +"* c #AC741C", +"= c #B2741E", +"- c #B37C1E", +"; c #BB7C1E", +": c #835B21", +"> c #8F6125", +", c #956727", +"< c #916B2E", +"1 c #996B2C", +"2 c #B47B23", +"3 c #BD7C20", +"4 c #A17330", +"5 c #AB7D3B", +"6 c #C17F20", +"7 c #B9831F", +"8 c #BB842B", +"9 c #BD8533", +"0 c #B68F3D", +"q c #BE8C3B", +"w c #C4801F", +"e c #FE8C03", +"r c #F38A0F", +"t c #FD8E0A", +"y c #FF910C", +"u c #F78F13", +"i c #F98F10", +"p c #F79016", +"a c #FE9314", +"s c #F6931E", +"d c #FD961B", +"f c #FE991E", +"g c #C58421", +"h c #CD8621", +"j c #C78B21", +"k c #CC8B23", +"l c #C2852B", +"z c #C08B2D", +"x c #D28722", +"c c #D38B25", +"v c #DB8E22", +"b c #D28E2C", +"n c #D49323", +"m c #DC9224", +"M c #DC9B25", +"N c #D4922D", +"B c #DF972A", +"V c #DF982E", +"C c #C18D33", +"Z c #C58E38", +"A c #CB9332", +"S c #C2933C", +"D c #CD9339", +"F c #CC9938", +"G c #D19733", +"H c #DA9230", +"J c #D59935", +"K c #DC9C33", +"L c #DC9E3B", +"P c #E49124", +"I c #EA9426", +"U c #E09D26", +"Y c #EC972B", +"T c #F79625", +"R c #F99524", +"E c #F69A26", +"W c #F89825", +"Q c #F2972B", +"! c #F59A2C", +"~ c #F89B2B", +"^ c #E79D33", +"/ c #EF9D31", +"( c #E19F3A", +") c #EF9D3A", +"_ c #F49C33", +"` c #F99E32", +"' c #F49F39", +"] c #D6A13E", +"[ c #DAA33B", +"{ c #E3A127", +"} c #E7A328", +"| c #EDA32C", +" . c #EDA829", +".. c #FFA325", +"X. c #FFAB25", +"o. c #F3A42B", +"O. c #FFA429", +"+. c #F4A929", +"@. c #FFAC2A", +"#. c #FFB227", +"$. c #FFB32C", +"%. c #FFBA2D", +"&. c #EEA830", +"*. c #F7A334", +"=. c #FAA036", +"-. c #FCAB34", +";. c #F4A13C", +":. c #F9A33B", +">. c #F4A83B", +",. c #FFA83F", +"<. c #FDB432", +"1. c #FFBB33", +"2. c #FFB73A", +"3. c #FDB93E", +"4. c #FFC12F", +"5. c #FFC432", +"6. c #FFC338", +"7. c #D2A043", +"8. c #D8A140", +"9. c #EEA144", +"0. c #E2A840", +"q. c #EDA34B", +"w. c #F4A444", +"e. c #F9A642", +"r. c #FBA945", +"t. c #F3A64B", +"y. c #F4A84E", +"u. c #FBAB4B", +"i. c #EEB041", +"p. c #FABA44", +"a. c #ECA653", +"s. c #EEAC5D", +"d. c #F3AA53", +"f. c #FAAE53", +"g. c #F2AD5A", +"h. c #FBB056", +"j. c #F6B15E", +"k. c #FBB25B", +"l. c #DDAF79", +"z. c #E3A962", +"x. c #EBAE63", +"c. c #E4AC68", +"v. c #EAAF69", +"b. c #EEB065", +"n. c #E7B06C", +"m. c #EEB36B", +"M. c #F5B263", +"N. c #FBB461", +"B. c #E6B274", +"V. c #ECB574", +"C. c #E7B57B", +"Z. c #EAB77C", +"A. c #ECB97C", +"S. c #F2B770", +"D. c #F0BB7A", +"F. c #DBB485", +"G. c #DFB888", +"H. c #E4B984", +"J. c #EDBD82", +"K. c #E5BC8B", +"L. c #EABE8A", +"P. c #F0BE82", +"I. c #E0BF96", +"U. c #EDC089", +"Y. c #F0C28B", +"T. c #E5C194", +"R. c #E9C191", +"E. c #E4C39C", +"W. c #EBC699", +"Q. c #EBC99F", +"!. c #DFC3A0", +"~. c #DDCAAF", +"^. c #CFC7BD", +"/. c #D2CBB6", +"(. c #DBC8B1", +"). c #DBCDBB", +"_. c #E2C6A4", +"`. c #E6C8A5", +"'. c #EACBA5", +"]. c #E1C7A8", +"[. c #E3CBAD", +"{. c #EACCAA", +"}. c #EED1AC", +"|. c #E1CDB3", +" X c #E3CFB8", +".X c #E6D1B6", +"XX c #EBD2B3", +"oX c #E3D1BB", +"OX c #EAD6BB", +"+X c #EBD8BF", +"@X c #D3CDC2", +"#X c #D8CDC2", +"$X c #D0CECA", +"%X c #DDD3C4", +"&X c #D3D2CC", +"*X c #DDD5CB", +"=X c #CCD3D5", +"-X c #C9D7DF", +";X c #D2D4D6", +":X c #DEDAD4", +">X c #DDDCDB", +",X c #E2D4C2", +".N b b b b N >.( C > HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX4 L _ *.@.<.$.X.X...X.X.X.X.X.X...X.@.$.<.@.*./ G , HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX< L -.@.$.X...R R R T T T T W W W W W W T T T T R R W ..X.$.@.*.J HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXD -.%.X.W R T T W W W W W W W W W W W W W W W W W W W W W W T T R W X.%.+.A HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXS -.$.X.R T T W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W T T R X.$.-.C HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXF <.@.f R T W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W T R W #.<.A HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX[ <.X.f T W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W T R X.$.K HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX0.$...R T W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W E W W W W W W W T R ..%.G HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXS 1...R T W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W E ~ ~ E W W W W W W W W W T R X.1.A HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX3.X.d T W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ E W W W W W W W W W W T R @.2.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX7.5.f T W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ~ E W W W W W W W W W W W W T W %.z HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX3.X.s T W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W T R $.<.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX1...R W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ E E W W W W W W W W W W W W W R ..1.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX0 5.f T W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W T W 5.8 HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX8.$.s W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ` ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W T R %.N HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXi.#.R W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ` ` ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W R $.&.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXp.X.R W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ` ` ` ` ` ~ ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W R @.<.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXp.X.R W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ` ` ` ` ` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W R @.<.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXi.X.R W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W E ~ E ~ W R ~ ~ ~ ~ ~ ~ ` ` ` ` ` ` ` ` ` ` ~ ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W R @.| HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX] #.R W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ! s e t d ~ ` ` ` ` ` ` =.=.=.` ` ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W W R %.N HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXq %.R W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W E W E ~ ~ ~ ~ y l.=XI.x.) p a =.` ` =.=.=.=.=.=.` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W R %.2 HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX5 5.d W W W W W W W W W W W W W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ~ ~ t (.jXVXNXuX@XF.W ` =.:.` W =.:.=.=.` ` ` ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W T R 5.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX1.f T W W W W W W W W W W W W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ~ ~ R Q eXDXSXSXDXgX#Xa ` =.=.;.q.W a a R ` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W T W %.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX3...T W W W W W W W W W W W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ~ ~ ~ ` a a.NXSXGXGXAXNXV.a :.:.f c.tX*XE.n.9.R ~ ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W W W T @.@.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXD #.R W W W W W W W W W W W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ` t H.VXSXGXGXDXmXy.f :.:.a I.hXBXCXNXiX^.' W ` ~ ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W W W R %.g HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX5.d W W W W W W W W W W W W W W W W W W W W W W W W W E ~ W ~ ~ ~ ~ ~ ~ ~ ~ ~ ` ` ` i |.CXGXGXGXCX3X~ ` :.:.R %XCXSXGXAXNX>XW ~ ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W W R 5.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX2.W T W W W W W W W W W W W W W W W W W W W W W W W W ~ ~ ~ s t e a W ~ ` ` ` ` ` ` W ! eXFXGXGXSXVX[.d :.:.~ w.uXFXGXGXSXVXW.a ` ` ` ` ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W W T ..@.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHX9 $.R W W W W W W W W W W W W W W W W W W W W W W E W ~ ~ ~ y F./.B.9.T t t a ~ =.` =.a a.hXDXGXGXSXNXA.d :.e.R v.NXSXGXGXSXNXm.a =.` ` ` ~ ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W W W R %.= HXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHX6.d W W W W W W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ W i &XjXNXfX:X].B.q.T t a d e K.VXSXGXGXDXaXd.W e.e.d E.VXSXGXGXDXvXw.W =.` ` ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W W W W W %.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHXK X.T W W W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ~ a ) uXDXSXFXFXCXNXfX:X_.B.q.r .XFXGXGXGXCX3X=.=.e.,.~ %XCXGXGXGXCX1XW ` =.` ` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W W T $.m HXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHXHX5.R W W W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ~ ~ t x.NXSXGXGXGXSXSXDXFXCXNXmX8XcXSXGXGXGXCXW.e :.e.=.t.uXFXGXGXSXVXE.d :.=.=.` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W W W R %.HXHXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHX^ X.T W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ~ ~ ~ ` t T.VXSXGXGXGXGXGXGXGXSXSXFXGXGXGXGXGXGXFX}.9.' W e v.VXSXGXGXSXNXm.d :.=.=.=.` ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W W W W T @.P HXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXHX1.R W W W W W W W W W W W W W W W W E E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ s ;XNXAXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXSXFXNX>X|.V.XXFXGXGXGXFXbXy.~ :.:.=.=.` ` ` ` ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W W W R %.HXHXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHXH X.T W W W W W W W W W W W W W W E E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ` ` R ' $XsXNXVXFXSXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGXSXFXCXCXFXSXGXGXGXCXOXa :.:.:.=.=.=.` ` ` ~ ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W W W T $.c HXHXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXHX1.R W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ` ` ` ` ` ` ~ t.V.`.5XVXFXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXFXXFXGXGXGXGXGXGXGXSXCX{.e.P.'.2XvXNXBXDXSXGXGXGXGXGXGXGXGXGXSXDXjX~.y W =.` ` ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W W W @.HXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHX: 1.R W W W W E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ` ` ` ` ` ` =.=.=.=.=.:.:.:.:.:.:.:.:.e.e.e.~ s.fXDXGXGXGXGXGXGXGXSXNXD.f =.=.,.M.L.oXaXVXDXSXGXGXGXGXGXGXGXGXGXAXVX(.t ~ ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W R %. HXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXl #.T W W W E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ` ` ` ` ` ` =.=.=.=.:.:.:.:.:.:.:.:.:.e.e.e.e.r.W H.NXSXGXGXGXGXGXGXGXDXzXg.r.f.f.f.r.=.=.g.`.fXBXAXGXGXGXGXGXGXGXGXGXAXjXH.t =.` ` ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W T $.6 HXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHX~ ..W W W W E ~ ~ ~ ~ ~ ~ ~ ~ ` ` ` ` ` ` =.=.=.=.=.:.:.:.:.:.:.:.e.e.e.e.e.e.e.r.W |.CXGXGXGXGXGXGXGXGXBX1X,.f.f.f.f.h.h.f.,.~ d.3XVXAXGXGXGXGXGXGXGXGXGXDXsX' f ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W ..~ HXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHX$.R W W W W W E ~ ~ ~ ~ ~ ` ` ` ` ` ` =.=.=.=.=.=.:.:.:.:.:.:.:.e.e.e.e.e.r.r.r.,.w.>XFXGXGXGXGXGXGXGXSXNX`.=.f.h.h.h.h.f.f.f.f.=.~ ,XVXSXGXGXGXGXGXGXGXGXSXVXT.y ` ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W W R $.HXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXX %.T W W W W W E ~ ~ ~ ~ ~ ` ` ` ` =.=.=.=.=.:.:.:.:.:.:.:.:.e.e.e.e.e.e.r.r.r.u.=.x.fXDXGXGXGXGXGXGXGXSXmXA.,.h.h.h.k.k.h.f.f.f.f.:.~ 5XFXGXGXGXGXGXGXGXGXGXCX:XW ~ ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W W T $.. HXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHX8 $.T W W W W W W E ~ ~ ~ ~ ~ ` ` ` =.=.=.:.:.:.:.:.:.:.:.e.e.e.e.e.r.r.r.r.r.u.u.~ K.NXSXGXGXGXGXGXGXGXDXzXj.r.k.k.k.k.k.h.f.f.f.f.f.W V.VXSXGXGXGXGXGXGXGXGXDXuXw.f ` ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W T $.3 HXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXY ..W W W W W W W E ~ ~ ~ ~ ~ ~ ` ` ` =.=.=.:.:.:.:.:.e.e.e.e.e.e.r.r.r.r.u.u.u.u.~ |.CXGXGXGXGXGXGXGXGXBX2Xr.f.k.k.k.k.k.k.h.f.f.f.f.,.d.bXFXGXGXGXGXGXGXGXGXDXfXd.d =.` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W O.P HXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXO.W W W W W W W W W ~ ~ ~ ~ ~ ~ ` ` ` ` =.=.:.:.:.:.e.e.e.e.r.r.r.r.r.r.u.u.u.u.r.w.>XFXGXGXGXGXGXGXGXSXNX'.,.k.k.k.k.k.k.k.h.h.f.f.f.e.y.kXFXGXGXGXGXGXGXGXGXDXfXg.d =.` ` ` ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W O.HXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHX$.R W W W W W W W W E ~ ~ ~ ~ ~ ` ` ` ` =.=.=.:.:.:.:.e.e.r.r.r.r.u.u.u.u.u.u.f.=.b.fXDXGXGXGXGXGXGXGXSXmXJ.r.k.k.k.k.k.k.k.h.h.f.f.f.:.s.mXFXGXGXGXGXGXGXGXGXDXpXy.R =.` ` ` ~ ~ ~ ~ ~ E E W W W W W W W W W W W W W W W W W $.HXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHX1.R W W W W W W W W W ~ ~ ~ ~ ~ ~ ` ` ` =.=.=.:.:.:.:.e.e.e.r.r.u.u.u.u.u.u.u.f.=.K.NXSXGXGXGXGXGXGXGXFXxXM.u.k.k.k.k.k.k.k.k.h.f.f.k.~ K.VXSXGXGXGXGXGXGXGXGXCX5X=.~ =.=.` ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W $.HXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHX+ $.T W W W W W W W W W W ~ ~ ~ ~ ~ ~ ` ` ` =.=.=.:.:.:.:.e.e.e.r.r.u.u.u.u.f.f.f.=.|.CXGXGXGXGXGXGXGXGXFXXFXGXGXGXGXGXGXGXGXFX9XA.b.u.r.r.u.u.h.h.h.u.r.O.w.:XCXSXGXGXGXGXGXGXGXGXSXhXL.a :.=.=.=.` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W T $.* HXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXV X.T W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ` ` ` =.=.=.:.:.:.:.e.e.e.r.r.u.u.u.u.f.,.b.fXFXGXGXGXGXGXGXGXGXSXFXVXpX*X[.R.V.M.g.d.d.g.b.T.pXCXSXGXGXGXGXGXGXGXGXGXDXpXe.~ :.:.=.=.` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W T $.; HXHXHXHXHXHXHX", +"HXHXHXHXHXHXHX| O.T W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ` ` ` ` =.=.:.:.:.:.:.e.e.r.r.u.u.u.u.f.=.K.NXSXGXGXGXGXGXGXGXGXGXGXSXFXFXBXNXmXuX>X3X3XyXmXVXFXSXGXGXGXGXGXGXGXGXGXAXhXE.d :.:.:.=.=.` ` ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W T @.h HXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXc @.T W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ` ` ` ` =.:.:.:.:.:.e.e.e.r.r.u.u.u.u.=.|.BXGXGXGXGXGXGXGXGXGXGXGXGXGXGXSXSXSXFXFXFXFXFXSXSXGXGXGXGXGXGXGXGXGXGXAXNX>X~ =.e.:.:.:.=.` ` ` ` ~ ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W @.h HXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXk @.T W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ` ` ` =.=.:.:.:.:.e.e.e.r.r.r.u.u.r.w.>XFXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXSXZXNXeXe.~ e.:.:.:.:.=.=.=.` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W @.h HXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXc @.T W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ` ` ` =.=.=.:.:.:.:.e.e.e.r.r.u.u.=.x.fXFXGXGXGXGXGXGXGXGXGXFXFXSXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXFXCXfXoX:.~ r.e.:.:.:.:.:.=.` ` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W @.h HXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXc @.T W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ` ` ` ` =.=.:.:.:.:.:.e.e.r.r.r.u.~ K.NXSXGXGXGXGXGXGXGXSXZX6XkXmXNXBXDXAXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGX0X'.S.~ =.u.e.e.e.:.:.:.:.=.=.` ` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W @.h HXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXk @.T W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ` ` ` ` =.=.:.:.:.:.e.e.e.r.r.u.~ |.CXGXGXGXGXGXGXGXGXFX4X,.k.D.Q.,XkXmXNXDXSXSXGXGXGXGXGXGXGXGXGXGXGXXFXGXGXGXGXGXGXGXSXVX{.,.f.u.r.u.N.J.{.5XNXBXAXSXGXGXGXGXGXGXGXGXGXFXMXH.W r.u.r.e.e.e.:.:.:.:.=.=.` ` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W T @.h HXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXo.O.T W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ` ` ` =.=.=.:.:.:.:.e.e.e.r.O.s.fXFXGXGXGXGXGXGXGXSXmXJ.r.N.N.N.N.h.r.r.f.J.1XhXBXAXGXGXGXGXGXGXGXGXSXDXjX!.W e.u.r.e.e.e.:.:.:.:.=.=.` ` ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W T @.g HXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXB X.T W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ` ` ` =.=.:.:.:.:.:.e.e.r.W H.NXSXGXGXGXGXGXGXGXDXuXM.u.k.k.N.N.N.N.N.h.,.e.D.>XNXSXGXGXGXGXGXGXGXGXSXZXjXE.W r.r.e.e.e.:.:.:.:.=.=.=.` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W T $.- HXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXl @.T W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ` ` ` =.=.=.:.:.:.:.e.e.r.W |.CXGXGXGXGXGXGXGXGXBX2Xr.h.k.k.k.k.k.k.k.k.k.h.,.,.|.NXZXGXGXGXGXGXGXGXGXGXZXgXV.~ u.e.e.e.:.:.:.:.:.=.=.` ` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W T $.% HXHXHXHXHXHXHX", +"HXHXHXHXHXHXHX@ $.T W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ` ` ` ` =.=.:.:.:.:.e.:.' >XFXGXGXGXGXGXGXGXSXNX{.,.k.k.k.k.k.k.k.k.k.k.k.k.u.~ `.NXSXGXGXGXGXGXGXGXGXSXCX>X=.e.r.r.e.e.:.:.:.:.:.=.=.` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W T $.. HXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHX%.R W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ` ` ` ` =.=.:.:.:.:.e.~ s.fXFXGXGXGXGXGXGXGXSXNXJ.,.k.k.k.k.k.k.k.k.k.k.h.h.k.u.O.2XCXGXGXGXGXGXGXGXGXGXAXhXV.~ u.r.e.e.e.:.:.:.:.=.=.=.` ` ` ~ ~ ~ ~ ~ W W W W W W W W W W $.HXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHX$.R W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ` ` ` ` ~ :.:.:.:.e.f Z.VXSXGXGXGXGXGXGXGXDXzXM.r.k.k.k.k.k.k.k.h.h.h.h.f.f.k.=.V.NXSXGXGXGXGXGXGXGXGXSXVX`.W r.e.e.e.e.:.:.:.:.=.=.=.` ` ` ~ ~ ~ ~ ~ ~ E W W W W W W W W $.HXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXO.W W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ` ` =.~ Q a a W =.=.t XCXGXGXGXGXGXGXGXGXBX2Xr.f.k.k.k.k.k.k.h.h.h.h.f.f.f.f.r.y.kXFXGXGXGXGXGXGXGXGXGXBX,X~ :.e.e.e.:.:.:.:.:.:.=.=.` ` ` ` ~ ~ ~ ~ ~ E W W W W W W W ~ ..HXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXI O.W W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ` a z.-X_.B.q.! u C.NXSXGXGXGXGXGXGXGXSXNX'.=.h.h.k.k.k.h.h.f.f.f.f.f.f.f.f.r.w.5XFXGXGXGXGXGXGXGXGXGXCX2X=.:.e.:.:.:.:.:.:.:.:.=.=.=.` ` ` ` ~ ~ ~ ~ ~ E W W W W W W O.P HXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXk @.T W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ~ t ).jXVXNXaX2X1XBXDXSXGXGXGXGXGXGXGXSXmXA.:.h.h.h.h.h.f.f.f.f.f.f.f.f.f.f.,.d.vXFXGXGXGXGXGXGXGXGXGXCX1X` =.:.:.:.:.:.:.=.=.=.=.=.=.` ` ` ` ~ ~ ~ ~ ~ ~ W W W W W T $.; HXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXo %.T W W W W W W W W W W W W W W W W W W W W ~ ~ ~ ` y q.fXZXSXSXFXFXFXSXSXGXGXGXGXGXGXGXGXFXxXj.r.f.h.h.h.f.f.f.f.f.f.f.f.u.u.f.W B.NXSXGXGXGXGXGXGXGXGXSXBXoXW :.:.:.:.:.:.=.=.=.=.=.` ` ` ` ` ` ~ ~ ~ ~ ~ ~ W W W W W W %. HXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHX$.R W W W W W W W W W W W W W W W W W W W E ~ ~ ~ ` e !.CXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXFX+Xd ,.f.h.h.h.f.f.f.f.f.f.u.u.u.f.,.T :XFXGXGXGXGXGXGXGXGXGXSXNXE.f :.:.:.:.:.=.=.=.=.` ` ` ` ` ` ~ ~ ~ ~ ~ ~ ~ ~ ~ W W W W R $.HXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHX~ ..W W W W W W W W W W W W W W W W W W W W E ~ ~ a _ aXFXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXFX7XV.s.:.=.:.,.u.f.f.f.f.u.u.u.r.~ s ~.VXSXGXGXGXGXGXGXGXGXGXAXhXV.d :.:.=.=.=.=.=.` ` ` ` ` ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ W W W W O.E HXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXg $.T W W W W W W W W W W W W W W W W W W W E ~ ~ e G.hXAXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXSXFXVXpX*X_.Z.x.t.:.` ~ ~ ~ ~ ~ ' x.*XVXSXGXGXGXGXGXGXGXGXGXGXDXuXw.W :.=.=.=.=.` ` ` ` ` ` ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ W W W W W W T $.; HXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHX %.R W W W W W W W W W W W W W W W W W W W W ~ d T qXgXBXFXSXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXSXFXFXBXNXaX>X,X[._.T.T.E.|.:XNXCXSXGXGXGXGXGXGXGXGXGXGXSXVX Xd =.=.=.=.` ` ` ` ` ` ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ E W W W W W W R %.HXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHX@.W W W W W W W W W W W W W W W W W W W W W ~ R ` s.H.oXkXNXNXCXFXSXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGXSXSXDXFXCXCXBXVXVXBXCXFXSXSXGXGXGXGXGXGXGXGXGXGXGXAXhXm.a :.` =.` ` ` ` ` ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ W W W W W W W W W W @.HXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXx @.T W W W W W W W W W W W W W W W W W W W W ~ ~ y t a _ g.L.oXkXhXVXCXFXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXSXGXSXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXSXBX:Xf ~ ` ` ` ` ` ` ` ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ E W W W W W W W W W T $.h HXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHX%.R W W W W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ d a t a ' s.R.oXnXDXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXZXhXg.y =.` ` ` ` ` ~ ~ ~ ~ ~ ~ ~ ~ ~ E ~ E W W W W W W W W W W R %.HXHXHXHXHXHXHXHXHXHXHX", +"HXHXHXHXHXHXHXHXHXHXHXO.~ W W W W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ` ` ~ W a a d ! 6GE^AcefzH-Q68Na_`LCJIqYh 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 meupimVz+(n82@9i{?7p6RY_$%TRr9g0000 c #D6882B", +", c #DE8C2A", +"< c #CE8C3C", +"1 c #D28934", +"2 c #D98E32", +"3 c #D28E3C", +"4 c #DF9132", +"5 c #D6903E", +"6 c #DD933B", +"7 c #E58C22", +"8 c #E98F23", +"9 c #E38F2B", +"0 c #E88F28", +"q c #ED9124", +"w c #E6922D", +"e c #EB942B", +"r c #EF982F", +"t c #F59624", +"y c #F89723", +"u c #F79826", +"i c #F89825", +"p c #F1972A", +"a c #F59A2C", +"s c #F89B2B", +"d c #E59534", +"f c #EA9632", +"g c #EE9933", +"h c #E3963B", +"j c #E6993D", +"k c #EC9C3B", +"l c #F49C33", +"z c #F99E32", +"x c #F29E3A", +"c c #F7A037", +"v c #F9A036", +"b c #F5A13C", +"n c #F9A33B", +"m c #CE9147", +"M c #D29245", +"N c #DC9641", +"B c #DD9846", +"V c #D2954B", +"C c #DC9A4B", +"Z c #E59C44", +"A c #EA9E43", +"S c #E39E4B", +"D c #E89F49", +"F c #F09F40", +"G c #EDA145", +"H c #E6A14D", +"J c #EBA34B", +"K c #F4A443", +"L c #F9A642", +"P c #F7A847", +"I c #FAA846", +"U c #F3A64A", +"Y c #F8A748", +"T c #F5A94D", +"R c #FAAA4B", +"E c #E6A454", +"W c #EBA552", +"Q c #EDA856", +"! c #E4A55B", +"~ c #E8A75B", +"^ c #E7A95E", +"/ c #EBA95B", +"( c #F0A751", +") c #F4AB53", +"_ c #FAAE53", +"` c #F4AE5A", +"' c #F8AF59", +"] c #FAB057", +"[ c #F6B15E", +"{ c #FAB25B", +"} c #DFAD6F", +"| c #DCAE77", +" . c #DFB27D", +".. c #E5AA64", +"X. c #E8AB61", +"o. c #E5AE6C", +"O. c #E6B06F", +"+. c #ECB16C", +"@. c #F5B365", +"#. c #FBB562", +"$. c #FBB867", +"%. c #F5B66B", +"&. c #FAB768", +"*. c #F4B86F", +"=. c #FBB96A", +"-. c #E1AE71", +";. c #E5B174", +":. c #EBB573", +">. c #EFB977", +",. c #E5B47A", +"<. c #EEBA7B", +"1. c #F3B770", +"2. c #F3B974", +"3. c #FBBC72", +"4. c #F3BC7B", +"5. c #F8BF7A", +"6. c #FAC079", +"7. c #DCB382", +"8. c #DFBB8F", +"9. c #DABB96", +"0. c #DBBD99", +"q. c #E2B682", +"w. c #E4B985", +"e. c #ECBD84", +"r. c #E3BB8B", +"t. c #EABF8C", +"y. c #F1BE83", +"u. c #E2BE92", +"i. c #D3BDA2", +"p. c #DEC09C", +"a. c #EEC28D", +"s. c #F4C286", +"d. c #F8C282", +"f. c #F3C48B", +"g. c #E7C297", +"h. c #ECC393", +"j. c #E2C29D", +"k. c #EAC69B", +"l. c #ECC89F", +"z. c #F1C694", +"x. c #F2C897", +"c. c #F1CA9B", +"v. c #DBC2A3", +"b. c #D6C2AB", +"n. c #DDC7AD", +"m. c #DEC9AF", +"M. c #D3C4B3", +"N. c #DDCAB3", +"B. c #D2C7B9", +"V. c #D6C9BA", +"C. c #DDCEBB", +"Z. c #DFD0BE", +"A. c #E2C5A2", +"S. c #E8C7A0", +"D. c #E6C9A5", +"F. c #EBCBA4", +"G. c #E2C7A8", +"H. c #E3CAAC", +"J. c #EBCDA9", +"K. c #EFD2AF", +"L. c #F3D1A7", +"P. c #F1D1A9", +"I. c #E4CEB3", +"U. c #E8CFB1", +"Y. c #E1CFBA", +"T. c #E6D0B6", +"R. c #E9D1B4", +"E. c #E4D2BC", +"W. c #EAD4BA", +"Q. c #F4D5B0", +"!. c #F4D9B9", +"~. c #CDCDCD", +"^. c #D5CCC3", +"/. c #D4CFCA", +"(. c #DED2C3", +"). c #D3D1CE", +"_. c #DED6CC", +"`. c #D5D5D5", +"'. c #DBD7D1", +"]. c #DEDAD4", +"[. c #DDDDDC", +"{. c #E3D5C3", +"}. c #E9D7C1", +"|. c #EBD9C4", +" X c #E1D6CA", +".X c #E3D9CD", +"XX c #EADDCD", +"oX c #E1DBD4", +"OX c #E8DFD4", +"+X c #E1DEDB", +"@X c #EDE3D7", +"#X c #E3E1DE", +"$X c #E8E3DC", +"%X c #F6E5D2", +"&X c #F4EBDF", +"*X c #E4E4E4", +"=X c #ECE7E2", +"-X c #EDE9E4", +";X c #ECECEC", +":X c #F0EBE7", +">X c #F4F4F4", +",X c #FEFEFE", +"X>X>X>X;X;X*X[.`.r.n n z v v v v c x l p l x x c c v v v v z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u i u X>X>X>X>X>X;X*X[.`.@.n n v v v v v c g E | S k f r l l l z z z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u i e X>X,X,X,X,X>X>X;X*X_.R n v v v v v v x e 0.`.`.V.p.;.H f e e p l l z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u y , X>X,X,X,X,X>X>X;X*XI.L n v v v v n n x g V.`.[.[.[.[.[.(.p.;.S f r l z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u u y X,X,X,X,X,X>X>X;X*Xa.n n v v v n n n l A `.[.*X*X-X-X*X*X*X[.`.V.9.K z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u i u X,X,X,X,X,X>X>X-X[.%.n n n n n n n b p o.[.*X;X;X;X>X;X;X*X*X[.`.~.T z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u y 0 X>X,X,X,X,X,X>X;X*XoXR L n n n n n n b g u.*X-X;X>X>X>X>X>X;X*X*X[.N.L n z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u y X>X,X,X,X,X>X>X;X*XI.L L n n n n n n b g C.*X;X>X>X,X,X,X>X>X;X*X[.g.L n z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u i u X,X,X,X,X,X>X>X;X*Xh.L L n n n n n n l G [.*X;X>X,X,X,X,X>X>X;X*X[.2.n n z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u y w X,X,X,X,X,X>X>X-X[.%.L n n n n n n b l o.*X;X>X>X,X,X,X,X,X>X;X*X]._ n v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i y X>X,X,X,X,X,X>X;X*XoXR L n n n n n n b g j.*X;X>X>X,X,X,X,X,X>X;X*XE.I n v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u y t X>X,X,X,X,X>X>X;X*XT.I L n n n n n n b k Z.*X;X>X,X,X,X,X,X>X>X;X*Xl.L n v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u y ; X,X,X,X,X,X>X>X;X*Xh.L L n n n n L L x G [.*X;X>X,X,X,X,X,X>X>X;X*X4.n n v v v z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u i u X>X,X,X,X,X,X>X>X-X[.%.L L n n n L L L l ;.*X;X>X>X,X,X,X,X,X>X;X*X[._ L n v v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u y q X>X>X>X;X;X;X;X*X*X*X*X].N.q.! d e e r p q ,.-X;X>X>X,X,X,X,X,X>X;X*XoX_ I L n L L L L K g j.*X;X>X>X,X,X,X,X,X>X;X*XE.Y L n v v v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t X>X>X>X>X>X>X>X>X;X;X;X;X*X*X*X*X_.I.r.o.Z w D.;X>X>X,X,X,X,X,X,X>X;X*XW.R I L L L L L L K k Y.*X;X>X,X,X,X,X,X>X>X;X*Xl.L L n v v v v v z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u y q X>X,X,X,X,X,X,X>X>X>X>X>X>X;X;X;X;X*X*X*X*X$X}.=X>X>X>X,X,X,X,X,X,X>X;X*Xx.I I L L L L L L x J [.*X;X>X,X,X,X,X,X>X>X;X*X4.L n n v v v v v z z z z z z s s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X;X;X;X;X;X>X>X>X>X,X,X,X,X,X,X,X>X>X;X&.L L L L L L L L x ;.*X;X>X>X,X,X,X,X,X>X;X*X[.' L n n n v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u y t X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X>X>X@Xb l x x K L L L L k j.*X;X>X>X,X,X,X,X,X>X;X*XE.R L n n n n v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X:XW.g.;.H k k k b F k {.;X>X>X,X,X,X,X,X>X>X;X*XS.I L n n n n v v v v v z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i y t X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X;X*X+XE.j.,.~ j A =X;X>X>X,X,X,X,X,X>X>X;X*X4.I L n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X;X;X*X*X*X*XXX}.;X>X>X,X,X,X,X,X,X>X>X;X#X{ I n n n n n n v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i y t X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X;X;X;X;X;X>X>X>X,X,X,X,X,X,X,X>X>X;X|.R I n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X>X>X>X,X,X,X,X,X,X,X,X>X>X;XF.L L n n n n n n n v v v v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i y q X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X,X,X,X,X,X,X,X,X,X,X,X>X>X;X@.a x b b n n n n n v v v v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X|.e.G g l c b n n n n v v v v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 0 X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X-X+XG...k g l b n n n v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i y t X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X;X*X*X(.w.A g l c c v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t = X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X-X*X*X'.u.A r l x c v v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X;X;X*X*X].u.k r l c v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X;X;X*X*X_.q.g p l z v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 7 X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X;X;X*X[.C.W p l c v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i y t X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X;X;X;X;X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X;X*X*X[.w.r a l z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X-X-X-X*X*X-X;X;X;X;X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X;X*X[.H.g a z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 0 X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;Xf.3.x.R..X+X*X*X*X*X;X;X;X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X*X*X(.k p z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i y t X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X$.{ { { $.3.f.F.{.[.*X*X*X;X;X;X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X-X*X_.W p z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t @ X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X-X|.{ ] _ ] { { { { $.3.h.R..X*X*X*X;X;X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X-X*X'.k p z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 0 X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XJ._ ] _ _ _ _ ] { { { #.$.$.f.T.oX*X*X;X;X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X-X*X_.l a z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t t X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xs._ _ _ _ _ _ _ _ _ ] { { { { { =.l..X*X*X;X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X*XH.t z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X+X&.] _ _ _ _ _ _ _ _ _ _ _ _ ] { { { #.k.oX*X-X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X[.:.t z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t ; X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X{.{ { _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ] _ { J.*X*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X'.l s z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XF.{ { _ _ _ _ _ _ _ _ ] _ _ _ _ _ _ _ _ _ _ _ y.oX*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.t.u z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t t X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xs.{ ] _ _ _ _ _ ] ] ] ] ] _ _ _ _ _ _ _ _ _ _ _ ' .X*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X'.z z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X].&.{ ] _ _ _ ] ] ] ] ] ] ] _ _ _ _ _ _ _ _ _ _ _ R R oX*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.:.u z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t ; X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X{.#.{ _ _ _ _ ] ] ] ] ] ] ] ] _ _ _ _ _ _ _ _ _ _ _ I @.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XD.s z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XF.{ { _ ' ] ] ] ] ] { { { ] ] ] _ _ _ _ _ _ _ _ R R _ n k.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X_.n z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t t X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xs.{ { ] ] ] ] { { { { { { ] ] ] _ _ _ _ _ _ _ _ R R R I T +X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.T z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.=.{ { ] ] ] { { { { { { { ] ] ] _ _ _ _ _ _ _ _ _ R R R K D.*X;X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.%.z z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t = X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X{.#.{ ] ] { { { { { { { { { { ] ] ] _ _ _ _ _ _ _ _ R R R K e.*X;X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X-X[.<.v v z z z z z z z s s s s s s s s s s s s u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 7 X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XJ.{ { { { { { { { { { { { { { { ] ] ] _ _ _ _ _ _ _ _ R R K +.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X<.n v v z z z z z z s s s s s s s s s s s s u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xs.#.{ { { { { { { { { { { { { { ] ] ] ] _ _ _ _ _ _ _ _ R U / *X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xe.n n v v z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t t X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.=.#.{ { { { { { { { { { { { { { ] ] ] ' _ _ _ _ _ _ _ _ R K +.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X<.n n v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u t X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X{.$.#.{ { { { { { { { { { { { { { { ] ] ] ] _ _ _ _ _ _ _ T K ,.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X-X[.>.n n v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t @ X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XJ.#.#.{ { { { { { { { { { { { { { { ] ] ] ] _ _ _ _ _ _ _ T G j.*X;X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.%.n n v v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t = X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xf.#.{ { { { { { { { { { { { { { { { { ] ] ] _ _ _ _ _ _ _ T J X-X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X]._ L n v v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t ; X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X#X3.#.{ { { { { { { { { { { { { { { { { { ] ] ] _ _ _ _ _ ) G ..*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X{.R L n v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 7 X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X|.=.#.{ { { { { { { { { { { { { { { { { { { ] ] ' _ _ _ _ T k E.*X;X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XH.L L n v v v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;Xc.] { { { { { { { { #.{ { { { { { { { { { { ] ] ] _ _ _ ( A w.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X-X[.a.L n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;Xx.( Q ( ) ` [ [ { #.#.#.{ { { { { { { { { { { ] ] _ ) T D o.*X;X;X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.[ L n n n v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u q X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;XOXI.u.O./ Q Q ` ` [ [ [ { { { { { { { { { ] ' ) ( J H r.*X-X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XE.R I n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u q X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X;X*X*X_.H.r.;.X./ Q Q ) ) ` ` ` ` ` ) ) ( J H W ,.{.*X;X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.y.I L n n n n v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u t X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X;X;X*X*X*X*X].(.H.u.q.;.^ ^ ~ ~ E E ~ o.r.G. X*X*X;X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X_._ Y L n n n n n v v v z z z z z z z s s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i u t @ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X;X;X;X;X*X*X*X*X*X*X[.]..X X XoX+X*X*X*X-X;X;X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X[.f.R I n n n n n n v v v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t = X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X;X;X;X;X;X;X-X-X*X*X*X-X;X;X;X;X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X;X*X X_ R L n n n n n n v v v v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t = X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X>X>X>X>X>X;X;X;X>X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X[.%.R I L n n n n n n n v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i t - X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X>X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X*X[.k.R R L n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i t ; X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X;X*X[.l.] _ I L L n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i t ; X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X-X*X[.l.{ _ Y L L L n n n n n n n n v v v v v v z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i t ; X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X*X*X].h.{ _ R L L L L L n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i t ; X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X;X*X[.T.3.{ ] R I L L L L L n n n n n n n n n v v v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i t ; X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X;X;X;X;X;X;X;X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X*XW.s.#.{ _ R I I L L L L L L n n n n n n n n v v v v z z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i t ; X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X-XQ.|.OX*X*X*X*X*X;X;X;X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X&X!.L.d.#.{ ] R R I I I L L L L L L n n n n n n n n n v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i t ; X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;XXX3.3.3.s.c.R..X[.*X*X*X-X;X;X;X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X%X{ L R _ _ R R R I I I I L L L L L L n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i t ; X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X-XK.&.=.=.&.=.3.3.d.c.R..X[.*X*X*X;X;X;X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;XJ.J K Y R R Y I I I I L L L L L L n n n n n n n n n v v v v z z z z z z z s s s s s s s s s s s s u u u i i i i i i i i i i i i i i i i i i i i i i i i t ; X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xf.$.#.#.#.#.&.&.=.=.3.3.f.F.}.+X*X*X*X;X;X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;XOX:.K U R R I I I I I L L L L L L n n n n n n n n v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i t ; X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X-X+X3.$.#.{ { #.#.#.#.$.$.&.=.=.3.6.c.W.+X*X*X;X;X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X*Xj.K K R R I I I I I L L L L L n n n n n n n n v v v v v v z z z z z z s s s s s s s s s s s s u u u i i i i i i i i i i i i i i i i i i i i i i u t = X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X{.&.#.{ { { { #.#.#.#.#.#.#.#.$.$.=.=.5.J..X*X*X;X;X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X*XH.K K R R I I I I L L L L L L n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i u t = X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XJ.#.#.{ { { { #.#.#.#.#.#.#.#.{ #.#.$.$.$.=.z.{.*X*X;X;X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X*X*XC.U K R I I I I I L L L L L L n n n n n n n n v v v v v z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i u q * s u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u u u u s s s s s s s s s s s z z z z z z z v v v v v n n n n n n n n L L L L L L I I K A Z.*X;X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xf.#.#.{ { { { { #.#.#.#.{ { { { { { { #.#.#.#.$.z.{.*X*X;X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X*XC.b K Y I I I I L L L L L L n n n n n n n n n v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i u q + X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.3.#.{ { { { { { #.#.#.{ { { { { { { { { { { #.#.#.$.F.+X*X;X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X[.H.b P I I I I I L L L L L n n n n n n n n n v v v v v z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i t q X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X{.$.#.{ { { { { { { { { { { { { { { { { { { { { { { #.{ 2.{.*X;X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X[.e.b Y I I I I L L L L L L n n n n n n n n v v v v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i t q X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XJ.#.#.{ { { { { { { { { { { { { { { { { { { { { { { { { { { U.*X;X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X].T L Y I I I I L L L L L L n n n n n n n n v v v v v z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i t q X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xf.#.{ { { { { { { { { { { { { { { { { { { { { { { { { ] { { _ R.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X-X*XD.L R I I I I L L L L L L n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i t q X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.=.#.{ { { { { { { { { { { { { { { { { { { { { { ] ] ] ] ] { ' R T.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X[.` L I I I I I L L L L L L n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i t 8 X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X{.$.#.{ { { { { { { { { { { { { { { { { { { { ] ] ] ] ] ] ] _ ] _ R oX*X;X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X-X[.g.n I Y I I I I L L L L L n n n n n n n n n v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i t 7 X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XJ.{ { { { { { { { { { { { { { { { { { { { { ] ] ] ] ] _ _ _ _ _ ] Y <.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X(.I I I I I I L L L L L L L n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i u t ; X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xf.{ { { { { { { { { { { { { { { { { { { ] ] ] ] ] ] _ _ _ _ _ _ _ _ T .X-X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.[ L I I I L L L L L L L L n n n n n n n n n v v v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i u q = X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.=.{ { ] { { { { { { { { { { { { { { ] ] ] ] ] ] _ _ _ _ _ _ _ _ _ _ P g.*X;X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X-X[.e.n I L L L L L L L L L L n n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i t q X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X{.#.{ ] ] { { { { { { { { { { { ] ] ] ] ] ] ] ] _ _ _ _ _ _ _ _ _ _ _ Y +.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xg.L I L L L L L L L L L n n n n n n n n n n n v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i t q X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XJ.{ { ] ] ] { { { { { { { { { ] ] ] ] ] ' _ _ _ _ _ _ _ _ _ _ _ _ _ _ T Q #X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XD.I I L L L L L L L n n n n n n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i t q X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xf.{ { ] ] ] ] { { { { { { { ] ] ] ] ] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ Y W +X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XI.I I L L L L L n n n n n n n n n n n n n n n v v v v v v z z z z z z z s s s s s s s s s s u u u u u i i i i i i i i i i i i t 7 X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.=.{ ] ] ] ] ] { { { { { ] ] ] ] ] ' _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ R R T W +X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XE.I L L L L n n n n n n n n n n n n n n v v v v v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i u q ; X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X{.#.{ _ _ ] ] ] ] { ] ] ] ] ] ] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ R R R R K X.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XE.I L n n n n n n n n n n n n n n n n v v v v v v v v v z z z z z z z z s s s s s s s s s s s s u u u i i i i i i i i i i i t q @ X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XJ.{ { _ _ _ ] ] ] ] ] ] ] ] ' _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ R R R R R x q.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XD.R L n n n n n n n n n n n n n n n v v v v v v v v v z z z z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i t q X>X>X>X>X>X>X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xs.{ ] _ _ _ ] ] ] ] ] ] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ R R R R R R R T k G.*X;X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XS.I L n n n n n n n n n n n n n v v v v v v v v v z z z z z z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i t q X>X>X,X,X>X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X#X&.{ _ _ _ _ _ ] ] ] ] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ R R R R R R R R K A oX;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xh.L L n n n n n n n n n n v v v v v v v v v z z z z z z z z z z z z z s s s s s s s s s s s s s s s u u u i i i i i i i i i t 7 X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X|.{ ] _ _ _ _ _ ] ] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ R R R R R R R R R U k u.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X-X[.2.L L n n n n n n n n n v v v v v v v v z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s u u u u i i i i i i i u q = X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;Xc.R _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ R R R R R R R R R R T k D +X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.' L n n n n n n n n v v v v v v v z v v z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s u u u u i i i i i i i t q X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;Xf.K G G U ) ) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ R R R R R R R R R R R U A j {.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X{.R L n n n n n n v v v v v v v v v z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s s u u u u i i i i i i t q X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X-XXXH.w.X.J J J T ) ) ) _ _ _ _ _ _ _ _ R R R R R R R R R R R Y K k D Y.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XJ.L L n n n n v v v v v v v v z z z z z z z z z z z z z s z s s s s s s s s s s s s s s s s s s s s u u u u u u u i i i i u t 7 X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X-X*X#X(.A.q...H J J U U T T T T R R R R R R R R R Y Y U K k A ;..X*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X-X[.4.L n n n v v v v v v v v z z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i t q * X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X;X;X*X*X*X[.(.H.u.,.^ J D G A J K K U U U U K k k k A E w.Y.*X*X;X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X]._ L n v v v v v v v v v v z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i t q X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X;X;X;X-X*X*X*X*X[._.N.A.u.;.;...E E E E ..;.q.j.I.+X*X*X;X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XH.I L n v v v v v v v v z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i t 8 X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X;X;X;X;X-X*X*X*X*X*X*X*X+X+X+X+X*X*X*X*X*X;X;X;X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.1.L n v v v v v v z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i u q ; X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X>X>X;X;X;X;X;X;X;X;X;X;X;X;X;X;X;X;X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X XR L n v v v v v z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i i i i t q X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X>X>X>X>X>X>X>X>X>X>X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X-X[.a.L n v v v z z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i i i i i t 8 X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X]._ L n v z z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u i i i i i i i i i i i i i i i u q ; X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.a.L n v z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s s s u u u u u u u i i i i i i i i i i i i i i i i t q X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X_.R L n z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s s u u u u u u u i i i i i i i i i i i i i i i i i i t 8 X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X[.2.L n z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s u s u u u u u u u u i i i i i i i i i i i i i i i i i i t q = X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X[.D.L L v z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i i i i i i i i i i i i t q X>X,X,X,X,X,X,X,X,X>X>X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X-X*X XR L n z z z z z z z z s s s s s s s s s s s s s s s s s s s s s s u u u u u u u i i i i i i i i i i i i i i i i i i i i i i u q 7 X>X,X,X,X,X,X,X,X>X>X>X>X;X;X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X-X*X'._ I n z z z z z z z s s s s s s s s s s s s s s s s s s s s s s u u u u u u i u u i i i i i i i i i i i i i i i i i i i i i i t q o X>X,X,X,X,X,X,X>X>X>X=X;X-X-X-X;X;X;X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X-X*X].%.L L z z z z z z s s s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i t 8 X>X,X,X,X,X,X>X>X;X=X=.5.c.W.oX*X*X-X;X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X;X*X*X_.%.I L z z z z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i t q - X>X,X,X,X,X,X>X>X;X|._ _ _ { #.4.l.}.$X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X;X;X*X[.E.{ I L v z z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u i u i i i i i i i i i i i i i i i i i i i i i i i i i i i t 8 X,X,X,X,X,X>X>X;X*XF.R R R R _ _ { { { 4.-X>X>X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X>X;X;X*X*X[.k._ I n z z z s s s s s s s s s s s s s s s s s s s s s s s u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q 7 X,X,X,X,X,X>X>X;X*X4.R I I I I R R R b U -X>X>X,X,X,X,X,X,X,X,X>X>X>X>X>X;X>X>X>X>X>X>X>X>X>X>X>X>X>X>X>X>X>X>X>X>X;X;X;X-X*X*X[.T.*.R L n z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q X>X,X,X,X,X,X>X;X*X+X] R I L I I I I P x t.;X>X>X,X,X,X,X,X,X,X>X>X;X;X;X;X-X-X-X;X;X;X;X;X;X;X;X;X;X;X;X;X;X;X;X;X;X*X*X*X[.].U.4.R I L v z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u q 7 X>X,X,X,X,X>X>X;X*XE.R Y L L I I I I K k I.-X;X>X,X,X,X,X,X,X>X>X;X|.f.J.W..X[.[.*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X[._.I.h.#.R L L n z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u i u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q . X,X,X,X,X,X>X>X;X*Xl.I I L L L I I P K A oX-X>X>X,X,X,X,X,X>X>X;X;Xs.R _ _ { #.4.y.S.l.T.{.{. XoXoXoXoX].oX{.{.E.k.a.2.{ _ I L n v z z s s s s s s s s s s s s s s s s s s s s s s u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u q 7 X,X,X,X,X,X>X>X;X[.2.I I L L L L I L x ^ *X;X>X>X,X,X,X,X,X>X>X;X*X#.I I I I Y I R I _ R _ ] { { [ { { { { ] _ R R I I L n n v z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q . X>X,X,X,X,X,X>X;X*X]._ Y L L L L L I L k r.*X;X>X>X,X,X,X,X,X>X;X-X.XR L n n n n n n L L L L L L L n L n n n L n n n c v z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u q 7 X>X,X,X,X,X>X>X;X*XT.R I L L L L L L K k H.*X;X>X>X,X,X,X,X>X>X;X*XJ.L L n n n n n v v v v v v v v v z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 8 X,X,X,X,X,X>X>X;X*Xk.I I n L L L L L b k ].*X;X>X,X,X,X,X,X>X>X;X*Xy.L n n n n v v v v v v v v v z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q 7 X,X,X,X,X,X>X>X-X[.2.L L n L L L L L l ^ [.-X>X>X,X,X,X,X,X>X;X*X[.[ L n n n v v v v v v v z z v z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 8 X,X,X,X,X,X>X;X*X]._ L L n L L L L K g r.*X;X>X>X,X,X,X,X,X>X;X*X{.R L n v v v v v v v z z z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q - X,X,X,X,X>X>X;X*XE.I L n n n L L L b g H.*X;X>X>X,X,X,X,X>X>X;X*XF.L L v v v v v v v z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u q 8 X>X>X,X>X>X>X;X*Xk.L L n n n n L L x k _.*X;X>X,X,X,X,X,X>X>X;X*Xy.n n v v v v v v z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q # X>X>X>X>X;X*X[.2.L L n n n n n b l ~ [.-X>X>X,X,X,X,X,X>X;X*X[.' L n v v v z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s s s u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q 7 X>X,X,X,X,X,X>X;X*X{.I n c v v z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 8 X>X,X,X,X,X>X>X;X*XF.L n v z z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q - X>X,X,X,X,X>X>X;X*X4.n n z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q 7 .L n n n n n n b l E [.*X;X>X>X,X,X,X>X>X;X*X[.' n v z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 8 X>X>X>X>X>X;X*X{.I n z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q - X>X>X;X;X*X[.S.n n z z z z z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q 7 ?Y_k3o~`OG;JBOMMl zel`FAIP`S2OewEI>kpPqlxKpk?>yy&)kD{Y008XV>kkItYRV1(U^7H(YMPk1;D|Va z3(iAGPg7IK1CMh;yQ2WWrz_RW+d6J>yK3(uYKx^t$IRVC_YeRioX1kX)h1)>B>*0_ zmFw`cGZAc-JiOadbS-K^+t_qdBl^?#mpZGTkFbrjIH52l_%{1~fbaC=1Y?YKYo1pGZ{XGKV-ngr+)(Yj@fiTjFp_)%iFedi z7z%N~2Ee0DavxXS`>&_VO+>8#p^HEth--wMA=VVAmo%^E0qS=Ecg%{BY(P8!xRd-f zKtL`BFfp&Ex)JEeiF?ZjbY$$#VF98TfTaC8`!*bY2sl`Tm`ZG@ss_^Y4ud3_stOo_ zZ5~MLYzVH{2zW(X$8I_700bv#NHhXM3L5}T0q!P7Ap^#AXlrF(j=jg3Qe6h1sv)5% z@o1|IPiewSe($-NzW%-a)$VsS;Cu(RIg-X%Mk zZq{^LwwJz3ktBtc!vbm7P3o2R>}F$UpA>PtUI875RQ)RR9@iHc_=)0O>-L=iPF+2m zlwXy3di4|>>s4SHJ<`CWA%$HW5nii16SNwAprv2-?glzQE6Z5!mW1D@|Gi zfVj$~n8V@>KvQDY002Dtu;aw7cs_$#768yn4>@>8oq6*Up1dl~lqY*0Rq<_rZ_+xv zd0&sh$rdtnjV9HTox%`ALL(M%w?uI#1YETPc|o@1bA_4up}IU24P?R16bz<3_T zs1t{mW4r5xq|dLMf21Q7A5wIT@%0AJ!)3Pw`Onv;6IzI}TkL`_XSlWS-xE*?evpyf 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 OQ1rBov c #F79827", +", c #F89825", +"< c #F0962B", +"1 c #F59A2D", +"2 c #F99B2B", +"3 c #EC9732", +"4 c #EC9A37", +"5 c #E2963B", +"6 c #E6983A", +"7 c #EC9C3B", +"8 c #F69D33", +"9 c #F99E32", +"0 c #F49E3A", +"q c #F9A036", +"w c #F6A13C", +"e c #F9A33B", +"r c #D79341", +"t c #DC9641", +"y c #E39A43", +"u c #EA9D42", +"i c #EFA041", +"p c #EDA34B", +"a c #F5A443", +"s c #F9A643", +"d c #FAA846", +"f c #F2A64C", +"g c #F9AA4B", +"h c #E5A251", +"j c #ECA756", +"k c #EBA758", +"l c #FAAF57", +"z c #FBB057", +"x c #FBB25B", +"c c #DFB179", +"v c #E4AA65", +"b c #EBAE64", +"n c #E9AF69", +"m c #FBB665", +"M c #F1B46A", +"N c #F8B96D", +"B c #E5B071", +"V c #EBB777", +"C c #EEB877", +"Z c #E7B478", +"A c #EBB97D", +"S c #F0B671", +"D c #F2B871", +"F c #EFBC80", +"G c #E6BD8D", +"H c #EDBF88", +"J c #E6BF90", +"K c #F1C187", +"L c #F1C288", +"P c #E5C093", +"I c #EEC493", +"U c #E1C19B", +"Y c #E9C69C", +"T c #ECC89D", +"R c #F1C897", +"E c #DFC5A4", +"W c #DBCBB8", +"Q c #E2C7A7", +"! c #EBCBA6", +"~ c #E6CBAB", +"^ c #E9D2B7", +"/ c #E5D1B9", +"( c #EBD6BD", +") c #EFD9BE", +"_ c #DDD0C2", +"` c #DCD7D2", +"' c #DEDEDE", +"] c #ECDAC5", +"[ c #EDDECB", +"{ c #E9E0D5", +"} c #E7E0D9", +"| c #E9E2DB", +" . c #EFE8DF", +".. c #E5E5E5", +"X. c #EBE7E2", +"o. c #EFEAE6", +"O. c #ECECEC", +"+. c #F2ECE6", +"@. c #F1F0EE", +"#. c #F4F4F4", +"$. c #FBFBFB", +"%. c None", +/* pixels */ +"%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.", +"%.%.%.%.%.%.%.%.%.%.%.%.%.%.t 5 5 $ %.%.%.%.%.%.%.%.%.%.%.%.%.%.", +"%.%.%.%.%.%.%.%.%.%.%.r u w q 9 9 9 8 4 # %.%.%.%.%.%.%.%.%.%.%.", +"%.%.%.%.%.%.%.%.%.y s e 9 2 , , , : > 2 9 q 5 %.%.%.%.%.%.%.%.%.", +"%.%.%.%.%.%.%.%.s q 2 , , , , : , > 2 2 > > 2 9 %.%.%.%.%.%.%.%.", +"%.%.%.%.%.%.t e 1 , , , , : : ; > 2 9 9 2 , , > 2 + %.%.%.%.%.%.", +"%.%.%.%.%.$ e 2 , , , , , , ; u u 8 1 1 2 > , , > > + %.%.%.%.%.", +"%.%.%.%.%.e 2 , , : > ; ; > < ` ` 0 c n 1 2 , , , > , %.%.%.%.%.", +"%.%.%.%.e 1 , , , , ; h v - 3 ..! w ' _ 9 2 > , , , > : %.%.%.%.", +"%.%.%.6 q , : , > 2 > W ..| [ #.H V ..D 9 9 2 , , , , , % %.%.%.", +"%.%.%.e 2 , > 2 2 2 9 b ! #.$.$.#.#.#.Y i 1 2 > , , , > ; %.%.%.", +"%.%.@ q > 2 2 2 9 q e q 0 o.$.+.) { #.#.| b 2 2 , , , , : X %.%.", +"%.%.4 9 2 2 9 q e e s w b O.#.( m x I @.$...f > > , , , : & %.%.", +"%.%.8 > 2 2 9 e s d g a P #.#.L x l a [ $.#.A 2 2 , : , , ; %.%.", +"%.+ 1 , , 2 2 q e d g f / $.#.T n k Z o.$.O.M 9 2 > , , , ; X %.", +"%.* 2 , , , 2 9 q e s f X.$.#.O.O.O.#.$.+.Y g e 9 2 , , , ; o %.", +"%.* 2 , , , 2 2 q e w n O.$.[ R ( O.$.$.[ d s e 9 2 2 , , ; o %.", +"%.+ 2 , , , > 2 8 8 1 G #.#.T m m N ] #.#.~ s e e 9 2 > : ; X %.", +"%.%.> , , , , 2 < v B [ $.O.m z z s b #.$...g e e q 9 2 ; = %.%.", +"%.%.= : , , , : 7 ' O.#.$.@.C j p u ~ #.$.} g q 9 9 2 2 ; % %.%.", +"%.%.o , , , , : 0 G ^ .$.#.O.X.{ X.#.$.#.Y e 9 2 2 > , ; %.%.", +"%.%.%., : , , , 2 2 2 M O.) ] #.#.#.#.O./ d 9 2 > , , ; = %.%.%.", +"%.%.%.& ; , , , , 2 ; Q ..g F O.K A H S s 9 2 > , : , ; o %.%.%.", +"%.%.%.%.; ; , , , , 2 E _ d ' ..d q q 9 2 > , : , , ; = %.%.%.%.", +"%.%.%.%.%.; : , , , 2 q d g U J e 2 2 > , , , , , ; = %.%.%.%.%.", +"%.%.%.%.%.o ; : , , , 2 9 q 9 q 9 > , : , , , , ; = . %.%.%.%.%.", +"%.%.%.%.%.%.. ; ; , , > 2 2 2 > , , , , , , , ; = %.%.%.%.%.%.", +"%.%.%.%.%.%.%.%.= ; : > 2 2 , , : , , , , ; ; & %.%.%.%.%.%.%.%.", +"%.%.%.%.%.%.%.%.%.. = ; > : , , , , ; ; = = X %.%.%.%.%.%.%.%.%.", +"%.%.%.%.%.%.%.%.%.%.%. % = ; ; ; ; & O %.%.%.%.%.%.%.%.%.%.%.", +"%.%.%.%.%.%.%.%.%.%.%.%.%.%. X X %.%.%.%.%.%.%.%.%.%.%.%.%.%.", +"%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%." +}; diff --git a/share/pixmaps/bitcoin64.png b/share/pixmaps/bitcoin64.png new file mode 100644 index 0000000000000000000000000000000000000000..e96da5462614fe34b27d4511e91172e3eaa1a625 GIT binary patch literal 26310 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 O07#0;iWCd!`u-oZi{Qxs literal 0 HcmV?d00001 diff --git a/share/pixmaps/bitcoin64.xpm b/share/pixmaps/bitcoin64.xpm new file mode 100644 index 0000000..851829d --- /dev/null +++ b/share/pixmaps/bitcoin64.xpm @@ -0,0 +1,242 @@ +/* XPM */ +static char *bitcoin__[] = { +/* columns rows colors chars-per-pixel */ +"64 64 172 2", +" c #8F6319", +". c #8F6A1A", +"X c #90651A", +"o c #916C1A", +"O c #AF7C1E", +"+ c #B1781E", +"@ c #9A7026", +"# c #AC801F", +"$ c #B1811F", +"% c #A9812B", +"& c #B08320", +"* c #BB8621", +"= c #BD8E22", +"- c #A58132", +"; c #FC8400", +": c #FD8A03", +"> c #FD8E0C", +", c #FF910E", +"< c #F98F14", +"1 c #F79117", +"2 c #FD9314", +"3 c #FC951B", +"4 c #FE9A1D", +"5 c #CA8E22", +"6 c #CC8E2A", +"7 c #D48D23", +"8 c #C39223", +"9 c #CE9925", +"0 c #D19C25", +"q c #D19329", +"w c #D5992B", +"e c #DD9D33", +"r c #D69F3C", +"t c #E29425", +"y c #E79925", +"u c #EA9926", +"i c #E69A2C", +"p c #F79625", +"a c #F99524", +"s c #F79825", +"d c #F89825", +"f c #F3962A", +"g c #F69B2C", +"h c #F89B2B", +"j c #E19F30", +"k c #EE9B34", +"l c #F49D33", +"z c #F99E32", +"x c #F39F3B", +"c c #DFA731", +"v c #D7A43D", +"b c #DCA63C", +"n c #EEA328", +"m c #FFA225", +"M c #FFAB26", +"N c #F3A529", +"B c #FEA429", +"V c #F4AB2A", +"C c #FFAC2A", +"Z c #FFB325", +"A c #FFB42C", +"S c #FFBB2D", +"D c #E3A335", +"F c #E5A438", +"G c #EDA03D", +"H c #F7A037", +"J c #FAA135", +"K c #F3AB31", +"L c #FEAB31", +"P c #F4A13C", +"I c #F9A33B", +"U c #FDB432", +"Y c #FFBF37", +"T c #FFC12F", +"R c #FFC230", +"E c #FFC03E", +"W c #DFAF41", +"Q c #ECA34D", +"! c #EDA84E", +"~ c #F2A343", +"^ c #FAA642", +"/ c #FAA846", +"( c #F1A74C", +") c #F6A94F", +"_ c #FAAA4A", +"` c #E7A451", +"' c #ECA754", +"] c #EFAA56", +"[ c #ECAC5B", +"{ c #F3AA52", +"} c #FCAE52", +"| c #FBB056", +" . c #FBB25C", +".. c #E7AB61", +"X. c #ECB067", +"o. c #E7B36D", +"O. c #EBB36C", +"+. c #F2B163", +"@. c #FCB460", +"#. c #F0B56B", +"$. c #E3B274", +"%. c #EDB672", +"&. c #EDB877", +"*. c #E2B57C", +"=. c #ECB97B", +"-. c #E4BA83", +";. c #EBBD83", +":. c #E7BF8D", +">. c #EBBD88", +",. c #E9C08C", +"<. c #E7C496", +"1. c #EBC393", +"2. c #EBC997", +"3. c #E7C49A", +"4. c #E9C69A", +"5. c #E3CA9D", +"6. c #E9C89E", +"7. c #DCC9AE", +"8. c #DDCBB2", +"9. c #E3C7A2", +"0. c #E5CAA3", +"q. c #E9CBA3", +"w. c #E5CEAB", +"e. c #E8CEAA", +"r. c #E4D4AC", +"t. c #EBD2AF", +"y. c #E7CFB2", +"u. c #E1D4B4", +"i. c #E8D5B6", +"p. c #E5D7BB", +"a. c #E9D6BB", +"s. c #E5D8B9", +"d. c #EAD8BE", +"f. c #F0D6B4", +"g. c #DFDFC6", +"h. c #E3D6C1", +"j. c #E9D7C0", +"k. c #E6DAC5", +"l. c #EBDCC7", +"z. c #E5DCCA", +"x. c #EADEC9", +"c. c #E8DFD0", +"v. c #D7E2D9", +"b. c #E3E0C9", +"n. c #EEE2CB", +"m. c #E6E1D4", +"M. c #E9E2D3", +"N. c #E4E4DC", +"B. c #E9E5DE", +"V. c #F4EDDE", +"C. c #DFE8E6", +"Z. c #DEEEE8", +"A. c #DFF2F3", +"S. c #DDFFFF", +"D. c #E1E6E0", +"F. c #E8E6E2", +"G. c #E8E9E5", +"H. c #E5EFEC", +"J. c #E8E9EA", +"K. c #EAF3EE", +"L. c #F3F3EB", +"P. c #E7EDF2", +"I. c #E8EEF3", +"U. c #E7F4F7", +"Y. c #E9F0F7", +"T. c #EBF5FD", +"R. c #E4FEFF", +"E. c #ECFCFF", +"W. c #F4F5F4", +"Q. c #F4FFFF", +"!. c #FEFFFF", +"~. c None", +/* pixels */ +"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.F L h C C A A A A C C h L e ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.D N C m d d a a p a a p a a d m m C N j ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.- K M m a p s d d d d d d d d d d d d s p d m M V % ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.Y M d a d d d d d d d d d d d d d d d d h h d s a d M U ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.E m 4 a d d d d d d d d d d d d d d d d d d h h h d d d a d M U ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.C 4 a d d d d d d d d d d d d d d d d d h h h h h h d d d d d a m C ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.~.~.~.W S a p d d d d d d d d d d d d d d d d h h h h g g h h h d d d d d p a S c ~.~.~.~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.~.~.v M a s d d d d d d d d d d d d d d d h h h h h g z z g h h d d d d d d s a C w ~.~.~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.~.r Z a d d d d d d d d d d d d d d d g 4 : 2 h z z z z z h h h h d d d d d d d a S q ~.~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.b Z a d d d d d d d d d d d d d d h h 4 x $.l a z H h h H z h h h d d d d d d d d a A w ~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.T a s d d d d d d d d d d d d h h h g : $.R.T.7.a B x f > a H h h d d d d d d d d s a R ~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.U a s d d d d d d d d d d d d h h h h z : e.!.!.p.2 3 8.D.5.' a h h h d d d d d d d d p d A ~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.U M p d d d d d d d d d d h h 1 : : 2 h h p B.!.Q.%., l J.!.R.-.> z h h h d d d d d d d d p C N ~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.S a d d d d d d d d d d h d 3 7.r.O.G p ; k E.!.T.( , [ E.!.T.~ 4 z h h h d d d d d d d d d a S ~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.V d s d d d d d d d d h h h 2 l E.!.Q.T.m.:.q.!.!.l.: : -.Q.!.c.a z z z g h h d d d d d d d d s m A ~.~.~.~.~.~.~.", +"~.~.~.~.~.~.@ S a d d d d d d d h h h h z : *.R.!.!.!.!.Q.!.!.!.V.,.Q d.!.Q.1.2 I z z h h h d d d d d d d d d a S X ~.~.~.~.~.~.", +"~.~.~.~.~.~.U d s d d d d d h h h h h g z a [ 5.M.Q.!.!.!.!.!.!.!.Q.E.!.!.Q.&.; 3 J H z h h h d d d d d d d d s h C ~.~.~.~.~.~.", +"~.~.~.~.~.~.S a d d d d h h h h h h z z z I d > < %.W.!.!.!.!.!.!.!.!.!.!.!.W.s.[ > 4 H g h h d d d d d d d d d a S ~.~.~.~.~.~.", +"~.~.~.~.~.i M p d d d h h h h g z z z z J H I I J > x.!.!.!.!.Q.T.E.Q.!.!.!.!.!.E.u.f 2 H h h h d d d d d d d d p C 7 ~.~.~.~.~.", +"~.~.~.~.~.C a d h h h h h g g z z z J J I I I I J P J.!.!.!.!.d.P =.e.G.E.!.!.!.!.Q.Z.f 2 z h h d d d d d d d d d d A ~.~.~.~.~.", +"~.~.~.~.~.A a h h h h h g z z z J H I I I I ^ / d X.E.!.!.!.Q.1.4 I J I ;.U.!.!.!.!.!.N.1 h g h h d d d d d d d d a S ~.~.~.~.~.", +"~.~.~.~.6 C p d h h h z z J J J I I I I ^ ^ ^ _ a 3.Q.!.!.!.E.#.I . ._ 3 ] K.!.!.!.!.E.O., z h h h d d d d d d d p A + ~.~.~.~.", +"~.~.~.~.i B d d h h h g z J I I I I ^ ^ ^ / / _ h k.!.!.!.!.J.) } . .| .3 6.Q.!.!.!.Q.q.> z g h h d d d d d d d d B t ~.~.~.~.", +"~.~.~.~.B d d d d h h h z z J I I ^ / / / _ _ ^ ( I.!.!.!.Q.d.I . . .| .d 1.Q.!.!.!.Q.q.2 z h h h d d d d d d d d d B ~.~.~.~.", +"~.~.~.~.C a d d d d h h g z J H I ^ ^ / _ _ } J %.E.!.!.!.Q.;.4 _ } | } J f m.!.!.!.!.Q.;.2 J z g h h d d d d d d d a A ~.~.~.~.", +"~.~.~.~.C a d d d d h h h z z J I I ^ ^ / _ } z 6.Q.!.!.!.!.n.<.&.+.{ ) ] h.Q.!.!.!.!.R.~ d H z z h h h d d d d d d a A ~.~.~.~.", +"~.~.~.~.A a d d d d d h h g z z H I I ^ / _ _ z k.!.!.!.!.!.!.Q.E.I.F.F.T.Q.!.!.!.!.E.9.2 I J z z h h h d d d d d d d A ~.~.~.~.", +"~.~.~.~.S a d d d d d h h h z z J I I ^ ^ / I ( P.!.!.!.!.Q.Q.!.!.!.!.!.!.!.!.!.!.E.w.d J I I J z h h h d d d d d d d A ~.~.~.~.", +"~.~.~.~.A a d d d d d d h h h z J J I I ^ / h O.E.!.!.!.Q.f.1.z.Y.E.!.!.!.!.!.!.L.! , ^ / I I H z z h h h d d d d d d A ~.~.~.~.", +"~.~.~.~.S p d d d d d d h h h z z J I I ^ / d <.Q.!.!.!.E.+.d _ +.>.k.E.!.!.!.!.Q.s.P J _ ^ I I J z z h h h d d d d d A ~.~.~.~.", +"~.~.~.~.C a d d d d d d d h h g z z H I I ^ d k.!.!.!.!.J.{ | @.} I I O.H.!.!.!.!.Q.C.l I ^ I I H J z g h h d d d d a A ~.~.~.~.", +"~.~.~.~.B a d d d d d d d h h h h z z J I J x P.!.!.!.Q.j.I . . . . .B { K.!.!.!.!.Q.0.a / ^ I I J z z h h h d d d a A ~.~.~.~.", +"~.~.~.~.B d d d d d d d d d h h h J h f 2 ; [ E.!.!.!.Q.1.I . . .| | .d 4.Q.!.!.!.!.m.z I ^ I I I J z h h h h d d d B ~.~.~.~.", +"~.~.~.~.u B d d d d d d d d h h z , ' v.q.X.M.!.!.!.!.E.#.^ . .| } } } d >.Q.!.!.!.!.F.x J I I I J J z z h h h d d C t ~.~.~.~.", +"~.~.~.~.7 C p d d d d d d d d h h : y.Q.Q.Q.!.!.!.!.!.B.d B / _ } } } J 1 k.!.!.!.!.!.c.s J I H J J z z z h h h h s A + ~.~.~.~.", +"~.~.~.~.~.A a d d d d d d d d h > ` R.!.!.!.!.!.!.!.!.L.q.=.[ ~ z h h l 0.Q.!.!.!.!.Q.q.2 I J J z z h h h h h h h a S ~.~.~.~.~.", +"~.~.~.~.~.C d d d d d d d d d d > ..g.Y.E.Q.!.!.!.!.!.!.Q.E.T.B.k.a.d.P.Q.!.!.!.!.!.E.[ 2 J z z z g h h h h d d d d C ~.~.~.~.~.", +"~.~.~.~.~.y C p d d d d d d d d g 3 > l [ <.x.W.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.Q.z.> z z z h h h h h d d d d p C 7 ~.~.~.~.~.", +"~.~.~.~.~.~.S a d d d d d d d d d h h 3 , > ; =.Q.!.W.T.Q.!.!.!.!.!.!.!.!.!.!.!.Q.A.g 2 z h h h h h h d d d d d a S ~.~.~.~.~.~.", +"~.~.~.~.~.~.C h s d d d d d d d d d h g z H : <.!.!.t.l &.V.!.!.Q.Q.Q.Q.!.Q.Q.E.b.l > H h h h h h d d d d d d s m C ~.~.~.~.~.~.", +"~.~.~.~.~.~.X S a d d d d d d d d d h h h h p N.!.Q.=.: < c.!.Q.2.&.e.a.d.i.6.[ < 2 z h h h h d d d d d d d d a S ~.~.~.~.~.~.", +"~.~.~.~.~.~.~.A h s d d d d d d d d d h g 2 ~ E.!.E.{ 2 [ E.!.T.l : 2 1 3 2 > > h z h h h h d d d d d d d d s m A ~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.S a d d d d d d d d d h h : -.R.!.B.h 2 =.Q.!.M.p z z z h h z g h h h d d d d d d d d d d d a S ~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.N C p d d d d d d d d d h 3 ' 2.N.9.2 3 z.!.!.q.> J z h h h h h h d d d d d d d d d d d d p C n ~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.S h p d d d d d d d d d z 3 : p l J g 8.T.S.O.> z h h h h h d d d d d d d d d d d d d p h S ~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.S a s d d d d d d d d h h z d h I J a P o.P d g h h h d d d d d d d d d d d d d d s a S ~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.* S a s d d d d d d d d h h g z J J h 3 > d z h h h d d d d d d d d d d d d d d s a S * ~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.~.$ T a s d d d d d d d h h h z z z h g g h h d d d d d d d d d d d d d d d d s a T O ~.~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.~.~.& S a p d d d d d d h h h z g h h h h h d d d d d d d d d d d d d d d d p a S # ~.~.~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.~.~.~.8 S d p d d d d d d h h g h h h h d d d d d d d d d d d d d d d d d p h S = ~.~.~.~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.S A a s d d d d h h h h h d d d d d d d d d d d d d d d d d s a A S ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.0 T m p d d d d h h h d d d d d d d d d d d d d d d d d p B S 9 ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.V S m a p d h d d d d d d d d d d d d d d d d p a m S V ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.o V S C d p p d d d d d d d d d d d d p p d C S N . ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.5 C S A B d d a a d d a a a d B A S C 5 ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.O t B A A A A A A A A B t O ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", +"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~." +}; diff --git a/share/pixmaps/check.ico b/share/pixmaps/check.ico new file mode 100644 index 0000000000000000000000000000000000000000..0c4e6e81473d47761346fd71e171a807bda5ec58 GIT binary patch literal 766 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 literal 0 HcmV?d00001 diff --git a/share/pixmaps/nsis-header.bmp b/share/pixmaps/nsis-header.bmp new file mode 100644 index 0000000000000000000000000000000000000000..064eadc3e1bd195fb5a90e6aea59278f0cc7d76c GIT binary patch literal 9740 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 X1Cz!Y9up(-{R@9v{4Ygpt%2VFFK}M{ literal 0 HcmV?d00001 diff --git a/share/pixmaps/nsis-wizard.bmp b/share/pixmaps/nsis-wizard.bmp new file mode 100644 index 0000000000000000000000000000000000000000..54244083ae7ca8d0c7f8d105c7860dd4034c582d GIT binary patch literal 52572 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 M2rKaaOaaFJ7rj~tvH$=8 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 + + + + CFBundleIconFile + bitcoin.icns + CFBundlePackageType + APPL + CFBundleGetInfoString + $VERSION, Copyright © 2009-$YEAR The Bitcoin developers + CFBundleShortVersionString + $VERSION + CFBundleVersion + $VERSION + CFBundleSignature + ???? + CFBundleExecutable + Bitcoin-Qt + CFBundleIdentifier + org.bitcoinfoundation.Bitcoin-Qt + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + org.bitcoinfoundation.BitcoinPayment + CFBundleURLSchemes + + bitcoin + + + + + diff --git a/share/qt/clean_mac_info_plist.py b/share/qt/clean_mac_info_plist.py new file mode 100644 index 0000000..df677f5 --- /dev/null +++ b/share/qt/clean_mac_info_plist.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python +# Jonas Schnelli, 2013 +# make sure the Bitcoin-Qt.app contains the right plist (including the right version) +# fix made because of serval bugs in Qt mac deployment (https://bugreports.qt-project.org/browse/QTBUG-21267) + +from string import Template +from datetime import date + +bitcoinDir = "./"; + +inFile = bitcoinDir+"/share/qt/Info.plist" +outFile = "Bitcoin-Qt.app/Contents/Info.plist" +version = "unknown"; + +fileForGrabbingVersion = bitcoinDir+"bitcoin-qt.pro" +for line in open(fileForGrabbingVersion): + lineArr = line.replace(" ", "").split("="); + if lineArr[0].startswith("VERSION"): + version = lineArr[1].replace("\n", ""); + +fIn = open(inFile, "r") +fileContent = fIn.read() +s = Template(fileContent) +newFileContent = s.substitute(VERSION=version,YEAR=date.today().year) + +fOut = open(outFile, "w"); +fOut.write(newFileContent); + +print "Info.plist fresh created" \ No newline at end of file diff --git a/share/qt/extract_strings_qt.py b/share/qt/extract_strings_qt.py new file mode 100644 index 0000000..1267b18 --- /dev/null +++ b/share/qt/extract_strings_qt.py @@ -0,0 +1,72 @@ +#!/usr/bin/python +''' +Extract _("...") strings for translation and convert to Qt4 stringdefs so that +they can be picked up by Qt linguist. +''' +from subprocess import Popen, PIPE +import glob +import operator + +OUT_CPP="src/qt/bitcoinstrings.cpp" +EMPTY=['""'] + +def parse_po(text): + """ + Parse 'po' format produced by xgettext. + Return a list of (msgid,msgstr) tuples. + """ + messages = [] + msgid = [] + msgstr = [] + in_msgid = False + in_msgstr = False + + for line in text.split('\n'): + line = line.rstrip('\r') + if line.startswith('msgid '): + if in_msgstr: + messages.append((msgid, msgstr)) + in_msgstr = False + # message start + in_msgid = True + + msgid = [line[6:]] + elif line.startswith('msgstr '): + in_msgid = False + in_msgstr = True + msgstr = [line[7:]] + elif line.startswith('"'): + if in_msgid: + msgid.append(line) + if in_msgstr: + msgstr.append(line) + + if in_msgstr: + messages.append((msgid, msgstr)) + + return messages + +files = glob.glob('src/*.cpp') + glob.glob('src/*.h') + +# xgettext -n --keyword=_ $FILES +child = Popen(['xgettext','--output=-','-n','--keyword=_'] + files, stdout=PIPE) +(out, err) = child.communicate() + +messages = parse_po(out) + +f = open(OUT_CPP, 'w') +f.write("""#include +// Automatically generated by extract_strings.py +#ifdef __GNUC__ +#define UNUSED __attribute__((unused)) +#else +#define UNUSED +#endif +""") +f.write('static const char UNUSED *bitcoin_strings[] = {\n') +messages.sort(key=operator.itemgetter(0)) +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 0000000..c3cece1 --- /dev/null +++ b/share/ui.rc @@ -0,0 +1,15 @@ +bitcoin ICON "pixmaps/bitcoin.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 0000000..14054c3 --- /dev/null +++ b/src/addrman.cpp @@ -0,0 +1,528 @@ +// 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 hash1 = Hash2(ss1.begin(), ss1.end()).Get64(); + + CDataStream ss2(SER_GETHASH, 0); + std::vector vchGroupKey = GetGroup(); + ss2 << nKey << vchGroupKey << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP); + uint64 hash2 = Hash2(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 hash1 = Hash2(ss1.begin(), ss1.end()).Get64(); + + CDataStream ss2(SER_GETHASH, 0); + ss2 << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP); + uint64 hash2 = Hash2(ss2.begin(), ss2.end()).Get64(); + return hash2 % ADDRMAN_NEW_BUCKET_COUNT; +} + +bool CAddrInfo::IsTerrible(int64 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 nNow) const +{ + double fChance = 1.0; + + int64 nSinceLastSeen = nNow - nTime; + int64 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 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 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 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 < 24 * 60 * 60); + int64 nUpdateInterval = (fCurrentlyOnline ? 60 * 60 : 24 * 60 * 60); + if (addr.nTime && (!pinfo->nTime || pinfo->nTime < addr.nTime - nUpdateInterval - nTimePenalty)) + pinfo->nTime = max((int64)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)0, (int64)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 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) +{ + int 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 (int n = 0; n nUpdateInterval) + info.nTime = nTime; +} diff --git a/src/addrman.h b/src/addrman.h new file mode 100644 index 0000000..7af6afd --- /dev/null +++ b/src/addrman.h @@ -0,0 +1,503 @@ +// 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 nLastSuccess; + + // last try whatsoever by us: + // int64 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 nNow = GetAdjustedTime()) const; + + // Calculate the relative chance this entry should be given when selecting nodes to connect to + double GetChance(int64 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 nTime); + + // Add an entry to the "new" table. + bool Add_(const CAddress &addr, const CNetAddr& source, int64 nTimePenalty); + + // Mark an entry as attempted to connect. + void Attempt_(const CService &addr, int64 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); + + // Mark an entry as currently-connected-to. + void Connected_(const CService &addr, int64 nTime); + +public: + + 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); + } + } + } + } + } + });) + + 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 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 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 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 nTime = GetAdjustedTime()) + { + { + LOCK(cs); + Check(); + Good_(addr, nTime); + Check(); + } + } + + // Mark an entry as connection attempted to. + void Attempt(const CService &addr, int64 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; + } + + // Mark an entry as currently-connected-to. + void Connected(const CService &addr, int64 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 0000000..71bf023 --- /dev/null +++ b/src/alert.cpp @@ -0,0 +1,268 @@ +// +// Alert system +// + +#include +#include +#include +#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 = "04317422847840aaf195de8442ebecedf5b095cdbb9bc716bda9110971b28a49e0ead8564ff0db22209e0374782c093bb899692d524e9d6a6956e7c5ecbcd68284"; +static const char* pszTestKey = "04317430343f91cc401d56d68b123028bf52e5fca1939df127f63c6467cdf9c8e2c14b61104cf817d0b780da337893ecc4aaff1309e536162dabbdb45200ca2b0a"; + +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 = %"PRI64d"\n" + " nExpiration = %"PRI64d"\n" + " nID = %d\n" + " nCancel = %d\n" + " setCancel = %s\n" + " nMinVer = %d\n" + " nMaxVer = %d\n" + " setSubVer = %s\n" + " nPriority = %d\n" + " strComment = \"%s\"\n" + " strStatusBar = \"%s\"\n" + ")\n", + nVersion, + nRelayUntil, + nExpiration, + nID, + nCancel, + strSetCancel.c_str(), + nMinVer, + nMaxVer, + strSetSubVer.c_str(), + nPriority, + strComment.c_str(), + strStatusBar.c_str()); +} + +void 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 Hash2(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; + // 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(Hash2(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(bool fThread) +{ + 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 and -alertnotify if it applies to me + if(AppliesToMe()) + { + uiInterface.NotifyAlertChanged(GetHash(), CT_NEW); + std::string strCmd = GetArg("-alertnotify", ""); + if (!strCmd.empty()) + { + // Alert text should be plain ascii coming from a trusted source, but to + // be safe we first strip anything not in safeChars, then add single quotes around + // the whole string before passing it to the shell: + std::string singleQuote("'"); + // safeChars chosen to allow simple messages/URLs/email addresses, but avoid anything + // even possibly remotely dangerous like & or > + std::string safeChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890 .,;_/:?@"); + std::string safeStatus; + for (std::string::size_type i = 0; i < strStatusBar.size(); i++) + { + if (safeChars.find(strStatusBar[i]) != std::string::npos) + safeStatus.push_back(strStatusBar[i]); + } + safeStatus = singleQuote+safeStatus+singleQuote; + boost::replace_all(strCmd, "%s", safeStatus); + + if (fThread) + boost::thread t(runCommand, strCmd); // thread runs free + else + runCommand(strCmd); + } + } + } + + 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 0000000..25e140f --- /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 nRelayUntil; // when newer nodes stop relaying to newer nodes + int64 nExpiration; + int nID; + int nCancel; + std::set setCancel; + int nMinVer; // lowest version inclusive + int nMaxVer; // highest version inclusive + std::set setSubVer; // empty matches all + int nPriority; + + // Actions + std::string strComment; + std::string strStatusBar; + std::string strReserved; + + IMPLEMENT_SERIALIZE + ( + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(nRelayUntil); + READWRITE(nExpiration); + READWRITE(nID); + READWRITE(nCancel); + READWRITE(setCancel); + READWRITE(nMinVer); + READWRITE(nMaxVer); + READWRITE(setSubVer); + READWRITE(nPriority); + + READWRITE(strComment); + READWRITE(strStatusBar); + READWRITE(strReserved); + ) + + void SetNull(); + + 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(bool fThread = true); + + /* + * 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 0000000..42a2186 --- /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 GetLockedPagcount() + { + 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); +#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); +#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.h b/src/base58.h new file mode 100644 index 0000000..cd1ff57 --- /dev/null +++ b/src/base58.h @@ -0,0 +1,467 @@ +// 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 "bignum.h" +#include "key.h" +#include "script.h" +#include "allocators.h" + +static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + +// Encode a byte sequence as a base58-encoded string +inline std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend) +{ + CAutoBN_CTX pctx; + CBigNum bn58 = 58; + CBigNum bn0 = 0; + + // Convert big endian data to little endian + // Extra zero at the end make sure bignum will interpret as a positive number + std::vector vchTmp(pend-pbegin+1, 0); + reverse_copy(pbegin, pend, vchTmp.begin()); + + // Convert little endian data to bignum + CBigNum bn; + bn.setvch(vchTmp); + + // Convert bignum to std::string + std::string str; + // 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.getulong(); + str += pszBase58[c]; + } + + // Leading zeroes encoded as base58 zeros + for (const unsigned char* p = pbegin; p < pend && *p == 0; p++) + str += pszBase58[0]; + + // Convert little endian std::string to big endian + reverse(str.begin(), str.end()); + return str; +} + +// Encode a byte vector as a base58-encoded string +inline 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 +inline bool DecodeBase58(const char* psz, std::vector& vchRet) +{ + CAutoBN_CTX pctx; + vchRet.clear(); + CBigNum bn58 = 58; + CBigNum bn = 0; + CBigNum bnChar; + while (isspace(*psz)) + psz++; + + // Convert big endian string to bignum + for (const char* p = psz; *p; p++) + { + const char* p1 = strchr(pszBase58, *p); + if (p1 == NULL) + { + while (isspace(*p)) + p++; + if (*p != '\0') + return false; + break; + } + bnChar.setulong(p1 - pszBase58); + if (!BN_mul(&bn, &bn, &bn58, pctx)) + throw bignum_error("DecodeBase58 : BN_mul failed"); + bn += bnChar; + } + + // Get bignum as little endian data + std::vector vchTmp = bn.getvch(); + + // Trim off sign byte if present + if (vchTmp.size() >= 2 && vchTmp.end()[-1] == 0 && vchTmp.end()[-2] >= 0x80) + vchTmp.erase(vchTmp.end()-1); + + // Restore leading zeros + int nLeadingZeros = 0; + for (const char* p = psz; *p == pszBase58[0]; p++) + nLeadingZeros++; + vchRet.assign(nLeadingZeros + vchTmp.size(), 0); + + // Convert little endian data to big endian + reverse_copy(vchTmp.begin(), vchTmp.end(), vchRet.end() - vchTmp.size()); + return true; +} + +// Decode a base58-encoded string str into byte vector vchRet +// returns true if decoding is successful +inline 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 +inline std::string EncodeBase58Check(const std::vector& vchIn) +{ + // add 4-byte hash check to the end + std::vector vch(vchIn); + uint256 hash = Hash2(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 +inline bool DecodeBase58Check(const char* psz, std::vector& vchRet) +{ + if (!DecodeBase58(psz, vchRet)) + return false; + if (vchRet.size() < 4) + { + vchRet.clear(); + return false; + } + uint256 hash = Hash2(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 +inline bool DecodeBase58Check(const std::string& str, std::vector& vchRet) +{ + return DecodeBase58Check(str.c_str(), vchRet); +} + + + + + +/** Base class for all base58-encoded data */ +class CBase58Data +{ +protected: + // the version byte + unsigned char nVersion; + + // the actually encoded data + typedef std::vector > vector_uchar; + vector_uchar vchData; + + CBase58Data() + { + nVersion = 0; + vchData.clear(); + } + + void SetData(int nVersionIn, const void* pdata, size_t nSize) + { + nVersion = nVersionIn; + vchData.resize(nSize); + if (!vchData.empty()) + memcpy(&vchData[0], pdata, nSize); + } + + void SetData(int nVersionIn, const unsigned char *pbegin, const unsigned char *pend) + { + SetData(nVersionIn, (void*)pbegin, pend - pbegin); + } + +public: + bool 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 SetString(const std::string& str) + { + return SetString(str.c_str()); + } + + std::string ToString() const + { + std::vector vch(1, nVersion); + vch.insert(vch.end(), vchData.begin(), vchData.end()); + return EncodeBase58Check(vch); + } + + int 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 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 = 38, + SCRIPT_ADDRESS = 98, + PUBKEY_ADDRESS_TEST = 119, + SCRIPT_ADDRESS_TEST = 199, + }; + + bool Set(const CKeyID &id) { + SetData(fTestNet ? PUBKEY_ADDRESS_TEST : PUBKEY_ADDRESS, &id, 20); + return true; + } + + bool Set(const CScriptID &id) { + SetData(fTestNet ? SCRIPT_ADDRESS_TEST : SCRIPT_ADDRESS, &id, 20); + return true; + } + + bool Set(const CTxDestination &dest) + { + return boost::apply_visitor(CBitcoinAddressVisitor(this), dest); + } + + bool 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; + } + + CBitcoinAddress() + { + } + + CBitcoinAddress(const CTxDestination &dest) + { + Set(dest); + } + + CBitcoinAddress(const std::string& strAddress) + { + SetString(strAddress); + } + + CBitcoinAddress(const char* pszAddress) + { + SetString(pszAddress); + } + + CTxDestination 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 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 IsScript() const { + if (!IsValid()) + return false; + switch (nVersion) { + case SCRIPT_ADDRESS: + case SCRIPT_ADDRESS_TEST: { + return true; + } + default: return false; + } + } +}; + +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: + enum + { + PRIVKEY_ADDRESS = CBitcoinAddress::PUBKEY_ADDRESS + 128, + PRIVKEY_ADDRESS_TEST = CBitcoinAddress::PUBKEY_ADDRESS_TEST + 128, + }; + + void SetSecret(const CSecret& vchSecret, bool fCompressed) + { + assert(vchSecret.size() == 32); + SetData(fTestNet ? PRIVKEY_ADDRESS_TEST : PRIVKEY_ADDRESS, &vchSecret[0], vchSecret.size()); + if (fCompressed) + vchData.push_back(1); + } + + CSecret GetSecret(bool &fCompressedOut) + { + CSecret vchSecret; + vchSecret.resize(32); + memcpy(&vchSecret[0], &vchData[0], 32); + fCompressedOut = vchData.size() == 33; + return vchSecret; + } + + bool IsValid() const + { + bool fExpectTestNet = false; + switch(nVersion) + { + case 128: + break; + case PRIVKEY_ADDRESS: + break; + + case 239: + fExpectTestNet = true; + break; + case PRIVKEY_ADDRESS_TEST: + fExpectTestNet = true; + break; + + default: + return false; + } + return fExpectTestNet == fTestNet && (vchData.size() == 32 || (vchData.size() == 33 && vchData[32] == 1)); + } + + bool SetString(const char* pszSecret) + { + return CBase58Data::SetString(pszSecret) && IsValid(); + } + + bool SetString(const std::string& strSecret) + { + return SetString(strSecret.c_str()); + } + + CBitcoinSecret(const CSecret& vchSecret, bool fCompressed) + { + SetSecret(vchSecret, fCompressed); + } + + CBitcoinSecret() + { + } +}; + +#endif // BITCOIN_BASE58_H diff --git a/src/bignum.h b/src/bignum.h new file mode 100644 index 0000000..0881807 --- /dev/null +++ b/src/bignum.h @@ -0,0 +1,590 @@ +// 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" // for uint64 + +/** 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(char n) is not portable. Use 'signed char' or 'unsigned char'. + CBigNum(signed char n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(short n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(int n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(long n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(int64 n) { BN_init(this); setint64(n); } + CBigNum(unsigned char n) { BN_init(this); setulong(n); } + CBigNum(unsigned short n) { BN_init(this); setulong(n); } + CBigNum(unsigned int n) { BN_init(this); setulong(n); } + CBigNum(unsigned long n) { BN_init(this); setulong(n); } + CBigNum(uint64 n) { BN_init(this); setuint64(n); } + explicit CBigNum(uint256 n) { BN_init(this); setuint256(n); } + + explicit CBigNum(const std::vector& vch) + { + BN_init(this); + setvch(vch); + } + + void setulong(unsigned long n) + { + if (!BN_set_word(this, n)) + throw bignum_error("CBigNum conversion from unsigned long : BN_set_word failed"); + } + + unsigned long getulong() const + { + return BN_get_word(this); + } + + unsigned int getuint() const + { + return BN_get_word(this); + } + + int getint() const + { + unsigned long n = BN_get_word(this); + if (!BN_is_negative(this)) + return (n > (unsigned long)std::numeric_limits::max() ? std::numeric_limits::max() : n); + else + return (n > (unsigned long)std::numeric_limits::max() ? std::numeric_limits::min() : -(int)n); + } + + void setint64(int64 sn) + { + unsigned char pch[sizeof(sn) + 6]; + unsigned char* p = pch + 4; + bool fNegative; + uint64 n; + + if (sn < (int64)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++) + { + unsigned char c = (n >> 56) & 0xff; + n <<= 8; + if (fLeadingZeroes) + { + if (c == 0) + continue; + if (c & 0x80) + *p++ = (fNegative ? 0x80 : 0); + else if (fNegative) + c |= 0x80; + fLeadingZeroes = false; + } + *p++ = c; + } + unsigned int nSize = p - (pch + 4); + pch[0] = (nSize >> 24) & 0xff; + pch[1] = (nSize >> 16) & 0xff; + pch[2] = (nSize >> 8) & 0xff; + pch[3] = (nSize) & 0xff; + BN_mpi2bn(pch, p - pch, this); + } + + void setuint64(uint64 n) + { + unsigned char pch[sizeof(n) + 6]; + unsigned char* p = pch + 4; + bool fLeadingZeroes = true; + for (int i = 0; i < 8; i++) + { + unsigned char c = (n >> 56) & 0xff; + n <<= 8; + if (fLeadingZeroes) + { + if (c == 0) + continue; + if (c & 0x80) + *p++ = 0; + fLeadingZeroes = false; + } + *p++ = c; + } + unsigned int nSize = p - (pch + 4); + pch[0] = (nSize >> 24) & 0xff; + pch[1] = (nSize >> 16) & 0xff; + pch[2] = (nSize >> 8) & 0xff; + pch[3] = (nSize) & 0xff; + BN_mpi2bn(pch, p - pch, this); + } + + void setuint256(uint256 n) + { + unsigned char pch[sizeof(n) + 6]; + unsigned char* p = pch + 4; + bool fLeadingZeroes = true; + unsigned char* pbegin = (unsigned char*)&n; + unsigned char* psrc = pbegin + sizeof(n); + while (psrc != pbegin) + { + unsigned char c = *(--psrc); + if (fLeadingZeroes) + { + if (c == 0) + continue; + if (c & 0x80) + *p++ = 0; + fLeadingZeroes = false; + } + *p++ = c; + } + unsigned int nSize = p - (pch + 4); + pch[0] = (nSize >> 24) & 0xff; + pch[1] = (nSize >> 16) & 0xff; + pch[2] = (nSize >> 8) & 0xff; + pch[3] = (nSize >> 0) & 0xff; + BN_mpi2bn(pch, p - pch, this); + } + + uint256 getuint256() 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 (unsigned int i = 0, j = vch.size()-1; i < sizeof(n) && j >= 4; i++, j--) + ((unsigned char*)&n)[i] = vch[j]; + return n; + } + + void setvch(const std::vector& vch) + { + std::vector vch2(vch.size() + 4); + unsigned int nSize = vch.size(); + // BIGNUM's byte stream format expects 4 bytes of + // big endian size data info at the front + vch2[0] = (nSize >> 24) & 0xff; + vch2[1] = (nSize >> 16) & 0xff; + vch2[2] = (nSize >> 8) & 0xff; + vch2[3] = (nSize >> 0) & 0xff; + // swap data to big endian + reverse_copy(vch.begin(), vch.end(), vch2.begin() + 4); + BN_mpi2bn(&vch2[0], vch2.size(), this); + } + + 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; + } + + // The "compact" format is a representation of a whole + // number N using an unsigned 32bit number similar to a + // floating point format. + // The most significant 8 bits are the unsigned exponent of base 256. + // This exponent can be thought of as "number of bytes of N". + // The lower 23 bits are the mantissa. + // Bit number 24 (0x800000) represents the sign of N. + // N = (-1^sign) * mantissa * 256^(exponent-3) + // + // Satoshi's original implementation used BN_bn2mpi() and BN_mpi2bn(). + // MPI uses the most significant bit of the first byte as sign. + // Thus 0x1234560000 is compact (0x05123456) + // and 0xc0de000000 is compact (0x0600c0de) + // (0x05c0de00) would be -0x40de000000 + // + // Bitcoin only uses this "compact" format for encoding difficulty + // targets, which are unsigned 256bit quantities. Thus, all the + // complexities of the sign bit and using base 256 are probably an + // implementation accident. + // + // This implementation directly uses shifts instead of going + // through an intermediate MPI representation. + CBigNum& SetCompact(unsigned int nCompact) + { + unsigned int nSize = nCompact >> 24; + bool fNegative =(nCompact & 0x00800000) != 0; + unsigned int nWord = nCompact & 0x007fffff; + if (nSize <= 3) + { + nWord >>= 8*(3-nSize); + BN_set_word(this, nWord); + } + else + { + BN_set_word(this, nWord); + BN_lshift(this, this, 8*(nSize-3)); + } + BN_set_negative(this, fNegative); + return *this; + } + + unsigned int GetCompact() const + { + unsigned int nSize = BN_num_bytes(this); + unsigned int nCompact = 0; + if (nSize <= 3) + nCompact = BN_get_word(this) << 8*(3-nSize); + else + { + CBigNum bn; + BN_rshift(&bn, this, 8*(nSize-3)); + nCompact = BN_get_word(&bn); + } + // The 0x00800000 bit denotes the sign. + // Thus, if it is already set, divide the mantissa by 256 and increase the exponent. + if (nCompact & 0x00800000) + { + nCompact >>= 8; + nSize++; + } + nCompact |= nSize << 24; + nCompact |= (BN_is_negative(this) ? 0x00800000 : 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[(unsigned char)*psz++]; + *this += n; + } + if (fNegative) + *this = 0 - *this; + } + + std::string ToString(int nBase=10) const + { + CAutoBN_CTX pctx; + CBigNum bnBase = nBase; + CBigNum bn0 = 0; + std::string str; + CBigNum bn = *this; + BN_set_negative(&bn, false); + CBigNum dv; + CBigNum rem; + if (BN_cmp(&bn, &bn0) == 0) + return "0"; + while (BN_cmp(&bn, &bn0) > 0) + { + if (!BN_div(&dv, &rem, &bn, &bnBase, pctx)) + throw bignum_error("CBigNum::ToString() : BN_div failed"); + bn = dv; + unsigned int c = rem.getulong(); + str += "0123456789abcdef"[c]; + } + if (BN_is_negative(this)) + str += "-"; + reverse(str.begin(), str.end()); + return str; + } + + std::string GetHex() const + { + return ToString(16); + } + + unsigned int GetSerializeSize(int nType=0, int nVersion=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); + } + + + bool operator!() const + { + return BN_is_zero(this); + } + + CBigNum& operator+=(const CBigNum& b) + { + if (!BN_add(this, this, &b)) + throw bignum_error("CBigNum::operator+= : BN_add failed"); + return *this; + } + + CBigNum& operator-=(const CBigNum& b) + { + *this = *this - b; + return *this; + } + + CBigNum& operator*=(const CBigNum& b) + { + CAutoBN_CTX pctx; + if (!BN_mul(this, this, &b, pctx)) + throw bignum_error("CBigNum::operator*= : BN_mul failed"); + return *this; + } + + CBigNum& operator/=(const CBigNum& b) + { + *this = *this / b; + return *this; + } + + CBigNum& operator%=(const CBigNum& b) + { + *this = *this % b; + return *this; + } + + CBigNum& operator<<=(unsigned int shift) + { + if (!BN_lshift(this, this, shift)) + throw bignum_error("CBigNum:operator<<= : BN_lshift failed"); + return *this; + } + + CBigNum& operator>>=(unsigned int shift) + { + // Note: BN_rshift segfaults on 64-bit if 2^shift is greater than the number + // if built on ubuntu 9.04 or 9.10, probably depends on version of OpenSSL + CBigNum a = 1; + a <<= shift; + if (BN_cmp(&a, this) > 0) + { + *this = 0; + return *this; + } + + if (!BN_rshift(this, this, shift)) + throw bignum_error("CBigNum:operator>>= : BN_rshift failed"); + return *this; + } + + + CBigNum& operator++() + { + // prefix operator + if (!BN_add(this, this, BN_value_one())) + throw bignum_error("CBigNum::operator++ : BN_add failed"); + return *this; + } + + const CBigNum operator++(int) + { + // postfix operator + const CBigNum ret = *this; + ++(*this); + return ret; + } + + CBigNum& operator--() + { + // prefix operator + CBigNum r; + if (!BN_sub(&r, this, BN_value_one())) + throw bignum_error("CBigNum::operator-- : BN_sub failed"); + *this = r; + return *this; + } + + const CBigNum operator--(int) + { + // postfix operator + const CBigNum ret = *this; + --(*this); + return ret; + } + + + friend inline const CBigNum operator-(const CBigNum& a, const CBigNum& b); + friend inline const CBigNum operator/(const CBigNum& a, const CBigNum& b); + friend inline const CBigNum operator%(const CBigNum& a, const CBigNum& b); +}; + + + +inline const CBigNum operator+(const CBigNum& a, const CBigNum& b) +{ + CBigNum r; + if (!BN_add(&r, &a, &b)) + throw bignum_error("CBigNum::operator+ : BN_add failed"); + return r; +} + +inline const CBigNum operator-(const CBigNum& a, const CBigNum& b) +{ + CBigNum r; + if (!BN_sub(&r, &a, &b)) + throw bignum_error("CBigNum::operator- : BN_sub failed"); + return r; +} + +inline const CBigNum operator-(const CBigNum& a) +{ + CBigNum r(a); + BN_set_negative(&r, !BN_is_negative(&r)); + return r; +} + +inline const CBigNum operator*(const CBigNum& a, const CBigNum& b) +{ + CAutoBN_CTX pctx; + CBigNum r; + if (!BN_mul(&r, &a, &b, pctx)) + throw bignum_error("CBigNum::operator* : BN_mul failed"); + return r; +} + +inline const CBigNum operator/(const CBigNum& a, const CBigNum& b) +{ + CAutoBN_CTX pctx; + CBigNum r; + if (!BN_div(&r, NULL, &a, &b, pctx)) + throw bignum_error("CBigNum::operator/ : BN_div failed"); + return r; +} + +inline const CBigNum operator%(const CBigNum& a, const CBigNum& b) +{ + CAutoBN_CTX pctx; + CBigNum r; + if (!BN_mod(&r, &a, &b, pctx)) + throw bignum_error("CBigNum::operator% : BN_div failed"); + return r; +} + +inline const CBigNum operator<<(const CBigNum& a, unsigned int shift) +{ + CBigNum r; + if (!BN_lshift(&r, &a, shift)) + throw bignum_error("CBigNum:operator<< : BN_lshift failed"); + return r; +} + +inline const CBigNum operator>>(const CBigNum& a, unsigned int shift) +{ + CBigNum r = a; + r >>= shift; + return r; +} + +inline bool operator==(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) == 0); } +inline bool operator!=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) != 0); } +inline bool operator<=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) <= 0); } +inline bool operator>=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) >= 0); } +inline bool operator<(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) < 0); } +inline bool operator>(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) > 0); } + +#endif diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp new file mode 100644 index 0000000..145f772 --- /dev/null +++ b/src/bitcoinrpc.cpp @@ -0,0 +1,1454 @@ +// 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" +#include "alert.h" +#include "checkpoints.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace boost; +using namespace boost::asio; +using namespace json_spirit; + +// Key used by getwork/getblocktemplate miners. +// Allocated in StartRPCThreads, free'd in StopRPCThreads +CReserveKey* pMiningKey = NULL; + +static std::string strRPCUserColonPass; + +// These are created by StartRPCThreads, destroyed in StopRPCThreads +static asio::io_service* rpc_io_service = NULL; +static ssl::context* rpc_ssl_context = NULL; +static boost::thread_group* rpc_worker_group = NULL; + +static inline unsigned short GetDefaultRPCPort() +{ + return GetBoolArg("-testnet", false) ? 41742 : 31742; +} + +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 AmountFromValue(const Value& value) +{ + double dAmount = value.get_real(); + if (dAmount <= 0.0 || dAmount > 21000000.0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); + int64 nAmount = roundint64(dAmount * COIN); + if (!MoneyRange(nAmount)) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); + return nAmount; +} + +Value ValueFromAmount(int64 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)); +} + + + +/// +/// 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) +{ + // Accept the deprecated and ignored 'detach' boolean argument + if (fHelp || params.size() > 1) + throw runtime_error( + "stop\n" + "Stop Greenchain server."); + // Shutdown will take long enough that the response should get back + StartShutdown(); + return "Greenchain server stopping"; +} + +// Send alert (first introduced in ppcoin) +// 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 base58 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."); + + // Prepare the alert message + CAlert alert; + 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()); + + // Prepare master key and sign alert message + CBitcoinSecret vchSecret; + if (!vchSecret.SetString(params[1].get_str())) + throw runtime_error("Invalid alert master key"); + CKey key; + bool fCompressed; + CSecret secret = vchSecret.GetSecret(fCompressed); + key.SetSecret(secret, fCompressed); // if key is not correct openssl may crash + if (!key.Sign(Hash2(alert.vchMsg.begin(), alert.vchMsg.end()), alert.vchSig)) + throw runtime_error( + "Unable to sign alert, check alert master key?\n"); + + // Process alert + 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; +} + +// RPC commands related to sync checkpoints +// get information of sync-checkpoint (first introduced in ppcoin) +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())); + if (mapBlockIndex.count(Checkpoints::hashSyncCheckpoint)) + { + pindexCheckpoint = mapBlockIndex[Checkpoints::hashSyncCheckpoint]; + result.push_back(Pair("height", pindexCheckpoint->nHeight)); + result.push_back(Pair("timestamp", (boost::int64_t) pindexCheckpoint->GetBlockTime())); + } + result.push_back(Pair("subscribemode", Checkpoints::IsSyncCheckpointEnforced()? "enforce" : "advisory")); + if (mapArgs.count("-checkpointkey")) + result.push_back(Pair("checkpointmaster", true)); + + return result; +} + +Value sendcheckpoint(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "sendcheckpoint \n" + "Send a synchronized checkpoint.\n"); + + if (!mapArgs.count("-checkpointkey") || CSyncCheckpoint::strMasterPrivKey.empty()) + throw runtime_error("Not a checkpointmaster node, first set checkpointkey in configuration and restart client. "); + + std::string strHash = params[0].get_str(); + uint256 hash(strHash); + + if (!Checkpoints::SendSyncCheckpoint(hash)) + throw runtime_error("Failed to send checkpoint, check log. "); + + Object result; + CBlockIndex* pindexCheckpoint; + + result.push_back(Pair("synccheckpoint", Checkpoints::hashSyncCheckpoint.ToString().c_str())); + if (mapBlockIndex.count(Checkpoints::hashSyncCheckpoint)) + { + pindexCheckpoint = mapBlockIndex[Checkpoints::hashSyncCheckpoint]; + result.push_back(Pair("height", pindexCheckpoint->nHeight)); + result.push_back(Pair("timestamp", (boost::int64_t) pindexCheckpoint->GetBlockTime())); + } + result.push_back(Pair("subscribemode", Checkpoints::IsSyncCheckpointEnforced()? "enforce" : "advisory")); + if (mapArgs.count("-checkpointkey")) + result.push_back(Pair("checkpointmaster", true)); + + return result; +} + +Value enforcecheckpoint(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "enforcecheckpoint \n" + " is true or false to enable or disable enforcement of broadcasted checkpoints by developer."); + + bool fEnforceCheckpoint = params[0].get_bool(); + if (mapArgs.count("-checkpointkey") && !fEnforceCheckpoint) + throw runtime_error( + "checkpoint master node must enforce synchronized checkpoints."); + if (fEnforceCheckpoint) + Checkpoints::strCheckpointWarning = ""; + mapArgs["-checkpointenforce"] = (fEnforceCheckpoint ? "1" : "0"); + return Value::null; +} + +// +// Call Table +// + + +static const CRPCCommand vRPCCommands[] = +{ // name actor (function) okSafeMode threadSafe + // ------------------------ ----------------------- ---------- ---------- + { "help", &help, true, true }, + { "stop", &stop, true, true }, + { "getblockcount", &getblockcount, true, false }, + { "getconnectioncount", &getconnectioncount, true, false }, + { "getpeerinfo", &getpeerinfo, true, false }, + { "addnode", &addnode, true, true }, + { "getaddednodeinfo", &getaddednodeinfo, true, true }, + { "getdifficulty", &getdifficulty, true, false }, + { "getgenerate", &getgenerate, true, false }, + { "setgenerate", &setgenerate, true, false }, + { "gethashespersec", &gethashespersec, true, false }, + { "getinfo", &getinfo, true, false }, + { "getmininginfo", &getmininginfo, true, false }, + { "getnewaddress", &getnewaddress, true, false }, + { "getaccountaddress", &getaccountaddress, true, false }, + { "setaccount", &setaccount, true, false }, + { "getaccount", &getaccount, false, false }, + { "getaddressesbyaccount", &getaddressesbyaccount, true, false }, + { "sendtoaddress", &sendtoaddress, 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 }, + { "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 }, + { "createmultisig", &createmultisig, true, true }, + { "getrawmempool", &getrawmempool, true, false }, + { "getblock", &getblock, 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 }, + { "getwork2", &getwork2, true, false }, + { "listaccounts", &listaccounts, false, false }, + { "settxfee", &settxfee, false, false }, + { "getblocktemplate", &getblocktemplate, true, false }, + { "submitblock", &submitblock, false, false }, + { "listsinceblock", &listsinceblock, false, false }, + { "dumpprivkey", &dumpprivkey, true, false }, + { "importprivkey", &importprivkey, false, false }, + { "listunspent", &listunspent, false, false }, + { "getrawtransaction", &getrawtransaction, false, false }, + { "createrawtransaction", &createrawtransaction, false, false }, + { "decoderawtransaction", &decoderawtransaction, false, false }, + { "signrawtransaction", &signrawtransaction, false, false }, + { "sendrawtransaction", &sendrawtransaction, false, false }, + { "gettxoutsetinfo", &gettxoutsetinfo, true, false }, + { "gettxout", &gettxout, true, false }, + { "lockunspent", &lockunspent, false, false }, + { "listlockunspent", &listlockunspent, false, false }, + { "makekeypair", &makekeypair, false, false }, + { "sendalert", &sendalert, false, false }, + { "getcheckpoint", &getcheckpoint, false, false }, + { "sendcheckpoint", &sendcheckpoint, false, false }, + { "enforcecheckpoint", &enforcecheckpoint, 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: Greenchain-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() +{ + char buffer[64]; + time_t now; + time(&now); + struct tm* now_gmt = gmtime(&now); + string locale(setlocale(LC_TIME, NULL)); + setlocale(LC_TIME, "C"); // we want POSIX (aka "C") weekday/month strings + strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt); + setlocale(LC_TIME, locale.c_str()); + return string(buffer); +} + +static string HTTPReply(int nStatus, const string& strMsg, bool keepalive) +{ + if (nStatus == HTTP_UNAUTHORIZED) + return strprintf("HTTP/1.0 401 Authorization Required\r\n" + "Date: %s\r\n" + "Server: Greenchain-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: Greenchain-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()); +} + +bool ReadHTTPRequestLine(std::basic_istream& stream, int &proto, + string& http_method, string& http_uri) +{ + string str; + getline(stream, str); + + // HTTP request line is space-delimited + vector vWords; + boost::split(vWords, str, boost::is_any_of(" ")); + if (vWords.size() < 2) + return false; + + // HTTP methods permitted: GET, POST + http_method = vWords[0]; + if (http_method != "GET" && http_method != "POST") + return false; + + // HTTP URI must be an absolute path, relative to current host + http_uri = vWords[1]; + if (http_uri.size() == 0 || http_uri[0] != '/') + return false; + + // parse proto, if present + string strProto = ""; + if (vWords.size() > 2) + strProto = vWords[2]; + + proto = 0; + const char *ver = strstr(strProto.c_str(), "HTTP/1."); + if (ver != NULL) + proto = atoi(ver+7); + + return true; +} + +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 ReadHTTPHeaders(std::basic_istream& stream, map& mapHeadersRet) +{ + int nLen = 0; + loop + { + string str; + std::getline(stream, str); + if (str.empty() || str == "\r") + break; + string::size_type nColon = str.find(":"); + if (nColon != string::npos) + { + string strHeader = str.substr(0, nColon); + boost::trim(strHeader); + boost::to_lower(strHeader); + string strValue = str.substr(nColon+1); + boost::trim(strValue); + mapHeadersRet[strHeader] = strValue; + if (strHeader == "content-length") + nLen = atoi(strValue.c_str()); + } + } + return nLen; +} + +int ReadHTTPMessage(std::basic_istream& stream, map& mapHeadersRet, string& strMessageRet, + int nProto) +{ + mapHeadersRet.clear(); + strMessageRet = ""; + + // Read header + int nLen = ReadHTTPHeaders(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 HTTP_OK; +} + +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 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; + 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 ServiceConnection(AcceptedConnection *conn); + +// 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) +{ + // 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; + } + else { + ServiceConnection(conn); + conn->close(); + delete conn; + } +} + +void StartRPCThreads() +{ + // getwork/getblocktemplate mining rewards paid here: + pMiningKey = new CReserveKey(pwalletMain); + + strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]; + if ((mapArgs["-rpcpassword"] == "") || + (mapArgs["-rpcuser"] == mapArgs["-rpcpassword"])) + { + unsigned char rand_pwd[32]; + RAND_bytes(rand_pwd, 32); + string strWhatAmI = "To use Greenchaind"; + 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=Greenchainrpc\n" + "rpcpassword=%s\n" + "(you do not need to remember this password)\n" + "The username and password MUST NOT be the same.\n" + "If the file does not exist, create it with owner-readable-only file permissions.\n" + "It is also recommended to set alertnotify so you are notified of problems;\n" + "for example: alertnotify=echo %%s | mail -s \"Greenchain Alert\" admin@foo.com\n"), + strWhatAmI.c_str(), + GetConfigFile().string().c_str(), + EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str()), + "", CClientUIInterface::MSG_ERROR); + StartShutdown(); + return; + } + + assert(rpc_io_service == NULL); + rpc_io_service = new asio::io_service(); + rpc_ssl_context = new ssl::context(*rpc_io_service, ssl::context::sslv23); + + const bool fUseSSL = GetBoolArg("-rpcssl"); + + if (fUseSSL) + { + rpc_ssl_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)) rpc_ssl_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)) rpc_ssl_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(rpc_ssl_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(*rpc_io_service)); + + 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, *rpc_ssl_context, fUseSSL); + + 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(*rpc_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, *rpc_ssl_context, fUseSSL); + + 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, "", CClientUIInterface::MSG_ERROR); + StartShutdown(); + return; + } + + rpc_worker_group = new boost::thread_group(); + for (int i = 0; i < GetArg("-rpcthreads", 4); i++) + rpc_worker_group->create_thread(boost::bind(&asio::io_service::run, rpc_io_service)); +} + +void StopRPCThreads() +{ + delete pMiningKey; pMiningKey = NULL; + + if (rpc_io_service == NULL) return; + + rpc_io_service->stop(); + rpc_worker_group->join_all(); + delete rpc_worker_group; rpc_worker_group = NULL; + delete rpc_ssl_context; rpc_ssl_context = NULL; + delete rpc_io_service; rpc_io_service = NULL; +} + +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 != "getwork2" && 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"; +} + +void ServiceConnection(AcceptedConnection *conn) +{ + bool fRun = true; + while (fRun) + { + int nProto = 0; + map mapHeaders; + string strRequest, strMethod, strURI; + + // Read HTTP request line + if (!ReadHTTPRequestLine(conn->stream(), nProto, strMethod, strURI)) + break; + + // Read HTTP message headers and body + ReadHTTPMessage(conn->stream(), mapHeaders, strRequest, nProto); + + if (strURI != "/") { + conn->stream() << HTTPReply(HTTP_NOT_FOUND, "", false) << std::flush; + break; + } + + // 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) + MilliSleep(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; + } + } +} + +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->threadSafe) + 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 HTTP reply status + int nProto = 0; + int nStatus = ReadHTTPStatus(stream, nProto); + + // Receive HTTP reply message headers and body + map mapHeaders; + string strReply; + ReadHTTPMessage(stream, mapHeaders, strReply, nProto); + + 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); + + int 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 == "setgenerate" && n > 0) ConvertTo(params[0]); + if (strMethod == "setgenerate" && n > 1) ConvertTo(params[1]); + if (strMethod == "sendtoaddress" && n > 1) ConvertTo(params[1]); + if (strMethod == "settxfee" && n > 0) ConvertTo(params[0]); + if (strMethod == "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 == "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 == "getblocktemplate" && n > 0) ConvertTo(params[0]); + if (strMethod == "listsinceblock" && n > 1) ConvertTo(params[1]); + if (strMethod == "sendmany" && n > 1) ConvertTo(params[1]); + if (strMethod == "sendmany" && n > 2) ConvertTo(params[2]); + if (strMethod == "addmultisigaddress" && n > 0) ConvertTo(params[0]); + if (strMethod == "addmultisigaddress" && n > 1) ConvertTo(params[1]); + if (strMethod == "createmultisig" && n > 0) ConvertTo(params[0]); + if (strMethod == "createmultisig" && 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 == "signrawtransaction" && n > 1) ConvertTo(params[1], true); + if (strMethod == "signrawtransaction" && n > 2) ConvertTo(params[2], true); + if (strMethod == "gettxout" && n > 1) ConvertTo(params[1]); + if (strMethod == "gettxout" && n > 2) ConvertTo(params[2]); + if (strMethod == "lockunspent" && n > 0) ConvertTo(params[0]); + if (strMethod == "lockunspent" && n > 1) ConvertTo(params[1]); + if (strMethod == "importprivkey" && n > 2) ConvertTo(params[2]); + 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 == "enforcecheckpoint" && n > 0) ConvertTo(params[0]); + + 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 (boost::thread_interrupted) { + throw; + } + 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 (boost::thread_interrupted) { + throw; + } + 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 0000000..f52882f --- /dev/null +++ b/src/bitcoinrpc.h @@ -0,0 +1,209 @@ +// 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; +class CReserveKey; + +#include "json/json_spirit_reader_template.h" +#include "json/json_spirit_writer_template.h" +#include "json/json_spirit_utils.h" + +#include "util.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 StartRPCThreads(); +void StopRPCThreads(); +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 threadSafe; +}; + +/** + * 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 CReserveKey* pMiningKey; + +extern int64 nWalletUnlockTime; +extern int64 AmountFromValue(const json_spirit::Value& value); +extern json_spirit::Value ValueFromAmount(int64 amount); +extern double GetDifficulty(const CBlockIndex* blockindex = NULL); +extern std::string HexBits(unsigned int nBits); +extern std::string HelpRequiringPassphrase(); +extern void EnsureWalletIsUnlocked(); + +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 addnode(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getaddednodeinfo(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 getgenerate(const json_spirit::Array& params, bool fHelp); // in rpcmining.cpp +extern json_spirit::Value setgenerate(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value gethashespersec(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getmininginfo(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getwork(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getwork2(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 createmultisig(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 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 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 lockunspent(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value listlockunspent(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 signrawtransaction(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value sendrawtransaction(const json_spirit::Array& params, bool fHelp); + +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 gettxoutsetinfo(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value gettxout(const json_spirit::Array& params, bool fHelp); + +extern json_spirit::Value makekeypair(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value sendalert(const json_spirit::Array& params, bool fHelp); + +extern json_spirit::Value getcheckpoint(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value sendcheckpoint(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value enforcecheckpoint(const json_spirit::Array& params, bool fHelp); + +#endif diff --git a/src/blake.c b/src/blake.c new file mode 100644 index 0000000..0650b9c --- /dev/null +++ b/src/blake.c @@ -0,0 +1,1120 @@ +/* $Id: blake.c 252 2011-06-07 17:55:14Z tp $ */ +/* + * BLAKE implementation. + * + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * 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 END)============================= + * + * @author Thomas Pornin + */ + +#include +#include +#include + +#include "sph_blake.h" + +#ifdef __cplusplus +extern "C"{ +#endif + +#if SPH_SMALL_FOOTPRINT && !defined SPH_SMALL_FOOTPRINT_BLAKE +#define SPH_SMALL_FOOTPRINT_BLAKE 1 +#endif + +#if SPH_SMALL_FOOTPRINT_BLAKE +#define SPH_COMPACT_BLAKE_32 1 +#endif + +#if SPH_64 && (SPH_SMALL_FOOTPRINT_BLAKE || !SPH_64_TRUE) +#define SPH_COMPACT_BLAKE_64 1 +#endif + +#ifdef _MSC_VER +#pragma warning (disable: 4146) +#endif + +static const sph_u32 IV224[8] = { + SPH_C32(0xC1059ED8), SPH_C32(0x367CD507), + SPH_C32(0x3070DD17), SPH_C32(0xF70E5939), + SPH_C32(0xFFC00B31), SPH_C32(0x68581511), + SPH_C32(0x64F98FA7), SPH_C32(0xBEFA4FA4) +}; + +static const sph_u32 IV256[8] = { + SPH_C32(0x6A09E667), SPH_C32(0xBB67AE85), + SPH_C32(0x3C6EF372), SPH_C32(0xA54FF53A), + SPH_C32(0x510E527F), SPH_C32(0x9B05688C), + SPH_C32(0x1F83D9AB), SPH_C32(0x5BE0CD19) +}; + +#if SPH_64 + +static const sph_u64 IV384[8] = { + SPH_C64(0xCBBB9D5DC1059ED8), SPH_C64(0x629A292A367CD507), + SPH_C64(0x9159015A3070DD17), SPH_C64(0x152FECD8F70E5939), + SPH_C64(0x67332667FFC00B31), SPH_C64(0x8EB44A8768581511), + SPH_C64(0xDB0C2E0D64F98FA7), SPH_C64(0x47B5481DBEFA4FA4) +}; + +static const sph_u64 IV512[8] = { + SPH_C64(0x6A09E667F3BCC908), SPH_C64(0xBB67AE8584CAA73B), + SPH_C64(0x3C6EF372FE94F82B), SPH_C64(0xA54FF53A5F1D36F1), + SPH_C64(0x510E527FADE682D1), SPH_C64(0x9B05688C2B3E6C1F), + SPH_C64(0x1F83D9ABFB41BD6B), SPH_C64(0x5BE0CD19137E2179) +}; + +#endif + +#if SPH_COMPACT_BLAKE_32 || SPH_COMPACT_BLAKE_64 + +static const unsigned sigma[16][16] = { + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, + { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, + { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, + { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, + { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, + { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, + { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, + { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }, + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, + { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, + { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, + { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } +}; + +/* + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + 14 10 4 8 9 15 13 6 1 12 0 2 11 7 5 3 + 11 8 12 0 5 2 15 13 10 14 3 6 7 1 9 4 + 7 9 3 1 13 12 11 14 2 6 5 10 4 0 15 8 + 9 0 5 7 2 4 10 15 14 1 11 12 6 8 3 13 + 2 12 6 10 0 11 8 3 4 13 7 5 15 14 1 9 + 12 5 1 15 14 13 4 10 0 7 6 3 9 2 8 11 + 13 11 7 14 12 1 3 9 5 0 15 4 8 6 2 10 + 6 15 14 9 11 3 0 8 12 2 13 7 1 4 10 5 + 10 2 8 4 7 6 1 5 15 11 9 14 3 12 13 0 +*/ +#endif + +#define Z00 0 +#define Z01 1 +#define Z02 2 +#define Z03 3 +#define Z04 4 +#define Z05 5 +#define Z06 6 +#define Z07 7 +#define Z08 8 +#define Z09 9 +#define Z0A A +#define Z0B B +#define Z0C C +#define Z0D D +#define Z0E E +#define Z0F F + +#define Z10 E +#define Z11 A +#define Z12 4 +#define Z13 8 +#define Z14 9 +#define Z15 F +#define Z16 D +#define Z17 6 +#define Z18 1 +#define Z19 C +#define Z1A 0 +#define Z1B 2 +#define Z1C B +#define Z1D 7 +#define Z1E 5 +#define Z1F 3 + +#define Z20 B +#define Z21 8 +#define Z22 C +#define Z23 0 +#define Z24 5 +#define Z25 2 +#define Z26 F +#define Z27 D +#define Z28 A +#define Z29 E +#define Z2A 3 +#define Z2B 6 +#define Z2C 7 +#define Z2D 1 +#define Z2E 9 +#define Z2F 4 + +#define Z30 7 +#define Z31 9 +#define Z32 3 +#define Z33 1 +#define Z34 D +#define Z35 C +#define Z36 B +#define Z37 E +#define Z38 2 +#define Z39 6 +#define Z3A 5 +#define Z3B A +#define Z3C 4 +#define Z3D 0 +#define Z3E F +#define Z3F 8 + +#define Z40 9 +#define Z41 0 +#define Z42 5 +#define Z43 7 +#define Z44 2 +#define Z45 4 +#define Z46 A +#define Z47 F +#define Z48 E +#define Z49 1 +#define Z4A B +#define Z4B C +#define Z4C 6 +#define Z4D 8 +#define Z4E 3 +#define Z4F D + +#define Z50 2 +#define Z51 C +#define Z52 6 +#define Z53 A +#define Z54 0 +#define Z55 B +#define Z56 8 +#define Z57 3 +#define Z58 4 +#define Z59 D +#define Z5A 7 +#define Z5B 5 +#define Z5C F +#define Z5D E +#define Z5E 1 +#define Z5F 9 + +#define Z60 C +#define Z61 5 +#define Z62 1 +#define Z63 F +#define Z64 E +#define Z65 D +#define Z66 4 +#define Z67 A +#define Z68 0 +#define Z69 7 +#define Z6A 6 +#define Z6B 3 +#define Z6C 9 +#define Z6D 2 +#define Z6E 8 +#define Z6F B + +#define Z70 D +#define Z71 B +#define Z72 7 +#define Z73 E +#define Z74 C +#define Z75 1 +#define Z76 3 +#define Z77 9 +#define Z78 5 +#define Z79 0 +#define Z7A F +#define Z7B 4 +#define Z7C 8 +#define Z7D 6 +#define Z7E 2 +#define Z7F A + +#define Z80 6 +#define Z81 F +#define Z82 E +#define Z83 9 +#define Z84 B +#define Z85 3 +#define Z86 0 +#define Z87 8 +#define Z88 C +#define Z89 2 +#define Z8A D +#define Z8B 7 +#define Z8C 1 +#define Z8D 4 +#define Z8E A +#define Z8F 5 + +#define Z90 A +#define Z91 2 +#define Z92 8 +#define Z93 4 +#define Z94 7 +#define Z95 6 +#define Z96 1 +#define Z97 5 +#define Z98 F +#define Z99 B +#define Z9A 9 +#define Z9B E +#define Z9C 3 +#define Z9D C +#define Z9E D +#define Z9F 0 + +#define Mx(r, i) Mx_(Z ## r ## i) +#define Mx_(n) Mx__(n) +#define Mx__(n) M ## n + +#define CSx(r, i) CSx_(Z ## r ## i) +#define CSx_(n) CSx__(n) +#define CSx__(n) CS ## n + +#define CS0 SPH_C32(0x243F6A88) +#define CS1 SPH_C32(0x85A308D3) +#define CS2 SPH_C32(0x13198A2E) +#define CS3 SPH_C32(0x03707344) +#define CS4 SPH_C32(0xA4093822) +#define CS5 SPH_C32(0x299F31D0) +#define CS6 SPH_C32(0x082EFA98) +#define CS7 SPH_C32(0xEC4E6C89) +#define CS8 SPH_C32(0x452821E6) +#define CS9 SPH_C32(0x38D01377) +#define CSA SPH_C32(0xBE5466CF) +#define CSB SPH_C32(0x34E90C6C) +#define CSC SPH_C32(0xC0AC29B7) +#define CSD SPH_C32(0xC97C50DD) +#define CSE SPH_C32(0x3F84D5B5) +#define CSF SPH_C32(0xB5470917) + +#if SPH_COMPACT_BLAKE_32 + +static const sph_u32 CS[16] = { + SPH_C32(0x243F6A88), SPH_C32(0x85A308D3), + SPH_C32(0x13198A2E), SPH_C32(0x03707344), + SPH_C32(0xA4093822), SPH_C32(0x299F31D0), + SPH_C32(0x082EFA98), SPH_C32(0xEC4E6C89), + SPH_C32(0x452821E6), SPH_C32(0x38D01377), + SPH_C32(0xBE5466CF), SPH_C32(0x34E90C6C), + SPH_C32(0xC0AC29B7), SPH_C32(0xC97C50DD), + SPH_C32(0x3F84D5B5), SPH_C32(0xB5470917) +}; + +#endif + +#if SPH_64 + +#define CBx(r, i) CBx_(Z ## r ## i) +#define CBx_(n) CBx__(n) +#define CBx__(n) CB ## n + +#define CB0 SPH_C64(0x243F6A8885A308D3) +#define CB1 SPH_C64(0x13198A2E03707344) +#define CB2 SPH_C64(0xA4093822299F31D0) +#define CB3 SPH_C64(0x082EFA98EC4E6C89) +#define CB4 SPH_C64(0x452821E638D01377) +#define CB5 SPH_C64(0xBE5466CF34E90C6C) +#define CB6 SPH_C64(0xC0AC29B7C97C50DD) +#define CB7 SPH_C64(0x3F84D5B5B5470917) +#define CB8 SPH_C64(0x9216D5D98979FB1B) +#define CB9 SPH_C64(0xD1310BA698DFB5AC) +#define CBA SPH_C64(0x2FFD72DBD01ADFB7) +#define CBB SPH_C64(0xB8E1AFED6A267E96) +#define CBC SPH_C64(0xBA7C9045F12C7F99) +#define CBD SPH_C64(0x24A19947B3916CF7) +#define CBE SPH_C64(0x0801F2E2858EFC16) +#define CBF SPH_C64(0x636920D871574E69) + +#if SPH_COMPACT_BLAKE_64 + +static const sph_u64 CB[16] = { + SPH_C64(0x243F6A8885A308D3), SPH_C64(0x13198A2E03707344), + SPH_C64(0xA4093822299F31D0), SPH_C64(0x082EFA98EC4E6C89), + SPH_C64(0x452821E638D01377), SPH_C64(0xBE5466CF34E90C6C), + SPH_C64(0xC0AC29B7C97C50DD), SPH_C64(0x3F84D5B5B5470917), + SPH_C64(0x9216D5D98979FB1B), SPH_C64(0xD1310BA698DFB5AC), + SPH_C64(0x2FFD72DBD01ADFB7), SPH_C64(0xB8E1AFED6A267E96), + SPH_C64(0xBA7C9045F12C7F99), SPH_C64(0x24A19947B3916CF7), + SPH_C64(0x0801F2E2858EFC16), SPH_C64(0x636920D871574E69) +}; + +#endif + +#endif + +#define GS(m0, m1, c0, c1, a, b, c, d) do { \ + a = SPH_T32(a + b + (m0 ^ c1)); \ + d = SPH_ROTR32(d ^ a, 16); \ + c = SPH_T32(c + d); \ + b = SPH_ROTR32(b ^ c, 12); \ + a = SPH_T32(a + b + (m1 ^ c0)); \ + d = SPH_ROTR32(d ^ a, 8); \ + c = SPH_T32(c + d); \ + b = SPH_ROTR32(b ^ c, 7); \ + } while (0) + +#if SPH_COMPACT_BLAKE_32 + +#define ROUND_S(r) do { \ + GS(M[sigma[r][0x0]], M[sigma[r][0x1]], \ + CS[sigma[r][0x0]], CS[sigma[r][0x1]], V0, V4, V8, VC); \ + GS(M[sigma[r][0x2]], M[sigma[r][0x3]], \ + CS[sigma[r][0x2]], CS[sigma[r][0x3]], V1, V5, V9, VD); \ + GS(M[sigma[r][0x4]], M[sigma[r][0x5]], \ + CS[sigma[r][0x4]], CS[sigma[r][0x5]], V2, V6, VA, VE); \ + GS(M[sigma[r][0x6]], M[sigma[r][0x7]], \ + CS[sigma[r][0x6]], CS[sigma[r][0x7]], V3, V7, VB, VF); \ + GS(M[sigma[r][0x8]], M[sigma[r][0x9]], \ + CS[sigma[r][0x8]], CS[sigma[r][0x9]], V0, V5, VA, VF); \ + GS(M[sigma[r][0xA]], M[sigma[r][0xB]], \ + CS[sigma[r][0xA]], CS[sigma[r][0xB]], V1, V6, VB, VC); \ + GS(M[sigma[r][0xC]], M[sigma[r][0xD]], \ + CS[sigma[r][0xC]], CS[sigma[r][0xD]], V2, V7, V8, VD); \ + GS(M[sigma[r][0xE]], M[sigma[r][0xF]], \ + CS[sigma[r][0xE]], CS[sigma[r][0xF]], V3, V4, V9, VE); \ + } while (0) + +#else + +#define ROUND_S(r) do { \ + GS(Mx(r, 0), Mx(r, 1), CSx(r, 0), CSx(r, 1), V0, V4, V8, VC); \ + GS(Mx(r, 2), Mx(r, 3), CSx(r, 2), CSx(r, 3), V1, V5, V9, VD); \ + GS(Mx(r, 4), Mx(r, 5), CSx(r, 4), CSx(r, 5), V2, V6, VA, VE); \ + GS(Mx(r, 6), Mx(r, 7), CSx(r, 6), CSx(r, 7), V3, V7, VB, VF); \ + GS(Mx(r, 8), Mx(r, 9), CSx(r, 8), CSx(r, 9), V0, V5, VA, VF); \ + GS(Mx(r, A), Mx(r, B), CSx(r, A), CSx(r, B), V1, V6, VB, VC); \ + GS(Mx(r, C), Mx(r, D), CSx(r, C), CSx(r, D), V2, V7, V8, VD); \ + GS(Mx(r, E), Mx(r, F), CSx(r, E), CSx(r, F), V3, V4, V9, VE); \ + } while (0) + +#endif + +#if SPH_64 + +#define GB(m0, m1, c0, c1, a, b, c, d) do { \ + a = SPH_T64(a + b + (m0 ^ c1)); \ + d = SPH_ROTR64(d ^ a, 32); \ + c = SPH_T64(c + d); \ + b = SPH_ROTR64(b ^ c, 25); \ + a = SPH_T64(a + b + (m1 ^ c0)); \ + d = SPH_ROTR64(d ^ a, 16); \ + c = SPH_T64(c + d); \ + b = SPH_ROTR64(b ^ c, 11); \ + } while (0) + +#if SPH_COMPACT_BLAKE_64 + +#define ROUND_B(r) do { \ + GB(M[sigma[r][0x0]], M[sigma[r][0x1]], \ + CB[sigma[r][0x0]], CB[sigma[r][0x1]], V0, V4, V8, VC); \ + GB(M[sigma[r][0x2]], M[sigma[r][0x3]], \ + CB[sigma[r][0x2]], CB[sigma[r][0x3]], V1, V5, V9, VD); \ + GB(M[sigma[r][0x4]], M[sigma[r][0x5]], \ + CB[sigma[r][0x4]], CB[sigma[r][0x5]], V2, V6, VA, VE); \ + GB(M[sigma[r][0x6]], M[sigma[r][0x7]], \ + CB[sigma[r][0x6]], CB[sigma[r][0x7]], V3, V7, VB, VF); \ + GB(M[sigma[r][0x8]], M[sigma[r][0x9]], \ + CB[sigma[r][0x8]], CB[sigma[r][0x9]], V0, V5, VA, VF); \ + GB(M[sigma[r][0xA]], M[sigma[r][0xB]], \ + CB[sigma[r][0xA]], CB[sigma[r][0xB]], V1, V6, VB, VC); \ + GB(M[sigma[r][0xC]], M[sigma[r][0xD]], \ + CB[sigma[r][0xC]], CB[sigma[r][0xD]], V2, V7, V8, VD); \ + GB(M[sigma[r][0xE]], M[sigma[r][0xF]], \ + CB[sigma[r][0xE]], CB[sigma[r][0xF]], V3, V4, V9, VE); \ + } while (0) + +#else + +#define ROUND_B(r) do { \ + GB(Mx(r, 0), Mx(r, 1), CBx(r, 0), CBx(r, 1), V0, V4, V8, VC); \ + GB(Mx(r, 2), Mx(r, 3), CBx(r, 2), CBx(r, 3), V1, V5, V9, VD); \ + GB(Mx(r, 4), Mx(r, 5), CBx(r, 4), CBx(r, 5), V2, V6, VA, VE); \ + GB(Mx(r, 6), Mx(r, 7), CBx(r, 6), CBx(r, 7), V3, V7, VB, VF); \ + GB(Mx(r, 8), Mx(r, 9), CBx(r, 8), CBx(r, 9), V0, V5, VA, VF); \ + GB(Mx(r, A), Mx(r, B), CBx(r, A), CBx(r, B), V1, V6, VB, VC); \ + GB(Mx(r, C), Mx(r, D), CBx(r, C), CBx(r, D), V2, V7, V8, VD); \ + GB(Mx(r, E), Mx(r, F), CBx(r, E), CBx(r, F), V3, V4, V9, VE); \ + } while (0) + +#endif + +#endif + +#define DECL_STATE32 \ + sph_u32 H0, H1, H2, H3, H4, H5, H6, H7; \ + sph_u32 S0, S1, S2, S3, T0, T1; + +#define READ_STATE32(state) do { \ + H0 = (state)->H[0]; \ + H1 = (state)->H[1]; \ + H2 = (state)->H[2]; \ + H3 = (state)->H[3]; \ + H4 = (state)->H[4]; \ + H5 = (state)->H[5]; \ + H6 = (state)->H[6]; \ + H7 = (state)->H[7]; \ + S0 = (state)->S[0]; \ + S1 = (state)->S[1]; \ + S2 = (state)->S[2]; \ + S3 = (state)->S[3]; \ + T0 = (state)->T0; \ + T1 = (state)->T1; \ + } while (0) + +#define WRITE_STATE32(state) do { \ + (state)->H[0] = H0; \ + (state)->H[1] = H1; \ + (state)->H[2] = H2; \ + (state)->H[3] = H3; \ + (state)->H[4] = H4; \ + (state)->H[5] = H5; \ + (state)->H[6] = H6; \ + (state)->H[7] = H7; \ + (state)->S[0] = S0; \ + (state)->S[1] = S1; \ + (state)->S[2] = S2; \ + (state)->S[3] = S3; \ + (state)->T0 = T0; \ + (state)->T1 = T1; \ + } while (0) + +#if SPH_COMPACT_BLAKE_32 + +#define COMPRESS32 do { \ + sph_u32 M[16]; \ + sph_u32 V0, V1, V2, V3, V4, V5, V6, V7; \ + sph_u32 V8, V9, VA, VB, VC, VD, VE, VF; \ + unsigned r; \ + V0 = H0; \ + V1 = H1; \ + V2 = H2; \ + V3 = H3; \ + V4 = H4; \ + V5 = H5; \ + V6 = H6; \ + V7 = H7; \ + V8 = S0 ^ CS0; \ + V9 = S1 ^ CS1; \ + VA = S2 ^ CS2; \ + VB = S3 ^ CS3; \ + VC = T0 ^ CS4; \ + VD = T0 ^ CS5; \ + VE = T1 ^ CS6; \ + VF = T1 ^ CS7; \ + M[0x0] = sph_dec32be_aligned(buf + 0); \ + M[0x1] = sph_dec32be_aligned(buf + 4); \ + M[0x2] = sph_dec32be_aligned(buf + 8); \ + M[0x3] = sph_dec32be_aligned(buf + 12); \ + M[0x4] = sph_dec32be_aligned(buf + 16); \ + M[0x5] = sph_dec32be_aligned(buf + 20); \ + M[0x6] = sph_dec32be_aligned(buf + 24); \ + M[0x7] = sph_dec32be_aligned(buf + 28); \ + M[0x8] = sph_dec32be_aligned(buf + 32); \ + M[0x9] = sph_dec32be_aligned(buf + 36); \ + M[0xA] = sph_dec32be_aligned(buf + 40); \ + M[0xB] = sph_dec32be_aligned(buf + 44); \ + M[0xC] = sph_dec32be_aligned(buf + 48); \ + M[0xD] = sph_dec32be_aligned(buf + 52); \ + M[0xE] = sph_dec32be_aligned(buf + 56); \ + M[0xF] = sph_dec32be_aligned(buf + 60); \ + for (r = 0; r < 14; r ++) \ + ROUND_S(r); \ + H0 ^= S0 ^ V0 ^ V8; \ + H1 ^= S1 ^ V1 ^ V9; \ + H2 ^= S2 ^ V2 ^ VA; \ + H3 ^= S3 ^ V3 ^ VB; \ + H4 ^= S0 ^ V4 ^ VC; \ + H5 ^= S1 ^ V5 ^ VD; \ + H6 ^= S2 ^ V6 ^ VE; \ + H7 ^= S3 ^ V7 ^ VF; \ + } while (0) + +#else + +#define COMPRESS32 do { \ + sph_u32 M0, M1, M2, M3, M4, M5, M6, M7; \ + sph_u32 M8, M9, MA, MB, MC, MD, ME, MF; \ + sph_u32 V0, V1, V2, V3, V4, V5, V6, V7; \ + sph_u32 V8, V9, VA, VB, VC, VD, VE, VF; \ + V0 = H0; \ + V1 = H1; \ + V2 = H2; \ + V3 = H3; \ + V4 = H4; \ + V5 = H5; \ + V6 = H6; \ + V7 = H7; \ + V8 = S0 ^ CS0; \ + V9 = S1 ^ CS1; \ + VA = S2 ^ CS2; \ + VB = S3 ^ CS3; \ + VC = T0 ^ CS4; \ + VD = T0 ^ CS5; \ + VE = T1 ^ CS6; \ + VF = T1 ^ CS7; \ + M0 = sph_dec32be_aligned(buf + 0); \ + M1 = sph_dec32be_aligned(buf + 4); \ + M2 = sph_dec32be_aligned(buf + 8); \ + M3 = sph_dec32be_aligned(buf + 12); \ + M4 = sph_dec32be_aligned(buf + 16); \ + M5 = sph_dec32be_aligned(buf + 20); \ + M6 = sph_dec32be_aligned(buf + 24); \ + M7 = sph_dec32be_aligned(buf + 28); \ + M8 = sph_dec32be_aligned(buf + 32); \ + M9 = sph_dec32be_aligned(buf + 36); \ + MA = sph_dec32be_aligned(buf + 40); \ + MB = sph_dec32be_aligned(buf + 44); \ + MC = sph_dec32be_aligned(buf + 48); \ + MD = sph_dec32be_aligned(buf + 52); \ + ME = sph_dec32be_aligned(buf + 56); \ + MF = sph_dec32be_aligned(buf + 60); \ + ROUND_S(0); \ + ROUND_S(1); \ + ROUND_S(2); \ + ROUND_S(3); \ + ROUND_S(4); \ + ROUND_S(5); \ + ROUND_S(6); \ + ROUND_S(7); \ + ROUND_S(8); \ + ROUND_S(9); \ + ROUND_S(0); \ + ROUND_S(1); \ + ROUND_S(2); \ + ROUND_S(3); \ + H0 ^= S0 ^ V0 ^ V8; \ + H1 ^= S1 ^ V1 ^ V9; \ + H2 ^= S2 ^ V2 ^ VA; \ + H3 ^= S3 ^ V3 ^ VB; \ + H4 ^= S0 ^ V4 ^ VC; \ + H5 ^= S1 ^ V5 ^ VD; \ + H6 ^= S2 ^ V6 ^ VE; \ + H7 ^= S3 ^ V7 ^ VF; \ + } while (0) + +#endif + +#if SPH_64 + +#define DECL_STATE64 \ + sph_u64 H0, H1, H2, H3, H4, H5, H6, H7; \ + sph_u64 S0, S1, S2, S3, T0, T1; + +#define READ_STATE64(state) do { \ + H0 = (state)->H[0]; \ + H1 = (state)->H[1]; \ + H2 = (state)->H[2]; \ + H3 = (state)->H[3]; \ + H4 = (state)->H[4]; \ + H5 = (state)->H[5]; \ + H6 = (state)->H[6]; \ + H7 = (state)->H[7]; \ + S0 = (state)->S[0]; \ + S1 = (state)->S[1]; \ + S2 = (state)->S[2]; \ + S3 = (state)->S[3]; \ + T0 = (state)->T0; \ + T1 = (state)->T1; \ + } while (0) + +#define WRITE_STATE64(state) do { \ + (state)->H[0] = H0; \ + (state)->H[1] = H1; \ + (state)->H[2] = H2; \ + (state)->H[3] = H3; \ + (state)->H[4] = H4; \ + (state)->H[5] = H5; \ + (state)->H[6] = H6; \ + (state)->H[7] = H7; \ + (state)->S[0] = S0; \ + (state)->S[1] = S1; \ + (state)->S[2] = S2; \ + (state)->S[3] = S3; \ + (state)->T0 = T0; \ + (state)->T1 = T1; \ + } while (0) + +#if SPH_COMPACT_BLAKE_64 + +#define COMPRESS64 do { \ + sph_u64 M[16]; \ + sph_u64 V0, V1, V2, V3, V4, V5, V6, V7; \ + sph_u64 V8, V9, VA, VB, VC, VD, VE, VF; \ + unsigned r; \ + V0 = H0; \ + V1 = H1; \ + V2 = H2; \ + V3 = H3; \ + V4 = H4; \ + V5 = H5; \ + V6 = H6; \ + V7 = H7; \ + V8 = S0 ^ CB0; \ + V9 = S1 ^ CB1; \ + VA = S2 ^ CB2; \ + VB = S3 ^ CB3; \ + VC = T0 ^ CB4; \ + VD = T0 ^ CB5; \ + VE = T1 ^ CB6; \ + VF = T1 ^ CB7; \ + M[0x0] = sph_dec64be_aligned(buf + 0); \ + M[0x1] = sph_dec64be_aligned(buf + 8); \ + M[0x2] = sph_dec64be_aligned(buf + 16); \ + M[0x3] = sph_dec64be_aligned(buf + 24); \ + M[0x4] = sph_dec64be_aligned(buf + 32); \ + M[0x5] = sph_dec64be_aligned(buf + 40); \ + M[0x6] = sph_dec64be_aligned(buf + 48); \ + M[0x7] = sph_dec64be_aligned(buf + 56); \ + M[0x8] = sph_dec64be_aligned(buf + 64); \ + M[0x9] = sph_dec64be_aligned(buf + 72); \ + M[0xA] = sph_dec64be_aligned(buf + 80); \ + M[0xB] = sph_dec64be_aligned(buf + 88); \ + M[0xC] = sph_dec64be_aligned(buf + 96); \ + M[0xD] = sph_dec64be_aligned(buf + 104); \ + M[0xE] = sph_dec64be_aligned(buf + 112); \ + M[0xF] = sph_dec64be_aligned(buf + 120); \ + for (r = 0; r < 16; r ++) \ + ROUND_B(r); \ + H0 ^= S0 ^ V0 ^ V8; \ + H1 ^= S1 ^ V1 ^ V9; \ + H2 ^= S2 ^ V2 ^ VA; \ + H3 ^= S3 ^ V3 ^ VB; \ + H4 ^= S0 ^ V4 ^ VC; \ + H5 ^= S1 ^ V5 ^ VD; \ + H6 ^= S2 ^ V6 ^ VE; \ + H7 ^= S3 ^ V7 ^ VF; \ + } while (0) + +#else + +#define COMPRESS64 do { \ + sph_u64 M0, M1, M2, M3, M4, M5, M6, M7; \ + sph_u64 M8, M9, MA, MB, MC, MD, ME, MF; \ + sph_u64 V0, V1, V2, V3, V4, V5, V6, V7; \ + sph_u64 V8, V9, VA, VB, VC, VD, VE, VF; \ + V0 = H0; \ + V1 = H1; \ + V2 = H2; \ + V3 = H3; \ + V4 = H4; \ + V5 = H5; \ + V6 = H6; \ + V7 = H7; \ + V8 = S0 ^ CB0; \ + V9 = S1 ^ CB1; \ + VA = S2 ^ CB2; \ + VB = S3 ^ CB3; \ + VC = T0 ^ CB4; \ + VD = T0 ^ CB5; \ + VE = T1 ^ CB6; \ + VF = T1 ^ CB7; \ + M0 = sph_dec64be_aligned(buf + 0); \ + M1 = sph_dec64be_aligned(buf + 8); \ + M2 = sph_dec64be_aligned(buf + 16); \ + M3 = sph_dec64be_aligned(buf + 24); \ + M4 = sph_dec64be_aligned(buf + 32); \ + M5 = sph_dec64be_aligned(buf + 40); \ + M6 = sph_dec64be_aligned(buf + 48); \ + M7 = sph_dec64be_aligned(buf + 56); \ + M8 = sph_dec64be_aligned(buf + 64); \ + M9 = sph_dec64be_aligned(buf + 72); \ + MA = sph_dec64be_aligned(buf + 80); \ + MB = sph_dec64be_aligned(buf + 88); \ + MC = sph_dec64be_aligned(buf + 96); \ + MD = sph_dec64be_aligned(buf + 104); \ + ME = sph_dec64be_aligned(buf + 112); \ + MF = sph_dec64be_aligned(buf + 120); \ + ROUND_B(0); \ + ROUND_B(1); \ + ROUND_B(2); \ + ROUND_B(3); \ + ROUND_B(4); \ + ROUND_B(5); \ + ROUND_B(6); \ + ROUND_B(7); \ + ROUND_B(8); \ + ROUND_B(9); \ + ROUND_B(0); \ + ROUND_B(1); \ + ROUND_B(2); \ + ROUND_B(3); \ + ROUND_B(4); \ + ROUND_B(5); \ + H0 ^= S0 ^ V0 ^ V8; \ + H1 ^= S1 ^ V1 ^ V9; \ + H2 ^= S2 ^ V2 ^ VA; \ + H3 ^= S3 ^ V3 ^ VB; \ + H4 ^= S0 ^ V4 ^ VC; \ + H5 ^= S1 ^ V5 ^ VD; \ + H6 ^= S2 ^ V6 ^ VE; \ + H7 ^= S3 ^ V7 ^ VF; \ + } while (0) + +#endif + +#endif + +static const sph_u32 salt_zero_small[4] = { 0, 0, 0, 0 }; + +static void +blake32_init(sph_blake_small_context *sc, + const sph_u32 *iv, const sph_u32 *salt) +{ + memcpy(sc->H, iv, 8 * sizeof(sph_u32)); + memcpy(sc->S, salt, 4 * sizeof(sph_u32)); + sc->T0 = sc->T1 = 0; + sc->ptr = 0; +} + +static void +blake32(sph_blake_small_context *sc, const void *data, size_t len) +{ + unsigned char *buf; + size_t ptr; + DECL_STATE32 + + buf = sc->buf; + ptr = sc->ptr; + if (len < (sizeof sc->buf) - ptr) { + memcpy(buf + ptr, data, len); + ptr += len; + sc->ptr = ptr; + return; + } + + READ_STATE32(sc); + while (len > 0) { + size_t clen; + + clen = (sizeof sc->buf) - ptr; + if (clen > len) + clen = len; + memcpy(buf + ptr, data, clen); + ptr += clen; + data = (const unsigned char *)data + clen; + len -= clen; + if (ptr == sizeof sc->buf) { + if ((T0 = SPH_T32(T0 + 512)) < 512) + T1 = SPH_T32(T1 + 1); + COMPRESS32; + ptr = 0; + } + } + WRITE_STATE32(sc); + sc->ptr = ptr; +} + +static void +blake32_close(sph_blake_small_context *sc, + unsigned ub, unsigned n, void *dst, size_t out_size_w32) +{ + union { + unsigned char buf[64]; + sph_u32 dummy; + } u; + size_t ptr, k; + unsigned bit_len; + unsigned z; + sph_u32 th, tl; + unsigned char *out; + + ptr = sc->ptr; + bit_len = ((unsigned)ptr << 3) + n; + z = 0x80 >> n; + u.buf[ptr] = ((ub & -z) | z) & 0xFF; + tl = sc->T0 + bit_len; + th = sc->T1; + if (ptr == 0 && n == 0) { + sc->T0 = SPH_C32(0xFFFFFE00); + sc->T1 = SPH_C32(0xFFFFFFFF); + } else if (sc->T0 == 0) { + sc->T0 = SPH_C32(0xFFFFFE00) + bit_len; + sc->T1 = SPH_T32(sc->T1 - 1); + } else { + sc->T0 -= 512 - bit_len; + } + if (bit_len <= 446) { + memset(u.buf + ptr + 1, 0, 55 - ptr); + if (out_size_w32 == 8) + u.buf[55] |= 1; + sph_enc32be_aligned(u.buf + 56, th); + sph_enc32be_aligned(u.buf + 60, tl); + blake32(sc, u.buf + ptr, 64 - ptr); + } else { + memset(u.buf + ptr + 1, 0, 63 - ptr); + blake32(sc, u.buf + ptr, 64 - ptr); + sc->T0 = SPH_C32(0xFFFFFE00); + sc->T1 = SPH_C32(0xFFFFFFFF); + memset(u.buf, 0, 56); + if (out_size_w32 == 8) + u.buf[55] = 1; + sph_enc32be_aligned(u.buf + 56, th); + sph_enc32be_aligned(u.buf + 60, tl); + blake32(sc, u.buf, 64); + } + out = dst; + for (k = 0; k < out_size_w32; k ++) + sph_enc32be(out + (k << 2), sc->H[k]); +} + +#if SPH_64 + +static const sph_u64 salt_zero_big[4] = { 0, 0, 0, 0 }; + +static void +blake64_init(sph_blake_big_context *sc, + const sph_u64 *iv, const sph_u64 *salt) +{ + memcpy(sc->H, iv, 8 * sizeof(sph_u64)); + memcpy(sc->S, salt, 4 * sizeof(sph_u64)); + sc->T0 = sc->T1 = 0; + sc->ptr = 0; +} + +static void +blake64(sph_blake_big_context *sc, const void *data, size_t len) +{ + unsigned char *buf; + size_t ptr; + DECL_STATE64 + + buf = sc->buf; + ptr = sc->ptr; + if (len < (sizeof sc->buf) - ptr) { + memcpy(buf + ptr, data, len); + ptr += len; + sc->ptr = ptr; + return; + } + + READ_STATE64(sc); + while (len > 0) { + size_t clen; + + clen = (sizeof sc->buf) - ptr; + if (clen > len) + clen = len; + memcpy(buf + ptr, data, clen); + ptr += clen; + data = (const unsigned char *)data + clen; + len -= clen; + if (ptr == sizeof sc->buf) { + if ((T0 = SPH_T64(T0 + 1024)) < 1024) + T1 = SPH_T64(T1 + 1); + COMPRESS64; + ptr = 0; + } + } + WRITE_STATE64(sc); + sc->ptr = ptr; +} + +static void +blake64_close(sph_blake_big_context *sc, + unsigned ub, unsigned n, void *dst, size_t out_size_w64) +{ + union { + unsigned char buf[128]; + sph_u64 dummy; + } u; + size_t ptr, k; + unsigned bit_len; + unsigned z; + sph_u64 th, tl; + unsigned char *out; + + ptr = sc->ptr; + bit_len = ((unsigned)ptr << 3) + n; + z = 0x80 >> n; + u.buf[ptr] = ((ub & -z) | z) & 0xFF; + tl = sc->T0 + bit_len; + th = sc->T1; + if (ptr == 0 && n == 0) { + sc->T0 = SPH_C64(0xFFFFFFFFFFFFFC00); + sc->T1 = SPH_C64(0xFFFFFFFFFFFFFFFF); + } else if (sc->T0 == 0) { + sc->T0 = SPH_C64(0xFFFFFFFFFFFFFC00) + bit_len; + sc->T1 = SPH_T64(sc->T1 - 1); + } else { + sc->T0 -= 1024 - bit_len; + } + if (bit_len <= 894) { + memset(u.buf + ptr + 1, 0, 111 - ptr); + if (out_size_w64 == 8) + u.buf[111] |= 1; + sph_enc64be_aligned(u.buf + 112, th); + sph_enc64be_aligned(u.buf + 120, tl); + blake64(sc, u.buf + ptr, 128 - ptr); + } else { + memset(u.buf + ptr + 1, 0, 127 - ptr); + blake64(sc, u.buf + ptr, 128 - ptr); + sc->T0 = SPH_C64(0xFFFFFFFFFFFFFC00); + sc->T1 = SPH_C64(0xFFFFFFFFFFFFFFFF); + memset(u.buf, 0, 112); + if (out_size_w64 == 8) + u.buf[111] = 1; + sph_enc64be_aligned(u.buf + 112, th); + sph_enc64be_aligned(u.buf + 120, tl); + blake64(sc, u.buf, 128); + } + out = dst; + for (k = 0; k < out_size_w64; k ++) + sph_enc64be(out + (k << 3), sc->H[k]); +} + +#endif + +/* see sph_blake.h */ +void +sph_blake224_init(void *cc) +{ + blake32_init(cc, IV224, salt_zero_small); +} + +/* see sph_blake.h */ +void +sph_blake224(void *cc, const void *data, size_t len) +{ + blake32(cc, data, len); +} + +/* see sph_blake.h */ +void +sph_blake224_close(void *cc, void *dst) +{ + sph_blake224_addbits_and_close(cc, 0, 0, dst); +} + +/* see sph_blake.h */ +void +sph_blake224_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + blake32_close(cc, ub, n, dst, 7); + sph_blake224_init(cc); +} + +/* see sph_blake.h */ +void +sph_blake256_init(void *cc) +{ + blake32_init(cc, IV256, salt_zero_small); +} + +/* see sph_blake.h */ +void +sph_blake256(void *cc, const void *data, size_t len) +{ + blake32(cc, data, len); +} + +/* see sph_blake.h */ +void +sph_blake256_close(void *cc, void *dst) +{ + sph_blake256_addbits_and_close(cc, 0, 0, dst); +} + +/* see sph_blake.h */ +void +sph_blake256_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + blake32_close(cc, ub, n, dst, 8); + sph_blake256_init(cc); +} + +#if SPH_64 + +/* see sph_blake.h */ +void +sph_blake384_init(void *cc) +{ + blake64_init(cc, IV384, salt_zero_big); +} + +/* see sph_blake.h */ +void +sph_blake384(void *cc, const void *data, size_t len) +{ + blake64(cc, data, len); +} + +/* see sph_blake.h */ +void +sph_blake384_close(void *cc, void *dst) +{ + sph_blake384_addbits_and_close(cc, 0, 0, dst); +} + +/* see sph_blake.h */ +void +sph_blake384_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + blake64_close(cc, ub, n, dst, 6); + sph_blake384_init(cc); +} + +/* see sph_blake.h */ +void +sph_blake512_init(void *cc) +{ + blake64_init(cc, IV512, salt_zero_big); +} + +/* see sph_blake.h */ +void +sph_blake512(void *cc, const void *data, size_t len) +{ + blake64(cc, data, len); +} + +/* see sph_blake.h */ +void +sph_blake512_close(void *cc, void *dst) +{ + sph_blake512_addbits_and_close(cc, 0, 0, dst); +} + +/* see sph_blake.h */ +void +sph_blake512_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + blake64_close(cc, ub, n, dst, 8); + sph_blake512_init(cc); +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/src/bloom.cpp b/src/bloom.cpp new file mode 100644 index 0000000..d9ec2ef --- /dev/null +++ b/src/bloom.cpp @@ -0,0 +1,160 @@ +// 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 + +#include "bloom.h" +#include "main.h" +#include "script.h" + +#define LN2SQUARED 0.4804530139182014246671025263266649717305529515945455 +#define LN2 0.6931471805599453094172321214581765680755001343602552 + +using namespace std; + +static const unsigned char bit_mask[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; + +CBloomFilter::CBloomFilter(unsigned int nElements, double nFPRate, unsigned int nTweakIn, unsigned char nFlagsIn) : +// The ideal size for a bloom filter with a given number of elements and false positive rate is: +// - nElements * log(fp rate) / ln(2)^2 +// We ignore filter parameters which will create a bloom filter larger than the protocol limits +vData(min((unsigned int)(-1 / LN2SQUARED * nElements * log(nFPRate)), MAX_BLOOM_FILTER_SIZE * 8) / 8), +// The ideal number of hash functions is filter size * ln(2) / number of elements +// Again, we ignore filter parameters which will create a bloom filter with more hash functions than the protocol limits +// See http://en.wikipedia.org/wiki/Bloom_filter for an explanation of these formulas +nHashFuncs(min((unsigned int)(vData.size() * 8 / nElements * LN2), MAX_HASH_FUNCS)), +nTweak(nTweakIn), +nFlags(nFlagsIn) +{ +} + +inline unsigned int CBloomFilter::Hash(unsigned int nHashNum, const std::vector& vDataToHash) const +{ + // 0xFBA4C795 chosen as it guarantees a reasonable bit difference between nHashNum values. + return MurmurHash3(nHashNum * 0xFBA4C795 + nTweak, vDataToHash) % (vData.size() * 8); +} + +void CBloomFilter::insert(const vector& vKey) +{ + if (vData.size() == 1 && vData[0] == 0xff) + return; + for (unsigned int i = 0; i < nHashFuncs; i++) + { + unsigned int nIndex = Hash(i, vKey); + // Sets bit nIndex of vData + vData[nIndex >> 3] |= bit_mask[7 & nIndex]; + } +} + +void CBloomFilter::insert(const COutPoint& outpoint) +{ + CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); + stream << outpoint; + vector data(stream.begin(), stream.end()); + insert(data); +} + +void CBloomFilter::insert(const uint256& hash) +{ + vector data(hash.begin(), hash.end()); + insert(data); +} + +bool CBloomFilter::contains(const vector& vKey) const +{ + if (vData.size() == 1 && vData[0] == 0xff) + return true; + for (unsigned int i = 0; i < nHashFuncs; i++) + { + unsigned int nIndex = Hash(i, vKey); + // Checks bit nIndex of vData + if (!(vData[nIndex >> 3] & bit_mask[7 & nIndex])) + return false; + } + return true; +} + +bool CBloomFilter::contains(const COutPoint& outpoint) const +{ + CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); + stream << outpoint; + vector data(stream.begin(), stream.end()); + return contains(data); +} + +bool CBloomFilter::contains(const uint256& hash) const +{ + vector data(hash.begin(), hash.end()); + return contains(data); +} + +bool CBloomFilter::IsWithinSizeConstraints() const +{ + return vData.size() <= MAX_BLOOM_FILTER_SIZE && nHashFuncs <= MAX_HASH_FUNCS; +} + +bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx, const uint256& hash) +{ + bool fFound = false; + // Match if the filter contains the hash of tx + // for finding tx when they appear in a block + if (contains(hash)) + fFound = true; + + for (unsigned int i = 0; i < tx.vout.size(); i++) + { + const CTxOut& txout = tx.vout[i]; + // Match if the filter contains any arbitrary script data element in any scriptPubKey in tx + // If this matches, also add the specific output that was matched. + // This means clients don't have to update the filter themselves when a new relevant tx + // is discovered in order to find spending transactions, which avoids round-tripping and race conditions. + CScript::const_iterator pc = txout.scriptPubKey.begin(); + vector data; + while (pc < txout.scriptPubKey.end()) + { + opcodetype opcode; + if (!txout.scriptPubKey.GetOp(pc, opcode, data)) + break; + if (data.size() != 0 && contains(data)) + { + fFound = true; + if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_ALL) + insert(COutPoint(hash, i)); + else if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_P2PUBKEY_ONLY) + { + txnouttype type; + vector > vSolutions; + if (Solver(txout.scriptPubKey, type, vSolutions) && + (type == TX_PUBKEY || type == TX_MULTISIG)) + insert(COutPoint(hash, i)); + } + break; + } + } + } + + if (fFound) + return true; + + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + // Match if the filter contains an outpoint tx spends + if (contains(txin.prevout)) + return true; + + // Match if the filter contains any arbitrary script data element in any scriptSig in tx + CScript::const_iterator pc = txin.scriptSig.begin(); + vector data; + while (pc < txin.scriptSig.end()) + { + opcodetype opcode; + if (!txin.scriptSig.GetOp(pc, opcode, data)) + break; + if (data.size() != 0 && contains(data)) + return true; + } + } + + return false; +} diff --git a/src/bloom.h b/src/bloom.h new file mode 100644 index 0000000..389ae74 --- /dev/null +++ b/src/bloom.h @@ -0,0 +1,88 @@ +// 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_BLOOM_H +#define BITCOIN_BLOOM_H + +#include + +#include "uint256.h" +#include "serialize.h" + +class COutPoint; +class CTransaction; + +// 20,000 items with fp rate < 0.1% or 10,000 items and <0.0001% +static const unsigned int MAX_BLOOM_FILTER_SIZE = 36000; // bytes +static const unsigned int MAX_HASH_FUNCS = 50; + +// First two bits of nFlags control how much IsRelevantAndUpdate actually updates +// The remaining bits are reserved +enum bloomflags +{ + BLOOM_UPDATE_NONE = 0, + BLOOM_UPDATE_ALL = 1, + // Only adds outpoints to the filter if the output is a pay-to-pubkey/pay-to-multisig script + BLOOM_UPDATE_P2PUBKEY_ONLY = 2, + BLOOM_UPDATE_MASK = 3, +}; + +/** + * BloomFilter is a probabilistic filter which SPV clients provide + * so that we can filter the transactions we sends them. + * + * This allows for significantly more efficient transaction and block downloads. + * + * Because bloom filters are probabilistic, an SPV node can increase the false- + * positive rate, making us send them transactions which aren't actually theirs, + * allowing clients to trade more bandwidth for more privacy by obfuscating which + * keys are owned by them. + */ +class CBloomFilter +{ +private: + std::vector vData; + unsigned int nHashFuncs; + unsigned int nTweak; + unsigned char nFlags; + + unsigned int Hash(unsigned int nHashNum, const std::vector& vDataToHash) const; + +public: + // Creates a new bloom filter which will provide the given fp rate when filled with the given number of elements + // Note that if the given parameters will result in a filter outside the bounds of the protocol limits, + // the filter created will be as close to the given parameters as possible within the protocol limits. + // This will apply if nFPRate is very low or nElements is unreasonably high. + // nTweak is a constant which is added to the seed value passed to the hash function + // It should generally always be a random value (and is largely only exposed for unit testing) + // nFlags should be one of the BLOOM_UPDATE_* enums (not _MASK) + CBloomFilter(unsigned int nElements, double nFPRate, unsigned int nTweak, unsigned char nFlagsIn); + // Using a filter initialized with this results in undefined behavior + // Should only be used for deserialization + CBloomFilter() {} + + IMPLEMENT_SERIALIZE + ( + READWRITE(vData); + READWRITE(nHashFuncs); + READWRITE(nTweak); + READWRITE(nFlags); + ) + + void insert(const std::vector& vKey); + void insert(const COutPoint& outpoint); + void insert(const uint256& hash); + + bool contains(const std::vector& vKey) const; + bool contains(const COutPoint& outpoint) const; + bool contains(const uint256& hash) const; + + // True if the size is <= MAX_BLOOM_FILTER_SIZE and the number of hash functions is <= MAX_HASH_FUNCS + // (catch a filter which was just deserialized which was too big) + bool IsWithinSizeConstraints() const; + + // Also adds any outputs which match the filter to the filter (to match their spending txes) + bool IsRelevantAndUpdate(const CTransaction& tx, const uint256& hash); +}; + +#endif /* BITCOIN_BLOOM_H */ diff --git a/src/bmw.c b/src/bmw.c new file mode 100644 index 0000000..b89a881 --- /dev/null +++ b/src/bmw.c @@ -0,0 +1,965 @@ +/* $Id: bmw.c 227 2010-06-16 17:28:38Z tp $ */ +/* + * BMW implementation. + * + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * 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 END)============================= + * + * @author Thomas Pornin + */ + +#include +#include +#include + +#ifdef __cplusplus +extern "C"{ +#endif + +#include "sph_bmw.h" + +#if SPH_SMALL_FOOTPRINT && !defined SPH_SMALL_FOOTPRINT_BMW +#define SPH_SMALL_FOOTPRINT_BMW 1 +#endif + +#ifdef _MSC_VER +#pragma warning (disable: 4146) +#endif + +static const sph_u32 IV224[] = { + SPH_C32(0x00010203), SPH_C32(0x04050607), + SPH_C32(0x08090A0B), SPH_C32(0x0C0D0E0F), + SPH_C32(0x10111213), SPH_C32(0x14151617), + SPH_C32(0x18191A1B), SPH_C32(0x1C1D1E1F), + SPH_C32(0x20212223), SPH_C32(0x24252627), + SPH_C32(0x28292A2B), SPH_C32(0x2C2D2E2F), + SPH_C32(0x30313233), SPH_C32(0x34353637), + SPH_C32(0x38393A3B), SPH_C32(0x3C3D3E3F) +}; + +static const sph_u32 IV256[] = { + SPH_C32(0x40414243), SPH_C32(0x44454647), + SPH_C32(0x48494A4B), SPH_C32(0x4C4D4E4F), + SPH_C32(0x50515253), SPH_C32(0x54555657), + SPH_C32(0x58595A5B), SPH_C32(0x5C5D5E5F), + SPH_C32(0x60616263), SPH_C32(0x64656667), + SPH_C32(0x68696A6B), SPH_C32(0x6C6D6E6F), + SPH_C32(0x70717273), SPH_C32(0x74757677), + SPH_C32(0x78797A7B), SPH_C32(0x7C7D7E7F) +}; + +#if SPH_64 + +static const sph_u64 IV384[] = { + SPH_C64(0x0001020304050607), SPH_C64(0x08090A0B0C0D0E0F), + SPH_C64(0x1011121314151617), SPH_C64(0x18191A1B1C1D1E1F), + SPH_C64(0x2021222324252627), SPH_C64(0x28292A2B2C2D2E2F), + SPH_C64(0x3031323334353637), SPH_C64(0x38393A3B3C3D3E3F), + SPH_C64(0x4041424344454647), SPH_C64(0x48494A4B4C4D4E4F), + SPH_C64(0x5051525354555657), SPH_C64(0x58595A5B5C5D5E5F), + SPH_C64(0x6061626364656667), SPH_C64(0x68696A6B6C6D6E6F), + SPH_C64(0x7071727374757677), SPH_C64(0x78797A7B7C7D7E7F) +}; + +static const sph_u64 IV512[] = { + SPH_C64(0x8081828384858687), SPH_C64(0x88898A8B8C8D8E8F), + SPH_C64(0x9091929394959697), SPH_C64(0x98999A9B9C9D9E9F), + SPH_C64(0xA0A1A2A3A4A5A6A7), SPH_C64(0xA8A9AAABACADAEAF), + SPH_C64(0xB0B1B2B3B4B5B6B7), SPH_C64(0xB8B9BABBBCBDBEBF), + SPH_C64(0xC0C1C2C3C4C5C6C7), SPH_C64(0xC8C9CACBCCCDCECF), + SPH_C64(0xD0D1D2D3D4D5D6D7), SPH_C64(0xD8D9DADBDCDDDEDF), + SPH_C64(0xE0E1E2E3E4E5E6E7), SPH_C64(0xE8E9EAEBECEDEEEF), + SPH_C64(0xF0F1F2F3F4F5F6F7), SPH_C64(0xF8F9FAFBFCFDFEFF) +}; + +#endif + +#define XCAT(x, y) XCAT_(x, y) +#define XCAT_(x, y) x ## y + +#define LPAR ( + +#define I16_16 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 +#define I16_17 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 +#define I16_18 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 +#define I16_19 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 +#define I16_20 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 +#define I16_21 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 +#define I16_22 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 +#define I16_23 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 +#define I16_24 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 +#define I16_25 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 +#define I16_26 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 +#define I16_27 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 +#define I16_28 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 +#define I16_29 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28 +#define I16_30 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 +#define I16_31 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 + +#define M16_16 0, 1, 3, 4, 7, 10, 11 +#define M16_17 1, 2, 4, 5, 8, 11, 12 +#define M16_18 2, 3, 5, 6, 9, 12, 13 +#define M16_19 3, 4, 6, 7, 10, 13, 14 +#define M16_20 4, 5, 7, 8, 11, 14, 15 +#define M16_21 5, 6, 8, 9, 12, 15, 16 +#define M16_22 6, 7, 9, 10, 13, 0, 1 +#define M16_23 7, 8, 10, 11, 14, 1, 2 +#define M16_24 8, 9, 11, 12, 15, 2, 3 +#define M16_25 9, 10, 12, 13, 0, 3, 4 +#define M16_26 10, 11, 13, 14, 1, 4, 5 +#define M16_27 11, 12, 14, 15, 2, 5, 6 +#define M16_28 12, 13, 15, 16, 3, 6, 7 +#define M16_29 13, 14, 0, 1, 4, 7, 8 +#define M16_30 14, 15, 1, 2, 5, 8, 9 +#define M16_31 15, 16, 2, 3, 6, 9, 10 + +#define ss0(x) (((x) >> 1) ^ SPH_T32((x) << 3) \ + ^ SPH_ROTL32(x, 4) ^ SPH_ROTL32(x, 19)) +#define ss1(x) (((x) >> 1) ^ SPH_T32((x) << 2) \ + ^ SPH_ROTL32(x, 8) ^ SPH_ROTL32(x, 23)) +#define ss2(x) (((x) >> 2) ^ SPH_T32((x) << 1) \ + ^ SPH_ROTL32(x, 12) ^ SPH_ROTL32(x, 25)) +#define ss3(x) (((x) >> 2) ^ SPH_T32((x) << 2) \ + ^ SPH_ROTL32(x, 15) ^ SPH_ROTL32(x, 29)) +#define ss4(x) (((x) >> 1) ^ (x)) +#define ss5(x) (((x) >> 2) ^ (x)) +#define rs1(x) SPH_ROTL32(x, 3) +#define rs2(x) SPH_ROTL32(x, 7) +#define rs3(x) SPH_ROTL32(x, 13) +#define rs4(x) SPH_ROTL32(x, 16) +#define rs5(x) SPH_ROTL32(x, 19) +#define rs6(x) SPH_ROTL32(x, 23) +#define rs7(x) SPH_ROTL32(x, 27) + +#define Ks(j) SPH_T32((sph_u32)(j) * SPH_C32(0x05555555)) + +#define add_elt_s(mf, hf, j0m, j1m, j3m, j4m, j7m, j10m, j11m, j16) \ + (SPH_T32(SPH_ROTL32(mf(j0m), j1m) + SPH_ROTL32(mf(j3m), j4m) \ + - SPH_ROTL32(mf(j10m), j11m) + Ks(j16)) ^ hf(j7m)) + +#define expand1s_inner(qf, mf, hf, i16, \ + i0, i1, i2, i3, i4, i5, i6, i7, i8, \ + i9, i10, i11, i12, i13, i14, i15, \ + i0m, i1m, i3m, i4m, i7m, i10m, i11m) \ + SPH_T32(ss1(qf(i0)) + ss2(qf(i1)) + ss3(qf(i2)) + ss0(qf(i3)) \ + + ss1(qf(i4)) + ss2(qf(i5)) + ss3(qf(i6)) + ss0(qf(i7)) \ + + ss1(qf(i8)) + ss2(qf(i9)) + ss3(qf(i10)) + ss0(qf(i11)) \ + + ss1(qf(i12)) + ss2(qf(i13)) + ss3(qf(i14)) + ss0(qf(i15)) \ + + add_elt_s(mf, hf, i0m, i1m, i3m, i4m, i7m, i10m, i11m, i16)) + +#define expand1s(qf, mf, hf, i16) \ + expand1s_(qf, mf, hf, i16, I16_ ## i16, M16_ ## i16) +#define expand1s_(qf, mf, hf, i16, ix, iy) \ + expand1s_inner LPAR qf, mf, hf, i16, ix, iy) + +#define expand2s_inner(qf, mf, hf, i16, \ + i0, i1, i2, i3, i4, i5, i6, i7, i8, \ + i9, i10, i11, i12, i13, i14, i15, \ + i0m, i1m, i3m, i4m, i7m, i10m, i11m) \ + SPH_T32(qf(i0) + rs1(qf(i1)) + qf(i2) + rs2(qf(i3)) \ + + qf(i4) + rs3(qf(i5)) + qf(i6) + rs4(qf(i7)) \ + + qf(i8) + rs5(qf(i9)) + qf(i10) + rs6(qf(i11)) \ + + qf(i12) + rs7(qf(i13)) + ss4(qf(i14)) + ss5(qf(i15)) \ + + add_elt_s(mf, hf, i0m, i1m, i3m, i4m, i7m, i10m, i11m, i16)) + +#define expand2s(qf, mf, hf, i16) \ + expand2s_(qf, mf, hf, i16, I16_ ## i16, M16_ ## i16) +#define expand2s_(qf, mf, hf, i16, ix, iy) \ + expand2s_inner LPAR qf, mf, hf, i16, ix, iy) + +#if SPH_64 + +#define sb0(x) (((x) >> 1) ^ SPH_T64((x) << 3) \ + ^ SPH_ROTL64(x, 4) ^ SPH_ROTL64(x, 37)) +#define sb1(x) (((x) >> 1) ^ SPH_T64((x) << 2) \ + ^ SPH_ROTL64(x, 13) ^ SPH_ROTL64(x, 43)) +#define sb2(x) (((x) >> 2) ^ SPH_T64((x) << 1) \ + ^ SPH_ROTL64(x, 19) ^ SPH_ROTL64(x, 53)) +#define sb3(x) (((x) >> 2) ^ SPH_T64((x) << 2) \ + ^ SPH_ROTL64(x, 28) ^ SPH_ROTL64(x, 59)) +#define sb4(x) (((x) >> 1) ^ (x)) +#define sb5(x) (((x) >> 2) ^ (x)) +#define rb1(x) SPH_ROTL64(x, 5) +#define rb2(x) SPH_ROTL64(x, 11) +#define rb3(x) SPH_ROTL64(x, 27) +#define rb4(x) SPH_ROTL64(x, 32) +#define rb5(x) SPH_ROTL64(x, 37) +#define rb6(x) SPH_ROTL64(x, 43) +#define rb7(x) SPH_ROTL64(x, 53) + +#define Kb(j) SPH_T64((sph_u64)(j) * SPH_C64(0x0555555555555555)) + +#if SPH_SMALL_FOOTPRINT_BMW + +static const sph_u64 Kb_tab[] = { + Kb(16), Kb(17), Kb(18), Kb(19), Kb(20), Kb(21), Kb(22), Kb(23), + Kb(24), Kb(25), Kb(26), Kb(27), Kb(28), Kb(29), Kb(30), Kb(31) +}; + +#define rol_off(mf, j, off) \ + SPH_ROTL64(mf(((j) + (off)) & 15), (((j) + (off)) & 15) + 1) + +#define add_elt_b(mf, hf, j) \ + (SPH_T64(rol_off(mf, j, 0) + rol_off(mf, j, 3) \ + - rol_off(mf, j, 10) + Kb_tab[j]) ^ hf(((j) + 7) & 15)) + +#define expand1b(qf, mf, hf, i) \ + SPH_T64(sb1(qf((i) - 16)) + sb2(qf((i) - 15)) \ + + sb3(qf((i) - 14)) + sb0(qf((i) - 13)) \ + + sb1(qf((i) - 12)) + sb2(qf((i) - 11)) \ + + sb3(qf((i) - 10)) + sb0(qf((i) - 9)) \ + + sb1(qf((i) - 8)) + sb2(qf((i) - 7)) \ + + sb3(qf((i) - 6)) + sb0(qf((i) - 5)) \ + + sb1(qf((i) - 4)) + sb2(qf((i) - 3)) \ + + sb3(qf((i) - 2)) + sb0(qf((i) - 1)) \ + + add_elt_b(mf, hf, (i) - 16)) + +#define expand2b(qf, mf, hf, i) \ + SPH_T64(qf((i) - 16) + rb1(qf((i) - 15)) \ + + qf((i) - 14) + rb2(qf((i) - 13)) \ + + qf((i) - 12) + rb3(qf((i) - 11)) \ + + qf((i) - 10) + rb4(qf((i) - 9)) \ + + qf((i) - 8) + rb5(qf((i) - 7)) \ + + qf((i) - 6) + rb6(qf((i) - 5)) \ + + qf((i) - 4) + rb7(qf((i) - 3)) \ + + sb4(qf((i) - 2)) + sb5(qf((i) - 1)) \ + + add_elt_b(mf, hf, (i) - 16)) + +#else + +#define add_elt_b(mf, hf, j0m, j1m, j3m, j4m, j7m, j10m, j11m, j16) \ + (SPH_T64(SPH_ROTL64(mf(j0m), j1m) + SPH_ROTL64(mf(j3m), j4m) \ + - SPH_ROTL64(mf(j10m), j11m) + Kb(j16)) ^ hf(j7m)) + +#define expand1b_inner(qf, mf, hf, i16, \ + i0, i1, i2, i3, i4, i5, i6, i7, i8, \ + i9, i10, i11, i12, i13, i14, i15, \ + i0m, i1m, i3m, i4m, i7m, i10m, i11m) \ + SPH_T64(sb1(qf(i0)) + sb2(qf(i1)) + sb3(qf(i2)) + sb0(qf(i3)) \ + + sb1(qf(i4)) + sb2(qf(i5)) + sb3(qf(i6)) + sb0(qf(i7)) \ + + sb1(qf(i8)) + sb2(qf(i9)) + sb3(qf(i10)) + sb0(qf(i11)) \ + + sb1(qf(i12)) + sb2(qf(i13)) + sb3(qf(i14)) + sb0(qf(i15)) \ + + add_elt_b(mf, hf, i0m, i1m, i3m, i4m, i7m, i10m, i11m, i16)) + +#define expand1b(qf, mf, hf, i16) \ + expand1b_(qf, mf, hf, i16, I16_ ## i16, M16_ ## i16) +#define expand1b_(qf, mf, hf, i16, ix, iy) \ + expand1b_inner LPAR qf, mf, hf, i16, ix, iy) + +#define expand2b_inner(qf, mf, hf, i16, \ + i0, i1, i2, i3, i4, i5, i6, i7, i8, \ + i9, i10, i11, i12, i13, i14, i15, \ + i0m, i1m, i3m, i4m, i7m, i10m, i11m) \ + SPH_T64(qf(i0) + rb1(qf(i1)) + qf(i2) + rb2(qf(i3)) \ + + qf(i4) + rb3(qf(i5)) + qf(i6) + rb4(qf(i7)) \ + + qf(i8) + rb5(qf(i9)) + qf(i10) + rb6(qf(i11)) \ + + qf(i12) + rb7(qf(i13)) + sb4(qf(i14)) + sb5(qf(i15)) \ + + add_elt_b(mf, hf, i0m, i1m, i3m, i4m, i7m, i10m, i11m, i16)) + +#define expand2b(qf, mf, hf, i16) \ + expand2b_(qf, mf, hf, i16, I16_ ## i16, M16_ ## i16) +#define expand2b_(qf, mf, hf, i16, ix, iy) \ + expand2b_inner LPAR qf, mf, hf, i16, ix, iy) + +#endif + +#endif + +#define MAKE_W(tt, i0, op01, i1, op12, i2, op23, i3, op34, i4) \ + tt((M(i0) ^ H(i0)) op01 (M(i1) ^ H(i1)) op12 (M(i2) ^ H(i2)) \ + op23 (M(i3) ^ H(i3)) op34 (M(i4) ^ H(i4))) + +#define Ws0 MAKE_W(SPH_T32, 5, -, 7, +, 10, +, 13, +, 14) +#define Ws1 MAKE_W(SPH_T32, 6, -, 8, +, 11, +, 14, -, 15) +#define Ws2 MAKE_W(SPH_T32, 0, +, 7, +, 9, -, 12, +, 15) +#define Ws3 MAKE_W(SPH_T32, 0, -, 1, +, 8, -, 10, +, 13) +#define Ws4 MAKE_W(SPH_T32, 1, +, 2, +, 9, -, 11, -, 14) +#define Ws5 MAKE_W(SPH_T32, 3, -, 2, +, 10, -, 12, +, 15) +#define Ws6 MAKE_W(SPH_T32, 4, -, 0, -, 3, -, 11, +, 13) +#define Ws7 MAKE_W(SPH_T32, 1, -, 4, -, 5, -, 12, -, 14) +#define Ws8 MAKE_W(SPH_T32, 2, -, 5, -, 6, +, 13, -, 15) +#define Ws9 MAKE_W(SPH_T32, 0, -, 3, +, 6, -, 7, +, 14) +#define Ws10 MAKE_W(SPH_T32, 8, -, 1, -, 4, -, 7, +, 15) +#define Ws11 MAKE_W(SPH_T32, 8, -, 0, -, 2, -, 5, +, 9) +#define Ws12 MAKE_W(SPH_T32, 1, +, 3, -, 6, -, 9, +, 10) +#define Ws13 MAKE_W(SPH_T32, 2, +, 4, +, 7, +, 10, +, 11) +#define Ws14 MAKE_W(SPH_T32, 3, -, 5, +, 8, -, 11, -, 12) +#define Ws15 MAKE_W(SPH_T32, 12, -, 4, -, 6, -, 9, +, 13) + +#if SPH_SMALL_FOOTPRINT_BMW + +#define MAKE_Qas do { \ + unsigned u; \ + sph_u32 Ws[16]; \ + Ws[ 0] = Ws0; \ + Ws[ 1] = Ws1; \ + Ws[ 2] = Ws2; \ + Ws[ 3] = Ws3; \ + Ws[ 4] = Ws4; \ + Ws[ 5] = Ws5; \ + Ws[ 6] = Ws6; \ + Ws[ 7] = Ws7; \ + Ws[ 8] = Ws8; \ + Ws[ 9] = Ws9; \ + Ws[10] = Ws10; \ + Ws[11] = Ws11; \ + Ws[12] = Ws12; \ + Ws[13] = Ws13; \ + Ws[14] = Ws14; \ + Ws[15] = Ws15; \ + for (u = 0; u < 15; u += 5) { \ + qt[u + 0] = SPH_T32(ss0(Ws[u + 0]) + H(u + 1)); \ + qt[u + 1] = SPH_T32(ss1(Ws[u + 1]) + H(u + 2)); \ + qt[u + 2] = SPH_T32(ss2(Ws[u + 2]) + H(u + 3)); \ + qt[u + 3] = SPH_T32(ss3(Ws[u + 3]) + H(u + 4)); \ + qt[u + 4] = SPH_T32(ss4(Ws[u + 4]) + H(u + 5)); \ + } \ + qt[15] = SPH_T32(ss0(Ws[15]) + H(0)); \ + } while (0) + +#define MAKE_Qbs do { \ + qt[16] = expand1s(Qs, M, H, 16); \ + qt[17] = expand1s(Qs, M, H, 17); \ + qt[18] = expand2s(Qs, M, H, 18); \ + qt[19] = expand2s(Qs, M, H, 19); \ + qt[20] = expand2s(Qs, M, H, 20); \ + qt[21] = expand2s(Qs, M, H, 21); \ + qt[22] = expand2s(Qs, M, H, 22); \ + qt[23] = expand2s(Qs, M, H, 23); \ + qt[24] = expand2s(Qs, M, H, 24); \ + qt[25] = expand2s(Qs, M, H, 25); \ + qt[26] = expand2s(Qs, M, H, 26); \ + qt[27] = expand2s(Qs, M, H, 27); \ + qt[28] = expand2s(Qs, M, H, 28); \ + qt[29] = expand2s(Qs, M, H, 29); \ + qt[30] = expand2s(Qs, M, H, 30); \ + qt[31] = expand2s(Qs, M, H, 31); \ + } while (0) + +#else + +#define MAKE_Qas do { \ + qt[ 0] = SPH_T32(ss0(Ws0 ) + H( 1)); \ + qt[ 1] = SPH_T32(ss1(Ws1 ) + H( 2)); \ + qt[ 2] = SPH_T32(ss2(Ws2 ) + H( 3)); \ + qt[ 3] = SPH_T32(ss3(Ws3 ) + H( 4)); \ + qt[ 4] = SPH_T32(ss4(Ws4 ) + H( 5)); \ + qt[ 5] = SPH_T32(ss0(Ws5 ) + H( 6)); \ + qt[ 6] = SPH_T32(ss1(Ws6 ) + H( 7)); \ + qt[ 7] = SPH_T32(ss2(Ws7 ) + H( 8)); \ + qt[ 8] = SPH_T32(ss3(Ws8 ) + H( 9)); \ + qt[ 9] = SPH_T32(ss4(Ws9 ) + H(10)); \ + qt[10] = SPH_T32(ss0(Ws10) + H(11)); \ + qt[11] = SPH_T32(ss1(Ws11) + H(12)); \ + qt[12] = SPH_T32(ss2(Ws12) + H(13)); \ + qt[13] = SPH_T32(ss3(Ws13) + H(14)); \ + qt[14] = SPH_T32(ss4(Ws14) + H(15)); \ + qt[15] = SPH_T32(ss0(Ws15) + H( 0)); \ + } while (0) + +#define MAKE_Qbs do { \ + qt[16] = expand1s(Qs, M, H, 16); \ + qt[17] = expand1s(Qs, M, H, 17); \ + qt[18] = expand2s(Qs, M, H, 18); \ + qt[19] = expand2s(Qs, M, H, 19); \ + qt[20] = expand2s(Qs, M, H, 20); \ + qt[21] = expand2s(Qs, M, H, 21); \ + qt[22] = expand2s(Qs, M, H, 22); \ + qt[23] = expand2s(Qs, M, H, 23); \ + qt[24] = expand2s(Qs, M, H, 24); \ + qt[25] = expand2s(Qs, M, H, 25); \ + qt[26] = expand2s(Qs, M, H, 26); \ + qt[27] = expand2s(Qs, M, H, 27); \ + qt[28] = expand2s(Qs, M, H, 28); \ + qt[29] = expand2s(Qs, M, H, 29); \ + qt[30] = expand2s(Qs, M, H, 30); \ + qt[31] = expand2s(Qs, M, H, 31); \ + } while (0) + +#endif + +#define MAKE_Qs do { \ + MAKE_Qas; \ + MAKE_Qbs; \ + } while (0) + +#define Qs(j) (qt[j]) + +#if SPH_64 + +#define Wb0 MAKE_W(SPH_T64, 5, -, 7, +, 10, +, 13, +, 14) +#define Wb1 MAKE_W(SPH_T64, 6, -, 8, +, 11, +, 14, -, 15) +#define Wb2 MAKE_W(SPH_T64, 0, +, 7, +, 9, -, 12, +, 15) +#define Wb3 MAKE_W(SPH_T64, 0, -, 1, +, 8, -, 10, +, 13) +#define Wb4 MAKE_W(SPH_T64, 1, +, 2, +, 9, -, 11, -, 14) +#define Wb5 MAKE_W(SPH_T64, 3, -, 2, +, 10, -, 12, +, 15) +#define Wb6 MAKE_W(SPH_T64, 4, -, 0, -, 3, -, 11, +, 13) +#define Wb7 MAKE_W(SPH_T64, 1, -, 4, -, 5, -, 12, -, 14) +#define Wb8 MAKE_W(SPH_T64, 2, -, 5, -, 6, +, 13, -, 15) +#define Wb9 MAKE_W(SPH_T64, 0, -, 3, +, 6, -, 7, +, 14) +#define Wb10 MAKE_W(SPH_T64, 8, -, 1, -, 4, -, 7, +, 15) +#define Wb11 MAKE_W(SPH_T64, 8, -, 0, -, 2, -, 5, +, 9) +#define Wb12 MAKE_W(SPH_T64, 1, +, 3, -, 6, -, 9, +, 10) +#define Wb13 MAKE_W(SPH_T64, 2, +, 4, +, 7, +, 10, +, 11) +#define Wb14 MAKE_W(SPH_T64, 3, -, 5, +, 8, -, 11, -, 12) +#define Wb15 MAKE_W(SPH_T64, 12, -, 4, -, 6, -, 9, +, 13) + +#if SPH_SMALL_FOOTPRINT_BMW + +#define MAKE_Qab do { \ + unsigned u; \ + sph_u64 Wb[16]; \ + Wb[ 0] = Wb0; \ + Wb[ 1] = Wb1; \ + Wb[ 2] = Wb2; \ + Wb[ 3] = Wb3; \ + Wb[ 4] = Wb4; \ + Wb[ 5] = Wb5; \ + Wb[ 6] = Wb6; \ + Wb[ 7] = Wb7; \ + Wb[ 8] = Wb8; \ + Wb[ 9] = Wb9; \ + Wb[10] = Wb10; \ + Wb[11] = Wb11; \ + Wb[12] = Wb12; \ + Wb[13] = Wb13; \ + Wb[14] = Wb14; \ + Wb[15] = Wb15; \ + for (u = 0; u < 15; u += 5) { \ + qt[u + 0] = SPH_T64(sb0(Wb[u + 0]) + H(u + 1)); \ + qt[u + 1] = SPH_T64(sb1(Wb[u + 1]) + H(u + 2)); \ + qt[u + 2] = SPH_T64(sb2(Wb[u + 2]) + H(u + 3)); \ + qt[u + 3] = SPH_T64(sb3(Wb[u + 3]) + H(u + 4)); \ + qt[u + 4] = SPH_T64(sb4(Wb[u + 4]) + H(u + 5)); \ + } \ + qt[15] = SPH_T64(sb0(Wb[15]) + H(0)); \ + } while (0) + +#define MAKE_Qbb do { \ + unsigned u; \ + for (u = 16; u < 18; u ++) \ + qt[u] = expand1b(Qb, M, H, u); \ + for (u = 18; u < 32; u ++) \ + qt[u] = expand2b(Qb, M, H, u); \ + } while (0) + +#else + +#define MAKE_Qab do { \ + qt[ 0] = SPH_T64(sb0(Wb0 ) + H( 1)); \ + qt[ 1] = SPH_T64(sb1(Wb1 ) + H( 2)); \ + qt[ 2] = SPH_T64(sb2(Wb2 ) + H( 3)); \ + qt[ 3] = SPH_T64(sb3(Wb3 ) + H( 4)); \ + qt[ 4] = SPH_T64(sb4(Wb4 ) + H( 5)); \ + qt[ 5] = SPH_T64(sb0(Wb5 ) + H( 6)); \ + qt[ 6] = SPH_T64(sb1(Wb6 ) + H( 7)); \ + qt[ 7] = SPH_T64(sb2(Wb7 ) + H( 8)); \ + qt[ 8] = SPH_T64(sb3(Wb8 ) + H( 9)); \ + qt[ 9] = SPH_T64(sb4(Wb9 ) + H(10)); \ + qt[10] = SPH_T64(sb0(Wb10) + H(11)); \ + qt[11] = SPH_T64(sb1(Wb11) + H(12)); \ + qt[12] = SPH_T64(sb2(Wb12) + H(13)); \ + qt[13] = SPH_T64(sb3(Wb13) + H(14)); \ + qt[14] = SPH_T64(sb4(Wb14) + H(15)); \ + qt[15] = SPH_T64(sb0(Wb15) + H( 0)); \ + } while (0) + +#define MAKE_Qbb do { \ + qt[16] = expand1b(Qb, M, H, 16); \ + qt[17] = expand1b(Qb, M, H, 17); \ + qt[18] = expand2b(Qb, M, H, 18); \ + qt[19] = expand2b(Qb, M, H, 19); \ + qt[20] = expand2b(Qb, M, H, 20); \ + qt[21] = expand2b(Qb, M, H, 21); \ + qt[22] = expand2b(Qb, M, H, 22); \ + qt[23] = expand2b(Qb, M, H, 23); \ + qt[24] = expand2b(Qb, M, H, 24); \ + qt[25] = expand2b(Qb, M, H, 25); \ + qt[26] = expand2b(Qb, M, H, 26); \ + qt[27] = expand2b(Qb, M, H, 27); \ + qt[28] = expand2b(Qb, M, H, 28); \ + qt[29] = expand2b(Qb, M, H, 29); \ + qt[30] = expand2b(Qb, M, H, 30); \ + qt[31] = expand2b(Qb, M, H, 31); \ + } while (0) + +#endif + +#define MAKE_Qb do { \ + MAKE_Qab; \ + MAKE_Qbb; \ + } while (0) + +#define Qb(j) (qt[j]) + +#endif + +#define FOLD(type, mkQ, tt, rol, mf, qf, dhf) do { \ + type qt[32], xl, xh; \ + mkQ; \ + xl = qf(16) ^ qf(17) ^ qf(18) ^ qf(19) \ + ^ qf(20) ^ qf(21) ^ qf(22) ^ qf(23); \ + xh = xl ^ qf(24) ^ qf(25) ^ qf(26) ^ qf(27) \ + ^ qf(28) ^ qf(29) ^ qf(30) ^ qf(31); \ + dhf( 0) = tt(((xh << 5) ^ (qf(16) >> 5) ^ mf( 0)) \ + + (xl ^ qf(24) ^ qf( 0))); \ + dhf( 1) = tt(((xh >> 7) ^ (qf(17) << 8) ^ mf( 1)) \ + + (xl ^ qf(25) ^ qf( 1))); \ + dhf( 2) = tt(((xh >> 5) ^ (qf(18) << 5) ^ mf( 2)) \ + + (xl ^ qf(26) ^ qf( 2))); \ + dhf( 3) = tt(((xh >> 1) ^ (qf(19) << 5) ^ mf( 3)) \ + + (xl ^ qf(27) ^ qf( 3))); \ + dhf( 4) = tt(((xh >> 3) ^ (qf(20) << 0) ^ mf( 4)) \ + + (xl ^ qf(28) ^ qf( 4))); \ + dhf( 5) = tt(((xh << 6) ^ (qf(21) >> 6) ^ mf( 5)) \ + + (xl ^ qf(29) ^ qf( 5))); \ + dhf( 6) = tt(((xh >> 4) ^ (qf(22) << 6) ^ mf( 6)) \ + + (xl ^ qf(30) ^ qf( 6))); \ + dhf( 7) = tt(((xh >> 11) ^ (qf(23) << 2) ^ mf( 7)) \ + + (xl ^ qf(31) ^ qf( 7))); \ + dhf( 8) = tt(rol(dhf(4), 9) + (xh ^ qf(24) ^ mf( 8)) \ + + ((xl << 8) ^ qf(23) ^ qf( 8))); \ + dhf( 9) = tt(rol(dhf(5), 10) + (xh ^ qf(25) ^ mf( 9)) \ + + ((xl >> 6) ^ qf(16) ^ qf( 9))); \ + dhf(10) = tt(rol(dhf(6), 11) + (xh ^ qf(26) ^ mf(10)) \ + + ((xl << 6) ^ qf(17) ^ qf(10))); \ + dhf(11) = tt(rol(dhf(7), 12) + (xh ^ qf(27) ^ mf(11)) \ + + ((xl << 4) ^ qf(18) ^ qf(11))); \ + dhf(12) = tt(rol(dhf(0), 13) + (xh ^ qf(28) ^ mf(12)) \ + + ((xl >> 3) ^ qf(19) ^ qf(12))); \ + dhf(13) = tt(rol(dhf(1), 14) + (xh ^ qf(29) ^ mf(13)) \ + + ((xl >> 4) ^ qf(20) ^ qf(13))); \ + dhf(14) = tt(rol(dhf(2), 15) + (xh ^ qf(30) ^ mf(14)) \ + + ((xl >> 7) ^ qf(21) ^ qf(14))); \ + dhf(15) = tt(rol(dhf(3), 16) + (xh ^ qf(31) ^ mf(15)) \ + + ((xl >> 2) ^ qf(22) ^ qf(15))); \ + } while (0) + +#define FOLDs FOLD(sph_u32, MAKE_Qs, SPH_T32, SPH_ROTL32, M, Qs, dH) + +#if SPH_64 + +#define FOLDb FOLD(sph_u64, MAKE_Qb, SPH_T64, SPH_ROTL64, M, Qb, dH) + +#endif + +static void +compress_small(const unsigned char *data, const sph_u32 h[16], sph_u32 dh[16]) +{ +#if SPH_LITTLE_FAST +#define M(x) sph_dec32le_aligned(data + 4 * (x)) +#else + sph_u32 mv[16]; + + mv[ 0] = sph_dec32le_aligned(data + 0); + mv[ 1] = sph_dec32le_aligned(data + 4); + mv[ 2] = sph_dec32le_aligned(data + 8); + mv[ 3] = sph_dec32le_aligned(data + 12); + mv[ 4] = sph_dec32le_aligned(data + 16); + mv[ 5] = sph_dec32le_aligned(data + 20); + mv[ 6] = sph_dec32le_aligned(data + 24); + mv[ 7] = sph_dec32le_aligned(data + 28); + mv[ 8] = sph_dec32le_aligned(data + 32); + mv[ 9] = sph_dec32le_aligned(data + 36); + mv[10] = sph_dec32le_aligned(data + 40); + mv[11] = sph_dec32le_aligned(data + 44); + mv[12] = sph_dec32le_aligned(data + 48); + mv[13] = sph_dec32le_aligned(data + 52); + mv[14] = sph_dec32le_aligned(data + 56); + mv[15] = sph_dec32le_aligned(data + 60); +#define M(x) (mv[x]) +#endif +#define H(x) (h[x]) +#define dH(x) (dh[x]) + + FOLDs; + +#undef M +#undef H +#undef dH +} + +static const sph_u32 final_s[16] = { + SPH_C32(0xaaaaaaa0), SPH_C32(0xaaaaaaa1), SPH_C32(0xaaaaaaa2), + SPH_C32(0xaaaaaaa3), SPH_C32(0xaaaaaaa4), SPH_C32(0xaaaaaaa5), + SPH_C32(0xaaaaaaa6), SPH_C32(0xaaaaaaa7), SPH_C32(0xaaaaaaa8), + SPH_C32(0xaaaaaaa9), SPH_C32(0xaaaaaaaa), SPH_C32(0xaaaaaaab), + SPH_C32(0xaaaaaaac), SPH_C32(0xaaaaaaad), SPH_C32(0xaaaaaaae), + SPH_C32(0xaaaaaaaf) +}; + +static void +bmw32_init(sph_bmw_small_context *sc, const sph_u32 *iv) +{ + memcpy(sc->H, iv, sizeof sc->H); + sc->ptr = 0; +#if SPH_64 + sc->bit_count = 0; +#else + sc->bit_count_high = 0; + sc->bit_count_low = 0; +#endif +} + +static void +bmw32(sph_bmw_small_context *sc, const void *data, size_t len) +{ + unsigned char *buf; + size_t ptr; + sph_u32 htmp[16]; + sph_u32 *h1, *h2; +#if !SPH_64 + sph_u32 tmp; +#endif + +#if SPH_64 + sc->bit_count += (sph_u64)len << 3; +#else + tmp = sc->bit_count_low; + sc->bit_count_low = SPH_T32(tmp + ((sph_u32)len << 3)); + if (sc->bit_count_low < tmp) + sc->bit_count_high ++; + sc->bit_count_high += len >> 29; +#endif + buf = sc->buf; + ptr = sc->ptr; + h1 = sc->H; + h2 = htmp; + while (len > 0) { + size_t clen; + + clen = (sizeof sc->buf) - ptr; + if (clen > len) + clen = len; + memcpy(buf + ptr, data, clen); + data = (const unsigned char *)data + clen; + len -= clen; + ptr += clen; + if (ptr == sizeof sc->buf) { + sph_u32 *ht; + + compress_small(buf, h1, h2); + ht = h1; + h1 = h2; + h2 = ht; + ptr = 0; + } + } + sc->ptr = ptr; + if (h1 != sc->H) + memcpy(sc->H, h1, sizeof sc->H); +} + +static void +bmw32_close(sph_bmw_small_context *sc, unsigned ub, unsigned n, + void *dst, size_t out_size_w32) +{ + unsigned char *buf, *out; + size_t ptr, u, v; + unsigned z; + sph_u32 h1[16], h2[16], *h; + + buf = sc->buf; + ptr = sc->ptr; + z = 0x80 >> n; + buf[ptr ++] = ((ub & -z) | z) & 0xFF; + h = sc->H; + if (ptr > (sizeof sc->buf) - 8) { + memset(buf + ptr, 0, (sizeof sc->buf) - ptr); + compress_small(buf, h, h1); + ptr = 0; + h = h1; + } + memset(buf + ptr, 0, (sizeof sc->buf) - 8 - ptr); +#if SPH_64 + sph_enc64le_aligned(buf + (sizeof sc->buf) - 8, + SPH_T64(sc->bit_count + n)); +#else + sph_enc32le_aligned(buf + (sizeof sc->buf) - 8, + sc->bit_count_low + n); + sph_enc32le_aligned(buf + (sizeof sc->buf) - 4, + SPH_T32(sc->bit_count_high)); +#endif + compress_small(buf, h, h2); + for (u = 0; u < 16; u ++) + sph_enc32le_aligned(buf + 4 * u, h2[u]); + compress_small(buf, final_s, h1); + out = dst; + for (u = 0, v = 16 - out_size_w32; u < out_size_w32; u ++, v ++) + sph_enc32le(out + 4 * u, h1[v]); +} + +#if SPH_64 + +static void +compress_big(const unsigned char *data, const sph_u64 h[16], sph_u64 dh[16]) +{ +#if SPH_LITTLE_FAST +#define M(x) sph_dec64le_aligned(data + 8 * (x)) +#else + sph_u64 mv[16]; + + mv[ 0] = sph_dec64le_aligned(data + 0); + mv[ 1] = sph_dec64le_aligned(data + 8); + mv[ 2] = sph_dec64le_aligned(data + 16); + mv[ 3] = sph_dec64le_aligned(data + 24); + mv[ 4] = sph_dec64le_aligned(data + 32); + mv[ 5] = sph_dec64le_aligned(data + 40); + mv[ 6] = sph_dec64le_aligned(data + 48); + mv[ 7] = sph_dec64le_aligned(data + 56); + mv[ 8] = sph_dec64le_aligned(data + 64); + mv[ 9] = sph_dec64le_aligned(data + 72); + mv[10] = sph_dec64le_aligned(data + 80); + mv[11] = sph_dec64le_aligned(data + 88); + mv[12] = sph_dec64le_aligned(data + 96); + mv[13] = sph_dec64le_aligned(data + 104); + mv[14] = sph_dec64le_aligned(data + 112); + mv[15] = sph_dec64le_aligned(data + 120); +#define M(x) (mv[x]) +#endif +#define H(x) (h[x]) +#define dH(x) (dh[x]) + + FOLDb; + +#undef M +#undef H +#undef dH +} + +static const sph_u64 final_b[16] = { + SPH_C64(0xaaaaaaaaaaaaaaa0), SPH_C64(0xaaaaaaaaaaaaaaa1), + SPH_C64(0xaaaaaaaaaaaaaaa2), SPH_C64(0xaaaaaaaaaaaaaaa3), + SPH_C64(0xaaaaaaaaaaaaaaa4), SPH_C64(0xaaaaaaaaaaaaaaa5), + SPH_C64(0xaaaaaaaaaaaaaaa6), SPH_C64(0xaaaaaaaaaaaaaaa7), + SPH_C64(0xaaaaaaaaaaaaaaa8), SPH_C64(0xaaaaaaaaaaaaaaa9), + SPH_C64(0xaaaaaaaaaaaaaaaa), SPH_C64(0xaaaaaaaaaaaaaaab), + SPH_C64(0xaaaaaaaaaaaaaaac), SPH_C64(0xaaaaaaaaaaaaaaad), + SPH_C64(0xaaaaaaaaaaaaaaae), SPH_C64(0xaaaaaaaaaaaaaaaf) +}; + +static void +bmw64_init(sph_bmw_big_context *sc, const sph_u64 *iv) +{ + memcpy(sc->H, iv, sizeof sc->H); + sc->ptr = 0; + sc->bit_count = 0; +} + +static void +bmw64(sph_bmw_big_context *sc, const void *data, size_t len) +{ + unsigned char *buf; + size_t ptr; + sph_u64 htmp[16]; + sph_u64 *h1, *h2; + + sc->bit_count += (sph_u64)len << 3; + buf = sc->buf; + ptr = sc->ptr; + h1 = sc->H; + h2 = htmp; + while (len > 0) { + size_t clen; + + clen = (sizeof sc->buf) - ptr; + if (clen > len) + clen = len; + memcpy(buf + ptr, data, clen); + data = (const unsigned char *)data + clen; + len -= clen; + ptr += clen; + if (ptr == sizeof sc->buf) { + sph_u64 *ht; + + compress_big(buf, h1, h2); + ht = h1; + h1 = h2; + h2 = ht; + ptr = 0; + } + } + sc->ptr = ptr; + if (h1 != sc->H) + memcpy(sc->H, h1, sizeof sc->H); +} + +static void +bmw64_close(sph_bmw_big_context *sc, unsigned ub, unsigned n, + void *dst, size_t out_size_w64) +{ + unsigned char *buf, *out; + size_t ptr, u, v; + unsigned z; + sph_u64 h1[16], h2[16], *h; + + buf = sc->buf; + ptr = sc->ptr; + z = 0x80 >> n; + buf[ptr ++] = ((ub & -z) | z) & 0xFF; + h = sc->H; + if (ptr > (sizeof sc->buf) - 8) { + memset(buf + ptr, 0, (sizeof sc->buf) - ptr); + compress_big(buf, h, h1); + ptr = 0; + h = h1; + } + memset(buf + ptr, 0, (sizeof sc->buf) - 8 - ptr); + sph_enc64le_aligned(buf + (sizeof sc->buf) - 8, + SPH_T64(sc->bit_count + n)); + compress_big(buf, h, h2); + for (u = 0; u < 16; u ++) + sph_enc64le_aligned(buf + 8 * u, h2[u]); + compress_big(buf, final_b, h1); + out = dst; + for (u = 0, v = 16 - out_size_w64; u < out_size_w64; u ++, v ++) + sph_enc64le(out + 8 * u, h1[v]); +} + +#endif + +/* see sph_bmw.h */ +void +sph_bmw224_init(void *cc) +{ + bmw32_init(cc, IV224); +} + +/* see sph_bmw.h */ +void +sph_bmw224(void *cc, const void *data, size_t len) +{ + bmw32(cc, data, len); +} + +/* see sph_bmw.h */ +void +sph_bmw224_close(void *cc, void *dst) +{ + sph_bmw224_addbits_and_close(cc, 0, 0, dst); +} + +/* see sph_bmw.h */ +void +sph_bmw224_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + bmw32_close(cc, ub, n, dst, 7); + sph_bmw224_init(cc); +} + +/* see sph_bmw.h */ +void +sph_bmw256_init(void *cc) +{ + bmw32_init(cc, IV256); +} + +/* see sph_bmw.h */ +void +sph_bmw256(void *cc, const void *data, size_t len) +{ + bmw32(cc, data, len); +} + +/* see sph_bmw.h */ +void +sph_bmw256_close(void *cc, void *dst) +{ + sph_bmw256_addbits_and_close(cc, 0, 0, dst); +} + +/* see sph_bmw.h */ +void +sph_bmw256_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + bmw32_close(cc, ub, n, dst, 8); + sph_bmw256_init(cc); +} + +#if SPH_64 + +/* see sph_bmw.h */ +void +sph_bmw384_init(void *cc) +{ + bmw64_init(cc, IV384); +} + +/* see sph_bmw.h */ +void +sph_bmw384(void *cc, const void *data, size_t len) +{ + bmw64(cc, data, len); +} + +/* see sph_bmw.h */ +void +sph_bmw384_close(void *cc, void *dst) +{ + sph_bmw384_addbits_and_close(cc, 0, 0, dst); +} + +/* see sph_bmw.h */ +void +sph_bmw384_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + bmw64_close(cc, ub, n, dst, 6); + sph_bmw384_init(cc); +} + +/* see sph_bmw.h */ +void +sph_bmw512_init(void *cc) +{ + bmw64_init(cc, IV512); +} + +/* see sph_bmw.h */ +void +sph_bmw512(void *cc, const void *data, size_t len) +{ + bmw64(cc, data, len); +} + +/* see sph_bmw.h */ +void +sph_bmw512_close(void *cc, void *dst) +{ + sph_bmw512_addbits_and_close(cc, 0, 0, dst); +} + +/* see sph_bmw.h */ +void +sph_bmw512_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + bmw64_close(cc, ub, n, dst, 8); + sph_bmw512_init(cc); +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp new file mode 100644 index 0000000..0e2318f --- /dev/null +++ b/src/checkpoints.cpp @@ -0,0 +1,544 @@ +// 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 "main.h" +#include "uint256.h" +#include "base58.h" +#include "db.h" +#include "txdb.h" + +namespace Checkpoints +{ + typedef std::map MapCheckpoints; + + // How many times we expect transactions after the last checkpoint to + // be slower. This number is a compromise, as it can't be accurate for + // every system. When reindexing from a fast disk with a slow CPU, it + // can be up to 20, while when downloading from a slow network with a + // fast multicore CPU, it won't be much higher than 1. + static const double fSigcheckVerificationFactor = 5.0; + + struct CCheckpointData { + const MapCheckpoints *mapCheckpoints; + int64 nTimeLastCheckpoint; + int64 nTransactionsLastCheckpoint; + double fTransactionsPerDay; + }; + + // 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, uint256("0x871b51bb9ac9dba4625d50f0a94323fd47352fe8e377a1973cfa9430fb8e0dd5")) + ; + static const CCheckpointData data = { + &mapCheckpoints, + 1533981061, // * UNIX timestamp of last checkpoint block + 0, // * total number of transactions between genesis and last checkpoint + // (the tx=... number in the SetBestChain debug.log lines) + 1440.0 // * estimated number of transactions per day after checkpoint + }; + + static MapCheckpoints mapCheckpointsTestnet = + boost::assign::map_list_of + ( 0, uint256("0x0000028542fd4edd1fae38410e16afbfe6edbb0155d7d71ed56330926c3933fc")) + ; + static const CCheckpointData dataTestnet = { + &mapCheckpointsTestnet, + 1392184910, + 0, + 2880.0 + }; + + const CCheckpointData &Checkpoints() { + if (fTestNet) + return dataTestnet; + else + return data; + } + + bool CheckBlock(int nHeight, const uint256& hash) + { + if (!GetBoolArg("-checkpoints", true)) + return true; + + const MapCheckpoints& checkpoints = *Checkpoints().mapCheckpoints; + + MapCheckpoints::const_iterator i = checkpoints.find(nHeight); + if (i == checkpoints.end()) return true; + return hash == i->second; + } + + // Guess how far we are in the verification process at the given block index + double GuessVerificationProgress(CBlockIndex *pindex) { + if (pindex==NULL) + return 0.0; + + int64 nNow = time(NULL); + + double fWorkBefore = 0.0; // Amount of work done before pindex + double fWorkAfter = 0.0; // Amount of work left after pindex (estimated) + // Work is defined as: 1.0 per transaction before the last checkoint, and + // fSigcheckVerificationFactor per transaction after. + + const CCheckpointData &data = Checkpoints(); + + if (pindex->nChainTx <= data.nTransactionsLastCheckpoint) { + double nCheapBefore = pindex->nChainTx; + double nCheapAfter = data.nTransactionsLastCheckpoint - pindex->nChainTx; + double nExpensiveAfter = (nNow - data.nTimeLastCheckpoint)/86400.0*data.fTransactionsPerDay; + fWorkBefore = nCheapBefore; + fWorkAfter = nCheapAfter + nExpensiveAfter*fSigcheckVerificationFactor; + } else { + double nCheapBefore = data.nTransactionsLastCheckpoint; + double nExpensiveBefore = pindex->nChainTx - data.nTransactionsLastCheckpoint; + double nExpensiveAfter = (nNow - pindex->nTime)/86400.0*data.fTransactionsPerDay; + fWorkBefore = nCheapBefore + nExpensiveBefore*fSigcheckVerificationFactor; + fWorkAfter = nExpensiveAfter*fSigcheckVerificationFactor; + } + + return fWorkBefore / (fWorkBefore + fWorkAfter); + } + + int GetTotalBlocksEstimate() + { + if (!GetBoolArg("-checkpoints", true)) + return 0; + + const MapCheckpoints& checkpoints = *Checkpoints().mapCheckpoints; + + return checkpoints.rbegin()->first; + } + + CBlockIndex* GetLastCheckpoint(const std::map& mapBlockIndex) + { + if (!GetBoolArg("-checkpoints", true)) + return NULL; + + const MapCheckpoints& checkpoints = *Checkpoints().mapCheckpoints; + + BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, checkpoints) + { + const uint256& hash = i.second; + std::map::const_iterator t = mapBlockIndex.find(hash); + if (t != mapBlockIndex.end()) + return t->second; + } + return NULL; + } + + uint256 GetLatestHardenedCheckpoint() + { + printf("GetLatestHardenedCheckpoint\n"); + const MapCheckpoints& checkpoints = *Checkpoints().mapCheckpoints; + + return (checkpoints.rbegin()->second); + } + + // ppcoin: synchronized checkpoint (centrally broadcasted) + uint256 hashSyncCheckpoint = 0; + uint256 hashPendingCheckpoint = 0; + CSyncCheckpoint checkpointMessage; + CSyncCheckpoint checkpointMessagePending; + uint256 hashInvalidCheckpoint = 0; + CCriticalSection cs_hashSyncCheckpoint; + std::string strCheckpointWarning; + + // ppcoin: only descendant of current sync-checkpoint is allowed + bool ValidateSyncCheckpoint(uint256 hashCheckpoint) + { + printf("ValidateSyncCheckpoint: hashCheckpoint=%s\n", hashCheckpoint.ToString().c_str()); + + if ( (hashSyncCheckpoint == 0) || (!mapBlockIndex.count(hashSyncCheckpoint)) ) + { + // NO SYNC CHECKPOINT + return true; + } + + 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: pprev1 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()); + } + + printf("ValidateSyncCheckpoint: OK\n"); + return true; + } + + bool WriteSyncCheckpoint(const uint256& hashCheckpoint) + { + if (!pblocktree->WriteSyncCheckpoint(hashCheckpoint)) + { + return error("WriteSyncCheckpoint(): failed to write to txdb sync checkpoint %s", hashCheckpoint.ToString().c_str()); + } + + hashSyncCheckpoint = hashCheckpoint; + return true; + } + + bool IsSyncCheckpointEnforced() + { + return (GetBoolArg("-checkpointenforce", true) || mapArgs.count("-checkpointkey")); // checkpoint master node is always enforced + } + + bool AcceptPendingSyncCheckpoint() + { + LOCK(cs_hashSyncCheckpoint); + if (hashPendingCheckpoint != 0 && mapBlockIndex.count(hashPendingCheckpoint)) + { + if (!ValidateSyncCheckpoint(hashPendingCheckpoint)) + { + hashPendingCheckpoint = 0; + checkpointMessagePending.SetNull(); + printf("AcceptPendingSyncCheckpoint: FAIL1\n"); + return false; + } + + CBlockIndex* pindexCheckpoint = mapBlockIndex[hashPendingCheckpoint]; + if (IsSyncCheckpointEnforced() && !pindexCheckpoint->IsInMainChain()) + { + CBlock block; + if (!block.ReadFromDisk(pindexCheckpoint)) + return error("AcceptPendingSyncCheckpoint: ReadFromDisk failed for sync checkpoint %s", hashPendingCheckpoint.ToString().c_str()); + CValidationState state; + if (!SetBestChain(state, pindexCheckpoint)) + { + hashInvalidCheckpoint = hashPendingCheckpoint; + return error("AcceptPendingSyncCheckpoint: SetBestChain failed for sync checkpoint %s", hashPendingCheckpoint.ToString().c_str()); + } + } + + 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() + { + // Search backward for a block with specified depth policy + const CBlockIndex *pindex = pindexBest; + while (pindex->pprev && pindex->nHeight + (int)GetArg("-checkpointdepth", -1) > pindexBest->nHeight) + pindex = pindex->pprev; + return pindex->GetBlockHash(); + } + + // Check against synchronized checkpoint + bool CheckSyncCheckpoint(const uint256& hashBlock, const CBlockIndex* pindexPrev) + { + int nHeight = pindexPrev->nHeight + 1; + printf("CheckSyncCheckpoint: nHeight=%d, hashSyncCheckpoint=%s\n", nHeight, hashSyncCheckpoint.ToString().c_str()); + + LOCK(cs_hashSyncCheckpoint); + + if ((hashSyncCheckpoint == 0) || (mapBlockIndex.count(hashSyncCheckpoint) == 0)) + return true; + + // 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("CheckSyncCheckpoint: 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() + { + printf("ResetSyncCheckpoint\n"); + LOCK(cs_hashSyncCheckpoint); + const uint256& hash = Checkpoints::GetLatestHardenedCheckpoint(); + 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()); + + CBlock block; + if (!block.ReadFromDisk(mapBlockIndex[hash])) + return error("ResetSyncCheckpoint: ReadFromDisk failed for hardened checkpoint %s", hash.ToString().c_str()); + CValidationState state; + if (!SetBestChain(state, mapBlockIndex[hash])) + { + return error("ResetSyncCheckpoint: SetBestChain failed for hardened checkpoint %s", hash.ToString().c_str()); + } + } + 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()); + } + + if (!WriteSyncCheckpoint((mapBlockIndex.count(hash) && mapBlockIndex[hash]->IsInMainChain())? hash : hashGenesisBlock)) + 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; + } + + void AskForPendingSyncCheckpoint(CNode* pfrom) + { + LOCK(cs_hashSyncCheckpoint); + if (pfrom && hashPendingCheckpoint != 0 && (!mapBlockIndex.count(hashPendingCheckpoint)) && (!mapOrphanBlocks.count(hashPendingCheckpoint))) + pfrom->AskFor(CInv(MSG_BLOCK, hashPendingCheckpoint)); + } + + // Verify sync checkpoint master pubkey and reset sync checkpoint if changed + bool CheckCheckpointPubKey() + { + std::string strPubKey = ""; + std::string strMasterPubKey = fTestNet? CSyncCheckpoint::strTestPubKey : CSyncCheckpoint::strMainPubKey; + if (!pblocktree->ReadCheckpointPubKey(strPubKey) || strPubKey != strMasterPubKey) + { + // write checkpoint master key to db + if (!pblocktree->WriteCheckpointPubKey(strMasterPubKey)) + return error("CheckCheckpointPubKey() : failed to write new checkpoint master key to db"); + if (!ResetSyncCheckpoint()) + return error("CheckCheckpointPubKey() : failed to reset sync-checkpoint"); + } + return true; + } + + bool SetCheckpointPrivKey(std::string strPrivKey) + { + // Test signing a sync-checkpoint with genesis block + CSyncCheckpoint checkpoint; + checkpoint.hashCheckpoint = hashGenesisBlock; + CDataStream sMsg(SER_NETWORK, PROTOCOL_VERSION); + sMsg << (CUnsignedSyncCheckpoint)checkpoint; + checkpoint.vchMsg = std::vector(sMsg.begin(), sMsg.end()); + + CBitcoinSecret vchSecret; + if (!vchSecret.SetString(strPrivKey)) + return error("SendSyncCheckpoint: Checkpoint master key invalid"); + CKey key; + bool fCompressed; + CSecret secret = vchSecret.GetSecret(fCompressed); + key.SetSecret(secret, fCompressed); // if key is not correct openssl may crash + if (!key.Sign(Hash2(checkpoint.vchMsg.begin(), checkpoint.vchMsg.end()), checkpoint.vchSig)) + return false; + + // Test signing successful, proceed + CSyncCheckpoint::strMasterPrivKey = strPrivKey; + return true; + } + + bool SendSyncCheckpoint(uint256 hashCheckpoint) + { + printf("SendSyncCheckpoint: hashCheckpoint=%s\n", hashCheckpoint.ToString().c_str()); + + 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."); + CBitcoinSecret vchSecret; + if (!vchSecret.SetString(CSyncCheckpoint::strMasterPrivKey)) + return error("SendSyncCheckpoint: Checkpoint master key invalid"); + CKey key; + bool fCompressed; + CSecret secret = vchSecret.GetSecret(fCompressed); + key.SetSecret(secret, fCompressed); // if key is not correct openssl may crash + if (!key.Sign(Hash2(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 + COINBASE_MATURITY); + } + + // Is the sync-checkpoint too old? + bool IsSyncCheckpointTooOld(unsigned int nSeconds) + { + LOCK(cs_hashSyncCheckpoint); + // sync-checkpoint should always be accepted block + assert(mapBlockIndex.count(hashSyncCheckpoint)); + const CBlockIndex* pindexSync = mapBlockIndex[hashSyncCheckpoint]; + return (pindexSync->GetBlockTime() + nSeconds < GetAdjustedTime()); + } + + // 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; + } + +} + +// ppcoin: sync-checkpoint master key +const std::string CSyncCheckpoint::strMainPubKey = "0466aa7cf205be5c40f114c80d0d4087959508ace5642c9b849af1ba78d7c6b969f3e8d36b3d44e5a0ac1d2d8f3e6f7452055713943870700385544c2a04c5aa55"; +const std::string CSyncCheckpoint::strTestPubKey = "041ba70a9e3afd1c0c13b7577e4f71ede2eee884df617fa28bfb0ee3fe993b9cc2835c16b794e46095bf425c4e2cdc2e628becdb196f0302840282d3d32d6c69bd"; +std::string CSyncCheckpoint::strMasterPrivKey = ""; + +// ppcoin: verify signature of sync-checkpoint message +bool CSyncCheckpoint::CheckSignature() +{ + CKey key; + std::string strMasterPubKey = fTestNet? CSyncCheckpoint::strTestPubKey : CSyncCheckpoint::strMainPubKey; + if (!key.SetPubKey(ParseHex(strMasterPubKey))) + return error("CSyncCheckpoint::CheckSignature() : SetPubKey failed"); + if (!key.Verify(Hash2(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; +} + +// ppcoin: 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)? Checkpoints::WantedByOrphan(mapOrphanBlocks[hashCheckpoint]) : hashCheckpoint)); + } + return false; + } + + if (!Checkpoints::ValidateSyncCheckpoint(hashCheckpoint)) + return false; + + 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()); + CValidationState state; + if (!SetBestChain(state, pindexCheckpoint)) + { + Checkpoints::hashInvalidCheckpoint = hashCheckpoint; + return error("ProcessSyncCheckpoint: SetBestChain failed for sync checkpoint %s", hashCheckpoint.ToString().c_str()); + } + } + + 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 0000000..df13b46 --- /dev/null +++ b/src/checkpoints.h @@ -0,0 +1,152 @@ +// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2011-2013 PPCoin 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 "hash.h" +#include "net.h" + +class uint256; +class CBlockIndex; +class CBlock; + +// 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 strMainPubKey; + static const std::string strTestPubKey; + 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 Hash2(this->vchMsg.begin(), this->vchMsg.end()); + } + + 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); +}; + +/** Block-chain checkpoints are compiled-in sanity checks. + * They are updated every release or three. + */ +namespace Checkpoints +{ + // Returns true if block passes checkpoint checks + bool CheckBlock(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); + + double GuessVerificationProgress(CBlockIndex *pindex); + + // Returns the block hash of latest hardened checkpoint + uint256 GetLatestHardenedCheckpoint(); + + // ppcoin: synchronized checkpoint + extern uint256 hashSyncCheckpoint; + extern CSyncCheckpoint checkpointMessage; + extern uint256 hashInvalidCheckpoint; + extern CCriticalSection cs_hashSyncCheckpoint; + extern std::string strCheckpointWarning; + + bool WriteSyncCheckpoint(const uint256& hashCheckpoint); + bool IsSyncCheckpointEnforced(); + bool AcceptPendingSyncCheckpoint(); + uint256 AutoSelectSyncCheckpoint(); + bool CheckSyncCheckpoint(const uint256& hashBlock, const CBlockIndex* pindexPrev); + bool WantedByPendingSyncCheckpoint(uint256 hashBlock); + bool ResetSyncCheckpoint(); + void AskForPendingSyncCheckpoint(CNode* pfrom); + bool CheckCheckpointPubKey(); + bool SetCheckpointPrivKey(std::string strPrivKey); + bool SendSyncCheckpoint(uint256 hashCheckpoint); + bool IsMatureSyncCheckpoint(); + bool IsSyncCheckpointTooOld(unsigned int nSeconds); + uint256 WantedByOrphan(const CBlock* pblockOrphan); + +} + + +#endif diff --git a/src/checkqueue.h b/src/checkqueue.h new file mode 100644 index 0000000..eba424f --- /dev/null +++ b/src/checkqueue.h @@ -0,0 +1,192 @@ +// 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 + +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; + + // 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--; + 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); + } + +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(); + } + + ~CCheckQueue() { + } + + friend class CCheckQueueControl; +}; + +/** 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) { + assert(pqueue->nTotal == pqueue->nIdle); + assert(pqueue->nTodo == 0); + assert(pqueue->fAllOk == true); + } + } + + 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 0000000..832932b --- /dev/null +++ b/src/clientversion.h @@ -0,0 +1,26 @@ +#ifndef CLIENTVERSION_H +#define CLIENTVERSION_H + +// +// client versioning and copyright year +// + +// 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 0 + +// Set to true for release, false for prerelease or test build +#define CLIENT_VERSION_IS_RELEASE true + +// Copyright year (2009-this) +// Todo: update this when changing our copyright comments in the source +#define COPYRIGHT_YEAR 2017 + +// 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/compat.h b/src/compat.h new file mode 100644 index 0000000..7062216 --- /dev/null +++ b/src/compat.h @@ -0,0 +1,64 @@ +// 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 +#define FD_SETSIZE 1024 // max number of fds in fd_set +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +typedef u_int SOCKET; +#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 0000000..a2b62a8 --- /dev/null +++ b/src/crypter.cpp @@ -0,0 +1,121 @@ +// 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 +#ifdef WIN32 +#include +#endif + +#include "crypter.h" + +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); + if (fOk) fOk = EVP_EncryptUpdate(&ctx, &vchCiphertext[0], &nCLen, &vchPlaintext[0], nLen); + if (fOk) fOk = EVP_EncryptFinal_ex(&ctx, (&vchCiphertext[0])+nCLen, &nFLen); + 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); + if (fOk) fOk = EVP_DecryptUpdate(&ctx, &vchPlaintext[0], &nPLen, &vchCiphertext[0], nLen); + if (fOk) fOk = EVP_DecryptFinal_ex(&ctx, (&vchPlaintext[0])+nPLen, &nFLen); + 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 0000000..6f75170 --- /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/db.cpp b/src/db.cpp new file mode 100644 index 0000000..ccc5ea8 --- /dev/null +++ b/src/db.cpp @@ -0,0 +1,585 @@ +// 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 "util.h" +#include "main.h" +#include "init.h" +#include "checkpoints.h" +#include +#include +#include + +#ifndef WIN32 +#include "sys/stat.h" +#endif + +using namespace std; +using namespace boost; + + +unsigned int nWalletDBUpdated; + + + +// +// 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(path.string().c_str(), 0); +} + +CDBEnv::CDBEnv() : dbenv(DB_CXX_NO_EXCEPTIONS) +{ + fDbEnvInit = false; + fMockDb = false; +} + +CDBEnv::~CDBEnv() +{ + EnvShutdown(); +} + +void CDBEnv::Close() +{ + EnvShutdown(); +} + +bool CDBEnv::Open(const boost::filesystem::path& pathIn) +{ + if (fDbEnvInit) + return true; + + boost::this_thread::interruption_point(); + + path = pathIn; + filesystem::path pathLogDir = path / "database"; + filesystem::create_directory(pathLogDir); + filesystem::path pathErrorFile = path / "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; + + dbenv.set_lg_dir(pathLogDir.string().c_str()); + dbenv.set_cachesize(0, 0x100000, 1); // 1 MiB should be enough for just the wallet + dbenv.set_lg_bsize(0x10000); + dbenv.set_lg_max(1048576); + dbenv.set_lk_max_locks(40000); + dbenv.set_lk_max_objects(40000); + 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); + dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1); + int ret = dbenv.open(path.string().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; + return true; +} + +void CDBEnv::MakeMock() +{ + if (fDbEnvInit) + throw runtime_error("CDBEnv::MakeMock(): already initialized"); + + boost::this_thread::interruption_point(); + + 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); + dbenv.log_set_config(DB_LOG_IN_MEMORY, 1); + 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 == DB_VERIFY_BAD) + { + printf("Error: Salvage found errors, all data may not be recoverable.\n"); + if (!fAggressive) + { + printf("Error: Rerun with aggressive mode to ignore errors and continue.\n"); + return false; + } + } + if (result != 0 && result != DB_VERIFY_BAD) + { + printf("ERROR: db salvage failed: %d\n",result); + 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'); + 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 + fMockDb ? pszFile : "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; + } + } +} + +void CDB::Flush() +{ + if (activeTxn) + return; + + // Flush database activity from memory pool to disk log + unsigned int nMinutes = 0; + if (fReadOnly) + nMinutes = 1; + + bitdb.dbenv.txn_checkpoint(nMinutes ? GetArg("-dblogsize", 100)*1024 : 0, nMinutes, 0); +} + +void CDB::Close() +{ + if (!pdb) + return; + if (activeTxn) + activeTxn->abort(); + activeTxn = NULL; + pdb = NULL; + + Flush(); + + { + 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 (true) + { + { + 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; + } + } + MilliSleep(100); + } + return false; +} + + +void CDBEnv::Flush(bool fShutdown) +{ + int64 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); + 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"PRI64d"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(); + if (!fMockDb) + boost::filesystem::remove_all(path / "database"); + } + } + } +} + + + + + + + + + + + +// +// 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 = Hash2(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 (std::exception &e) { + 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); + vector vchData; + vchData.resize(dataSize); + uint256 hashIn; + + // read data and checksum from file + try { + filein.read((char *)&vchData[0], dataSize); + filein >> hashIn; + } + catch (std::exception &e) { + 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 = Hash2(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 (std::exception &e) { + 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 0000000..f5e45d0 --- /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 CMasterKey; +class COutPoint; +class CWallet; +class CWalletTx; +class CTxIndex; + +extern unsigned int nWalletDBUpdated; + +void ThreadFlushWalletDB(const std::string& strWalletFile); +bool BackupWallet(const CWallet& wallet, const std::string& strDest); + + +class CDBEnv +{ +private: + bool fDbEnvInit; + bool fMockDb; + boost::filesystem::path path; + + 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(const boost::filesystem::path &path); + void Close(); + void Flush(bool fShutdown); + void CheckpointLSN(std::string strFile); + + 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 Flush(); + 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], 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) { + 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], ssKey.size()); + + // Value + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + ssValue.reserve(10000); + ssValue << value; + Dbt datValue(&ssValue[0], 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], 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], 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(ssKey.size()); + } + Dbt datValue; + if (fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) + { + datValue.set_data(&ssValue[0]); + datValue.set_size(ssValue.size()); + } + datKey.set_flags(DB_DBT_MALLOC); + datValue.set_flags(DB_DBT_MALLOC); + int ret = pcursor->get(&datKey, &datValue, fFlags); + if (ret != 0) + return ret; + else if (datKey.get_data() == NULL || datValue.get_data() == NULL) + return 99999; + + // Convert to streams + ssKey.SetType(SER_DISK); + ssKey.clear(); + ssKey.write((char*)datKey.get_data(), datKey.get_size()); + ssValue.SetType(SER_DISK); + ssValue.clear(); + ssValue.write((char*)datValue.get_data(), datValue.get_size()); + + // Clear and free memory + memset(datKey.get_data(), 0, datKey.get_size()); + memset(datValue.get_data(), 0, datValue.get_size()); + free(datKey.get_data()); + free(datValue.get_data()); + return 0; + } + +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/groestl.c b/src/groestl.c new file mode 100644 index 0000000..928bc41 --- /dev/null +++ b/src/groestl.c @@ -0,0 +1,3123 @@ +/* $Id: groestl.c 260 2011-07-21 01:02:38Z tp $ */ +/* + * Groestl implementation. + * + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * 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 END)============================= + * + * @author Thomas Pornin + */ + +#include +#include + +#include "sph_groestl.h" + +#ifdef __cplusplus +extern "C"{ +#endif + +#if SPH_SMALL_FOOTPRINT && !defined SPH_SMALL_FOOTPRINT_GROESTL +#define SPH_SMALL_FOOTPRINT_GROESTL 1 +#endif + +/* + * Apparently, the 32-bit-only version is not faster than the 64-bit + * version unless using the "small footprint" code on a 32-bit machine. + */ +#if !defined SPH_GROESTL_64 +#if SPH_SMALL_FOOTPRINT_GROESTL && !SPH_64_TRUE +#define SPH_GROESTL_64 0 +#else +#define SPH_GROESTL_64 1 +#endif +#endif + +#if !SPH_64 +#undef SPH_GROESTL_64 +#endif + +#ifdef _MSC_VER +#pragma warning (disable: 4146) +#endif + +/* + * The internal representation may use either big-endian or + * little-endian. Using the platform default representation speeds up + * encoding and decoding between bytes and the matrix columns. + */ + +#undef USE_LE +#if SPH_GROESTL_LITTLE_ENDIAN +#define USE_LE 1 +#elif SPH_GROESTL_BIG_ENDIAN +#define USE_LE 0 +#elif SPH_LITTLE_ENDIAN +#define USE_LE 1 +#endif + +#if USE_LE + +#define C32e(x) ((SPH_C32(x) >> 24) \ + | ((SPH_C32(x) >> 8) & SPH_C32(0x0000FF00)) \ + | ((SPH_C32(x) << 8) & SPH_C32(0x00FF0000)) \ + | ((SPH_C32(x) << 24) & SPH_C32(0xFF000000))) +#define dec32e_aligned sph_dec32le_aligned +#define enc32e sph_enc32le +#define B32_0(x) ((x) & 0xFF) +#define B32_1(x) (((x) >> 8) & 0xFF) +#define B32_2(x) (((x) >> 16) & 0xFF) +#define B32_3(x) ((x) >> 24) + +#define R32u(u, d) SPH_T32(((u) << 16) | ((d) >> 16)) +#define R32d(u, d) SPH_T32(((u) >> 16) | ((d) << 16)) + +#define PC32up(j, r) ((sph_u32)((j) + (r))) +#define PC32dn(j, r) 0 +#define QC32up(j, r) SPH_C32(0xFFFFFFFF) +#define QC32dn(j, r) (((sph_u32)(r) << 24) ^ SPH_T32(~((sph_u32)(j) << 24))) + +#if SPH_64 +#define C64e(x) ((SPH_C64(x) >> 56) \ + | ((SPH_C64(x) >> 40) & SPH_C64(0x000000000000FF00)) \ + | ((SPH_C64(x) >> 24) & SPH_C64(0x0000000000FF0000)) \ + | ((SPH_C64(x) >> 8) & SPH_C64(0x00000000FF000000)) \ + | ((SPH_C64(x) << 8) & SPH_C64(0x000000FF00000000)) \ + | ((SPH_C64(x) << 24) & SPH_C64(0x0000FF0000000000)) \ + | ((SPH_C64(x) << 40) & SPH_C64(0x00FF000000000000)) \ + | ((SPH_C64(x) << 56) & SPH_C64(0xFF00000000000000))) +#define dec64e_aligned sph_dec64le_aligned +#define enc64e sph_enc64le +#define B64_0(x) ((x) & 0xFF) +#define B64_1(x) (((x) >> 8) & 0xFF) +#define B64_2(x) (((x) >> 16) & 0xFF) +#define B64_3(x) (((x) >> 24) & 0xFF) +#define B64_4(x) (((x) >> 32) & 0xFF) +#define B64_5(x) (((x) >> 40) & 0xFF) +#define B64_6(x) (((x) >> 48) & 0xFF) +#define B64_7(x) ((x) >> 56) +#define R64 SPH_ROTL64 +#define PC64(j, r) ((sph_u64)((j) + (r))) +#define QC64(j, r) (((sph_u64)(r) << 56) ^ SPH_T64(~((sph_u64)(j) << 56))) +#endif + +#else + +#define C32e(x) SPH_C32(x) +#define dec32e_aligned sph_dec32be_aligned +#define enc32e sph_enc32be +#define B32_0(x) ((x) >> 24) +#define B32_1(x) (((x) >> 16) & 0xFF) +#define B32_2(x) (((x) >> 8) & 0xFF) +#define B32_3(x) ((x) & 0xFF) + +#define R32u(u, d) SPH_T32(((u) >> 16) | ((d) << 16)) +#define R32d(u, d) SPH_T32(((u) << 16) | ((d) >> 16)) + +#define PC32up(j, r) ((sph_u32)((j) + (r)) << 24) +#define PC32dn(j, r) 0 +#define QC32up(j, r) SPH_C32(0xFFFFFFFF) +#define QC32dn(j, r) ((sph_u32)(r) ^ SPH_T32(~(sph_u32)(j))) + +#if SPH_64 +#define C64e(x) SPH_C64(x) +#define dec64e_aligned sph_dec64be_aligned +#define enc64e sph_enc64be +#define B64_0(x) ((x) >> 56) +#define B64_1(x) (((x) >> 48) & 0xFF) +#define B64_2(x) (((x) >> 40) & 0xFF) +#define B64_3(x) (((x) >> 32) & 0xFF) +#define B64_4(x) (((x) >> 24) & 0xFF) +#define B64_5(x) (((x) >> 16) & 0xFF) +#define B64_6(x) (((x) >> 8) & 0xFF) +#define B64_7(x) ((x) & 0xFF) +#define R64 SPH_ROTR64 +#define PC64(j, r) ((sph_u64)((j) + (r)) << 56) +#define QC64(j, r) ((sph_u64)(r) ^ SPH_T64(~(sph_u64)(j))) +#endif + +#endif + +#if SPH_GROESTL_64 + +static const sph_u64 T0[] = { + C64e(0xc632f4a5f497a5c6), C64e(0xf86f978497eb84f8), + C64e(0xee5eb099b0c799ee), C64e(0xf67a8c8d8cf78df6), + C64e(0xffe8170d17e50dff), C64e(0xd60adcbddcb7bdd6), + C64e(0xde16c8b1c8a7b1de), C64e(0x916dfc54fc395491), + C64e(0x6090f050f0c05060), C64e(0x0207050305040302), + C64e(0xce2ee0a9e087a9ce), C64e(0x56d1877d87ac7d56), + C64e(0xe7cc2b192bd519e7), C64e(0xb513a662a67162b5), + C64e(0x4d7c31e6319ae64d), C64e(0xec59b59ab5c39aec), + C64e(0x8f40cf45cf05458f), C64e(0x1fa3bc9dbc3e9d1f), + C64e(0x8949c040c0094089), C64e(0xfa68928792ef87fa), + C64e(0xefd03f153fc515ef), C64e(0xb29426eb267febb2), + C64e(0x8ece40c94007c98e), C64e(0xfbe61d0b1ded0bfb), + C64e(0x416e2fec2f82ec41), C64e(0xb31aa967a97d67b3), + C64e(0x5f431cfd1cbefd5f), C64e(0x456025ea258aea45), + C64e(0x23f9dabfda46bf23), C64e(0x535102f702a6f753), + C64e(0xe445a196a1d396e4), C64e(0x9b76ed5bed2d5b9b), + C64e(0x75285dc25deac275), C64e(0xe1c5241c24d91ce1), + C64e(0x3dd4e9aee97aae3d), C64e(0x4cf2be6abe986a4c), + C64e(0x6c82ee5aeed85a6c), C64e(0x7ebdc341c3fc417e), + C64e(0xf5f3060206f102f5), C64e(0x8352d14fd11d4f83), + C64e(0x688ce45ce4d05c68), C64e(0x515607f407a2f451), + C64e(0xd18d5c345cb934d1), C64e(0xf9e1180818e908f9), + C64e(0xe24cae93aedf93e2), C64e(0xab3e9573954d73ab), + C64e(0x6297f553f5c45362), C64e(0x2a6b413f41543f2a), + C64e(0x081c140c14100c08), C64e(0x9563f652f6315295), + C64e(0x46e9af65af8c6546), C64e(0x9d7fe25ee2215e9d), + C64e(0x3048782878602830), C64e(0x37cff8a1f86ea137), + C64e(0x0a1b110f11140f0a), C64e(0x2febc4b5c45eb52f), + C64e(0x0e151b091b1c090e), C64e(0x247e5a365a483624), + C64e(0x1badb69bb6369b1b), C64e(0xdf98473d47a53ddf), + C64e(0xcda76a266a8126cd), C64e(0x4ef5bb69bb9c694e), + C64e(0x7f334ccd4cfecd7f), C64e(0xea50ba9fbacf9fea), + C64e(0x123f2d1b2d241b12), C64e(0x1da4b99eb93a9e1d), + C64e(0x58c49c749cb07458), C64e(0x3446722e72682e34), + C64e(0x3641772d776c2d36), C64e(0xdc11cdb2cda3b2dc), + C64e(0xb49d29ee2973eeb4), C64e(0x5b4d16fb16b6fb5b), + C64e(0xa4a501f60153f6a4), C64e(0x76a1d74dd7ec4d76), + C64e(0xb714a361a37561b7), C64e(0x7d3449ce49face7d), + C64e(0x52df8d7b8da47b52), C64e(0xdd9f423e42a13edd), + C64e(0x5ecd937193bc715e), C64e(0x13b1a297a2269713), + C64e(0xa6a204f50457f5a6), C64e(0xb901b868b86968b9), + C64e(0x0000000000000000), C64e(0xc1b5742c74992cc1), + C64e(0x40e0a060a0806040), C64e(0xe3c2211f21dd1fe3), + C64e(0x793a43c843f2c879), C64e(0xb69a2ced2c77edb6), + C64e(0xd40dd9bed9b3bed4), C64e(0x8d47ca46ca01468d), + C64e(0x671770d970ced967), C64e(0x72afdd4bdde44b72), + C64e(0x94ed79de7933de94), C64e(0x98ff67d4672bd498), + C64e(0xb09323e8237be8b0), C64e(0x855bde4ade114a85), + C64e(0xbb06bd6bbd6d6bbb), C64e(0xc5bb7e2a7e912ac5), + C64e(0x4f7b34e5349ee54f), C64e(0xedd73a163ac116ed), + C64e(0x86d254c55417c586), C64e(0x9af862d7622fd79a), + C64e(0x6699ff55ffcc5566), C64e(0x11b6a794a7229411), + C64e(0x8ac04acf4a0fcf8a), C64e(0xe9d9301030c910e9), + C64e(0x040e0a060a080604), C64e(0xfe66988198e781fe), + C64e(0xa0ab0bf00b5bf0a0), C64e(0x78b4cc44ccf04478), + C64e(0x25f0d5bad54aba25), C64e(0x4b753ee33e96e34b), + C64e(0xa2ac0ef30e5ff3a2), C64e(0x5d4419fe19bafe5d), + C64e(0x80db5bc05b1bc080), C64e(0x0580858a850a8a05), + C64e(0x3fd3ecadec7ead3f), C64e(0x21fedfbcdf42bc21), + C64e(0x70a8d848d8e04870), C64e(0xf1fd0c040cf904f1), + C64e(0x63197adf7ac6df63), C64e(0x772f58c158eec177), + C64e(0xaf309f759f4575af), C64e(0x42e7a563a5846342), + C64e(0x2070503050403020), C64e(0xe5cb2e1a2ed11ae5), + C64e(0xfdef120e12e10efd), C64e(0xbf08b76db7656dbf), + C64e(0x8155d44cd4194c81), C64e(0x18243c143c301418), + C64e(0x26795f355f4c3526), C64e(0xc3b2712f719d2fc3), + C64e(0xbe8638e13867e1be), C64e(0x35c8fda2fd6aa235), + C64e(0x88c74fcc4f0bcc88), C64e(0x2e654b394b5c392e), + C64e(0x936af957f93d5793), C64e(0x55580df20daaf255), + C64e(0xfc619d829de382fc), C64e(0x7ab3c947c9f4477a), + C64e(0xc827efacef8bacc8), C64e(0xba8832e7326fe7ba), + C64e(0x324f7d2b7d642b32), C64e(0xe642a495a4d795e6), + C64e(0xc03bfba0fb9ba0c0), C64e(0x19aab398b3329819), + C64e(0x9ef668d16827d19e), C64e(0xa322817f815d7fa3), + C64e(0x44eeaa66aa886644), C64e(0x54d6827e82a87e54), + C64e(0x3bdde6abe676ab3b), C64e(0x0b959e839e16830b), + C64e(0x8cc945ca4503ca8c), C64e(0xc7bc7b297b9529c7), + C64e(0x6b056ed36ed6d36b), C64e(0x286c443c44503c28), + C64e(0xa72c8b798b5579a7), C64e(0xbc813de23d63e2bc), + C64e(0x1631271d272c1d16), C64e(0xad379a769a4176ad), + C64e(0xdb964d3b4dad3bdb), C64e(0x649efa56fac85664), + C64e(0x74a6d24ed2e84e74), C64e(0x1436221e22281e14), + C64e(0x92e476db763fdb92), C64e(0x0c121e0a1e180a0c), + C64e(0x48fcb46cb4906c48), C64e(0xb88f37e4376be4b8), + C64e(0x9f78e75de7255d9f), C64e(0xbd0fb26eb2616ebd), + C64e(0x43692aef2a86ef43), C64e(0xc435f1a6f193a6c4), + C64e(0x39dae3a8e372a839), C64e(0x31c6f7a4f762a431), + C64e(0xd38a593759bd37d3), C64e(0xf274868b86ff8bf2), + C64e(0xd583563256b132d5), C64e(0x8b4ec543c50d438b), + C64e(0x6e85eb59ebdc596e), C64e(0xda18c2b7c2afb7da), + C64e(0x018e8f8c8f028c01), C64e(0xb11dac64ac7964b1), + C64e(0x9cf16dd26d23d29c), C64e(0x49723be03b92e049), + C64e(0xd81fc7b4c7abb4d8), C64e(0xacb915fa1543faac), + C64e(0xf3fa090709fd07f3), C64e(0xcfa06f256f8525cf), + C64e(0xca20eaafea8fafca), C64e(0xf47d898e89f38ef4), + C64e(0x476720e9208ee947), C64e(0x1038281828201810), + C64e(0x6f0b64d564ded56f), C64e(0xf073838883fb88f0), + C64e(0x4afbb16fb1946f4a), C64e(0x5cca967296b8725c), + C64e(0x38546c246c702438), C64e(0x575f08f108aef157), + C64e(0x732152c752e6c773), C64e(0x9764f351f3355197), + C64e(0xcbae6523658d23cb), C64e(0xa125847c84597ca1), + C64e(0xe857bf9cbfcb9ce8), C64e(0x3e5d6321637c213e), + C64e(0x96ea7cdd7c37dd96), C64e(0x611e7fdc7fc2dc61), + C64e(0x0d9c9186911a860d), C64e(0x0f9b9485941e850f), + C64e(0xe04bab90abdb90e0), C64e(0x7cbac642c6f8427c), + C64e(0x712657c457e2c471), C64e(0xcc29e5aae583aacc), + C64e(0x90e373d8733bd890), C64e(0x06090f050f0c0506), + C64e(0xf7f4030103f501f7), C64e(0x1c2a36123638121c), + C64e(0xc23cfea3fe9fa3c2), C64e(0x6a8be15fe1d45f6a), + C64e(0xaebe10f91047f9ae), C64e(0x69026bd06bd2d069), + C64e(0x17bfa891a82e9117), C64e(0x9971e858e8295899), + C64e(0x3a5369276974273a), C64e(0x27f7d0b9d04eb927), + C64e(0xd991483848a938d9), C64e(0xebde351335cd13eb), + C64e(0x2be5ceb3ce56b32b), C64e(0x2277553355443322), + C64e(0xd204d6bbd6bfbbd2), C64e(0xa9399070904970a9), + C64e(0x07878089800e8907), C64e(0x33c1f2a7f266a733), + C64e(0x2decc1b6c15ab62d), C64e(0x3c5a66226678223c), + C64e(0x15b8ad92ad2a9215), C64e(0xc9a96020608920c9), + C64e(0x875cdb49db154987), C64e(0xaab01aff1a4fffaa), + C64e(0x50d8887888a07850), C64e(0xa52b8e7a8e517aa5), + C64e(0x03898a8f8a068f03), C64e(0x594a13f813b2f859), + C64e(0x09929b809b128009), C64e(0x1a2339173934171a), + C64e(0x651075da75cada65), C64e(0xd784533153b531d7), + C64e(0x84d551c65113c684), C64e(0xd003d3b8d3bbb8d0), + C64e(0x82dc5ec35e1fc382), C64e(0x29e2cbb0cb52b029), + C64e(0x5ac3997799b4775a), C64e(0x1e2d3311333c111e), + C64e(0x7b3d46cb46f6cb7b), C64e(0xa8b71ffc1f4bfca8), + C64e(0x6d0c61d661dad66d), C64e(0x2c624e3a4e583a2c) +}; + +#if !SPH_SMALL_FOOTPRINT_GROESTL + +static const sph_u64 T1[] = { + C64e(0xc6c632f4a5f497a5), C64e(0xf8f86f978497eb84), + C64e(0xeeee5eb099b0c799), C64e(0xf6f67a8c8d8cf78d), + C64e(0xffffe8170d17e50d), C64e(0xd6d60adcbddcb7bd), + C64e(0xdede16c8b1c8a7b1), C64e(0x91916dfc54fc3954), + C64e(0x606090f050f0c050), C64e(0x0202070503050403), + C64e(0xcece2ee0a9e087a9), C64e(0x5656d1877d87ac7d), + C64e(0xe7e7cc2b192bd519), C64e(0xb5b513a662a67162), + C64e(0x4d4d7c31e6319ae6), C64e(0xecec59b59ab5c39a), + C64e(0x8f8f40cf45cf0545), C64e(0x1f1fa3bc9dbc3e9d), + C64e(0x898949c040c00940), C64e(0xfafa68928792ef87), + C64e(0xefefd03f153fc515), C64e(0xb2b29426eb267feb), + C64e(0x8e8ece40c94007c9), C64e(0xfbfbe61d0b1ded0b), + C64e(0x41416e2fec2f82ec), C64e(0xb3b31aa967a97d67), + C64e(0x5f5f431cfd1cbefd), C64e(0x45456025ea258aea), + C64e(0x2323f9dabfda46bf), C64e(0x53535102f702a6f7), + C64e(0xe4e445a196a1d396), C64e(0x9b9b76ed5bed2d5b), + C64e(0x7575285dc25deac2), C64e(0xe1e1c5241c24d91c), + C64e(0x3d3dd4e9aee97aae), C64e(0x4c4cf2be6abe986a), + C64e(0x6c6c82ee5aeed85a), C64e(0x7e7ebdc341c3fc41), + C64e(0xf5f5f3060206f102), C64e(0x838352d14fd11d4f), + C64e(0x68688ce45ce4d05c), C64e(0x51515607f407a2f4), + C64e(0xd1d18d5c345cb934), C64e(0xf9f9e1180818e908), + C64e(0xe2e24cae93aedf93), C64e(0xabab3e9573954d73), + C64e(0x626297f553f5c453), C64e(0x2a2a6b413f41543f), + C64e(0x08081c140c14100c), C64e(0x959563f652f63152), + C64e(0x4646e9af65af8c65), C64e(0x9d9d7fe25ee2215e), + C64e(0x3030487828786028), C64e(0x3737cff8a1f86ea1), + C64e(0x0a0a1b110f11140f), C64e(0x2f2febc4b5c45eb5), + C64e(0x0e0e151b091b1c09), C64e(0x24247e5a365a4836), + C64e(0x1b1badb69bb6369b), C64e(0xdfdf98473d47a53d), + C64e(0xcdcda76a266a8126), C64e(0x4e4ef5bb69bb9c69), + C64e(0x7f7f334ccd4cfecd), C64e(0xeaea50ba9fbacf9f), + C64e(0x12123f2d1b2d241b), C64e(0x1d1da4b99eb93a9e), + C64e(0x5858c49c749cb074), C64e(0x343446722e72682e), + C64e(0x363641772d776c2d), C64e(0xdcdc11cdb2cda3b2), + C64e(0xb4b49d29ee2973ee), C64e(0x5b5b4d16fb16b6fb), + C64e(0xa4a4a501f60153f6), C64e(0x7676a1d74dd7ec4d), + C64e(0xb7b714a361a37561), C64e(0x7d7d3449ce49face), + C64e(0x5252df8d7b8da47b), C64e(0xdddd9f423e42a13e), + C64e(0x5e5ecd937193bc71), C64e(0x1313b1a297a22697), + C64e(0xa6a6a204f50457f5), C64e(0xb9b901b868b86968), + C64e(0x0000000000000000), C64e(0xc1c1b5742c74992c), + C64e(0x4040e0a060a08060), C64e(0xe3e3c2211f21dd1f), + C64e(0x79793a43c843f2c8), C64e(0xb6b69a2ced2c77ed), + C64e(0xd4d40dd9bed9b3be), C64e(0x8d8d47ca46ca0146), + C64e(0x67671770d970ced9), C64e(0x7272afdd4bdde44b), + C64e(0x9494ed79de7933de), C64e(0x9898ff67d4672bd4), + C64e(0xb0b09323e8237be8), C64e(0x85855bde4ade114a), + C64e(0xbbbb06bd6bbd6d6b), C64e(0xc5c5bb7e2a7e912a), + C64e(0x4f4f7b34e5349ee5), C64e(0xededd73a163ac116), + C64e(0x8686d254c55417c5), C64e(0x9a9af862d7622fd7), + C64e(0x666699ff55ffcc55), C64e(0x1111b6a794a72294), + C64e(0x8a8ac04acf4a0fcf), C64e(0xe9e9d9301030c910), + C64e(0x04040e0a060a0806), C64e(0xfefe66988198e781), + C64e(0xa0a0ab0bf00b5bf0), C64e(0x7878b4cc44ccf044), + C64e(0x2525f0d5bad54aba), C64e(0x4b4b753ee33e96e3), + C64e(0xa2a2ac0ef30e5ff3), C64e(0x5d5d4419fe19bafe), + C64e(0x8080db5bc05b1bc0), C64e(0x050580858a850a8a), + C64e(0x3f3fd3ecadec7ead), C64e(0x2121fedfbcdf42bc), + C64e(0x7070a8d848d8e048), C64e(0xf1f1fd0c040cf904), + C64e(0x6363197adf7ac6df), C64e(0x77772f58c158eec1), + C64e(0xafaf309f759f4575), C64e(0x4242e7a563a58463), + C64e(0x2020705030504030), C64e(0xe5e5cb2e1a2ed11a), + C64e(0xfdfdef120e12e10e), C64e(0xbfbf08b76db7656d), + C64e(0x818155d44cd4194c), C64e(0x1818243c143c3014), + C64e(0x2626795f355f4c35), C64e(0xc3c3b2712f719d2f), + C64e(0xbebe8638e13867e1), C64e(0x3535c8fda2fd6aa2), + C64e(0x8888c74fcc4f0bcc), C64e(0x2e2e654b394b5c39), + C64e(0x93936af957f93d57), C64e(0x5555580df20daaf2), + C64e(0xfcfc619d829de382), C64e(0x7a7ab3c947c9f447), + C64e(0xc8c827efacef8bac), C64e(0xbaba8832e7326fe7), + C64e(0x32324f7d2b7d642b), C64e(0xe6e642a495a4d795), + C64e(0xc0c03bfba0fb9ba0), C64e(0x1919aab398b33298), + C64e(0x9e9ef668d16827d1), C64e(0xa3a322817f815d7f), + C64e(0x4444eeaa66aa8866), C64e(0x5454d6827e82a87e), + C64e(0x3b3bdde6abe676ab), C64e(0x0b0b959e839e1683), + C64e(0x8c8cc945ca4503ca), C64e(0xc7c7bc7b297b9529), + C64e(0x6b6b056ed36ed6d3), C64e(0x28286c443c44503c), + C64e(0xa7a72c8b798b5579), C64e(0xbcbc813de23d63e2), + C64e(0x161631271d272c1d), C64e(0xadad379a769a4176), + C64e(0xdbdb964d3b4dad3b), C64e(0x64649efa56fac856), + C64e(0x7474a6d24ed2e84e), C64e(0x141436221e22281e), + C64e(0x9292e476db763fdb), C64e(0x0c0c121e0a1e180a), + C64e(0x4848fcb46cb4906c), C64e(0xb8b88f37e4376be4), + C64e(0x9f9f78e75de7255d), C64e(0xbdbd0fb26eb2616e), + C64e(0x4343692aef2a86ef), C64e(0xc4c435f1a6f193a6), + C64e(0x3939dae3a8e372a8), C64e(0x3131c6f7a4f762a4), + C64e(0xd3d38a593759bd37), C64e(0xf2f274868b86ff8b), + C64e(0xd5d583563256b132), C64e(0x8b8b4ec543c50d43), + C64e(0x6e6e85eb59ebdc59), C64e(0xdada18c2b7c2afb7), + C64e(0x01018e8f8c8f028c), C64e(0xb1b11dac64ac7964), + C64e(0x9c9cf16dd26d23d2), C64e(0x4949723be03b92e0), + C64e(0xd8d81fc7b4c7abb4), C64e(0xacacb915fa1543fa), + C64e(0xf3f3fa090709fd07), C64e(0xcfcfa06f256f8525), + C64e(0xcaca20eaafea8faf), C64e(0xf4f47d898e89f38e), + C64e(0x47476720e9208ee9), C64e(0x1010382818282018), + C64e(0x6f6f0b64d564ded5), C64e(0xf0f073838883fb88), + C64e(0x4a4afbb16fb1946f), C64e(0x5c5cca967296b872), + C64e(0x3838546c246c7024), C64e(0x57575f08f108aef1), + C64e(0x73732152c752e6c7), C64e(0x979764f351f33551), + C64e(0xcbcbae6523658d23), C64e(0xa1a125847c84597c), + C64e(0xe8e857bf9cbfcb9c), C64e(0x3e3e5d6321637c21), + C64e(0x9696ea7cdd7c37dd), C64e(0x61611e7fdc7fc2dc), + C64e(0x0d0d9c9186911a86), C64e(0x0f0f9b9485941e85), + C64e(0xe0e04bab90abdb90), C64e(0x7c7cbac642c6f842), + C64e(0x71712657c457e2c4), C64e(0xcccc29e5aae583aa), + C64e(0x9090e373d8733bd8), C64e(0x0606090f050f0c05), + C64e(0xf7f7f4030103f501), C64e(0x1c1c2a3612363812), + C64e(0xc2c23cfea3fe9fa3), C64e(0x6a6a8be15fe1d45f), + C64e(0xaeaebe10f91047f9), C64e(0x6969026bd06bd2d0), + C64e(0x1717bfa891a82e91), C64e(0x999971e858e82958), + C64e(0x3a3a536927697427), C64e(0x2727f7d0b9d04eb9), + C64e(0xd9d991483848a938), C64e(0xebebde351335cd13), + C64e(0x2b2be5ceb3ce56b3), C64e(0x2222775533554433), + C64e(0xd2d204d6bbd6bfbb), C64e(0xa9a9399070904970), + C64e(0x0707878089800e89), C64e(0x3333c1f2a7f266a7), + C64e(0x2d2decc1b6c15ab6), C64e(0x3c3c5a6622667822), + C64e(0x1515b8ad92ad2a92), C64e(0xc9c9a96020608920), + C64e(0x87875cdb49db1549), C64e(0xaaaab01aff1a4fff), + C64e(0x5050d8887888a078), C64e(0xa5a52b8e7a8e517a), + C64e(0x0303898a8f8a068f), C64e(0x59594a13f813b2f8), + C64e(0x0909929b809b1280), C64e(0x1a1a233917393417), + C64e(0x65651075da75cada), C64e(0xd7d784533153b531), + C64e(0x8484d551c65113c6), C64e(0xd0d003d3b8d3bbb8), + C64e(0x8282dc5ec35e1fc3), C64e(0x2929e2cbb0cb52b0), + C64e(0x5a5ac3997799b477), C64e(0x1e1e2d3311333c11), + C64e(0x7b7b3d46cb46f6cb), C64e(0xa8a8b71ffc1f4bfc), + C64e(0x6d6d0c61d661dad6), C64e(0x2c2c624e3a4e583a) +}; + +static const sph_u64 T2[] = { + C64e(0xa5c6c632f4a5f497), C64e(0x84f8f86f978497eb), + C64e(0x99eeee5eb099b0c7), C64e(0x8df6f67a8c8d8cf7), + C64e(0x0dffffe8170d17e5), C64e(0xbdd6d60adcbddcb7), + C64e(0xb1dede16c8b1c8a7), C64e(0x5491916dfc54fc39), + C64e(0x50606090f050f0c0), C64e(0x0302020705030504), + C64e(0xa9cece2ee0a9e087), C64e(0x7d5656d1877d87ac), + C64e(0x19e7e7cc2b192bd5), C64e(0x62b5b513a662a671), + C64e(0xe64d4d7c31e6319a), C64e(0x9aecec59b59ab5c3), + C64e(0x458f8f40cf45cf05), C64e(0x9d1f1fa3bc9dbc3e), + C64e(0x40898949c040c009), C64e(0x87fafa68928792ef), + C64e(0x15efefd03f153fc5), C64e(0xebb2b29426eb267f), + C64e(0xc98e8ece40c94007), C64e(0x0bfbfbe61d0b1ded), + C64e(0xec41416e2fec2f82), C64e(0x67b3b31aa967a97d), + C64e(0xfd5f5f431cfd1cbe), C64e(0xea45456025ea258a), + C64e(0xbf2323f9dabfda46), C64e(0xf753535102f702a6), + C64e(0x96e4e445a196a1d3), C64e(0x5b9b9b76ed5bed2d), + C64e(0xc27575285dc25dea), C64e(0x1ce1e1c5241c24d9), + C64e(0xae3d3dd4e9aee97a), C64e(0x6a4c4cf2be6abe98), + C64e(0x5a6c6c82ee5aeed8), C64e(0x417e7ebdc341c3fc), + C64e(0x02f5f5f3060206f1), C64e(0x4f838352d14fd11d), + C64e(0x5c68688ce45ce4d0), C64e(0xf451515607f407a2), + C64e(0x34d1d18d5c345cb9), C64e(0x08f9f9e1180818e9), + C64e(0x93e2e24cae93aedf), C64e(0x73abab3e9573954d), + C64e(0x53626297f553f5c4), C64e(0x3f2a2a6b413f4154), + C64e(0x0c08081c140c1410), C64e(0x52959563f652f631), + C64e(0x654646e9af65af8c), C64e(0x5e9d9d7fe25ee221), + C64e(0x2830304878287860), C64e(0xa13737cff8a1f86e), + C64e(0x0f0a0a1b110f1114), C64e(0xb52f2febc4b5c45e), + C64e(0x090e0e151b091b1c), C64e(0x3624247e5a365a48), + C64e(0x9b1b1badb69bb636), C64e(0x3ddfdf98473d47a5), + C64e(0x26cdcda76a266a81), C64e(0x694e4ef5bb69bb9c), + C64e(0xcd7f7f334ccd4cfe), C64e(0x9feaea50ba9fbacf), + C64e(0x1b12123f2d1b2d24), C64e(0x9e1d1da4b99eb93a), + C64e(0x745858c49c749cb0), C64e(0x2e343446722e7268), + C64e(0x2d363641772d776c), C64e(0xb2dcdc11cdb2cda3), + C64e(0xeeb4b49d29ee2973), C64e(0xfb5b5b4d16fb16b6), + C64e(0xf6a4a4a501f60153), C64e(0x4d7676a1d74dd7ec), + C64e(0x61b7b714a361a375), C64e(0xce7d7d3449ce49fa), + C64e(0x7b5252df8d7b8da4), C64e(0x3edddd9f423e42a1), + C64e(0x715e5ecd937193bc), C64e(0x971313b1a297a226), + C64e(0xf5a6a6a204f50457), C64e(0x68b9b901b868b869), + C64e(0x0000000000000000), C64e(0x2cc1c1b5742c7499), + C64e(0x604040e0a060a080), C64e(0x1fe3e3c2211f21dd), + C64e(0xc879793a43c843f2), C64e(0xedb6b69a2ced2c77), + C64e(0xbed4d40dd9bed9b3), C64e(0x468d8d47ca46ca01), + C64e(0xd967671770d970ce), C64e(0x4b7272afdd4bdde4), + C64e(0xde9494ed79de7933), C64e(0xd49898ff67d4672b), + C64e(0xe8b0b09323e8237b), C64e(0x4a85855bde4ade11), + C64e(0x6bbbbb06bd6bbd6d), C64e(0x2ac5c5bb7e2a7e91), + C64e(0xe54f4f7b34e5349e), C64e(0x16ededd73a163ac1), + C64e(0xc58686d254c55417), C64e(0xd79a9af862d7622f), + C64e(0x55666699ff55ffcc), C64e(0x941111b6a794a722), + C64e(0xcf8a8ac04acf4a0f), C64e(0x10e9e9d9301030c9), + C64e(0x0604040e0a060a08), C64e(0x81fefe66988198e7), + C64e(0xf0a0a0ab0bf00b5b), C64e(0x447878b4cc44ccf0), + C64e(0xba2525f0d5bad54a), C64e(0xe34b4b753ee33e96), + C64e(0xf3a2a2ac0ef30e5f), C64e(0xfe5d5d4419fe19ba), + C64e(0xc08080db5bc05b1b), C64e(0x8a050580858a850a), + C64e(0xad3f3fd3ecadec7e), C64e(0xbc2121fedfbcdf42), + C64e(0x487070a8d848d8e0), C64e(0x04f1f1fd0c040cf9), + C64e(0xdf6363197adf7ac6), C64e(0xc177772f58c158ee), + C64e(0x75afaf309f759f45), C64e(0x634242e7a563a584), + C64e(0x3020207050305040), C64e(0x1ae5e5cb2e1a2ed1), + C64e(0x0efdfdef120e12e1), C64e(0x6dbfbf08b76db765), + C64e(0x4c818155d44cd419), C64e(0x141818243c143c30), + C64e(0x352626795f355f4c), C64e(0x2fc3c3b2712f719d), + C64e(0xe1bebe8638e13867), C64e(0xa23535c8fda2fd6a), + C64e(0xcc8888c74fcc4f0b), C64e(0x392e2e654b394b5c), + C64e(0x5793936af957f93d), C64e(0xf25555580df20daa), + C64e(0x82fcfc619d829de3), C64e(0x477a7ab3c947c9f4), + C64e(0xacc8c827efacef8b), C64e(0xe7baba8832e7326f), + C64e(0x2b32324f7d2b7d64), C64e(0x95e6e642a495a4d7), + C64e(0xa0c0c03bfba0fb9b), C64e(0x981919aab398b332), + C64e(0xd19e9ef668d16827), C64e(0x7fa3a322817f815d), + C64e(0x664444eeaa66aa88), C64e(0x7e5454d6827e82a8), + C64e(0xab3b3bdde6abe676), C64e(0x830b0b959e839e16), + C64e(0xca8c8cc945ca4503), C64e(0x29c7c7bc7b297b95), + C64e(0xd36b6b056ed36ed6), C64e(0x3c28286c443c4450), + C64e(0x79a7a72c8b798b55), C64e(0xe2bcbc813de23d63), + C64e(0x1d161631271d272c), C64e(0x76adad379a769a41), + C64e(0x3bdbdb964d3b4dad), C64e(0x5664649efa56fac8), + C64e(0x4e7474a6d24ed2e8), C64e(0x1e141436221e2228), + C64e(0xdb9292e476db763f), C64e(0x0a0c0c121e0a1e18), + C64e(0x6c4848fcb46cb490), C64e(0xe4b8b88f37e4376b), + C64e(0x5d9f9f78e75de725), C64e(0x6ebdbd0fb26eb261), + C64e(0xef4343692aef2a86), C64e(0xa6c4c435f1a6f193), + C64e(0xa83939dae3a8e372), C64e(0xa43131c6f7a4f762), + C64e(0x37d3d38a593759bd), C64e(0x8bf2f274868b86ff), + C64e(0x32d5d583563256b1), C64e(0x438b8b4ec543c50d), + C64e(0x596e6e85eb59ebdc), C64e(0xb7dada18c2b7c2af), + C64e(0x8c01018e8f8c8f02), C64e(0x64b1b11dac64ac79), + C64e(0xd29c9cf16dd26d23), C64e(0xe04949723be03b92), + C64e(0xb4d8d81fc7b4c7ab), C64e(0xfaacacb915fa1543), + C64e(0x07f3f3fa090709fd), C64e(0x25cfcfa06f256f85), + C64e(0xafcaca20eaafea8f), C64e(0x8ef4f47d898e89f3), + C64e(0xe947476720e9208e), C64e(0x1810103828182820), + C64e(0xd56f6f0b64d564de), C64e(0x88f0f073838883fb), + C64e(0x6f4a4afbb16fb194), C64e(0x725c5cca967296b8), + C64e(0x243838546c246c70), C64e(0xf157575f08f108ae), + C64e(0xc773732152c752e6), C64e(0x51979764f351f335), + C64e(0x23cbcbae6523658d), C64e(0x7ca1a125847c8459), + C64e(0x9ce8e857bf9cbfcb), C64e(0x213e3e5d6321637c), + C64e(0xdd9696ea7cdd7c37), C64e(0xdc61611e7fdc7fc2), + C64e(0x860d0d9c9186911a), C64e(0x850f0f9b9485941e), + C64e(0x90e0e04bab90abdb), C64e(0x427c7cbac642c6f8), + C64e(0xc471712657c457e2), C64e(0xaacccc29e5aae583), + C64e(0xd89090e373d8733b), C64e(0x050606090f050f0c), + C64e(0x01f7f7f4030103f5), C64e(0x121c1c2a36123638), + C64e(0xa3c2c23cfea3fe9f), C64e(0x5f6a6a8be15fe1d4), + C64e(0xf9aeaebe10f91047), C64e(0xd06969026bd06bd2), + C64e(0x911717bfa891a82e), C64e(0x58999971e858e829), + C64e(0x273a3a5369276974), C64e(0xb92727f7d0b9d04e), + C64e(0x38d9d991483848a9), C64e(0x13ebebde351335cd), + C64e(0xb32b2be5ceb3ce56), C64e(0x3322227755335544), + C64e(0xbbd2d204d6bbd6bf), C64e(0x70a9a93990709049), + C64e(0x890707878089800e), C64e(0xa73333c1f2a7f266), + C64e(0xb62d2decc1b6c15a), C64e(0x223c3c5a66226678), + C64e(0x921515b8ad92ad2a), C64e(0x20c9c9a960206089), + C64e(0x4987875cdb49db15), C64e(0xffaaaab01aff1a4f), + C64e(0x785050d8887888a0), C64e(0x7aa5a52b8e7a8e51), + C64e(0x8f0303898a8f8a06), C64e(0xf859594a13f813b2), + C64e(0x800909929b809b12), C64e(0x171a1a2339173934), + C64e(0xda65651075da75ca), C64e(0x31d7d784533153b5), + C64e(0xc68484d551c65113), C64e(0xb8d0d003d3b8d3bb), + C64e(0xc38282dc5ec35e1f), C64e(0xb02929e2cbb0cb52), + C64e(0x775a5ac3997799b4), C64e(0x111e1e2d3311333c), + C64e(0xcb7b7b3d46cb46f6), C64e(0xfca8a8b71ffc1f4b), + C64e(0xd66d6d0c61d661da), C64e(0x3a2c2c624e3a4e58) +}; + +static const sph_u64 T3[] = { + C64e(0x97a5c6c632f4a5f4), C64e(0xeb84f8f86f978497), + C64e(0xc799eeee5eb099b0), C64e(0xf78df6f67a8c8d8c), + C64e(0xe50dffffe8170d17), C64e(0xb7bdd6d60adcbddc), + C64e(0xa7b1dede16c8b1c8), C64e(0x395491916dfc54fc), + C64e(0xc050606090f050f0), C64e(0x0403020207050305), + C64e(0x87a9cece2ee0a9e0), C64e(0xac7d5656d1877d87), + C64e(0xd519e7e7cc2b192b), C64e(0x7162b5b513a662a6), + C64e(0x9ae64d4d7c31e631), C64e(0xc39aecec59b59ab5), + C64e(0x05458f8f40cf45cf), C64e(0x3e9d1f1fa3bc9dbc), + C64e(0x0940898949c040c0), C64e(0xef87fafa68928792), + C64e(0xc515efefd03f153f), C64e(0x7febb2b29426eb26), + C64e(0x07c98e8ece40c940), C64e(0xed0bfbfbe61d0b1d), + C64e(0x82ec41416e2fec2f), C64e(0x7d67b3b31aa967a9), + C64e(0xbefd5f5f431cfd1c), C64e(0x8aea45456025ea25), + C64e(0x46bf2323f9dabfda), C64e(0xa6f753535102f702), + C64e(0xd396e4e445a196a1), C64e(0x2d5b9b9b76ed5bed), + C64e(0xeac27575285dc25d), C64e(0xd91ce1e1c5241c24), + C64e(0x7aae3d3dd4e9aee9), C64e(0x986a4c4cf2be6abe), + C64e(0xd85a6c6c82ee5aee), C64e(0xfc417e7ebdc341c3), + C64e(0xf102f5f5f3060206), C64e(0x1d4f838352d14fd1), + C64e(0xd05c68688ce45ce4), C64e(0xa2f451515607f407), + C64e(0xb934d1d18d5c345c), C64e(0xe908f9f9e1180818), + C64e(0xdf93e2e24cae93ae), C64e(0x4d73abab3e957395), + C64e(0xc453626297f553f5), C64e(0x543f2a2a6b413f41), + C64e(0x100c08081c140c14), C64e(0x3152959563f652f6), + C64e(0x8c654646e9af65af), C64e(0x215e9d9d7fe25ee2), + C64e(0x6028303048782878), C64e(0x6ea13737cff8a1f8), + C64e(0x140f0a0a1b110f11), C64e(0x5eb52f2febc4b5c4), + C64e(0x1c090e0e151b091b), C64e(0x483624247e5a365a), + C64e(0x369b1b1badb69bb6), C64e(0xa53ddfdf98473d47), + C64e(0x8126cdcda76a266a), C64e(0x9c694e4ef5bb69bb), + C64e(0xfecd7f7f334ccd4c), C64e(0xcf9feaea50ba9fba), + C64e(0x241b12123f2d1b2d), C64e(0x3a9e1d1da4b99eb9), + C64e(0xb0745858c49c749c), C64e(0x682e343446722e72), + C64e(0x6c2d363641772d77), C64e(0xa3b2dcdc11cdb2cd), + C64e(0x73eeb4b49d29ee29), C64e(0xb6fb5b5b4d16fb16), + C64e(0x53f6a4a4a501f601), C64e(0xec4d7676a1d74dd7), + C64e(0x7561b7b714a361a3), C64e(0xface7d7d3449ce49), + C64e(0xa47b5252df8d7b8d), C64e(0xa13edddd9f423e42), + C64e(0xbc715e5ecd937193), C64e(0x26971313b1a297a2), + C64e(0x57f5a6a6a204f504), C64e(0x6968b9b901b868b8), + C64e(0x0000000000000000), C64e(0x992cc1c1b5742c74), + C64e(0x80604040e0a060a0), C64e(0xdd1fe3e3c2211f21), + C64e(0xf2c879793a43c843), C64e(0x77edb6b69a2ced2c), + C64e(0xb3bed4d40dd9bed9), C64e(0x01468d8d47ca46ca), + C64e(0xced967671770d970), C64e(0xe44b7272afdd4bdd), + C64e(0x33de9494ed79de79), C64e(0x2bd49898ff67d467), + C64e(0x7be8b0b09323e823), C64e(0x114a85855bde4ade), + C64e(0x6d6bbbbb06bd6bbd), C64e(0x912ac5c5bb7e2a7e), + C64e(0x9ee54f4f7b34e534), C64e(0xc116ededd73a163a), + C64e(0x17c58686d254c554), C64e(0x2fd79a9af862d762), + C64e(0xcc55666699ff55ff), C64e(0x22941111b6a794a7), + C64e(0x0fcf8a8ac04acf4a), C64e(0xc910e9e9d9301030), + C64e(0x080604040e0a060a), C64e(0xe781fefe66988198), + C64e(0x5bf0a0a0ab0bf00b), C64e(0xf0447878b4cc44cc), + C64e(0x4aba2525f0d5bad5), C64e(0x96e34b4b753ee33e), + C64e(0x5ff3a2a2ac0ef30e), C64e(0xbafe5d5d4419fe19), + C64e(0x1bc08080db5bc05b), C64e(0x0a8a050580858a85), + C64e(0x7ead3f3fd3ecadec), C64e(0x42bc2121fedfbcdf), + C64e(0xe0487070a8d848d8), C64e(0xf904f1f1fd0c040c), + C64e(0xc6df6363197adf7a), C64e(0xeec177772f58c158), + C64e(0x4575afaf309f759f), C64e(0x84634242e7a563a5), + C64e(0x4030202070503050), C64e(0xd11ae5e5cb2e1a2e), + C64e(0xe10efdfdef120e12), C64e(0x656dbfbf08b76db7), + C64e(0x194c818155d44cd4), C64e(0x30141818243c143c), + C64e(0x4c352626795f355f), C64e(0x9d2fc3c3b2712f71), + C64e(0x67e1bebe8638e138), C64e(0x6aa23535c8fda2fd), + C64e(0x0bcc8888c74fcc4f), C64e(0x5c392e2e654b394b), + C64e(0x3d5793936af957f9), C64e(0xaaf25555580df20d), + C64e(0xe382fcfc619d829d), C64e(0xf4477a7ab3c947c9), + C64e(0x8bacc8c827efacef), C64e(0x6fe7baba8832e732), + C64e(0x642b32324f7d2b7d), C64e(0xd795e6e642a495a4), + C64e(0x9ba0c0c03bfba0fb), C64e(0x32981919aab398b3), + C64e(0x27d19e9ef668d168), C64e(0x5d7fa3a322817f81), + C64e(0x88664444eeaa66aa), C64e(0xa87e5454d6827e82), + C64e(0x76ab3b3bdde6abe6), C64e(0x16830b0b959e839e), + C64e(0x03ca8c8cc945ca45), C64e(0x9529c7c7bc7b297b), + C64e(0xd6d36b6b056ed36e), C64e(0x503c28286c443c44), + C64e(0x5579a7a72c8b798b), C64e(0x63e2bcbc813de23d), + C64e(0x2c1d161631271d27), C64e(0x4176adad379a769a), + C64e(0xad3bdbdb964d3b4d), C64e(0xc85664649efa56fa), + C64e(0xe84e7474a6d24ed2), C64e(0x281e141436221e22), + C64e(0x3fdb9292e476db76), C64e(0x180a0c0c121e0a1e), + C64e(0x906c4848fcb46cb4), C64e(0x6be4b8b88f37e437), + C64e(0x255d9f9f78e75de7), C64e(0x616ebdbd0fb26eb2), + C64e(0x86ef4343692aef2a), C64e(0x93a6c4c435f1a6f1), + C64e(0x72a83939dae3a8e3), C64e(0x62a43131c6f7a4f7), + C64e(0xbd37d3d38a593759), C64e(0xff8bf2f274868b86), + C64e(0xb132d5d583563256), C64e(0x0d438b8b4ec543c5), + C64e(0xdc596e6e85eb59eb), C64e(0xafb7dada18c2b7c2), + C64e(0x028c01018e8f8c8f), C64e(0x7964b1b11dac64ac), + C64e(0x23d29c9cf16dd26d), C64e(0x92e04949723be03b), + C64e(0xabb4d8d81fc7b4c7), C64e(0x43faacacb915fa15), + C64e(0xfd07f3f3fa090709), C64e(0x8525cfcfa06f256f), + C64e(0x8fafcaca20eaafea), C64e(0xf38ef4f47d898e89), + C64e(0x8ee947476720e920), C64e(0x2018101038281828), + C64e(0xded56f6f0b64d564), C64e(0xfb88f0f073838883), + C64e(0x946f4a4afbb16fb1), C64e(0xb8725c5cca967296), + C64e(0x70243838546c246c), C64e(0xaef157575f08f108), + C64e(0xe6c773732152c752), C64e(0x3551979764f351f3), + C64e(0x8d23cbcbae652365), C64e(0x597ca1a125847c84), + C64e(0xcb9ce8e857bf9cbf), C64e(0x7c213e3e5d632163), + C64e(0x37dd9696ea7cdd7c), C64e(0xc2dc61611e7fdc7f), + C64e(0x1a860d0d9c918691), C64e(0x1e850f0f9b948594), + C64e(0xdb90e0e04bab90ab), C64e(0xf8427c7cbac642c6), + C64e(0xe2c471712657c457), C64e(0x83aacccc29e5aae5), + C64e(0x3bd89090e373d873), C64e(0x0c050606090f050f), + C64e(0xf501f7f7f4030103), C64e(0x38121c1c2a361236), + C64e(0x9fa3c2c23cfea3fe), C64e(0xd45f6a6a8be15fe1), + C64e(0x47f9aeaebe10f910), C64e(0xd2d06969026bd06b), + C64e(0x2e911717bfa891a8), C64e(0x2958999971e858e8), + C64e(0x74273a3a53692769), C64e(0x4eb92727f7d0b9d0), + C64e(0xa938d9d991483848), C64e(0xcd13ebebde351335), + C64e(0x56b32b2be5ceb3ce), C64e(0x4433222277553355), + C64e(0xbfbbd2d204d6bbd6), C64e(0x4970a9a939907090), + C64e(0x0e89070787808980), C64e(0x66a73333c1f2a7f2), + C64e(0x5ab62d2decc1b6c1), C64e(0x78223c3c5a662266), + C64e(0x2a921515b8ad92ad), C64e(0x8920c9c9a9602060), + C64e(0x154987875cdb49db), C64e(0x4fffaaaab01aff1a), + C64e(0xa0785050d8887888), C64e(0x517aa5a52b8e7a8e), + C64e(0x068f0303898a8f8a), C64e(0xb2f859594a13f813), + C64e(0x12800909929b809b), C64e(0x34171a1a23391739), + C64e(0xcada65651075da75), C64e(0xb531d7d784533153), + C64e(0x13c68484d551c651), C64e(0xbbb8d0d003d3b8d3), + C64e(0x1fc38282dc5ec35e), C64e(0x52b02929e2cbb0cb), + C64e(0xb4775a5ac3997799), C64e(0x3c111e1e2d331133), + C64e(0xf6cb7b7b3d46cb46), C64e(0x4bfca8a8b71ffc1f), + C64e(0xdad66d6d0c61d661), C64e(0x583a2c2c624e3a4e) +}; + +#endif + +static const sph_u64 T4[] = { + C64e(0xf497a5c6c632f4a5), C64e(0x97eb84f8f86f9784), + C64e(0xb0c799eeee5eb099), C64e(0x8cf78df6f67a8c8d), + C64e(0x17e50dffffe8170d), C64e(0xdcb7bdd6d60adcbd), + C64e(0xc8a7b1dede16c8b1), C64e(0xfc395491916dfc54), + C64e(0xf0c050606090f050), C64e(0x0504030202070503), + C64e(0xe087a9cece2ee0a9), C64e(0x87ac7d5656d1877d), + C64e(0x2bd519e7e7cc2b19), C64e(0xa67162b5b513a662), + C64e(0x319ae64d4d7c31e6), C64e(0xb5c39aecec59b59a), + C64e(0xcf05458f8f40cf45), C64e(0xbc3e9d1f1fa3bc9d), + C64e(0xc00940898949c040), C64e(0x92ef87fafa689287), + C64e(0x3fc515efefd03f15), C64e(0x267febb2b29426eb), + C64e(0x4007c98e8ece40c9), C64e(0x1ded0bfbfbe61d0b), + C64e(0x2f82ec41416e2fec), C64e(0xa97d67b3b31aa967), + C64e(0x1cbefd5f5f431cfd), C64e(0x258aea45456025ea), + C64e(0xda46bf2323f9dabf), C64e(0x02a6f753535102f7), + C64e(0xa1d396e4e445a196), C64e(0xed2d5b9b9b76ed5b), + C64e(0x5deac27575285dc2), C64e(0x24d91ce1e1c5241c), + C64e(0xe97aae3d3dd4e9ae), C64e(0xbe986a4c4cf2be6a), + C64e(0xeed85a6c6c82ee5a), C64e(0xc3fc417e7ebdc341), + C64e(0x06f102f5f5f30602), C64e(0xd11d4f838352d14f), + C64e(0xe4d05c68688ce45c), C64e(0x07a2f451515607f4), + C64e(0x5cb934d1d18d5c34), C64e(0x18e908f9f9e11808), + C64e(0xaedf93e2e24cae93), C64e(0x954d73abab3e9573), + C64e(0xf5c453626297f553), C64e(0x41543f2a2a6b413f), + C64e(0x14100c08081c140c), C64e(0xf63152959563f652), + C64e(0xaf8c654646e9af65), C64e(0xe2215e9d9d7fe25e), + C64e(0x7860283030487828), C64e(0xf86ea13737cff8a1), + C64e(0x11140f0a0a1b110f), C64e(0xc45eb52f2febc4b5), + C64e(0x1b1c090e0e151b09), C64e(0x5a483624247e5a36), + C64e(0xb6369b1b1badb69b), C64e(0x47a53ddfdf98473d), + C64e(0x6a8126cdcda76a26), C64e(0xbb9c694e4ef5bb69), + C64e(0x4cfecd7f7f334ccd), C64e(0xbacf9feaea50ba9f), + C64e(0x2d241b12123f2d1b), C64e(0xb93a9e1d1da4b99e), + C64e(0x9cb0745858c49c74), C64e(0x72682e343446722e), + C64e(0x776c2d363641772d), C64e(0xcda3b2dcdc11cdb2), + C64e(0x2973eeb4b49d29ee), C64e(0x16b6fb5b5b4d16fb), + C64e(0x0153f6a4a4a501f6), C64e(0xd7ec4d7676a1d74d), + C64e(0xa37561b7b714a361), C64e(0x49face7d7d3449ce), + C64e(0x8da47b5252df8d7b), C64e(0x42a13edddd9f423e), + C64e(0x93bc715e5ecd9371), C64e(0xa226971313b1a297), + C64e(0x0457f5a6a6a204f5), C64e(0xb86968b9b901b868), + C64e(0x0000000000000000), C64e(0x74992cc1c1b5742c), + C64e(0xa080604040e0a060), C64e(0x21dd1fe3e3c2211f), + C64e(0x43f2c879793a43c8), C64e(0x2c77edb6b69a2ced), + C64e(0xd9b3bed4d40dd9be), C64e(0xca01468d8d47ca46), + C64e(0x70ced967671770d9), C64e(0xdde44b7272afdd4b), + C64e(0x7933de9494ed79de), C64e(0x672bd49898ff67d4), + C64e(0x237be8b0b09323e8), C64e(0xde114a85855bde4a), + C64e(0xbd6d6bbbbb06bd6b), C64e(0x7e912ac5c5bb7e2a), + C64e(0x349ee54f4f7b34e5), C64e(0x3ac116ededd73a16), + C64e(0x5417c58686d254c5), C64e(0x622fd79a9af862d7), + C64e(0xffcc55666699ff55), C64e(0xa722941111b6a794), + C64e(0x4a0fcf8a8ac04acf), C64e(0x30c910e9e9d93010), + C64e(0x0a080604040e0a06), C64e(0x98e781fefe669881), + C64e(0x0b5bf0a0a0ab0bf0), C64e(0xccf0447878b4cc44), + C64e(0xd54aba2525f0d5ba), C64e(0x3e96e34b4b753ee3), + C64e(0x0e5ff3a2a2ac0ef3), C64e(0x19bafe5d5d4419fe), + C64e(0x5b1bc08080db5bc0), C64e(0x850a8a050580858a), + C64e(0xec7ead3f3fd3ecad), C64e(0xdf42bc2121fedfbc), + C64e(0xd8e0487070a8d848), C64e(0x0cf904f1f1fd0c04), + C64e(0x7ac6df6363197adf), C64e(0x58eec177772f58c1), + C64e(0x9f4575afaf309f75), C64e(0xa584634242e7a563), + C64e(0x5040302020705030), C64e(0x2ed11ae5e5cb2e1a), + C64e(0x12e10efdfdef120e), C64e(0xb7656dbfbf08b76d), + C64e(0xd4194c818155d44c), C64e(0x3c30141818243c14), + C64e(0x5f4c352626795f35), C64e(0x719d2fc3c3b2712f), + C64e(0x3867e1bebe8638e1), C64e(0xfd6aa23535c8fda2), + C64e(0x4f0bcc8888c74fcc), C64e(0x4b5c392e2e654b39), + C64e(0xf93d5793936af957), C64e(0x0daaf25555580df2), + C64e(0x9de382fcfc619d82), C64e(0xc9f4477a7ab3c947), + C64e(0xef8bacc8c827efac), C64e(0x326fe7baba8832e7), + C64e(0x7d642b32324f7d2b), C64e(0xa4d795e6e642a495), + C64e(0xfb9ba0c0c03bfba0), C64e(0xb332981919aab398), + C64e(0x6827d19e9ef668d1), C64e(0x815d7fa3a322817f), + C64e(0xaa88664444eeaa66), C64e(0x82a87e5454d6827e), + C64e(0xe676ab3b3bdde6ab), C64e(0x9e16830b0b959e83), + C64e(0x4503ca8c8cc945ca), C64e(0x7b9529c7c7bc7b29), + C64e(0x6ed6d36b6b056ed3), C64e(0x44503c28286c443c), + C64e(0x8b5579a7a72c8b79), C64e(0x3d63e2bcbc813de2), + C64e(0x272c1d161631271d), C64e(0x9a4176adad379a76), + C64e(0x4dad3bdbdb964d3b), C64e(0xfac85664649efa56), + C64e(0xd2e84e7474a6d24e), C64e(0x22281e141436221e), + C64e(0x763fdb9292e476db), C64e(0x1e180a0c0c121e0a), + C64e(0xb4906c4848fcb46c), C64e(0x376be4b8b88f37e4), + C64e(0xe7255d9f9f78e75d), C64e(0xb2616ebdbd0fb26e), + C64e(0x2a86ef4343692aef), C64e(0xf193a6c4c435f1a6), + C64e(0xe372a83939dae3a8), C64e(0xf762a43131c6f7a4), + C64e(0x59bd37d3d38a5937), C64e(0x86ff8bf2f274868b), + C64e(0x56b132d5d5835632), C64e(0xc50d438b8b4ec543), + C64e(0xebdc596e6e85eb59), C64e(0xc2afb7dada18c2b7), + C64e(0x8f028c01018e8f8c), C64e(0xac7964b1b11dac64), + C64e(0x6d23d29c9cf16dd2), C64e(0x3b92e04949723be0), + C64e(0xc7abb4d8d81fc7b4), C64e(0x1543faacacb915fa), + C64e(0x09fd07f3f3fa0907), C64e(0x6f8525cfcfa06f25), + C64e(0xea8fafcaca20eaaf), C64e(0x89f38ef4f47d898e), + C64e(0x208ee947476720e9), C64e(0x2820181010382818), + C64e(0x64ded56f6f0b64d5), C64e(0x83fb88f0f0738388), + C64e(0xb1946f4a4afbb16f), C64e(0x96b8725c5cca9672), + C64e(0x6c70243838546c24), C64e(0x08aef157575f08f1), + C64e(0x52e6c773732152c7), C64e(0xf33551979764f351), + C64e(0x658d23cbcbae6523), C64e(0x84597ca1a125847c), + C64e(0xbfcb9ce8e857bf9c), C64e(0x637c213e3e5d6321), + C64e(0x7c37dd9696ea7cdd), C64e(0x7fc2dc61611e7fdc), + C64e(0x911a860d0d9c9186), C64e(0x941e850f0f9b9485), + C64e(0xabdb90e0e04bab90), C64e(0xc6f8427c7cbac642), + C64e(0x57e2c471712657c4), C64e(0xe583aacccc29e5aa), + C64e(0x733bd89090e373d8), C64e(0x0f0c050606090f05), + C64e(0x03f501f7f7f40301), C64e(0x3638121c1c2a3612), + C64e(0xfe9fa3c2c23cfea3), C64e(0xe1d45f6a6a8be15f), + C64e(0x1047f9aeaebe10f9), C64e(0x6bd2d06969026bd0), + C64e(0xa82e911717bfa891), C64e(0xe82958999971e858), + C64e(0x6974273a3a536927), C64e(0xd04eb92727f7d0b9), + C64e(0x48a938d9d9914838), C64e(0x35cd13ebebde3513), + C64e(0xce56b32b2be5ceb3), C64e(0x5544332222775533), + C64e(0xd6bfbbd2d204d6bb), C64e(0x904970a9a9399070), + C64e(0x800e890707878089), C64e(0xf266a73333c1f2a7), + C64e(0xc15ab62d2decc1b6), C64e(0x6678223c3c5a6622), + C64e(0xad2a921515b8ad92), C64e(0x608920c9c9a96020), + C64e(0xdb154987875cdb49), C64e(0x1a4fffaaaab01aff), + C64e(0x88a0785050d88878), C64e(0x8e517aa5a52b8e7a), + C64e(0x8a068f0303898a8f), C64e(0x13b2f859594a13f8), + C64e(0x9b12800909929b80), C64e(0x3934171a1a233917), + C64e(0x75cada65651075da), C64e(0x53b531d7d7845331), + C64e(0x5113c68484d551c6), C64e(0xd3bbb8d0d003d3b8), + C64e(0x5e1fc38282dc5ec3), C64e(0xcb52b02929e2cbb0), + C64e(0x99b4775a5ac39977), C64e(0x333c111e1e2d3311), + C64e(0x46f6cb7b7b3d46cb), C64e(0x1f4bfca8a8b71ffc), + C64e(0x61dad66d6d0c61d6), C64e(0x4e583a2c2c624e3a) +}; + +#if !SPH_SMALL_FOOTPRINT_GROESTL + +static const sph_u64 T5[] = { + C64e(0xa5f497a5c6c632f4), C64e(0x8497eb84f8f86f97), + C64e(0x99b0c799eeee5eb0), C64e(0x8d8cf78df6f67a8c), + C64e(0x0d17e50dffffe817), C64e(0xbddcb7bdd6d60adc), + C64e(0xb1c8a7b1dede16c8), C64e(0x54fc395491916dfc), + C64e(0x50f0c050606090f0), C64e(0x0305040302020705), + C64e(0xa9e087a9cece2ee0), C64e(0x7d87ac7d5656d187), + C64e(0x192bd519e7e7cc2b), C64e(0x62a67162b5b513a6), + C64e(0xe6319ae64d4d7c31), C64e(0x9ab5c39aecec59b5), + C64e(0x45cf05458f8f40cf), C64e(0x9dbc3e9d1f1fa3bc), + C64e(0x40c00940898949c0), C64e(0x8792ef87fafa6892), + C64e(0x153fc515efefd03f), C64e(0xeb267febb2b29426), + C64e(0xc94007c98e8ece40), C64e(0x0b1ded0bfbfbe61d), + C64e(0xec2f82ec41416e2f), C64e(0x67a97d67b3b31aa9), + C64e(0xfd1cbefd5f5f431c), C64e(0xea258aea45456025), + C64e(0xbfda46bf2323f9da), C64e(0xf702a6f753535102), + C64e(0x96a1d396e4e445a1), C64e(0x5bed2d5b9b9b76ed), + C64e(0xc25deac27575285d), C64e(0x1c24d91ce1e1c524), + C64e(0xaee97aae3d3dd4e9), C64e(0x6abe986a4c4cf2be), + C64e(0x5aeed85a6c6c82ee), C64e(0x41c3fc417e7ebdc3), + C64e(0x0206f102f5f5f306), C64e(0x4fd11d4f838352d1), + C64e(0x5ce4d05c68688ce4), C64e(0xf407a2f451515607), + C64e(0x345cb934d1d18d5c), C64e(0x0818e908f9f9e118), + C64e(0x93aedf93e2e24cae), C64e(0x73954d73abab3e95), + C64e(0x53f5c453626297f5), C64e(0x3f41543f2a2a6b41), + C64e(0x0c14100c08081c14), C64e(0x52f63152959563f6), + C64e(0x65af8c654646e9af), C64e(0x5ee2215e9d9d7fe2), + C64e(0x2878602830304878), C64e(0xa1f86ea13737cff8), + C64e(0x0f11140f0a0a1b11), C64e(0xb5c45eb52f2febc4), + C64e(0x091b1c090e0e151b), C64e(0x365a483624247e5a), + C64e(0x9bb6369b1b1badb6), C64e(0x3d47a53ddfdf9847), + C64e(0x266a8126cdcda76a), C64e(0x69bb9c694e4ef5bb), + C64e(0xcd4cfecd7f7f334c), C64e(0x9fbacf9feaea50ba), + C64e(0x1b2d241b12123f2d), C64e(0x9eb93a9e1d1da4b9), + C64e(0x749cb0745858c49c), C64e(0x2e72682e34344672), + C64e(0x2d776c2d36364177), C64e(0xb2cda3b2dcdc11cd), + C64e(0xee2973eeb4b49d29), C64e(0xfb16b6fb5b5b4d16), + C64e(0xf60153f6a4a4a501), C64e(0x4dd7ec4d7676a1d7), + C64e(0x61a37561b7b714a3), C64e(0xce49face7d7d3449), + C64e(0x7b8da47b5252df8d), C64e(0x3e42a13edddd9f42), + C64e(0x7193bc715e5ecd93), C64e(0x97a226971313b1a2), + C64e(0xf50457f5a6a6a204), C64e(0x68b86968b9b901b8), + C64e(0x0000000000000000), C64e(0x2c74992cc1c1b574), + C64e(0x60a080604040e0a0), C64e(0x1f21dd1fe3e3c221), + C64e(0xc843f2c879793a43), C64e(0xed2c77edb6b69a2c), + C64e(0xbed9b3bed4d40dd9), C64e(0x46ca01468d8d47ca), + C64e(0xd970ced967671770), C64e(0x4bdde44b7272afdd), + C64e(0xde7933de9494ed79), C64e(0xd4672bd49898ff67), + C64e(0xe8237be8b0b09323), C64e(0x4ade114a85855bde), + C64e(0x6bbd6d6bbbbb06bd), C64e(0x2a7e912ac5c5bb7e), + C64e(0xe5349ee54f4f7b34), C64e(0x163ac116ededd73a), + C64e(0xc55417c58686d254), C64e(0xd7622fd79a9af862), + C64e(0x55ffcc55666699ff), C64e(0x94a722941111b6a7), + C64e(0xcf4a0fcf8a8ac04a), C64e(0x1030c910e9e9d930), + C64e(0x060a080604040e0a), C64e(0x8198e781fefe6698), + C64e(0xf00b5bf0a0a0ab0b), C64e(0x44ccf0447878b4cc), + C64e(0xbad54aba2525f0d5), C64e(0xe33e96e34b4b753e), + C64e(0xf30e5ff3a2a2ac0e), C64e(0xfe19bafe5d5d4419), + C64e(0xc05b1bc08080db5b), C64e(0x8a850a8a05058085), + C64e(0xadec7ead3f3fd3ec), C64e(0xbcdf42bc2121fedf), + C64e(0x48d8e0487070a8d8), C64e(0x040cf904f1f1fd0c), + C64e(0xdf7ac6df6363197a), C64e(0xc158eec177772f58), + C64e(0x759f4575afaf309f), C64e(0x63a584634242e7a5), + C64e(0x3050403020207050), C64e(0x1a2ed11ae5e5cb2e), + C64e(0x0e12e10efdfdef12), C64e(0x6db7656dbfbf08b7), + C64e(0x4cd4194c818155d4), C64e(0x143c30141818243c), + C64e(0x355f4c352626795f), C64e(0x2f719d2fc3c3b271), + C64e(0xe13867e1bebe8638), C64e(0xa2fd6aa23535c8fd), + C64e(0xcc4f0bcc8888c74f), C64e(0x394b5c392e2e654b), + C64e(0x57f93d5793936af9), C64e(0xf20daaf25555580d), + C64e(0x829de382fcfc619d), C64e(0x47c9f4477a7ab3c9), + C64e(0xacef8bacc8c827ef), C64e(0xe7326fe7baba8832), + C64e(0x2b7d642b32324f7d), C64e(0x95a4d795e6e642a4), + C64e(0xa0fb9ba0c0c03bfb), C64e(0x98b332981919aab3), + C64e(0xd16827d19e9ef668), C64e(0x7f815d7fa3a32281), + C64e(0x66aa88664444eeaa), C64e(0x7e82a87e5454d682), + C64e(0xabe676ab3b3bdde6), C64e(0x839e16830b0b959e), + C64e(0xca4503ca8c8cc945), C64e(0x297b9529c7c7bc7b), + C64e(0xd36ed6d36b6b056e), C64e(0x3c44503c28286c44), + C64e(0x798b5579a7a72c8b), C64e(0xe23d63e2bcbc813d), + C64e(0x1d272c1d16163127), C64e(0x769a4176adad379a), + C64e(0x3b4dad3bdbdb964d), C64e(0x56fac85664649efa), + C64e(0x4ed2e84e7474a6d2), C64e(0x1e22281e14143622), + C64e(0xdb763fdb9292e476), C64e(0x0a1e180a0c0c121e), + C64e(0x6cb4906c4848fcb4), C64e(0xe4376be4b8b88f37), + C64e(0x5de7255d9f9f78e7), C64e(0x6eb2616ebdbd0fb2), + C64e(0xef2a86ef4343692a), C64e(0xa6f193a6c4c435f1), + C64e(0xa8e372a83939dae3), C64e(0xa4f762a43131c6f7), + C64e(0x3759bd37d3d38a59), C64e(0x8b86ff8bf2f27486), + C64e(0x3256b132d5d58356), C64e(0x43c50d438b8b4ec5), + C64e(0x59ebdc596e6e85eb), C64e(0xb7c2afb7dada18c2), + C64e(0x8c8f028c01018e8f), C64e(0x64ac7964b1b11dac), + C64e(0xd26d23d29c9cf16d), C64e(0xe03b92e04949723b), + C64e(0xb4c7abb4d8d81fc7), C64e(0xfa1543faacacb915), + C64e(0x0709fd07f3f3fa09), C64e(0x256f8525cfcfa06f), + C64e(0xafea8fafcaca20ea), C64e(0x8e89f38ef4f47d89), + C64e(0xe9208ee947476720), C64e(0x1828201810103828), + C64e(0xd564ded56f6f0b64), C64e(0x8883fb88f0f07383), + C64e(0x6fb1946f4a4afbb1), C64e(0x7296b8725c5cca96), + C64e(0x246c70243838546c), C64e(0xf108aef157575f08), + C64e(0xc752e6c773732152), C64e(0x51f33551979764f3), + C64e(0x23658d23cbcbae65), C64e(0x7c84597ca1a12584), + C64e(0x9cbfcb9ce8e857bf), C64e(0x21637c213e3e5d63), + C64e(0xdd7c37dd9696ea7c), C64e(0xdc7fc2dc61611e7f), + C64e(0x86911a860d0d9c91), C64e(0x85941e850f0f9b94), + C64e(0x90abdb90e0e04bab), C64e(0x42c6f8427c7cbac6), + C64e(0xc457e2c471712657), C64e(0xaae583aacccc29e5), + C64e(0xd8733bd89090e373), C64e(0x050f0c050606090f), + C64e(0x0103f501f7f7f403), C64e(0x123638121c1c2a36), + C64e(0xa3fe9fa3c2c23cfe), C64e(0x5fe1d45f6a6a8be1), + C64e(0xf91047f9aeaebe10), C64e(0xd06bd2d06969026b), + C64e(0x91a82e911717bfa8), C64e(0x58e82958999971e8), + C64e(0x276974273a3a5369), C64e(0xb9d04eb92727f7d0), + C64e(0x3848a938d9d99148), C64e(0x1335cd13ebebde35), + C64e(0xb3ce56b32b2be5ce), C64e(0x3355443322227755), + C64e(0xbbd6bfbbd2d204d6), C64e(0x70904970a9a93990), + C64e(0x89800e8907078780), C64e(0xa7f266a73333c1f2), + C64e(0xb6c15ab62d2decc1), C64e(0x226678223c3c5a66), + C64e(0x92ad2a921515b8ad), C64e(0x20608920c9c9a960), + C64e(0x49db154987875cdb), C64e(0xff1a4fffaaaab01a), + C64e(0x7888a0785050d888), C64e(0x7a8e517aa5a52b8e), + C64e(0x8f8a068f0303898a), C64e(0xf813b2f859594a13), + C64e(0x809b12800909929b), C64e(0x173934171a1a2339), + C64e(0xda75cada65651075), C64e(0x3153b531d7d78453), + C64e(0xc65113c68484d551), C64e(0xb8d3bbb8d0d003d3), + C64e(0xc35e1fc38282dc5e), C64e(0xb0cb52b02929e2cb), + C64e(0x7799b4775a5ac399), C64e(0x11333c111e1e2d33), + C64e(0xcb46f6cb7b7b3d46), C64e(0xfc1f4bfca8a8b71f), + C64e(0xd661dad66d6d0c61), C64e(0x3a4e583a2c2c624e) +}; + +static const sph_u64 T6[] = { + C64e(0xf4a5f497a5c6c632), C64e(0x978497eb84f8f86f), + C64e(0xb099b0c799eeee5e), C64e(0x8c8d8cf78df6f67a), + C64e(0x170d17e50dffffe8), C64e(0xdcbddcb7bdd6d60a), + C64e(0xc8b1c8a7b1dede16), C64e(0xfc54fc395491916d), + C64e(0xf050f0c050606090), C64e(0x0503050403020207), + C64e(0xe0a9e087a9cece2e), C64e(0x877d87ac7d5656d1), + C64e(0x2b192bd519e7e7cc), C64e(0xa662a67162b5b513), + C64e(0x31e6319ae64d4d7c), C64e(0xb59ab5c39aecec59), + C64e(0xcf45cf05458f8f40), C64e(0xbc9dbc3e9d1f1fa3), + C64e(0xc040c00940898949), C64e(0x928792ef87fafa68), + C64e(0x3f153fc515efefd0), C64e(0x26eb267febb2b294), + C64e(0x40c94007c98e8ece), C64e(0x1d0b1ded0bfbfbe6), + C64e(0x2fec2f82ec41416e), C64e(0xa967a97d67b3b31a), + C64e(0x1cfd1cbefd5f5f43), C64e(0x25ea258aea454560), + C64e(0xdabfda46bf2323f9), C64e(0x02f702a6f7535351), + C64e(0xa196a1d396e4e445), C64e(0xed5bed2d5b9b9b76), + C64e(0x5dc25deac2757528), C64e(0x241c24d91ce1e1c5), + C64e(0xe9aee97aae3d3dd4), C64e(0xbe6abe986a4c4cf2), + C64e(0xee5aeed85a6c6c82), C64e(0xc341c3fc417e7ebd), + C64e(0x060206f102f5f5f3), C64e(0xd14fd11d4f838352), + C64e(0xe45ce4d05c68688c), C64e(0x07f407a2f4515156), + C64e(0x5c345cb934d1d18d), C64e(0x180818e908f9f9e1), + C64e(0xae93aedf93e2e24c), C64e(0x9573954d73abab3e), + C64e(0xf553f5c453626297), C64e(0x413f41543f2a2a6b), + C64e(0x140c14100c08081c), C64e(0xf652f63152959563), + C64e(0xaf65af8c654646e9), C64e(0xe25ee2215e9d9d7f), + C64e(0x7828786028303048), C64e(0xf8a1f86ea13737cf), + C64e(0x110f11140f0a0a1b), C64e(0xc4b5c45eb52f2feb), + C64e(0x1b091b1c090e0e15), C64e(0x5a365a483624247e), + C64e(0xb69bb6369b1b1bad), C64e(0x473d47a53ddfdf98), + C64e(0x6a266a8126cdcda7), C64e(0xbb69bb9c694e4ef5), + C64e(0x4ccd4cfecd7f7f33), C64e(0xba9fbacf9feaea50), + C64e(0x2d1b2d241b12123f), C64e(0xb99eb93a9e1d1da4), + C64e(0x9c749cb0745858c4), C64e(0x722e72682e343446), + C64e(0x772d776c2d363641), C64e(0xcdb2cda3b2dcdc11), + C64e(0x29ee2973eeb4b49d), C64e(0x16fb16b6fb5b5b4d), + C64e(0x01f60153f6a4a4a5), C64e(0xd74dd7ec4d7676a1), + C64e(0xa361a37561b7b714), C64e(0x49ce49face7d7d34), + C64e(0x8d7b8da47b5252df), C64e(0x423e42a13edddd9f), + C64e(0x937193bc715e5ecd), C64e(0xa297a226971313b1), + C64e(0x04f50457f5a6a6a2), C64e(0xb868b86968b9b901), + C64e(0x0000000000000000), C64e(0x742c74992cc1c1b5), + C64e(0xa060a080604040e0), C64e(0x211f21dd1fe3e3c2), + C64e(0x43c843f2c879793a), C64e(0x2ced2c77edb6b69a), + C64e(0xd9bed9b3bed4d40d), C64e(0xca46ca01468d8d47), + C64e(0x70d970ced9676717), C64e(0xdd4bdde44b7272af), + C64e(0x79de7933de9494ed), C64e(0x67d4672bd49898ff), + C64e(0x23e8237be8b0b093), C64e(0xde4ade114a85855b), + C64e(0xbd6bbd6d6bbbbb06), C64e(0x7e2a7e912ac5c5bb), + C64e(0x34e5349ee54f4f7b), C64e(0x3a163ac116ededd7), + C64e(0x54c55417c58686d2), C64e(0x62d7622fd79a9af8), + C64e(0xff55ffcc55666699), C64e(0xa794a722941111b6), + C64e(0x4acf4a0fcf8a8ac0), C64e(0x301030c910e9e9d9), + C64e(0x0a060a080604040e), C64e(0x988198e781fefe66), + C64e(0x0bf00b5bf0a0a0ab), C64e(0xcc44ccf0447878b4), + C64e(0xd5bad54aba2525f0), C64e(0x3ee33e96e34b4b75), + C64e(0x0ef30e5ff3a2a2ac), C64e(0x19fe19bafe5d5d44), + C64e(0x5bc05b1bc08080db), C64e(0x858a850a8a050580), + C64e(0xecadec7ead3f3fd3), C64e(0xdfbcdf42bc2121fe), + C64e(0xd848d8e0487070a8), C64e(0x0c040cf904f1f1fd), + C64e(0x7adf7ac6df636319), C64e(0x58c158eec177772f), + C64e(0x9f759f4575afaf30), C64e(0xa563a584634242e7), + C64e(0x5030504030202070), C64e(0x2e1a2ed11ae5e5cb), + C64e(0x120e12e10efdfdef), C64e(0xb76db7656dbfbf08), + C64e(0xd44cd4194c818155), C64e(0x3c143c3014181824), + C64e(0x5f355f4c35262679), C64e(0x712f719d2fc3c3b2), + C64e(0x38e13867e1bebe86), C64e(0xfda2fd6aa23535c8), + C64e(0x4fcc4f0bcc8888c7), C64e(0x4b394b5c392e2e65), + C64e(0xf957f93d5793936a), C64e(0x0df20daaf2555558), + C64e(0x9d829de382fcfc61), C64e(0xc947c9f4477a7ab3), + C64e(0xefacef8bacc8c827), C64e(0x32e7326fe7baba88), + C64e(0x7d2b7d642b32324f), C64e(0xa495a4d795e6e642), + C64e(0xfba0fb9ba0c0c03b), C64e(0xb398b332981919aa), + C64e(0x68d16827d19e9ef6), C64e(0x817f815d7fa3a322), + C64e(0xaa66aa88664444ee), C64e(0x827e82a87e5454d6), + C64e(0xe6abe676ab3b3bdd), C64e(0x9e839e16830b0b95), + C64e(0x45ca4503ca8c8cc9), C64e(0x7b297b9529c7c7bc), + C64e(0x6ed36ed6d36b6b05), C64e(0x443c44503c28286c), + C64e(0x8b798b5579a7a72c), C64e(0x3de23d63e2bcbc81), + C64e(0x271d272c1d161631), C64e(0x9a769a4176adad37), + C64e(0x4d3b4dad3bdbdb96), C64e(0xfa56fac85664649e), + C64e(0xd24ed2e84e7474a6), C64e(0x221e22281e141436), + C64e(0x76db763fdb9292e4), C64e(0x1e0a1e180a0c0c12), + C64e(0xb46cb4906c4848fc), C64e(0x37e4376be4b8b88f), + C64e(0xe75de7255d9f9f78), C64e(0xb26eb2616ebdbd0f), + C64e(0x2aef2a86ef434369), C64e(0xf1a6f193a6c4c435), + C64e(0xe3a8e372a83939da), C64e(0xf7a4f762a43131c6), + C64e(0x593759bd37d3d38a), C64e(0x868b86ff8bf2f274), + C64e(0x563256b132d5d583), C64e(0xc543c50d438b8b4e), + C64e(0xeb59ebdc596e6e85), C64e(0xc2b7c2afb7dada18), + C64e(0x8f8c8f028c01018e), C64e(0xac64ac7964b1b11d), + C64e(0x6dd26d23d29c9cf1), C64e(0x3be03b92e0494972), + C64e(0xc7b4c7abb4d8d81f), C64e(0x15fa1543faacacb9), + C64e(0x090709fd07f3f3fa), C64e(0x6f256f8525cfcfa0), + C64e(0xeaafea8fafcaca20), C64e(0x898e89f38ef4f47d), + C64e(0x20e9208ee9474767), C64e(0x2818282018101038), + C64e(0x64d564ded56f6f0b), C64e(0x838883fb88f0f073), + C64e(0xb16fb1946f4a4afb), C64e(0x967296b8725c5cca), + C64e(0x6c246c7024383854), C64e(0x08f108aef157575f), + C64e(0x52c752e6c7737321), C64e(0xf351f33551979764), + C64e(0x6523658d23cbcbae), C64e(0x847c84597ca1a125), + C64e(0xbf9cbfcb9ce8e857), C64e(0x6321637c213e3e5d), + C64e(0x7cdd7c37dd9696ea), C64e(0x7fdc7fc2dc61611e), + C64e(0x9186911a860d0d9c), C64e(0x9485941e850f0f9b), + C64e(0xab90abdb90e0e04b), C64e(0xc642c6f8427c7cba), + C64e(0x57c457e2c4717126), C64e(0xe5aae583aacccc29), + C64e(0x73d8733bd89090e3), C64e(0x0f050f0c05060609), + C64e(0x030103f501f7f7f4), C64e(0x36123638121c1c2a), + C64e(0xfea3fe9fa3c2c23c), C64e(0xe15fe1d45f6a6a8b), + C64e(0x10f91047f9aeaebe), C64e(0x6bd06bd2d0696902), + C64e(0xa891a82e911717bf), C64e(0xe858e82958999971), + C64e(0x69276974273a3a53), C64e(0xd0b9d04eb92727f7), + C64e(0x483848a938d9d991), C64e(0x351335cd13ebebde), + C64e(0xceb3ce56b32b2be5), C64e(0x5533554433222277), + C64e(0xd6bbd6bfbbd2d204), C64e(0x9070904970a9a939), + C64e(0x8089800e89070787), C64e(0xf2a7f266a73333c1), + C64e(0xc1b6c15ab62d2dec), C64e(0x66226678223c3c5a), + C64e(0xad92ad2a921515b8), C64e(0x6020608920c9c9a9), + C64e(0xdb49db154987875c), C64e(0x1aff1a4fffaaaab0), + C64e(0x887888a0785050d8), C64e(0x8e7a8e517aa5a52b), + C64e(0x8a8f8a068f030389), C64e(0x13f813b2f859594a), + C64e(0x9b809b1280090992), C64e(0x39173934171a1a23), + C64e(0x75da75cada656510), C64e(0x533153b531d7d784), + C64e(0x51c65113c68484d5), C64e(0xd3b8d3bbb8d0d003), + C64e(0x5ec35e1fc38282dc), C64e(0xcbb0cb52b02929e2), + C64e(0x997799b4775a5ac3), C64e(0x3311333c111e1e2d), + C64e(0x46cb46f6cb7b7b3d), C64e(0x1ffc1f4bfca8a8b7), + C64e(0x61d661dad66d6d0c), C64e(0x4e3a4e583a2c2c62) +}; + +static const sph_u64 T7[] = { + C64e(0x32f4a5f497a5c6c6), C64e(0x6f978497eb84f8f8), + C64e(0x5eb099b0c799eeee), C64e(0x7a8c8d8cf78df6f6), + C64e(0xe8170d17e50dffff), C64e(0x0adcbddcb7bdd6d6), + C64e(0x16c8b1c8a7b1dede), C64e(0x6dfc54fc39549191), + C64e(0x90f050f0c0506060), C64e(0x0705030504030202), + C64e(0x2ee0a9e087a9cece), C64e(0xd1877d87ac7d5656), + C64e(0xcc2b192bd519e7e7), C64e(0x13a662a67162b5b5), + C64e(0x7c31e6319ae64d4d), C64e(0x59b59ab5c39aecec), + C64e(0x40cf45cf05458f8f), C64e(0xa3bc9dbc3e9d1f1f), + C64e(0x49c040c009408989), C64e(0x68928792ef87fafa), + C64e(0xd03f153fc515efef), C64e(0x9426eb267febb2b2), + C64e(0xce40c94007c98e8e), C64e(0xe61d0b1ded0bfbfb), + C64e(0x6e2fec2f82ec4141), C64e(0x1aa967a97d67b3b3), + C64e(0x431cfd1cbefd5f5f), C64e(0x6025ea258aea4545), + C64e(0xf9dabfda46bf2323), C64e(0x5102f702a6f75353), + C64e(0x45a196a1d396e4e4), C64e(0x76ed5bed2d5b9b9b), + C64e(0x285dc25deac27575), C64e(0xc5241c24d91ce1e1), + C64e(0xd4e9aee97aae3d3d), C64e(0xf2be6abe986a4c4c), + C64e(0x82ee5aeed85a6c6c), C64e(0xbdc341c3fc417e7e), + C64e(0xf3060206f102f5f5), C64e(0x52d14fd11d4f8383), + C64e(0x8ce45ce4d05c6868), C64e(0x5607f407a2f45151), + C64e(0x8d5c345cb934d1d1), C64e(0xe1180818e908f9f9), + C64e(0x4cae93aedf93e2e2), C64e(0x3e9573954d73abab), + C64e(0x97f553f5c4536262), C64e(0x6b413f41543f2a2a), + C64e(0x1c140c14100c0808), C64e(0x63f652f631529595), + C64e(0xe9af65af8c654646), C64e(0x7fe25ee2215e9d9d), + C64e(0x4878287860283030), C64e(0xcff8a1f86ea13737), + C64e(0x1b110f11140f0a0a), C64e(0xebc4b5c45eb52f2f), + C64e(0x151b091b1c090e0e), C64e(0x7e5a365a48362424), + C64e(0xadb69bb6369b1b1b), C64e(0x98473d47a53ddfdf), + C64e(0xa76a266a8126cdcd), C64e(0xf5bb69bb9c694e4e), + C64e(0x334ccd4cfecd7f7f), C64e(0x50ba9fbacf9feaea), + C64e(0x3f2d1b2d241b1212), C64e(0xa4b99eb93a9e1d1d), + C64e(0xc49c749cb0745858), C64e(0x46722e72682e3434), + C64e(0x41772d776c2d3636), C64e(0x11cdb2cda3b2dcdc), + C64e(0x9d29ee2973eeb4b4), C64e(0x4d16fb16b6fb5b5b), + C64e(0xa501f60153f6a4a4), C64e(0xa1d74dd7ec4d7676), + C64e(0x14a361a37561b7b7), C64e(0x3449ce49face7d7d), + C64e(0xdf8d7b8da47b5252), C64e(0x9f423e42a13edddd), + C64e(0xcd937193bc715e5e), C64e(0xb1a297a226971313), + C64e(0xa204f50457f5a6a6), C64e(0x01b868b86968b9b9), + C64e(0x0000000000000000), C64e(0xb5742c74992cc1c1), + C64e(0xe0a060a080604040), C64e(0xc2211f21dd1fe3e3), + C64e(0x3a43c843f2c87979), C64e(0x9a2ced2c77edb6b6), + C64e(0x0dd9bed9b3bed4d4), C64e(0x47ca46ca01468d8d), + C64e(0x1770d970ced96767), C64e(0xafdd4bdde44b7272), + C64e(0xed79de7933de9494), C64e(0xff67d4672bd49898), + C64e(0x9323e8237be8b0b0), C64e(0x5bde4ade114a8585), + C64e(0x06bd6bbd6d6bbbbb), C64e(0xbb7e2a7e912ac5c5), + C64e(0x7b34e5349ee54f4f), C64e(0xd73a163ac116eded), + C64e(0xd254c55417c58686), C64e(0xf862d7622fd79a9a), + C64e(0x99ff55ffcc556666), C64e(0xb6a794a722941111), + C64e(0xc04acf4a0fcf8a8a), C64e(0xd9301030c910e9e9), + C64e(0x0e0a060a08060404), C64e(0x66988198e781fefe), + C64e(0xab0bf00b5bf0a0a0), C64e(0xb4cc44ccf0447878), + C64e(0xf0d5bad54aba2525), C64e(0x753ee33e96e34b4b), + C64e(0xac0ef30e5ff3a2a2), C64e(0x4419fe19bafe5d5d), + C64e(0xdb5bc05b1bc08080), C64e(0x80858a850a8a0505), + C64e(0xd3ecadec7ead3f3f), C64e(0xfedfbcdf42bc2121), + C64e(0xa8d848d8e0487070), C64e(0xfd0c040cf904f1f1), + C64e(0x197adf7ac6df6363), C64e(0x2f58c158eec17777), + C64e(0x309f759f4575afaf), C64e(0xe7a563a584634242), + C64e(0x7050305040302020), C64e(0xcb2e1a2ed11ae5e5), + C64e(0xef120e12e10efdfd), C64e(0x08b76db7656dbfbf), + C64e(0x55d44cd4194c8181), C64e(0x243c143c30141818), + C64e(0x795f355f4c352626), C64e(0xb2712f719d2fc3c3), + C64e(0x8638e13867e1bebe), C64e(0xc8fda2fd6aa23535), + C64e(0xc74fcc4f0bcc8888), C64e(0x654b394b5c392e2e), + C64e(0x6af957f93d579393), C64e(0x580df20daaf25555), + C64e(0x619d829de382fcfc), C64e(0xb3c947c9f4477a7a), + C64e(0x27efacef8bacc8c8), C64e(0x8832e7326fe7baba), + C64e(0x4f7d2b7d642b3232), C64e(0x42a495a4d795e6e6), + C64e(0x3bfba0fb9ba0c0c0), C64e(0xaab398b332981919), + C64e(0xf668d16827d19e9e), C64e(0x22817f815d7fa3a3), + C64e(0xeeaa66aa88664444), C64e(0xd6827e82a87e5454), + C64e(0xdde6abe676ab3b3b), C64e(0x959e839e16830b0b), + C64e(0xc945ca4503ca8c8c), C64e(0xbc7b297b9529c7c7), + C64e(0x056ed36ed6d36b6b), C64e(0x6c443c44503c2828), + C64e(0x2c8b798b5579a7a7), C64e(0x813de23d63e2bcbc), + C64e(0x31271d272c1d1616), C64e(0x379a769a4176adad), + C64e(0x964d3b4dad3bdbdb), C64e(0x9efa56fac8566464), + C64e(0xa6d24ed2e84e7474), C64e(0x36221e22281e1414), + C64e(0xe476db763fdb9292), C64e(0x121e0a1e180a0c0c), + C64e(0xfcb46cb4906c4848), C64e(0x8f37e4376be4b8b8), + C64e(0x78e75de7255d9f9f), C64e(0x0fb26eb2616ebdbd), + C64e(0x692aef2a86ef4343), C64e(0x35f1a6f193a6c4c4), + C64e(0xdae3a8e372a83939), C64e(0xc6f7a4f762a43131), + C64e(0x8a593759bd37d3d3), C64e(0x74868b86ff8bf2f2), + C64e(0x83563256b132d5d5), C64e(0x4ec543c50d438b8b), + C64e(0x85eb59ebdc596e6e), C64e(0x18c2b7c2afb7dada), + C64e(0x8e8f8c8f028c0101), C64e(0x1dac64ac7964b1b1), + C64e(0xf16dd26d23d29c9c), C64e(0x723be03b92e04949), + C64e(0x1fc7b4c7abb4d8d8), C64e(0xb915fa1543faacac), + C64e(0xfa090709fd07f3f3), C64e(0xa06f256f8525cfcf), + C64e(0x20eaafea8fafcaca), C64e(0x7d898e89f38ef4f4), + C64e(0x6720e9208ee94747), C64e(0x3828182820181010), + C64e(0x0b64d564ded56f6f), C64e(0x73838883fb88f0f0), + C64e(0xfbb16fb1946f4a4a), C64e(0xca967296b8725c5c), + C64e(0x546c246c70243838), C64e(0x5f08f108aef15757), + C64e(0x2152c752e6c77373), C64e(0x64f351f335519797), + C64e(0xae6523658d23cbcb), C64e(0x25847c84597ca1a1), + C64e(0x57bf9cbfcb9ce8e8), C64e(0x5d6321637c213e3e), + C64e(0xea7cdd7c37dd9696), C64e(0x1e7fdc7fc2dc6161), + C64e(0x9c9186911a860d0d), C64e(0x9b9485941e850f0f), + C64e(0x4bab90abdb90e0e0), C64e(0xbac642c6f8427c7c), + C64e(0x2657c457e2c47171), C64e(0x29e5aae583aacccc), + C64e(0xe373d8733bd89090), C64e(0x090f050f0c050606), + C64e(0xf4030103f501f7f7), C64e(0x2a36123638121c1c), + C64e(0x3cfea3fe9fa3c2c2), C64e(0x8be15fe1d45f6a6a), + C64e(0xbe10f91047f9aeae), C64e(0x026bd06bd2d06969), + C64e(0xbfa891a82e911717), C64e(0x71e858e829589999), + C64e(0x5369276974273a3a), C64e(0xf7d0b9d04eb92727), + C64e(0x91483848a938d9d9), C64e(0xde351335cd13ebeb), + C64e(0xe5ceb3ce56b32b2b), C64e(0x7755335544332222), + C64e(0x04d6bbd6bfbbd2d2), C64e(0x399070904970a9a9), + C64e(0x878089800e890707), C64e(0xc1f2a7f266a73333), + C64e(0xecc1b6c15ab62d2d), C64e(0x5a66226678223c3c), + C64e(0xb8ad92ad2a921515), C64e(0xa96020608920c9c9), + C64e(0x5cdb49db15498787), C64e(0xb01aff1a4fffaaaa), + C64e(0xd8887888a0785050), C64e(0x2b8e7a8e517aa5a5), + C64e(0x898a8f8a068f0303), C64e(0x4a13f813b2f85959), + C64e(0x929b809b12800909), C64e(0x2339173934171a1a), + C64e(0x1075da75cada6565), C64e(0x84533153b531d7d7), + C64e(0xd551c65113c68484), C64e(0x03d3b8d3bbb8d0d0), + C64e(0xdc5ec35e1fc38282), C64e(0xe2cbb0cb52b02929), + C64e(0xc3997799b4775a5a), C64e(0x2d3311333c111e1e), + C64e(0x3d46cb46f6cb7b7b), C64e(0xb71ffc1f4bfca8a8), + C64e(0x0c61d661dad66d6d), C64e(0x624e3a4e583a2c2c) +}; + +#endif + +#define DECL_STATE_SMALL \ + sph_u64 H[8]; + +#define READ_STATE_SMALL(sc) do { \ + memcpy(H, (sc)->state.wide, sizeof H); \ + } while (0) + +#define WRITE_STATE_SMALL(sc) do { \ + memcpy((sc)->state.wide, H, sizeof H); \ + } while (0) + +#if SPH_SMALL_FOOTPRINT_GROESTL + +#define RSTT(d, a, b0, b1, b2, b3, b4, b5, b6, b7) do { \ + t[d] = T0[B64_0(a[b0])] \ + ^ R64(T0[B64_1(a[b1])], 8) \ + ^ R64(T0[B64_2(a[b2])], 16) \ + ^ R64(T0[B64_3(a[b3])], 24) \ + ^ T4[B64_4(a[b4])] \ + ^ R64(T4[B64_5(a[b5])], 8) \ + ^ R64(T4[B64_6(a[b6])], 16) \ + ^ R64(T4[B64_7(a[b7])], 24); \ + } while (0) + +#else + +#define RSTT(d, a, b0, b1, b2, b3, b4, b5, b6, b7) do { \ + t[d] = T0[B64_0(a[b0])] \ + ^ T1[B64_1(a[b1])] \ + ^ T2[B64_2(a[b2])] \ + ^ T3[B64_3(a[b3])] \ + ^ T4[B64_4(a[b4])] \ + ^ T5[B64_5(a[b5])] \ + ^ T6[B64_6(a[b6])] \ + ^ T7[B64_7(a[b7])]; \ + } while (0) + +#endif + +#define ROUND_SMALL_P(a, r) do { \ + sph_u64 t[8]; \ + a[0] ^= PC64(0x00, r); \ + a[1] ^= PC64(0x10, r); \ + a[2] ^= PC64(0x20, r); \ + a[3] ^= PC64(0x30, r); \ + a[4] ^= PC64(0x40, r); \ + a[5] ^= PC64(0x50, r); \ + a[6] ^= PC64(0x60, r); \ + a[7] ^= PC64(0x70, r); \ + RSTT(0, a, 0, 1, 2, 3, 4, 5, 6, 7); \ + RSTT(1, a, 1, 2, 3, 4, 5, 6, 7, 0); \ + RSTT(2, a, 2, 3, 4, 5, 6, 7, 0, 1); \ + RSTT(3, a, 3, 4, 5, 6, 7, 0, 1, 2); \ + RSTT(4, a, 4, 5, 6, 7, 0, 1, 2, 3); \ + RSTT(5, a, 5, 6, 7, 0, 1, 2, 3, 4); \ + RSTT(6, a, 6, 7, 0, 1, 2, 3, 4, 5); \ + RSTT(7, a, 7, 0, 1, 2, 3, 4, 5, 6); \ + a[0] = t[0]; \ + a[1] = t[1]; \ + a[2] = t[2]; \ + a[3] = t[3]; \ + a[4] = t[4]; \ + a[5] = t[5]; \ + a[6] = t[6]; \ + a[7] = t[7]; \ + } while (0) + +#define ROUND_SMALL_Q(a, r) do { \ + sph_u64 t[8]; \ + a[0] ^= QC64(0x00, r); \ + a[1] ^= QC64(0x10, r); \ + a[2] ^= QC64(0x20, r); \ + a[3] ^= QC64(0x30, r); \ + a[4] ^= QC64(0x40, r); \ + a[5] ^= QC64(0x50, r); \ + a[6] ^= QC64(0x60, r); \ + a[7] ^= QC64(0x70, r); \ + RSTT(0, a, 1, 3, 5, 7, 0, 2, 4, 6); \ + RSTT(1, a, 2, 4, 6, 0, 1, 3, 5, 7); \ + RSTT(2, a, 3, 5, 7, 1, 2, 4, 6, 0); \ + RSTT(3, a, 4, 6, 0, 2, 3, 5, 7, 1); \ + RSTT(4, a, 5, 7, 1, 3, 4, 6, 0, 2); \ + RSTT(5, a, 6, 0, 2, 4, 5, 7, 1, 3); \ + RSTT(6, a, 7, 1, 3, 5, 6, 0, 2, 4); \ + RSTT(7, a, 0, 2, 4, 6, 7, 1, 3, 5); \ + a[0] = t[0]; \ + a[1] = t[1]; \ + a[2] = t[2]; \ + a[3] = t[3]; \ + a[4] = t[4]; \ + a[5] = t[5]; \ + a[6] = t[6]; \ + a[7] = t[7]; \ + } while (0) + +#if SPH_SMALL_FOOTPRINT_GROESTL + +#define PERM_SMALL_P(a) do { \ + int r; \ + for (r = 0; r < 10; r ++) \ + ROUND_SMALL_P(a, r); \ + } while (0) + +#define PERM_SMALL_Q(a) do { \ + int r; \ + for (r = 0; r < 10; r ++) \ + ROUND_SMALL_Q(a, r); \ + } while (0) + +#else + +/* + * Apparently, unrolling more than that confuses GCC, resulting in + * lower performance, even though L1 cache would be no problem. + */ +#define PERM_SMALL_P(a) do { \ + int r; \ + for (r = 0; r < 10; r += 2) { \ + ROUND_SMALL_P(a, r + 0); \ + ROUND_SMALL_P(a, r + 1); \ + } \ + } while (0) + +#define PERM_SMALL_Q(a) do { \ + int r; \ + for (r = 0; r < 10; r += 2) { \ + ROUND_SMALL_Q(a, r + 0); \ + ROUND_SMALL_Q(a, r + 1); \ + } \ + } while (0) + +#endif + +#define COMPRESS_SMALL do { \ + sph_u64 g[8], m[8]; \ + size_t u; \ + for (u = 0; u < 8; u ++) { \ + m[u] = dec64e_aligned(buf + (u << 3)); \ + g[u] = m[u] ^ H[u]; \ + } \ + PERM_SMALL_P(g); \ + PERM_SMALL_Q(m); \ + for (u = 0; u < 8; u ++) \ + H[u] ^= g[u] ^ m[u]; \ + } while (0) + +#define FINAL_SMALL do { \ + sph_u64 x[8]; \ + size_t u; \ + memcpy(x, H, sizeof x); \ + PERM_SMALL_P(x); \ + for (u = 0; u < 8; u ++) \ + H[u] ^= x[u]; \ + } while (0) + +#define DECL_STATE_BIG \ + sph_u64 H[16]; + +#define READ_STATE_BIG(sc) do { \ + memcpy(H, (sc)->state.wide, sizeof H); \ + } while (0) + +#define WRITE_STATE_BIG(sc) do { \ + memcpy((sc)->state.wide, H, sizeof H); \ + } while (0) + +#if SPH_SMALL_FOOTPRINT_GROESTL + +#define RBTT(d, a, b0, b1, b2, b3, b4, b5, b6, b7) do { \ + t[d] = T0[B64_0(a[b0])] \ + ^ R64(T0[B64_1(a[b1])], 8) \ + ^ R64(T0[B64_2(a[b2])], 16) \ + ^ R64(T0[B64_3(a[b3])], 24) \ + ^ T4[B64_4(a[b4])] \ + ^ R64(T4[B64_5(a[b5])], 8) \ + ^ R64(T4[B64_6(a[b6])], 16) \ + ^ R64(T4[B64_7(a[b7])], 24); \ + } while (0) + +#else + +#define RBTT(d, a, b0, b1, b2, b3, b4, b5, b6, b7) do { \ + t[d] = T0[B64_0(a[b0])] \ + ^ T1[B64_1(a[b1])] \ + ^ T2[B64_2(a[b2])] \ + ^ T3[B64_3(a[b3])] \ + ^ T4[B64_4(a[b4])] \ + ^ T5[B64_5(a[b5])] \ + ^ T6[B64_6(a[b6])] \ + ^ T7[B64_7(a[b7])]; \ + } while (0) + +#endif + +#if SPH_SMALL_FOOTPRINT_GROESTL + +#define ROUND_BIG_P(a, r) do { \ + sph_u64 t[16]; \ + size_t u; \ + a[0x0] ^= PC64(0x00, r); \ + a[0x1] ^= PC64(0x10, r); \ + a[0x2] ^= PC64(0x20, r); \ + a[0x3] ^= PC64(0x30, r); \ + a[0x4] ^= PC64(0x40, r); \ + a[0x5] ^= PC64(0x50, r); \ + a[0x6] ^= PC64(0x60, r); \ + a[0x7] ^= PC64(0x70, r); \ + a[0x8] ^= PC64(0x80, r); \ + a[0x9] ^= PC64(0x90, r); \ + a[0xA] ^= PC64(0xA0, r); \ + a[0xB] ^= PC64(0xB0, r); \ + a[0xC] ^= PC64(0xC0, r); \ + a[0xD] ^= PC64(0xD0, r); \ + a[0xE] ^= PC64(0xE0, r); \ + a[0xF] ^= PC64(0xF0, r); \ + for (u = 0; u < 16; u += 4) { \ + RBTT(u + 0, a, u + 0, (u + 1) & 0xF, \ + (u + 2) & 0xF, (u + 3) & 0xF, (u + 4) & 0xF, \ + (u + 5) & 0xF, (u + 6) & 0xF, (u + 11) & 0xF); \ + RBTT(u + 1, a, u + 1, (u + 2) & 0xF, \ + (u + 3) & 0xF, (u + 4) & 0xF, (u + 5) & 0xF, \ + (u + 6) & 0xF, (u + 7) & 0xF, (u + 12) & 0xF); \ + RBTT(u + 2, a, u + 2, (u + 3) & 0xF, \ + (u + 4) & 0xF, (u + 5) & 0xF, (u + 6) & 0xF, \ + (u + 7) & 0xF, (u + 8) & 0xF, (u + 13) & 0xF); \ + RBTT(u + 3, a, u + 3, (u + 4) & 0xF, \ + (u + 5) & 0xF, (u + 6) & 0xF, (u + 7) & 0xF, \ + (u + 8) & 0xF, (u + 9) & 0xF, (u + 14) & 0xF); \ + } \ + memcpy(a, t, sizeof t); \ + } while (0) + +#define ROUND_BIG_Q(a, r) do { \ + sph_u64 t[16]; \ + size_t u; \ + a[0x0] ^= QC64(0x00, r); \ + a[0x1] ^= QC64(0x10, r); \ + a[0x2] ^= QC64(0x20, r); \ + a[0x3] ^= QC64(0x30, r); \ + a[0x4] ^= QC64(0x40, r); \ + a[0x5] ^= QC64(0x50, r); \ + a[0x6] ^= QC64(0x60, r); \ + a[0x7] ^= QC64(0x70, r); \ + a[0x8] ^= QC64(0x80, r); \ + a[0x9] ^= QC64(0x90, r); \ + a[0xA] ^= QC64(0xA0, r); \ + a[0xB] ^= QC64(0xB0, r); \ + a[0xC] ^= QC64(0xC0, r); \ + a[0xD] ^= QC64(0xD0, r); \ + a[0xE] ^= QC64(0xE0, r); \ + a[0xF] ^= QC64(0xF0, r); \ + for (u = 0; u < 16; u += 4) { \ + RBTT(u + 0, a, (u + 1) & 0xF, (u + 3) & 0xF, \ + (u + 5) & 0xF, (u + 11) & 0xF, (u + 0) & 0xF, \ + (u + 2) & 0xF, (u + 4) & 0xF, (u + 6) & 0xF); \ + RBTT(u + 1, a, (u + 2) & 0xF, (u + 4) & 0xF, \ + (u + 6) & 0xF, (u + 12) & 0xF, (u + 1) & 0xF, \ + (u + 3) & 0xF, (u + 5) & 0xF, (u + 7) & 0xF); \ + RBTT(u + 2, a, (u + 3) & 0xF, (u + 5) & 0xF, \ + (u + 7) & 0xF, (u + 13) & 0xF, (u + 2) & 0xF, \ + (u + 4) & 0xF, (u + 6) & 0xF, (u + 8) & 0xF); \ + RBTT(u + 3, a, (u + 4) & 0xF, (u + 6) & 0xF, \ + (u + 8) & 0xF, (u + 14) & 0xF, (u + 3) & 0xF, \ + (u + 5) & 0xF, (u + 7) & 0xF, (u + 9) & 0xF); \ + } \ + memcpy(a, t, sizeof t); \ + } while (0) + +#else + +#define ROUND_BIG_P(a, r) do { \ + sph_u64 t[16]; \ + a[0x0] ^= PC64(0x00, r); \ + a[0x1] ^= PC64(0x10, r); \ + a[0x2] ^= PC64(0x20, r); \ + a[0x3] ^= PC64(0x30, r); \ + a[0x4] ^= PC64(0x40, r); \ + a[0x5] ^= PC64(0x50, r); \ + a[0x6] ^= PC64(0x60, r); \ + a[0x7] ^= PC64(0x70, r); \ + a[0x8] ^= PC64(0x80, r); \ + a[0x9] ^= PC64(0x90, r); \ + a[0xA] ^= PC64(0xA0, r); \ + a[0xB] ^= PC64(0xB0, r); \ + a[0xC] ^= PC64(0xC0, r); \ + a[0xD] ^= PC64(0xD0, r); \ + a[0xE] ^= PC64(0xE0, r); \ + a[0xF] ^= PC64(0xF0, r); \ + RBTT(0x0, a, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0xB); \ + RBTT(0x1, a, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xC); \ + RBTT(0x2, a, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0xD); \ + RBTT(0x3, a, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xE); \ + RBTT(0x4, a, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xF); \ + RBTT(0x5, a, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0x0); \ + RBTT(0x6, a, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0x1); \ + RBTT(0x7, a, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0x2); \ + RBTT(0x8, a, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0x3); \ + RBTT(0x9, a, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x4); \ + RBTT(0xA, a, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x0, 0x5); \ + RBTT(0xB, a, 0xB, 0xC, 0xD, 0xE, 0xF, 0x0, 0x1, 0x6); \ + RBTT(0xC, a, 0xC, 0xD, 0xE, 0xF, 0x0, 0x1, 0x2, 0x7); \ + RBTT(0xD, a, 0xD, 0xE, 0xF, 0x0, 0x1, 0x2, 0x3, 0x8); \ + RBTT(0xE, a, 0xE, 0xF, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9); \ + RBTT(0xF, a, 0xF, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0xA); \ + a[0x0] = t[0x0]; \ + a[0x1] = t[0x1]; \ + a[0x2] = t[0x2]; \ + a[0x3] = t[0x3]; \ + a[0x4] = t[0x4]; \ + a[0x5] = t[0x5]; \ + a[0x6] = t[0x6]; \ + a[0x7] = t[0x7]; \ + a[0x8] = t[0x8]; \ + a[0x9] = t[0x9]; \ + a[0xA] = t[0xA]; \ + a[0xB] = t[0xB]; \ + a[0xC] = t[0xC]; \ + a[0xD] = t[0xD]; \ + a[0xE] = t[0xE]; \ + a[0xF] = t[0xF]; \ + } while (0) + +#define ROUND_BIG_Q(a, r) do { \ + sph_u64 t[16]; \ + a[0x0] ^= QC64(0x00, r); \ + a[0x1] ^= QC64(0x10, r); \ + a[0x2] ^= QC64(0x20, r); \ + a[0x3] ^= QC64(0x30, r); \ + a[0x4] ^= QC64(0x40, r); \ + a[0x5] ^= QC64(0x50, r); \ + a[0x6] ^= QC64(0x60, r); \ + a[0x7] ^= QC64(0x70, r); \ + a[0x8] ^= QC64(0x80, r); \ + a[0x9] ^= QC64(0x90, r); \ + a[0xA] ^= QC64(0xA0, r); \ + a[0xB] ^= QC64(0xB0, r); \ + a[0xC] ^= QC64(0xC0, r); \ + a[0xD] ^= QC64(0xD0, r); \ + a[0xE] ^= QC64(0xE0, r); \ + a[0xF] ^= QC64(0xF0, r); \ + RBTT(0x0, a, 0x1, 0x3, 0x5, 0xB, 0x0, 0x2, 0x4, 0x6); \ + RBTT(0x1, a, 0x2, 0x4, 0x6, 0xC, 0x1, 0x3, 0x5, 0x7); \ + RBTT(0x2, a, 0x3, 0x5, 0x7, 0xD, 0x2, 0x4, 0x6, 0x8); \ + RBTT(0x3, a, 0x4, 0x6, 0x8, 0xE, 0x3, 0x5, 0x7, 0x9); \ + RBTT(0x4, a, 0x5, 0x7, 0x9, 0xF, 0x4, 0x6, 0x8, 0xA); \ + RBTT(0x5, a, 0x6, 0x8, 0xA, 0x0, 0x5, 0x7, 0x9, 0xB); \ + RBTT(0x6, a, 0x7, 0x9, 0xB, 0x1, 0x6, 0x8, 0xA, 0xC); \ + RBTT(0x7, a, 0x8, 0xA, 0xC, 0x2, 0x7, 0x9, 0xB, 0xD); \ + RBTT(0x8, a, 0x9, 0xB, 0xD, 0x3, 0x8, 0xA, 0xC, 0xE); \ + RBTT(0x9, a, 0xA, 0xC, 0xE, 0x4, 0x9, 0xB, 0xD, 0xF); \ + RBTT(0xA, a, 0xB, 0xD, 0xF, 0x5, 0xA, 0xC, 0xE, 0x0); \ + RBTT(0xB, a, 0xC, 0xE, 0x0, 0x6, 0xB, 0xD, 0xF, 0x1); \ + RBTT(0xC, a, 0xD, 0xF, 0x1, 0x7, 0xC, 0xE, 0x0, 0x2); \ + RBTT(0xD, a, 0xE, 0x0, 0x2, 0x8, 0xD, 0xF, 0x1, 0x3); \ + RBTT(0xE, a, 0xF, 0x1, 0x3, 0x9, 0xE, 0x0, 0x2, 0x4); \ + RBTT(0xF, a, 0x0, 0x2, 0x4, 0xA, 0xF, 0x1, 0x3, 0x5); \ + a[0x0] = t[0x0]; \ + a[0x1] = t[0x1]; \ + a[0x2] = t[0x2]; \ + a[0x3] = t[0x3]; \ + a[0x4] = t[0x4]; \ + a[0x5] = t[0x5]; \ + a[0x6] = t[0x6]; \ + a[0x7] = t[0x7]; \ + a[0x8] = t[0x8]; \ + a[0x9] = t[0x9]; \ + a[0xA] = t[0xA]; \ + a[0xB] = t[0xB]; \ + a[0xC] = t[0xC]; \ + a[0xD] = t[0xD]; \ + a[0xE] = t[0xE]; \ + a[0xF] = t[0xF]; \ + } while (0) + +#endif + +#define PERM_BIG_P(a) do { \ + int r; \ + for (r = 0; r < 14; r += 2) { \ + ROUND_BIG_P(a, r + 0); \ + ROUND_BIG_P(a, r + 1); \ + } \ + } while (0) + +#define PERM_BIG_Q(a) do { \ + int r; \ + for (r = 0; r < 14; r += 2) { \ + ROUND_BIG_Q(a, r + 0); \ + ROUND_BIG_Q(a, r + 1); \ + } \ + } while (0) + +/* obsolete +#if SPH_SMALL_FOOTPRINT_GROESTL + +#define COMPRESS_BIG do { \ + sph_u64 g[16], m[16], *ya; \ + const sph_u64 *yc; \ + size_t u; \ + int i; \ + for (u = 0; u < 16; u ++) { \ + m[u] = dec64e_aligned(buf + (u << 3)); \ + g[u] = m[u] ^ H[u]; \ + } \ + ya = g; \ + yc = CP; \ + for (i = 0; i < 2; i ++) { \ + PERM_BIG(ya, yc); \ + ya = m; \ + yc = CQ; \ + } \ + for (u = 0; u < 16; u ++) { \ + H[u] ^= g[u] ^ m[u]; \ + } \ + } while (0) + +#else +*/ + +#define COMPRESS_BIG do { \ + sph_u64 g[16], m[16]; \ + size_t u; \ + for (u = 0; u < 16; u ++) { \ + m[u] = dec64e_aligned(buf + (u << 3)); \ + g[u] = m[u] ^ H[u]; \ + } \ + PERM_BIG_P(g); \ + PERM_BIG_Q(m); \ + for (u = 0; u < 16; u ++) { \ + H[u] ^= g[u] ^ m[u]; \ + } \ + } while (0) + +/* obsolete +#endif +*/ + +#define FINAL_BIG do { \ + sph_u64 x[16]; \ + size_t u; \ + memcpy(x, H, sizeof x); \ + PERM_BIG_P(x); \ + for (u = 0; u < 16; u ++) \ + H[u] ^= x[u]; \ + } while (0) + +#else + +static const sph_u32 T0up[] = { + C32e(0xc632f4a5), C32e(0xf86f9784), C32e(0xee5eb099), C32e(0xf67a8c8d), + C32e(0xffe8170d), C32e(0xd60adcbd), C32e(0xde16c8b1), C32e(0x916dfc54), + C32e(0x6090f050), C32e(0x02070503), C32e(0xce2ee0a9), C32e(0x56d1877d), + C32e(0xe7cc2b19), C32e(0xb513a662), C32e(0x4d7c31e6), C32e(0xec59b59a), + C32e(0x8f40cf45), C32e(0x1fa3bc9d), C32e(0x8949c040), C32e(0xfa689287), + C32e(0xefd03f15), C32e(0xb29426eb), C32e(0x8ece40c9), C32e(0xfbe61d0b), + C32e(0x416e2fec), C32e(0xb31aa967), C32e(0x5f431cfd), C32e(0x456025ea), + C32e(0x23f9dabf), C32e(0x535102f7), C32e(0xe445a196), C32e(0x9b76ed5b), + C32e(0x75285dc2), C32e(0xe1c5241c), C32e(0x3dd4e9ae), C32e(0x4cf2be6a), + C32e(0x6c82ee5a), C32e(0x7ebdc341), C32e(0xf5f30602), C32e(0x8352d14f), + C32e(0x688ce45c), C32e(0x515607f4), C32e(0xd18d5c34), C32e(0xf9e11808), + C32e(0xe24cae93), C32e(0xab3e9573), C32e(0x6297f553), C32e(0x2a6b413f), + C32e(0x081c140c), C32e(0x9563f652), C32e(0x46e9af65), C32e(0x9d7fe25e), + C32e(0x30487828), C32e(0x37cff8a1), C32e(0x0a1b110f), C32e(0x2febc4b5), + C32e(0x0e151b09), C32e(0x247e5a36), C32e(0x1badb69b), C32e(0xdf98473d), + C32e(0xcda76a26), C32e(0x4ef5bb69), C32e(0x7f334ccd), C32e(0xea50ba9f), + C32e(0x123f2d1b), C32e(0x1da4b99e), C32e(0x58c49c74), C32e(0x3446722e), + C32e(0x3641772d), C32e(0xdc11cdb2), C32e(0xb49d29ee), C32e(0x5b4d16fb), + C32e(0xa4a501f6), C32e(0x76a1d74d), C32e(0xb714a361), C32e(0x7d3449ce), + C32e(0x52df8d7b), C32e(0xdd9f423e), C32e(0x5ecd9371), C32e(0x13b1a297), + C32e(0xa6a204f5), C32e(0xb901b868), C32e(0x00000000), C32e(0xc1b5742c), + C32e(0x40e0a060), C32e(0xe3c2211f), C32e(0x793a43c8), C32e(0xb69a2ced), + C32e(0xd40dd9be), C32e(0x8d47ca46), C32e(0x671770d9), C32e(0x72afdd4b), + C32e(0x94ed79de), C32e(0x98ff67d4), C32e(0xb09323e8), C32e(0x855bde4a), + C32e(0xbb06bd6b), C32e(0xc5bb7e2a), C32e(0x4f7b34e5), C32e(0xedd73a16), + C32e(0x86d254c5), C32e(0x9af862d7), C32e(0x6699ff55), C32e(0x11b6a794), + C32e(0x8ac04acf), C32e(0xe9d93010), C32e(0x040e0a06), C32e(0xfe669881), + C32e(0xa0ab0bf0), C32e(0x78b4cc44), C32e(0x25f0d5ba), C32e(0x4b753ee3), + C32e(0xa2ac0ef3), C32e(0x5d4419fe), C32e(0x80db5bc0), C32e(0x0580858a), + C32e(0x3fd3ecad), C32e(0x21fedfbc), C32e(0x70a8d848), C32e(0xf1fd0c04), + C32e(0x63197adf), C32e(0x772f58c1), C32e(0xaf309f75), C32e(0x42e7a563), + C32e(0x20705030), C32e(0xe5cb2e1a), C32e(0xfdef120e), C32e(0xbf08b76d), + C32e(0x8155d44c), C32e(0x18243c14), C32e(0x26795f35), C32e(0xc3b2712f), + C32e(0xbe8638e1), C32e(0x35c8fda2), C32e(0x88c74fcc), C32e(0x2e654b39), + C32e(0x936af957), C32e(0x55580df2), C32e(0xfc619d82), C32e(0x7ab3c947), + C32e(0xc827efac), C32e(0xba8832e7), C32e(0x324f7d2b), C32e(0xe642a495), + C32e(0xc03bfba0), C32e(0x19aab398), C32e(0x9ef668d1), C32e(0xa322817f), + C32e(0x44eeaa66), C32e(0x54d6827e), C32e(0x3bdde6ab), C32e(0x0b959e83), + C32e(0x8cc945ca), C32e(0xc7bc7b29), C32e(0x6b056ed3), C32e(0x286c443c), + C32e(0xa72c8b79), C32e(0xbc813de2), C32e(0x1631271d), C32e(0xad379a76), + C32e(0xdb964d3b), C32e(0x649efa56), C32e(0x74a6d24e), C32e(0x1436221e), + C32e(0x92e476db), C32e(0x0c121e0a), C32e(0x48fcb46c), C32e(0xb88f37e4), + C32e(0x9f78e75d), C32e(0xbd0fb26e), C32e(0x43692aef), C32e(0xc435f1a6), + C32e(0x39dae3a8), C32e(0x31c6f7a4), C32e(0xd38a5937), C32e(0xf274868b), + C32e(0xd5835632), C32e(0x8b4ec543), C32e(0x6e85eb59), C32e(0xda18c2b7), + C32e(0x018e8f8c), C32e(0xb11dac64), C32e(0x9cf16dd2), C32e(0x49723be0), + C32e(0xd81fc7b4), C32e(0xacb915fa), C32e(0xf3fa0907), C32e(0xcfa06f25), + C32e(0xca20eaaf), C32e(0xf47d898e), C32e(0x476720e9), C32e(0x10382818), + C32e(0x6f0b64d5), C32e(0xf0738388), C32e(0x4afbb16f), C32e(0x5cca9672), + C32e(0x38546c24), C32e(0x575f08f1), C32e(0x732152c7), C32e(0x9764f351), + C32e(0xcbae6523), C32e(0xa125847c), C32e(0xe857bf9c), C32e(0x3e5d6321), + C32e(0x96ea7cdd), C32e(0x611e7fdc), C32e(0x0d9c9186), C32e(0x0f9b9485), + C32e(0xe04bab90), C32e(0x7cbac642), C32e(0x712657c4), C32e(0xcc29e5aa), + C32e(0x90e373d8), C32e(0x06090f05), C32e(0xf7f40301), C32e(0x1c2a3612), + C32e(0xc23cfea3), C32e(0x6a8be15f), C32e(0xaebe10f9), C32e(0x69026bd0), + C32e(0x17bfa891), C32e(0x9971e858), C32e(0x3a536927), C32e(0x27f7d0b9), + C32e(0xd9914838), C32e(0xebde3513), C32e(0x2be5ceb3), C32e(0x22775533), + C32e(0xd204d6bb), C32e(0xa9399070), C32e(0x07878089), C32e(0x33c1f2a7), + C32e(0x2decc1b6), C32e(0x3c5a6622), C32e(0x15b8ad92), C32e(0xc9a96020), + C32e(0x875cdb49), C32e(0xaab01aff), C32e(0x50d88878), C32e(0xa52b8e7a), + C32e(0x03898a8f), C32e(0x594a13f8), C32e(0x09929b80), C32e(0x1a233917), + C32e(0x651075da), C32e(0xd7845331), C32e(0x84d551c6), C32e(0xd003d3b8), + C32e(0x82dc5ec3), C32e(0x29e2cbb0), C32e(0x5ac39977), C32e(0x1e2d3311), + C32e(0x7b3d46cb), C32e(0xa8b71ffc), C32e(0x6d0c61d6), C32e(0x2c624e3a) +}; + +static const sph_u32 T0dn[] = { + C32e(0xf497a5c6), C32e(0x97eb84f8), C32e(0xb0c799ee), C32e(0x8cf78df6), + C32e(0x17e50dff), C32e(0xdcb7bdd6), C32e(0xc8a7b1de), C32e(0xfc395491), + C32e(0xf0c05060), C32e(0x05040302), C32e(0xe087a9ce), C32e(0x87ac7d56), + C32e(0x2bd519e7), C32e(0xa67162b5), C32e(0x319ae64d), C32e(0xb5c39aec), + C32e(0xcf05458f), C32e(0xbc3e9d1f), C32e(0xc0094089), C32e(0x92ef87fa), + C32e(0x3fc515ef), C32e(0x267febb2), C32e(0x4007c98e), C32e(0x1ded0bfb), + C32e(0x2f82ec41), C32e(0xa97d67b3), C32e(0x1cbefd5f), C32e(0x258aea45), + C32e(0xda46bf23), C32e(0x02a6f753), C32e(0xa1d396e4), C32e(0xed2d5b9b), + C32e(0x5deac275), C32e(0x24d91ce1), C32e(0xe97aae3d), C32e(0xbe986a4c), + C32e(0xeed85a6c), C32e(0xc3fc417e), C32e(0x06f102f5), C32e(0xd11d4f83), + C32e(0xe4d05c68), C32e(0x07a2f451), C32e(0x5cb934d1), C32e(0x18e908f9), + C32e(0xaedf93e2), C32e(0x954d73ab), C32e(0xf5c45362), C32e(0x41543f2a), + C32e(0x14100c08), C32e(0xf6315295), C32e(0xaf8c6546), C32e(0xe2215e9d), + C32e(0x78602830), C32e(0xf86ea137), C32e(0x11140f0a), C32e(0xc45eb52f), + C32e(0x1b1c090e), C32e(0x5a483624), C32e(0xb6369b1b), C32e(0x47a53ddf), + C32e(0x6a8126cd), C32e(0xbb9c694e), C32e(0x4cfecd7f), C32e(0xbacf9fea), + C32e(0x2d241b12), C32e(0xb93a9e1d), C32e(0x9cb07458), C32e(0x72682e34), + C32e(0x776c2d36), C32e(0xcda3b2dc), C32e(0x2973eeb4), C32e(0x16b6fb5b), + C32e(0x0153f6a4), C32e(0xd7ec4d76), C32e(0xa37561b7), C32e(0x49face7d), + C32e(0x8da47b52), C32e(0x42a13edd), C32e(0x93bc715e), C32e(0xa2269713), + C32e(0x0457f5a6), C32e(0xb86968b9), C32e(0x00000000), C32e(0x74992cc1), + C32e(0xa0806040), C32e(0x21dd1fe3), C32e(0x43f2c879), C32e(0x2c77edb6), + C32e(0xd9b3bed4), C32e(0xca01468d), C32e(0x70ced967), C32e(0xdde44b72), + C32e(0x7933de94), C32e(0x672bd498), C32e(0x237be8b0), C32e(0xde114a85), + C32e(0xbd6d6bbb), C32e(0x7e912ac5), C32e(0x349ee54f), C32e(0x3ac116ed), + C32e(0x5417c586), C32e(0x622fd79a), C32e(0xffcc5566), C32e(0xa7229411), + C32e(0x4a0fcf8a), C32e(0x30c910e9), C32e(0x0a080604), C32e(0x98e781fe), + C32e(0x0b5bf0a0), C32e(0xccf04478), C32e(0xd54aba25), C32e(0x3e96e34b), + C32e(0x0e5ff3a2), C32e(0x19bafe5d), C32e(0x5b1bc080), C32e(0x850a8a05), + C32e(0xec7ead3f), C32e(0xdf42bc21), C32e(0xd8e04870), C32e(0x0cf904f1), + C32e(0x7ac6df63), C32e(0x58eec177), C32e(0x9f4575af), C32e(0xa5846342), + C32e(0x50403020), C32e(0x2ed11ae5), C32e(0x12e10efd), C32e(0xb7656dbf), + C32e(0xd4194c81), C32e(0x3c301418), C32e(0x5f4c3526), C32e(0x719d2fc3), + C32e(0x3867e1be), C32e(0xfd6aa235), C32e(0x4f0bcc88), C32e(0x4b5c392e), + C32e(0xf93d5793), C32e(0x0daaf255), C32e(0x9de382fc), C32e(0xc9f4477a), + C32e(0xef8bacc8), C32e(0x326fe7ba), C32e(0x7d642b32), C32e(0xa4d795e6), + C32e(0xfb9ba0c0), C32e(0xb3329819), C32e(0x6827d19e), C32e(0x815d7fa3), + C32e(0xaa886644), C32e(0x82a87e54), C32e(0xe676ab3b), C32e(0x9e16830b), + C32e(0x4503ca8c), C32e(0x7b9529c7), C32e(0x6ed6d36b), C32e(0x44503c28), + C32e(0x8b5579a7), C32e(0x3d63e2bc), C32e(0x272c1d16), C32e(0x9a4176ad), + C32e(0x4dad3bdb), C32e(0xfac85664), C32e(0xd2e84e74), C32e(0x22281e14), + C32e(0x763fdb92), C32e(0x1e180a0c), C32e(0xb4906c48), C32e(0x376be4b8), + C32e(0xe7255d9f), C32e(0xb2616ebd), C32e(0x2a86ef43), C32e(0xf193a6c4), + C32e(0xe372a839), C32e(0xf762a431), C32e(0x59bd37d3), C32e(0x86ff8bf2), + C32e(0x56b132d5), C32e(0xc50d438b), C32e(0xebdc596e), C32e(0xc2afb7da), + C32e(0x8f028c01), C32e(0xac7964b1), C32e(0x6d23d29c), C32e(0x3b92e049), + C32e(0xc7abb4d8), C32e(0x1543faac), C32e(0x09fd07f3), C32e(0x6f8525cf), + C32e(0xea8fafca), C32e(0x89f38ef4), C32e(0x208ee947), C32e(0x28201810), + C32e(0x64ded56f), C32e(0x83fb88f0), C32e(0xb1946f4a), C32e(0x96b8725c), + C32e(0x6c702438), C32e(0x08aef157), C32e(0x52e6c773), C32e(0xf3355197), + C32e(0x658d23cb), C32e(0x84597ca1), C32e(0xbfcb9ce8), C32e(0x637c213e), + C32e(0x7c37dd96), C32e(0x7fc2dc61), C32e(0x911a860d), C32e(0x941e850f), + C32e(0xabdb90e0), C32e(0xc6f8427c), C32e(0x57e2c471), C32e(0xe583aacc), + C32e(0x733bd890), C32e(0x0f0c0506), C32e(0x03f501f7), C32e(0x3638121c), + C32e(0xfe9fa3c2), C32e(0xe1d45f6a), C32e(0x1047f9ae), C32e(0x6bd2d069), + C32e(0xa82e9117), C32e(0xe8295899), C32e(0x6974273a), C32e(0xd04eb927), + C32e(0x48a938d9), C32e(0x35cd13eb), C32e(0xce56b32b), C32e(0x55443322), + C32e(0xd6bfbbd2), C32e(0x904970a9), C32e(0x800e8907), C32e(0xf266a733), + C32e(0xc15ab62d), C32e(0x6678223c), C32e(0xad2a9215), C32e(0x608920c9), + C32e(0xdb154987), C32e(0x1a4fffaa), C32e(0x88a07850), C32e(0x8e517aa5), + C32e(0x8a068f03), C32e(0x13b2f859), C32e(0x9b128009), C32e(0x3934171a), + C32e(0x75cada65), C32e(0x53b531d7), C32e(0x5113c684), C32e(0xd3bbb8d0), + C32e(0x5e1fc382), C32e(0xcb52b029), C32e(0x99b4775a), C32e(0x333c111e), + C32e(0x46f6cb7b), C32e(0x1f4bfca8), C32e(0x61dad66d), C32e(0x4e583a2c) +}; + +static const sph_u32 T1up[] = { + C32e(0xc6c632f4), C32e(0xf8f86f97), C32e(0xeeee5eb0), C32e(0xf6f67a8c), + C32e(0xffffe817), C32e(0xd6d60adc), C32e(0xdede16c8), C32e(0x91916dfc), + C32e(0x606090f0), C32e(0x02020705), C32e(0xcece2ee0), C32e(0x5656d187), + C32e(0xe7e7cc2b), C32e(0xb5b513a6), C32e(0x4d4d7c31), C32e(0xecec59b5), + C32e(0x8f8f40cf), C32e(0x1f1fa3bc), C32e(0x898949c0), C32e(0xfafa6892), + C32e(0xefefd03f), C32e(0xb2b29426), C32e(0x8e8ece40), C32e(0xfbfbe61d), + C32e(0x41416e2f), C32e(0xb3b31aa9), C32e(0x5f5f431c), C32e(0x45456025), + C32e(0x2323f9da), C32e(0x53535102), C32e(0xe4e445a1), C32e(0x9b9b76ed), + C32e(0x7575285d), C32e(0xe1e1c524), C32e(0x3d3dd4e9), C32e(0x4c4cf2be), + C32e(0x6c6c82ee), C32e(0x7e7ebdc3), C32e(0xf5f5f306), C32e(0x838352d1), + C32e(0x68688ce4), C32e(0x51515607), C32e(0xd1d18d5c), C32e(0xf9f9e118), + C32e(0xe2e24cae), C32e(0xabab3e95), C32e(0x626297f5), C32e(0x2a2a6b41), + C32e(0x08081c14), C32e(0x959563f6), C32e(0x4646e9af), C32e(0x9d9d7fe2), + C32e(0x30304878), C32e(0x3737cff8), C32e(0x0a0a1b11), C32e(0x2f2febc4), + C32e(0x0e0e151b), C32e(0x24247e5a), C32e(0x1b1badb6), C32e(0xdfdf9847), + C32e(0xcdcda76a), C32e(0x4e4ef5bb), C32e(0x7f7f334c), C32e(0xeaea50ba), + C32e(0x12123f2d), C32e(0x1d1da4b9), C32e(0x5858c49c), C32e(0x34344672), + C32e(0x36364177), C32e(0xdcdc11cd), C32e(0xb4b49d29), C32e(0x5b5b4d16), + C32e(0xa4a4a501), C32e(0x7676a1d7), C32e(0xb7b714a3), C32e(0x7d7d3449), + C32e(0x5252df8d), C32e(0xdddd9f42), C32e(0x5e5ecd93), C32e(0x1313b1a2), + C32e(0xa6a6a204), C32e(0xb9b901b8), C32e(0x00000000), C32e(0xc1c1b574), + C32e(0x4040e0a0), C32e(0xe3e3c221), C32e(0x79793a43), C32e(0xb6b69a2c), + C32e(0xd4d40dd9), C32e(0x8d8d47ca), C32e(0x67671770), C32e(0x7272afdd), + C32e(0x9494ed79), C32e(0x9898ff67), C32e(0xb0b09323), C32e(0x85855bde), + C32e(0xbbbb06bd), C32e(0xc5c5bb7e), C32e(0x4f4f7b34), C32e(0xededd73a), + C32e(0x8686d254), C32e(0x9a9af862), C32e(0x666699ff), C32e(0x1111b6a7), + C32e(0x8a8ac04a), C32e(0xe9e9d930), C32e(0x04040e0a), C32e(0xfefe6698), + C32e(0xa0a0ab0b), C32e(0x7878b4cc), C32e(0x2525f0d5), C32e(0x4b4b753e), + C32e(0xa2a2ac0e), C32e(0x5d5d4419), C32e(0x8080db5b), C32e(0x05058085), + C32e(0x3f3fd3ec), C32e(0x2121fedf), C32e(0x7070a8d8), C32e(0xf1f1fd0c), + C32e(0x6363197a), C32e(0x77772f58), C32e(0xafaf309f), C32e(0x4242e7a5), + C32e(0x20207050), C32e(0xe5e5cb2e), C32e(0xfdfdef12), C32e(0xbfbf08b7), + C32e(0x818155d4), C32e(0x1818243c), C32e(0x2626795f), C32e(0xc3c3b271), + C32e(0xbebe8638), C32e(0x3535c8fd), C32e(0x8888c74f), C32e(0x2e2e654b), + C32e(0x93936af9), C32e(0x5555580d), C32e(0xfcfc619d), C32e(0x7a7ab3c9), + C32e(0xc8c827ef), C32e(0xbaba8832), C32e(0x32324f7d), C32e(0xe6e642a4), + C32e(0xc0c03bfb), C32e(0x1919aab3), C32e(0x9e9ef668), C32e(0xa3a32281), + C32e(0x4444eeaa), C32e(0x5454d682), C32e(0x3b3bdde6), C32e(0x0b0b959e), + C32e(0x8c8cc945), C32e(0xc7c7bc7b), C32e(0x6b6b056e), C32e(0x28286c44), + C32e(0xa7a72c8b), C32e(0xbcbc813d), C32e(0x16163127), C32e(0xadad379a), + C32e(0xdbdb964d), C32e(0x64649efa), C32e(0x7474a6d2), C32e(0x14143622), + C32e(0x9292e476), C32e(0x0c0c121e), C32e(0x4848fcb4), C32e(0xb8b88f37), + C32e(0x9f9f78e7), C32e(0xbdbd0fb2), C32e(0x4343692a), C32e(0xc4c435f1), + C32e(0x3939dae3), C32e(0x3131c6f7), C32e(0xd3d38a59), C32e(0xf2f27486), + C32e(0xd5d58356), C32e(0x8b8b4ec5), C32e(0x6e6e85eb), C32e(0xdada18c2), + C32e(0x01018e8f), C32e(0xb1b11dac), C32e(0x9c9cf16d), C32e(0x4949723b), + C32e(0xd8d81fc7), C32e(0xacacb915), C32e(0xf3f3fa09), C32e(0xcfcfa06f), + C32e(0xcaca20ea), C32e(0xf4f47d89), C32e(0x47476720), C32e(0x10103828), + C32e(0x6f6f0b64), C32e(0xf0f07383), C32e(0x4a4afbb1), C32e(0x5c5cca96), + C32e(0x3838546c), C32e(0x57575f08), C32e(0x73732152), C32e(0x979764f3), + C32e(0xcbcbae65), C32e(0xa1a12584), C32e(0xe8e857bf), C32e(0x3e3e5d63), + C32e(0x9696ea7c), C32e(0x61611e7f), C32e(0x0d0d9c91), C32e(0x0f0f9b94), + C32e(0xe0e04bab), C32e(0x7c7cbac6), C32e(0x71712657), C32e(0xcccc29e5), + C32e(0x9090e373), C32e(0x0606090f), C32e(0xf7f7f403), C32e(0x1c1c2a36), + C32e(0xc2c23cfe), C32e(0x6a6a8be1), C32e(0xaeaebe10), C32e(0x6969026b), + C32e(0x1717bfa8), C32e(0x999971e8), C32e(0x3a3a5369), C32e(0x2727f7d0), + C32e(0xd9d99148), C32e(0xebebde35), C32e(0x2b2be5ce), C32e(0x22227755), + C32e(0xd2d204d6), C32e(0xa9a93990), C32e(0x07078780), C32e(0x3333c1f2), + C32e(0x2d2decc1), C32e(0x3c3c5a66), C32e(0x1515b8ad), C32e(0xc9c9a960), + C32e(0x87875cdb), C32e(0xaaaab01a), C32e(0x5050d888), C32e(0xa5a52b8e), + C32e(0x0303898a), C32e(0x59594a13), C32e(0x0909929b), C32e(0x1a1a2339), + C32e(0x65651075), C32e(0xd7d78453), C32e(0x8484d551), C32e(0xd0d003d3), + C32e(0x8282dc5e), C32e(0x2929e2cb), C32e(0x5a5ac399), C32e(0x1e1e2d33), + C32e(0x7b7b3d46), C32e(0xa8a8b71f), C32e(0x6d6d0c61), C32e(0x2c2c624e) +}; + +static const sph_u32 T1dn[] = { + C32e(0xa5f497a5), C32e(0x8497eb84), C32e(0x99b0c799), C32e(0x8d8cf78d), + C32e(0x0d17e50d), C32e(0xbddcb7bd), C32e(0xb1c8a7b1), C32e(0x54fc3954), + C32e(0x50f0c050), C32e(0x03050403), C32e(0xa9e087a9), C32e(0x7d87ac7d), + C32e(0x192bd519), C32e(0x62a67162), C32e(0xe6319ae6), C32e(0x9ab5c39a), + C32e(0x45cf0545), C32e(0x9dbc3e9d), C32e(0x40c00940), C32e(0x8792ef87), + C32e(0x153fc515), C32e(0xeb267feb), C32e(0xc94007c9), C32e(0x0b1ded0b), + C32e(0xec2f82ec), C32e(0x67a97d67), C32e(0xfd1cbefd), C32e(0xea258aea), + C32e(0xbfda46bf), C32e(0xf702a6f7), C32e(0x96a1d396), C32e(0x5bed2d5b), + C32e(0xc25deac2), C32e(0x1c24d91c), C32e(0xaee97aae), C32e(0x6abe986a), + C32e(0x5aeed85a), C32e(0x41c3fc41), C32e(0x0206f102), C32e(0x4fd11d4f), + C32e(0x5ce4d05c), C32e(0xf407a2f4), C32e(0x345cb934), C32e(0x0818e908), + C32e(0x93aedf93), C32e(0x73954d73), C32e(0x53f5c453), C32e(0x3f41543f), + C32e(0x0c14100c), C32e(0x52f63152), C32e(0x65af8c65), C32e(0x5ee2215e), + C32e(0x28786028), C32e(0xa1f86ea1), C32e(0x0f11140f), C32e(0xb5c45eb5), + C32e(0x091b1c09), C32e(0x365a4836), C32e(0x9bb6369b), C32e(0x3d47a53d), + C32e(0x266a8126), C32e(0x69bb9c69), C32e(0xcd4cfecd), C32e(0x9fbacf9f), + C32e(0x1b2d241b), C32e(0x9eb93a9e), C32e(0x749cb074), C32e(0x2e72682e), + C32e(0x2d776c2d), C32e(0xb2cda3b2), C32e(0xee2973ee), C32e(0xfb16b6fb), + C32e(0xf60153f6), C32e(0x4dd7ec4d), C32e(0x61a37561), C32e(0xce49face), + C32e(0x7b8da47b), C32e(0x3e42a13e), C32e(0x7193bc71), C32e(0x97a22697), + C32e(0xf50457f5), C32e(0x68b86968), C32e(0x00000000), C32e(0x2c74992c), + C32e(0x60a08060), C32e(0x1f21dd1f), C32e(0xc843f2c8), C32e(0xed2c77ed), + C32e(0xbed9b3be), C32e(0x46ca0146), C32e(0xd970ced9), C32e(0x4bdde44b), + C32e(0xde7933de), C32e(0xd4672bd4), C32e(0xe8237be8), C32e(0x4ade114a), + C32e(0x6bbd6d6b), C32e(0x2a7e912a), C32e(0xe5349ee5), C32e(0x163ac116), + C32e(0xc55417c5), C32e(0xd7622fd7), C32e(0x55ffcc55), C32e(0x94a72294), + C32e(0xcf4a0fcf), C32e(0x1030c910), C32e(0x060a0806), C32e(0x8198e781), + C32e(0xf00b5bf0), C32e(0x44ccf044), C32e(0xbad54aba), C32e(0xe33e96e3), + C32e(0xf30e5ff3), C32e(0xfe19bafe), C32e(0xc05b1bc0), C32e(0x8a850a8a), + C32e(0xadec7ead), C32e(0xbcdf42bc), C32e(0x48d8e048), C32e(0x040cf904), + C32e(0xdf7ac6df), C32e(0xc158eec1), C32e(0x759f4575), C32e(0x63a58463), + C32e(0x30504030), C32e(0x1a2ed11a), C32e(0x0e12e10e), C32e(0x6db7656d), + C32e(0x4cd4194c), C32e(0x143c3014), C32e(0x355f4c35), C32e(0x2f719d2f), + C32e(0xe13867e1), C32e(0xa2fd6aa2), C32e(0xcc4f0bcc), C32e(0x394b5c39), + C32e(0x57f93d57), C32e(0xf20daaf2), C32e(0x829de382), C32e(0x47c9f447), + C32e(0xacef8bac), C32e(0xe7326fe7), C32e(0x2b7d642b), C32e(0x95a4d795), + C32e(0xa0fb9ba0), C32e(0x98b33298), C32e(0xd16827d1), C32e(0x7f815d7f), + C32e(0x66aa8866), C32e(0x7e82a87e), C32e(0xabe676ab), C32e(0x839e1683), + C32e(0xca4503ca), C32e(0x297b9529), C32e(0xd36ed6d3), C32e(0x3c44503c), + C32e(0x798b5579), C32e(0xe23d63e2), C32e(0x1d272c1d), C32e(0x769a4176), + C32e(0x3b4dad3b), C32e(0x56fac856), C32e(0x4ed2e84e), C32e(0x1e22281e), + C32e(0xdb763fdb), C32e(0x0a1e180a), C32e(0x6cb4906c), C32e(0xe4376be4), + C32e(0x5de7255d), C32e(0x6eb2616e), C32e(0xef2a86ef), C32e(0xa6f193a6), + C32e(0xa8e372a8), C32e(0xa4f762a4), C32e(0x3759bd37), C32e(0x8b86ff8b), + C32e(0x3256b132), C32e(0x43c50d43), C32e(0x59ebdc59), C32e(0xb7c2afb7), + C32e(0x8c8f028c), C32e(0x64ac7964), C32e(0xd26d23d2), C32e(0xe03b92e0), + C32e(0xb4c7abb4), C32e(0xfa1543fa), C32e(0x0709fd07), C32e(0x256f8525), + C32e(0xafea8faf), C32e(0x8e89f38e), C32e(0xe9208ee9), C32e(0x18282018), + C32e(0xd564ded5), C32e(0x8883fb88), C32e(0x6fb1946f), C32e(0x7296b872), + C32e(0x246c7024), C32e(0xf108aef1), C32e(0xc752e6c7), C32e(0x51f33551), + C32e(0x23658d23), C32e(0x7c84597c), C32e(0x9cbfcb9c), C32e(0x21637c21), + C32e(0xdd7c37dd), C32e(0xdc7fc2dc), C32e(0x86911a86), C32e(0x85941e85), + C32e(0x90abdb90), C32e(0x42c6f842), C32e(0xc457e2c4), C32e(0xaae583aa), + C32e(0xd8733bd8), C32e(0x050f0c05), C32e(0x0103f501), C32e(0x12363812), + C32e(0xa3fe9fa3), C32e(0x5fe1d45f), C32e(0xf91047f9), C32e(0xd06bd2d0), + C32e(0x91a82e91), C32e(0x58e82958), C32e(0x27697427), C32e(0xb9d04eb9), + C32e(0x3848a938), C32e(0x1335cd13), C32e(0xb3ce56b3), C32e(0x33554433), + C32e(0xbbd6bfbb), C32e(0x70904970), C32e(0x89800e89), C32e(0xa7f266a7), + C32e(0xb6c15ab6), C32e(0x22667822), C32e(0x92ad2a92), C32e(0x20608920), + C32e(0x49db1549), C32e(0xff1a4fff), C32e(0x7888a078), C32e(0x7a8e517a), + C32e(0x8f8a068f), C32e(0xf813b2f8), C32e(0x809b1280), C32e(0x17393417), + C32e(0xda75cada), C32e(0x3153b531), C32e(0xc65113c6), C32e(0xb8d3bbb8), + C32e(0xc35e1fc3), C32e(0xb0cb52b0), C32e(0x7799b477), C32e(0x11333c11), + C32e(0xcb46f6cb), C32e(0xfc1f4bfc), C32e(0xd661dad6), C32e(0x3a4e583a) +}; + +static const sph_u32 T2up[] = { + C32e(0xa5c6c632), C32e(0x84f8f86f), C32e(0x99eeee5e), C32e(0x8df6f67a), + C32e(0x0dffffe8), C32e(0xbdd6d60a), C32e(0xb1dede16), C32e(0x5491916d), + C32e(0x50606090), C32e(0x03020207), C32e(0xa9cece2e), C32e(0x7d5656d1), + C32e(0x19e7e7cc), C32e(0x62b5b513), C32e(0xe64d4d7c), C32e(0x9aecec59), + C32e(0x458f8f40), C32e(0x9d1f1fa3), C32e(0x40898949), C32e(0x87fafa68), + C32e(0x15efefd0), C32e(0xebb2b294), C32e(0xc98e8ece), C32e(0x0bfbfbe6), + C32e(0xec41416e), C32e(0x67b3b31a), C32e(0xfd5f5f43), C32e(0xea454560), + C32e(0xbf2323f9), C32e(0xf7535351), C32e(0x96e4e445), C32e(0x5b9b9b76), + C32e(0xc2757528), C32e(0x1ce1e1c5), C32e(0xae3d3dd4), C32e(0x6a4c4cf2), + C32e(0x5a6c6c82), C32e(0x417e7ebd), C32e(0x02f5f5f3), C32e(0x4f838352), + C32e(0x5c68688c), C32e(0xf4515156), C32e(0x34d1d18d), C32e(0x08f9f9e1), + C32e(0x93e2e24c), C32e(0x73abab3e), C32e(0x53626297), C32e(0x3f2a2a6b), + C32e(0x0c08081c), C32e(0x52959563), C32e(0x654646e9), C32e(0x5e9d9d7f), + C32e(0x28303048), C32e(0xa13737cf), C32e(0x0f0a0a1b), C32e(0xb52f2feb), + C32e(0x090e0e15), C32e(0x3624247e), C32e(0x9b1b1bad), C32e(0x3ddfdf98), + C32e(0x26cdcda7), C32e(0x694e4ef5), C32e(0xcd7f7f33), C32e(0x9feaea50), + C32e(0x1b12123f), C32e(0x9e1d1da4), C32e(0x745858c4), C32e(0x2e343446), + C32e(0x2d363641), C32e(0xb2dcdc11), C32e(0xeeb4b49d), C32e(0xfb5b5b4d), + C32e(0xf6a4a4a5), C32e(0x4d7676a1), C32e(0x61b7b714), C32e(0xce7d7d34), + C32e(0x7b5252df), C32e(0x3edddd9f), C32e(0x715e5ecd), C32e(0x971313b1), + C32e(0xf5a6a6a2), C32e(0x68b9b901), C32e(0x00000000), C32e(0x2cc1c1b5), + C32e(0x604040e0), C32e(0x1fe3e3c2), C32e(0xc879793a), C32e(0xedb6b69a), + C32e(0xbed4d40d), C32e(0x468d8d47), C32e(0xd9676717), C32e(0x4b7272af), + C32e(0xde9494ed), C32e(0xd49898ff), C32e(0xe8b0b093), C32e(0x4a85855b), + C32e(0x6bbbbb06), C32e(0x2ac5c5bb), C32e(0xe54f4f7b), C32e(0x16ededd7), + C32e(0xc58686d2), C32e(0xd79a9af8), C32e(0x55666699), C32e(0x941111b6), + C32e(0xcf8a8ac0), C32e(0x10e9e9d9), C32e(0x0604040e), C32e(0x81fefe66), + C32e(0xf0a0a0ab), C32e(0x447878b4), C32e(0xba2525f0), C32e(0xe34b4b75), + C32e(0xf3a2a2ac), C32e(0xfe5d5d44), C32e(0xc08080db), C32e(0x8a050580), + C32e(0xad3f3fd3), C32e(0xbc2121fe), C32e(0x487070a8), C32e(0x04f1f1fd), + C32e(0xdf636319), C32e(0xc177772f), C32e(0x75afaf30), C32e(0x634242e7), + C32e(0x30202070), C32e(0x1ae5e5cb), C32e(0x0efdfdef), C32e(0x6dbfbf08), + C32e(0x4c818155), C32e(0x14181824), C32e(0x35262679), C32e(0x2fc3c3b2), + C32e(0xe1bebe86), C32e(0xa23535c8), C32e(0xcc8888c7), C32e(0x392e2e65), + C32e(0x5793936a), C32e(0xf2555558), C32e(0x82fcfc61), C32e(0x477a7ab3), + C32e(0xacc8c827), C32e(0xe7baba88), C32e(0x2b32324f), C32e(0x95e6e642), + C32e(0xa0c0c03b), C32e(0x981919aa), C32e(0xd19e9ef6), C32e(0x7fa3a322), + C32e(0x664444ee), C32e(0x7e5454d6), C32e(0xab3b3bdd), C32e(0x830b0b95), + C32e(0xca8c8cc9), C32e(0x29c7c7bc), C32e(0xd36b6b05), C32e(0x3c28286c), + C32e(0x79a7a72c), C32e(0xe2bcbc81), C32e(0x1d161631), C32e(0x76adad37), + C32e(0x3bdbdb96), C32e(0x5664649e), C32e(0x4e7474a6), C32e(0x1e141436), + C32e(0xdb9292e4), C32e(0x0a0c0c12), C32e(0x6c4848fc), C32e(0xe4b8b88f), + C32e(0x5d9f9f78), C32e(0x6ebdbd0f), C32e(0xef434369), C32e(0xa6c4c435), + C32e(0xa83939da), C32e(0xa43131c6), C32e(0x37d3d38a), C32e(0x8bf2f274), + C32e(0x32d5d583), C32e(0x438b8b4e), C32e(0x596e6e85), C32e(0xb7dada18), + C32e(0x8c01018e), C32e(0x64b1b11d), C32e(0xd29c9cf1), C32e(0xe0494972), + C32e(0xb4d8d81f), C32e(0xfaacacb9), C32e(0x07f3f3fa), C32e(0x25cfcfa0), + C32e(0xafcaca20), C32e(0x8ef4f47d), C32e(0xe9474767), C32e(0x18101038), + C32e(0xd56f6f0b), C32e(0x88f0f073), C32e(0x6f4a4afb), C32e(0x725c5cca), + C32e(0x24383854), C32e(0xf157575f), C32e(0xc7737321), C32e(0x51979764), + C32e(0x23cbcbae), C32e(0x7ca1a125), C32e(0x9ce8e857), C32e(0x213e3e5d), + C32e(0xdd9696ea), C32e(0xdc61611e), C32e(0x860d0d9c), C32e(0x850f0f9b), + C32e(0x90e0e04b), C32e(0x427c7cba), C32e(0xc4717126), C32e(0xaacccc29), + C32e(0xd89090e3), C32e(0x05060609), C32e(0x01f7f7f4), C32e(0x121c1c2a), + C32e(0xa3c2c23c), C32e(0x5f6a6a8b), C32e(0xf9aeaebe), C32e(0xd0696902), + C32e(0x911717bf), C32e(0x58999971), C32e(0x273a3a53), C32e(0xb92727f7), + C32e(0x38d9d991), C32e(0x13ebebde), C32e(0xb32b2be5), C32e(0x33222277), + C32e(0xbbd2d204), C32e(0x70a9a939), C32e(0x89070787), C32e(0xa73333c1), + C32e(0xb62d2dec), C32e(0x223c3c5a), C32e(0x921515b8), C32e(0x20c9c9a9), + C32e(0x4987875c), C32e(0xffaaaab0), C32e(0x785050d8), C32e(0x7aa5a52b), + C32e(0x8f030389), C32e(0xf859594a), C32e(0x80090992), C32e(0x171a1a23), + C32e(0xda656510), C32e(0x31d7d784), C32e(0xc68484d5), C32e(0xb8d0d003), + C32e(0xc38282dc), C32e(0xb02929e2), C32e(0x775a5ac3), C32e(0x111e1e2d), + C32e(0xcb7b7b3d), C32e(0xfca8a8b7), C32e(0xd66d6d0c), C32e(0x3a2c2c62) +}; + +static const sph_u32 T2dn[] = { + C32e(0xf4a5f497), C32e(0x978497eb), C32e(0xb099b0c7), C32e(0x8c8d8cf7), + C32e(0x170d17e5), C32e(0xdcbddcb7), C32e(0xc8b1c8a7), C32e(0xfc54fc39), + C32e(0xf050f0c0), C32e(0x05030504), C32e(0xe0a9e087), C32e(0x877d87ac), + C32e(0x2b192bd5), C32e(0xa662a671), C32e(0x31e6319a), C32e(0xb59ab5c3), + C32e(0xcf45cf05), C32e(0xbc9dbc3e), C32e(0xc040c009), C32e(0x928792ef), + C32e(0x3f153fc5), C32e(0x26eb267f), C32e(0x40c94007), C32e(0x1d0b1ded), + C32e(0x2fec2f82), C32e(0xa967a97d), C32e(0x1cfd1cbe), C32e(0x25ea258a), + C32e(0xdabfda46), C32e(0x02f702a6), C32e(0xa196a1d3), C32e(0xed5bed2d), + C32e(0x5dc25dea), C32e(0x241c24d9), C32e(0xe9aee97a), C32e(0xbe6abe98), + C32e(0xee5aeed8), C32e(0xc341c3fc), C32e(0x060206f1), C32e(0xd14fd11d), + C32e(0xe45ce4d0), C32e(0x07f407a2), C32e(0x5c345cb9), C32e(0x180818e9), + C32e(0xae93aedf), C32e(0x9573954d), C32e(0xf553f5c4), C32e(0x413f4154), + C32e(0x140c1410), C32e(0xf652f631), C32e(0xaf65af8c), C32e(0xe25ee221), + C32e(0x78287860), C32e(0xf8a1f86e), C32e(0x110f1114), C32e(0xc4b5c45e), + C32e(0x1b091b1c), C32e(0x5a365a48), C32e(0xb69bb636), C32e(0x473d47a5), + C32e(0x6a266a81), C32e(0xbb69bb9c), C32e(0x4ccd4cfe), C32e(0xba9fbacf), + C32e(0x2d1b2d24), C32e(0xb99eb93a), C32e(0x9c749cb0), C32e(0x722e7268), + C32e(0x772d776c), C32e(0xcdb2cda3), C32e(0x29ee2973), C32e(0x16fb16b6), + C32e(0x01f60153), C32e(0xd74dd7ec), C32e(0xa361a375), C32e(0x49ce49fa), + C32e(0x8d7b8da4), C32e(0x423e42a1), C32e(0x937193bc), C32e(0xa297a226), + C32e(0x04f50457), C32e(0xb868b869), C32e(0x00000000), C32e(0x742c7499), + C32e(0xa060a080), C32e(0x211f21dd), C32e(0x43c843f2), C32e(0x2ced2c77), + C32e(0xd9bed9b3), C32e(0xca46ca01), C32e(0x70d970ce), C32e(0xdd4bdde4), + C32e(0x79de7933), C32e(0x67d4672b), C32e(0x23e8237b), C32e(0xde4ade11), + C32e(0xbd6bbd6d), C32e(0x7e2a7e91), C32e(0x34e5349e), C32e(0x3a163ac1), + C32e(0x54c55417), C32e(0x62d7622f), C32e(0xff55ffcc), C32e(0xa794a722), + C32e(0x4acf4a0f), C32e(0x301030c9), C32e(0x0a060a08), C32e(0x988198e7), + C32e(0x0bf00b5b), C32e(0xcc44ccf0), C32e(0xd5bad54a), C32e(0x3ee33e96), + C32e(0x0ef30e5f), C32e(0x19fe19ba), C32e(0x5bc05b1b), C32e(0x858a850a), + C32e(0xecadec7e), C32e(0xdfbcdf42), C32e(0xd848d8e0), C32e(0x0c040cf9), + C32e(0x7adf7ac6), C32e(0x58c158ee), C32e(0x9f759f45), C32e(0xa563a584), + C32e(0x50305040), C32e(0x2e1a2ed1), C32e(0x120e12e1), C32e(0xb76db765), + C32e(0xd44cd419), C32e(0x3c143c30), C32e(0x5f355f4c), C32e(0x712f719d), + C32e(0x38e13867), C32e(0xfda2fd6a), C32e(0x4fcc4f0b), C32e(0x4b394b5c), + C32e(0xf957f93d), C32e(0x0df20daa), C32e(0x9d829de3), C32e(0xc947c9f4), + C32e(0xefacef8b), C32e(0x32e7326f), C32e(0x7d2b7d64), C32e(0xa495a4d7), + C32e(0xfba0fb9b), C32e(0xb398b332), C32e(0x68d16827), C32e(0x817f815d), + C32e(0xaa66aa88), C32e(0x827e82a8), C32e(0xe6abe676), C32e(0x9e839e16), + C32e(0x45ca4503), C32e(0x7b297b95), C32e(0x6ed36ed6), C32e(0x443c4450), + C32e(0x8b798b55), C32e(0x3de23d63), C32e(0x271d272c), C32e(0x9a769a41), + C32e(0x4d3b4dad), C32e(0xfa56fac8), C32e(0xd24ed2e8), C32e(0x221e2228), + C32e(0x76db763f), C32e(0x1e0a1e18), C32e(0xb46cb490), C32e(0x37e4376b), + C32e(0xe75de725), C32e(0xb26eb261), C32e(0x2aef2a86), C32e(0xf1a6f193), + C32e(0xe3a8e372), C32e(0xf7a4f762), C32e(0x593759bd), C32e(0x868b86ff), + C32e(0x563256b1), C32e(0xc543c50d), C32e(0xeb59ebdc), C32e(0xc2b7c2af), + C32e(0x8f8c8f02), C32e(0xac64ac79), C32e(0x6dd26d23), C32e(0x3be03b92), + C32e(0xc7b4c7ab), C32e(0x15fa1543), C32e(0x090709fd), C32e(0x6f256f85), + C32e(0xeaafea8f), C32e(0x898e89f3), C32e(0x20e9208e), C32e(0x28182820), + C32e(0x64d564de), C32e(0x838883fb), C32e(0xb16fb194), C32e(0x967296b8), + C32e(0x6c246c70), C32e(0x08f108ae), C32e(0x52c752e6), C32e(0xf351f335), + C32e(0x6523658d), C32e(0x847c8459), C32e(0xbf9cbfcb), C32e(0x6321637c), + C32e(0x7cdd7c37), C32e(0x7fdc7fc2), C32e(0x9186911a), C32e(0x9485941e), + C32e(0xab90abdb), C32e(0xc642c6f8), C32e(0x57c457e2), C32e(0xe5aae583), + C32e(0x73d8733b), C32e(0x0f050f0c), C32e(0x030103f5), C32e(0x36123638), + C32e(0xfea3fe9f), C32e(0xe15fe1d4), C32e(0x10f91047), C32e(0x6bd06bd2), + C32e(0xa891a82e), C32e(0xe858e829), C32e(0x69276974), C32e(0xd0b9d04e), + C32e(0x483848a9), C32e(0x351335cd), C32e(0xceb3ce56), C32e(0x55335544), + C32e(0xd6bbd6bf), C32e(0x90709049), C32e(0x8089800e), C32e(0xf2a7f266), + C32e(0xc1b6c15a), C32e(0x66226678), C32e(0xad92ad2a), C32e(0x60206089), + C32e(0xdb49db15), C32e(0x1aff1a4f), C32e(0x887888a0), C32e(0x8e7a8e51), + C32e(0x8a8f8a06), C32e(0x13f813b2), C32e(0x9b809b12), C32e(0x39173934), + C32e(0x75da75ca), C32e(0x533153b5), C32e(0x51c65113), C32e(0xd3b8d3bb), + C32e(0x5ec35e1f), C32e(0xcbb0cb52), C32e(0x997799b4), C32e(0x3311333c), + C32e(0x46cb46f6), C32e(0x1ffc1f4b), C32e(0x61d661da), C32e(0x4e3a4e58) +}; + +static const sph_u32 T3up[] = { + C32e(0x97a5c6c6), C32e(0xeb84f8f8), C32e(0xc799eeee), C32e(0xf78df6f6), + C32e(0xe50dffff), C32e(0xb7bdd6d6), C32e(0xa7b1dede), C32e(0x39549191), + C32e(0xc0506060), C32e(0x04030202), C32e(0x87a9cece), C32e(0xac7d5656), + C32e(0xd519e7e7), C32e(0x7162b5b5), C32e(0x9ae64d4d), C32e(0xc39aecec), + C32e(0x05458f8f), C32e(0x3e9d1f1f), C32e(0x09408989), C32e(0xef87fafa), + C32e(0xc515efef), C32e(0x7febb2b2), C32e(0x07c98e8e), C32e(0xed0bfbfb), + C32e(0x82ec4141), C32e(0x7d67b3b3), C32e(0xbefd5f5f), C32e(0x8aea4545), + C32e(0x46bf2323), C32e(0xa6f75353), C32e(0xd396e4e4), C32e(0x2d5b9b9b), + C32e(0xeac27575), C32e(0xd91ce1e1), C32e(0x7aae3d3d), C32e(0x986a4c4c), + C32e(0xd85a6c6c), C32e(0xfc417e7e), C32e(0xf102f5f5), C32e(0x1d4f8383), + C32e(0xd05c6868), C32e(0xa2f45151), C32e(0xb934d1d1), C32e(0xe908f9f9), + C32e(0xdf93e2e2), C32e(0x4d73abab), C32e(0xc4536262), C32e(0x543f2a2a), + C32e(0x100c0808), C32e(0x31529595), C32e(0x8c654646), C32e(0x215e9d9d), + C32e(0x60283030), C32e(0x6ea13737), C32e(0x140f0a0a), C32e(0x5eb52f2f), + C32e(0x1c090e0e), C32e(0x48362424), C32e(0x369b1b1b), C32e(0xa53ddfdf), + C32e(0x8126cdcd), C32e(0x9c694e4e), C32e(0xfecd7f7f), C32e(0xcf9feaea), + C32e(0x241b1212), C32e(0x3a9e1d1d), C32e(0xb0745858), C32e(0x682e3434), + C32e(0x6c2d3636), C32e(0xa3b2dcdc), C32e(0x73eeb4b4), C32e(0xb6fb5b5b), + C32e(0x53f6a4a4), C32e(0xec4d7676), C32e(0x7561b7b7), C32e(0xface7d7d), + C32e(0xa47b5252), C32e(0xa13edddd), C32e(0xbc715e5e), C32e(0x26971313), + C32e(0x57f5a6a6), C32e(0x6968b9b9), C32e(0x00000000), C32e(0x992cc1c1), + C32e(0x80604040), C32e(0xdd1fe3e3), C32e(0xf2c87979), C32e(0x77edb6b6), + C32e(0xb3bed4d4), C32e(0x01468d8d), C32e(0xced96767), C32e(0xe44b7272), + C32e(0x33de9494), C32e(0x2bd49898), C32e(0x7be8b0b0), C32e(0x114a8585), + C32e(0x6d6bbbbb), C32e(0x912ac5c5), C32e(0x9ee54f4f), C32e(0xc116eded), + C32e(0x17c58686), C32e(0x2fd79a9a), C32e(0xcc556666), C32e(0x22941111), + C32e(0x0fcf8a8a), C32e(0xc910e9e9), C32e(0x08060404), C32e(0xe781fefe), + C32e(0x5bf0a0a0), C32e(0xf0447878), C32e(0x4aba2525), C32e(0x96e34b4b), + C32e(0x5ff3a2a2), C32e(0xbafe5d5d), C32e(0x1bc08080), C32e(0x0a8a0505), + C32e(0x7ead3f3f), C32e(0x42bc2121), C32e(0xe0487070), C32e(0xf904f1f1), + C32e(0xc6df6363), C32e(0xeec17777), C32e(0x4575afaf), C32e(0x84634242), + C32e(0x40302020), C32e(0xd11ae5e5), C32e(0xe10efdfd), C32e(0x656dbfbf), + C32e(0x194c8181), C32e(0x30141818), C32e(0x4c352626), C32e(0x9d2fc3c3), + C32e(0x67e1bebe), C32e(0x6aa23535), C32e(0x0bcc8888), C32e(0x5c392e2e), + C32e(0x3d579393), C32e(0xaaf25555), C32e(0xe382fcfc), C32e(0xf4477a7a), + C32e(0x8bacc8c8), C32e(0x6fe7baba), C32e(0x642b3232), C32e(0xd795e6e6), + C32e(0x9ba0c0c0), C32e(0x32981919), C32e(0x27d19e9e), C32e(0x5d7fa3a3), + C32e(0x88664444), C32e(0xa87e5454), C32e(0x76ab3b3b), C32e(0x16830b0b), + C32e(0x03ca8c8c), C32e(0x9529c7c7), C32e(0xd6d36b6b), C32e(0x503c2828), + C32e(0x5579a7a7), C32e(0x63e2bcbc), C32e(0x2c1d1616), C32e(0x4176adad), + C32e(0xad3bdbdb), C32e(0xc8566464), C32e(0xe84e7474), C32e(0x281e1414), + C32e(0x3fdb9292), C32e(0x180a0c0c), C32e(0x906c4848), C32e(0x6be4b8b8), + C32e(0x255d9f9f), C32e(0x616ebdbd), C32e(0x86ef4343), C32e(0x93a6c4c4), + C32e(0x72a83939), C32e(0x62a43131), C32e(0xbd37d3d3), C32e(0xff8bf2f2), + C32e(0xb132d5d5), C32e(0x0d438b8b), C32e(0xdc596e6e), C32e(0xafb7dada), + C32e(0x028c0101), C32e(0x7964b1b1), C32e(0x23d29c9c), C32e(0x92e04949), + C32e(0xabb4d8d8), C32e(0x43faacac), C32e(0xfd07f3f3), C32e(0x8525cfcf), + C32e(0x8fafcaca), C32e(0xf38ef4f4), C32e(0x8ee94747), C32e(0x20181010), + C32e(0xded56f6f), C32e(0xfb88f0f0), C32e(0x946f4a4a), C32e(0xb8725c5c), + C32e(0x70243838), C32e(0xaef15757), C32e(0xe6c77373), C32e(0x35519797), + C32e(0x8d23cbcb), C32e(0x597ca1a1), C32e(0xcb9ce8e8), C32e(0x7c213e3e), + C32e(0x37dd9696), C32e(0xc2dc6161), C32e(0x1a860d0d), C32e(0x1e850f0f), + C32e(0xdb90e0e0), C32e(0xf8427c7c), C32e(0xe2c47171), C32e(0x83aacccc), + C32e(0x3bd89090), C32e(0x0c050606), C32e(0xf501f7f7), C32e(0x38121c1c), + C32e(0x9fa3c2c2), C32e(0xd45f6a6a), C32e(0x47f9aeae), C32e(0xd2d06969), + C32e(0x2e911717), C32e(0x29589999), C32e(0x74273a3a), C32e(0x4eb92727), + C32e(0xa938d9d9), C32e(0xcd13ebeb), C32e(0x56b32b2b), C32e(0x44332222), + C32e(0xbfbbd2d2), C32e(0x4970a9a9), C32e(0x0e890707), C32e(0x66a73333), + C32e(0x5ab62d2d), C32e(0x78223c3c), C32e(0x2a921515), C32e(0x8920c9c9), + C32e(0x15498787), C32e(0x4fffaaaa), C32e(0xa0785050), C32e(0x517aa5a5), + C32e(0x068f0303), C32e(0xb2f85959), C32e(0x12800909), C32e(0x34171a1a), + C32e(0xcada6565), C32e(0xb531d7d7), C32e(0x13c68484), C32e(0xbbb8d0d0), + C32e(0x1fc38282), C32e(0x52b02929), C32e(0xb4775a5a), C32e(0x3c111e1e), + C32e(0xf6cb7b7b), C32e(0x4bfca8a8), C32e(0xdad66d6d), C32e(0x583a2c2c) +}; + +static const sph_u32 T3dn[] = { + C32e(0x32f4a5f4), C32e(0x6f978497), C32e(0x5eb099b0), C32e(0x7a8c8d8c), + C32e(0xe8170d17), C32e(0x0adcbddc), C32e(0x16c8b1c8), C32e(0x6dfc54fc), + C32e(0x90f050f0), C32e(0x07050305), C32e(0x2ee0a9e0), C32e(0xd1877d87), + C32e(0xcc2b192b), C32e(0x13a662a6), C32e(0x7c31e631), C32e(0x59b59ab5), + C32e(0x40cf45cf), C32e(0xa3bc9dbc), C32e(0x49c040c0), C32e(0x68928792), + C32e(0xd03f153f), C32e(0x9426eb26), C32e(0xce40c940), C32e(0xe61d0b1d), + C32e(0x6e2fec2f), C32e(0x1aa967a9), C32e(0x431cfd1c), C32e(0x6025ea25), + C32e(0xf9dabfda), C32e(0x5102f702), C32e(0x45a196a1), C32e(0x76ed5bed), + C32e(0x285dc25d), C32e(0xc5241c24), C32e(0xd4e9aee9), C32e(0xf2be6abe), + C32e(0x82ee5aee), C32e(0xbdc341c3), C32e(0xf3060206), C32e(0x52d14fd1), + C32e(0x8ce45ce4), C32e(0x5607f407), C32e(0x8d5c345c), C32e(0xe1180818), + C32e(0x4cae93ae), C32e(0x3e957395), C32e(0x97f553f5), C32e(0x6b413f41), + C32e(0x1c140c14), C32e(0x63f652f6), C32e(0xe9af65af), C32e(0x7fe25ee2), + C32e(0x48782878), C32e(0xcff8a1f8), C32e(0x1b110f11), C32e(0xebc4b5c4), + C32e(0x151b091b), C32e(0x7e5a365a), C32e(0xadb69bb6), C32e(0x98473d47), + C32e(0xa76a266a), C32e(0xf5bb69bb), C32e(0x334ccd4c), C32e(0x50ba9fba), + C32e(0x3f2d1b2d), C32e(0xa4b99eb9), C32e(0xc49c749c), C32e(0x46722e72), + C32e(0x41772d77), C32e(0x11cdb2cd), C32e(0x9d29ee29), C32e(0x4d16fb16), + C32e(0xa501f601), C32e(0xa1d74dd7), C32e(0x14a361a3), C32e(0x3449ce49), + C32e(0xdf8d7b8d), C32e(0x9f423e42), C32e(0xcd937193), C32e(0xb1a297a2), + C32e(0xa204f504), C32e(0x01b868b8), C32e(0x00000000), C32e(0xb5742c74), + C32e(0xe0a060a0), C32e(0xc2211f21), C32e(0x3a43c843), C32e(0x9a2ced2c), + C32e(0x0dd9bed9), C32e(0x47ca46ca), C32e(0x1770d970), C32e(0xafdd4bdd), + C32e(0xed79de79), C32e(0xff67d467), C32e(0x9323e823), C32e(0x5bde4ade), + C32e(0x06bd6bbd), C32e(0xbb7e2a7e), C32e(0x7b34e534), C32e(0xd73a163a), + C32e(0xd254c554), C32e(0xf862d762), C32e(0x99ff55ff), C32e(0xb6a794a7), + C32e(0xc04acf4a), C32e(0xd9301030), C32e(0x0e0a060a), C32e(0x66988198), + C32e(0xab0bf00b), C32e(0xb4cc44cc), C32e(0xf0d5bad5), C32e(0x753ee33e), + C32e(0xac0ef30e), C32e(0x4419fe19), C32e(0xdb5bc05b), C32e(0x80858a85), + C32e(0xd3ecadec), C32e(0xfedfbcdf), C32e(0xa8d848d8), C32e(0xfd0c040c), + C32e(0x197adf7a), C32e(0x2f58c158), C32e(0x309f759f), C32e(0xe7a563a5), + C32e(0x70503050), C32e(0xcb2e1a2e), C32e(0xef120e12), C32e(0x08b76db7), + C32e(0x55d44cd4), C32e(0x243c143c), C32e(0x795f355f), C32e(0xb2712f71), + C32e(0x8638e138), C32e(0xc8fda2fd), C32e(0xc74fcc4f), C32e(0x654b394b), + C32e(0x6af957f9), C32e(0x580df20d), C32e(0x619d829d), C32e(0xb3c947c9), + C32e(0x27efacef), C32e(0x8832e732), C32e(0x4f7d2b7d), C32e(0x42a495a4), + C32e(0x3bfba0fb), C32e(0xaab398b3), C32e(0xf668d168), C32e(0x22817f81), + C32e(0xeeaa66aa), C32e(0xd6827e82), C32e(0xdde6abe6), C32e(0x959e839e), + C32e(0xc945ca45), C32e(0xbc7b297b), C32e(0x056ed36e), C32e(0x6c443c44), + C32e(0x2c8b798b), C32e(0x813de23d), C32e(0x31271d27), C32e(0x379a769a), + C32e(0x964d3b4d), C32e(0x9efa56fa), C32e(0xa6d24ed2), C32e(0x36221e22), + C32e(0xe476db76), C32e(0x121e0a1e), C32e(0xfcb46cb4), C32e(0x8f37e437), + C32e(0x78e75de7), C32e(0x0fb26eb2), C32e(0x692aef2a), C32e(0x35f1a6f1), + C32e(0xdae3a8e3), C32e(0xc6f7a4f7), C32e(0x8a593759), C32e(0x74868b86), + C32e(0x83563256), C32e(0x4ec543c5), C32e(0x85eb59eb), C32e(0x18c2b7c2), + C32e(0x8e8f8c8f), C32e(0x1dac64ac), C32e(0xf16dd26d), C32e(0x723be03b), + C32e(0x1fc7b4c7), C32e(0xb915fa15), C32e(0xfa090709), C32e(0xa06f256f), + C32e(0x20eaafea), C32e(0x7d898e89), C32e(0x6720e920), C32e(0x38281828), + C32e(0x0b64d564), C32e(0x73838883), C32e(0xfbb16fb1), C32e(0xca967296), + C32e(0x546c246c), C32e(0x5f08f108), C32e(0x2152c752), C32e(0x64f351f3), + C32e(0xae652365), C32e(0x25847c84), C32e(0x57bf9cbf), C32e(0x5d632163), + C32e(0xea7cdd7c), C32e(0x1e7fdc7f), C32e(0x9c918691), C32e(0x9b948594), + C32e(0x4bab90ab), C32e(0xbac642c6), C32e(0x2657c457), C32e(0x29e5aae5), + C32e(0xe373d873), C32e(0x090f050f), C32e(0xf4030103), C32e(0x2a361236), + C32e(0x3cfea3fe), C32e(0x8be15fe1), C32e(0xbe10f910), C32e(0x026bd06b), + C32e(0xbfa891a8), C32e(0x71e858e8), C32e(0x53692769), C32e(0xf7d0b9d0), + C32e(0x91483848), C32e(0xde351335), C32e(0xe5ceb3ce), C32e(0x77553355), + C32e(0x04d6bbd6), C32e(0x39907090), C32e(0x87808980), C32e(0xc1f2a7f2), + C32e(0xecc1b6c1), C32e(0x5a662266), C32e(0xb8ad92ad), C32e(0xa9602060), + C32e(0x5cdb49db), C32e(0xb01aff1a), C32e(0xd8887888), C32e(0x2b8e7a8e), + C32e(0x898a8f8a), C32e(0x4a13f813), C32e(0x929b809b), C32e(0x23391739), + C32e(0x1075da75), C32e(0x84533153), C32e(0xd551c651), C32e(0x03d3b8d3), + C32e(0xdc5ec35e), C32e(0xe2cbb0cb), C32e(0xc3997799), C32e(0x2d331133), + C32e(0x3d46cb46), C32e(0xb71ffc1f), C32e(0x0c61d661), C32e(0x624e3a4e) +}; + +#define DECL_STATE_SMALL \ + sph_u32 H[16]; + +#define READ_STATE_SMALL(sc) do { \ + memcpy(H, (sc)->state.narrow, sizeof H); \ + } while (0) + +#define WRITE_STATE_SMALL(sc) do { \ + memcpy((sc)->state.narrow, H, sizeof H); \ + } while (0) + +#define XCAT(x, y) XCAT_(x, y) +#define XCAT_(x, y) x ## y + +#define RSTT(d0, d1, a, b0, b1, b2, b3, b4, b5, b6, b7) do { \ + t[d0] = T0up[B32_0(a[b0])] \ + ^ T1up[B32_1(a[b1])] \ + ^ T2up[B32_2(a[b2])] \ + ^ T3up[B32_3(a[b3])] \ + ^ T0dn[B32_0(a[b4])] \ + ^ T1dn[B32_1(a[b5])] \ + ^ T2dn[B32_2(a[b6])] \ + ^ T3dn[B32_3(a[b7])]; \ + t[d1] = T0dn[B32_0(a[b0])] \ + ^ T1dn[B32_1(a[b1])] \ + ^ T2dn[B32_2(a[b2])] \ + ^ T3dn[B32_3(a[b3])] \ + ^ T0up[B32_0(a[b4])] \ + ^ T1up[B32_1(a[b5])] \ + ^ T2up[B32_2(a[b6])] \ + ^ T3up[B32_3(a[b7])]; \ + } while (0) + +#define ROUND_SMALL_P(a, r) do { \ + sph_u32 t[16]; \ + a[0x0] ^= PC32up(0x00, r); \ + a[0x1] ^= PC32dn(0x00, r); \ + a[0x2] ^= PC32up(0x10, r); \ + a[0x3] ^= PC32dn(0x10, r); \ + a[0x4] ^= PC32up(0x20, r); \ + a[0x5] ^= PC32dn(0x20, r); \ + a[0x6] ^= PC32up(0x30, r); \ + a[0x7] ^= PC32dn(0x30, r); \ + a[0x8] ^= PC32up(0x40, r); \ + a[0x9] ^= PC32dn(0x40, r); \ + a[0xA] ^= PC32up(0x50, r); \ + a[0xB] ^= PC32dn(0x50, r); \ + a[0xC] ^= PC32up(0x60, r); \ + a[0xD] ^= PC32dn(0x60, r); \ + a[0xE] ^= PC32up(0x70, r); \ + a[0xF] ^= PC32dn(0x70, r); \ + RSTT(0x0, 0x1, a, 0x0, 0x2, 0x4, 0x6, 0x9, 0xB, 0xD, 0xF); \ + RSTT(0x2, 0x3, a, 0x2, 0x4, 0x6, 0x8, 0xB, 0xD, 0xF, 0x1); \ + RSTT(0x4, 0x5, a, 0x4, 0x6, 0x8, 0xA, 0xD, 0xF, 0x1, 0x3); \ + RSTT(0x6, 0x7, a, 0x6, 0x8, 0xA, 0xC, 0xF, 0x1, 0x3, 0x5); \ + RSTT(0x8, 0x9, a, 0x8, 0xA, 0xC, 0xE, 0x1, 0x3, 0x5, 0x7); \ + RSTT(0xA, 0xB, a, 0xA, 0xC, 0xE, 0x0, 0x3, 0x5, 0x7, 0x9); \ + RSTT(0xC, 0xD, a, 0xC, 0xE, 0x0, 0x2, 0x5, 0x7, 0x9, 0xB); \ + RSTT(0xE, 0xF, a, 0xE, 0x0, 0x2, 0x4, 0x7, 0x9, 0xB, 0xD); \ + memcpy(a, t, sizeof t); \ + } while (0) + +#define ROUND_SMALL_Q(a, r) do { \ + sph_u32 t[16]; \ + a[0x0] ^= QC32up(0x00, r); \ + a[0x1] ^= QC32dn(0x00, r); \ + a[0x2] ^= QC32up(0x10, r); \ + a[0x3] ^= QC32dn(0x10, r); \ + a[0x4] ^= QC32up(0x20, r); \ + a[0x5] ^= QC32dn(0x20, r); \ + a[0x6] ^= QC32up(0x30, r); \ + a[0x7] ^= QC32dn(0x30, r); \ + a[0x8] ^= QC32up(0x40, r); \ + a[0x9] ^= QC32dn(0x40, r); \ + a[0xA] ^= QC32up(0x50, r); \ + a[0xB] ^= QC32dn(0x50, r); \ + a[0xC] ^= QC32up(0x60, r); \ + a[0xD] ^= QC32dn(0x60, r); \ + a[0xE] ^= QC32up(0x70, r); \ + a[0xF] ^= QC32dn(0x70, r); \ + RSTT(0x0, 0x1, a, 0x2, 0x6, 0xA, 0xE, 0x1, 0x5, 0x9, 0xD); \ + RSTT(0x2, 0x3, a, 0x4, 0x8, 0xC, 0x0, 0x3, 0x7, 0xB, 0xF); \ + RSTT(0x4, 0x5, a, 0x6, 0xA, 0xE, 0x2, 0x5, 0x9, 0xD, 0x1); \ + RSTT(0x6, 0x7, a, 0x8, 0xC, 0x0, 0x4, 0x7, 0xB, 0xF, 0x3); \ + RSTT(0x8, 0x9, a, 0xA, 0xE, 0x2, 0x6, 0x9, 0xD, 0x1, 0x5); \ + RSTT(0xA, 0xB, a, 0xC, 0x0, 0x4, 0x8, 0xB, 0xF, 0x3, 0x7); \ + RSTT(0xC, 0xD, a, 0xE, 0x2, 0x6, 0xA, 0xD, 0x1, 0x5, 0x9); \ + RSTT(0xE, 0xF, a, 0x0, 0x4, 0x8, 0xC, 0xF, 0x3, 0x7, 0xB); \ + memcpy(a, t, sizeof t); \ + } while (0) + +#if SPH_SMALL_FOOTPRINT_GROESTL + +#define PERM_SMALL_P(a) do { \ + int r; \ + for (r = 0; r < 10; r ++) \ + ROUND_SMALL_P(a, r); \ + } while (0) + +#define PERM_SMALL_Q(a) do { \ + int r; \ + for (r = 0; r < 10; r ++) \ + ROUND_SMALL_Q(a, r); \ + } while (0) + +#else + +#define PERM_SMALL_P(a) do { \ + int r; \ + for (r = 0; r < 10; r += 2) { \ + ROUND_SMALL_P(a, r + 0); \ + ROUND_SMALL_P(a, r + 1); \ + } \ + } while (0) + +#define PERM_SMALL_Q(a) do { \ + int r; \ + for (r = 0; r < 10; r += 2) { \ + ROUND_SMALL_Q(a, r + 0); \ + ROUND_SMALL_Q(a, r + 1); \ + } \ + } while (0) + +#endif + +#define COMPRESS_SMALL do { \ + sph_u32 g[16], m[16]; \ + size_t u; \ + for (u = 0; u < 16; u ++) { \ + m[u] = dec32e_aligned(buf + (u << 2)); \ + g[u] = m[u] ^ H[u]; \ + } \ + PERM_SMALL_P(g); \ + PERM_SMALL_Q(m); \ + for (u = 0; u < 16; u ++) \ + H[u] ^= g[u] ^ m[u]; \ + } while (0) + +#define FINAL_SMALL do { \ + sph_u32 x[16]; \ + size_t u; \ + memcpy(x, H, sizeof x); \ + PERM_SMALL_P(x); \ + for (u = 0; u < 16; u ++) \ + H[u] ^= x[u]; \ + } while (0) + +#define DECL_STATE_BIG \ + sph_u32 H[32]; + +#define READ_STATE_BIG(sc) do { \ + memcpy(H, (sc)->state.narrow, sizeof H); \ + } while (0) + +#define WRITE_STATE_BIG(sc) do { \ + memcpy((sc)->state.narrow, H, sizeof H); \ + } while (0) + +#if SPH_SMALL_FOOTPRINT_GROESTL + +#define RBTT(d0, d1, a, b0, b1, b2, b3, b4, b5, b6, b7) do { \ + sph_u32 fu2 = T0up[B32_2(a[b2])]; \ + sph_u32 fd2 = T0dn[B32_2(a[b2])]; \ + sph_u32 fu3 = T1up[B32_3(a[b3])]; \ + sph_u32 fd3 = T1dn[B32_3(a[b3])]; \ + sph_u32 fu6 = T0up[B32_2(a[b6])]; \ + sph_u32 fd6 = T0dn[B32_2(a[b6])]; \ + sph_u32 fu7 = T1up[B32_3(a[b7])]; \ + sph_u32 fd7 = T1dn[B32_3(a[b7])]; \ + t[d0] = T0up[B32_0(a[b0])] \ + ^ T1up[B32_1(a[b1])] \ + ^ R32u(fu2, fd2) \ + ^ R32u(fu3, fd3) \ + ^ T0dn[B32_0(a[b4])] \ + ^ T1dn[B32_1(a[b5])] \ + ^ R32d(fu6, fd6) \ + ^ R32d(fu7, fd7); \ + t[d1] = T0dn[B32_0(a[b0])] \ + ^ T1dn[B32_1(a[b1])] \ + ^ R32d(fu2, fd2) \ + ^ R32d(fu3, fd3) \ + ^ T0up[B32_0(a[b4])] \ + ^ T1up[B32_1(a[b5])] \ + ^ R32u(fu6, fd6) \ + ^ R32u(fu7, fd7); \ + } while (0) + +#else + +#define RBTT(d0, d1, a, b0, b1, b2, b3, b4, b5, b6, b7) do { \ + t[d0] = T0up[B32_0(a[b0])] \ + ^ T1up[B32_1(a[b1])] \ + ^ T2up[B32_2(a[b2])] \ + ^ T3up[B32_3(a[b3])] \ + ^ T0dn[B32_0(a[b4])] \ + ^ T1dn[B32_1(a[b5])] \ + ^ T2dn[B32_2(a[b6])] \ + ^ T3dn[B32_3(a[b7])]; \ + t[d1] = T0dn[B32_0(a[b0])] \ + ^ T1dn[B32_1(a[b1])] \ + ^ T2dn[B32_2(a[b2])] \ + ^ T3dn[B32_3(a[b3])] \ + ^ T0up[B32_0(a[b4])] \ + ^ T1up[B32_1(a[b5])] \ + ^ T2up[B32_2(a[b6])] \ + ^ T3up[B32_3(a[b7])]; \ + } while (0) + +#endif + +#if SPH_SMALL_FOOTPRINT_GROESTL + +#define ROUND_BIG_P(a, r) do { \ + sph_u32 t[32]; \ + size_t u; \ + a[0x00] ^= PC32up(0x00, r); \ + a[0x01] ^= PC32dn(0x00, r); \ + a[0x02] ^= PC32up(0x10, r); \ + a[0x03] ^= PC32dn(0x10, r); \ + a[0x04] ^= PC32up(0x20, r); \ + a[0x05] ^= PC32dn(0x20, r); \ + a[0x06] ^= PC32up(0x30, r); \ + a[0x07] ^= PC32dn(0x30, r); \ + a[0x08] ^= PC32up(0x40, r); \ + a[0x09] ^= PC32dn(0x40, r); \ + a[0x0A] ^= PC32up(0x50, r); \ + a[0x0B] ^= PC32dn(0x50, r); \ + a[0x0C] ^= PC32up(0x60, r); \ + a[0x0D] ^= PC32dn(0x60, r); \ + a[0x0E] ^= PC32up(0x70, r); \ + a[0x0F] ^= PC32dn(0x70, r); \ + a[0x10] ^= PC32up(0x80, r); \ + a[0x11] ^= PC32dn(0x80, r); \ + a[0x12] ^= PC32up(0x90, r); \ + a[0x13] ^= PC32dn(0x90, r); \ + a[0x14] ^= PC32up(0xA0, r); \ + a[0x15] ^= PC32dn(0xA0, r); \ + a[0x16] ^= PC32up(0xB0, r); \ + a[0x17] ^= PC32dn(0xB0, r); \ + a[0x18] ^= PC32up(0xC0, r); \ + a[0x19] ^= PC32dn(0xC0, r); \ + a[0x1A] ^= PC32up(0xD0, r); \ + a[0x1B] ^= PC32dn(0xD0, r); \ + a[0x1C] ^= PC32up(0xE0, r); \ + a[0x1D] ^= PC32dn(0xE0, r); \ + a[0x1E] ^= PC32up(0xF0, r); \ + a[0x1F] ^= PC32dn(0xF0, r); \ + for (u = 0; u < 32; u += 8) { \ + RBTT(u + 0x00, (u + 0x01) & 0x1F, a, \ + u + 0x00, (u + 0x02) & 0x1F, \ + (u + 0x04) & 0x1F, (u + 0x06) & 0x1F, \ + (u + 0x09) & 0x1F, (u + 0x0B) & 0x1F, \ + (u + 0x0D) & 0x1F, (u + 0x17) & 0x1F); \ + RBTT(u + 0x02, (u + 0x03) & 0x1F, a, \ + u + 0x02, (u + 0x04) & 0x1F, \ + (u + 0x06) & 0x1F, (u + 0x08) & 0x1F, \ + (u + 0x0B) & 0x1F, (u + 0x0D) & 0x1F, \ + (u + 0x0F) & 0x1F, (u + 0x19) & 0x1F); \ + RBTT(u + 0x04, (u + 0x05) & 0x1F, a, \ + u + 0x04, (u + 0x06) & 0x1F, \ + (u + 0x08) & 0x1F, (u + 0x0A) & 0x1F, \ + (u + 0x0D) & 0x1F, (u + 0x0F) & 0x1F, \ + (u + 0x11) & 0x1F, (u + 0x1B) & 0x1F); \ + RBTT(u + 0x06, (u + 0x07) & 0x1F, a, \ + u + 0x06, (u + 0x08) & 0x1F, \ + (u + 0x0A) & 0x1F, (u + 0x0C) & 0x1F, \ + (u + 0x0F) & 0x1F, (u + 0x11) & 0x1F, \ + (u + 0x13) & 0x1F, (u + 0x1D) & 0x1F); \ + } \ + memcpy(a, t, sizeof t); \ + } while (0) + +#define ROUND_BIG_Q(a, r) do { \ + sph_u32 t[32]; \ + size_t u; \ + a[0x00] ^= QC32up(0x00, r); \ + a[0x01] ^= QC32dn(0x00, r); \ + a[0x02] ^= QC32up(0x10, r); \ + a[0x03] ^= QC32dn(0x10, r); \ + a[0x04] ^= QC32up(0x20, r); \ + a[0x05] ^= QC32dn(0x20, r); \ + a[0x06] ^= QC32up(0x30, r); \ + a[0x07] ^= QC32dn(0x30, r); \ + a[0x08] ^= QC32up(0x40, r); \ + a[0x09] ^= QC32dn(0x40, r); \ + a[0x0A] ^= QC32up(0x50, r); \ + a[0x0B] ^= QC32dn(0x50, r); \ + a[0x0C] ^= QC32up(0x60, r); \ + a[0x0D] ^= QC32dn(0x60, r); \ + a[0x0E] ^= QC32up(0x70, r); \ + a[0x0F] ^= QC32dn(0x70, r); \ + a[0x10] ^= QC32up(0x80, r); \ + a[0x11] ^= QC32dn(0x80, r); \ + a[0x12] ^= QC32up(0x90, r); \ + a[0x13] ^= QC32dn(0x90, r); \ + a[0x14] ^= QC32up(0xA0, r); \ + a[0x15] ^= QC32dn(0xA0, r); \ + a[0x16] ^= QC32up(0xB0, r); \ + a[0x17] ^= QC32dn(0xB0, r); \ + a[0x18] ^= QC32up(0xC0, r); \ + a[0x19] ^= QC32dn(0xC0, r); \ + a[0x1A] ^= QC32up(0xD0, r); \ + a[0x1B] ^= QC32dn(0xD0, r); \ + a[0x1C] ^= QC32up(0xE0, r); \ + a[0x1D] ^= QC32dn(0xE0, r); \ + a[0x1E] ^= QC32up(0xF0, r); \ + a[0x1F] ^= QC32dn(0xF0, r); \ + for (u = 0; u < 32; u += 8) { \ + RBTT(u + 0x00, (u + 0x01) & 0x1F, a, \ + (u + 0x02) & 0x1F, (u + 0x06) & 0x1F, \ + (u + 0x0A) & 0x1F, (u + 0x16) & 0x1F, \ + (u + 0x01) & 0x1F, (u + 0x05) & 0x1F, \ + (u + 0x09) & 0x1F, (u + 0x0D) & 0x1F); \ + RBTT(u + 0x02, (u + 0x03) & 0x1F, a, \ + (u + 0x04) & 0x1F, (u + 0x08) & 0x1F, \ + (u + 0x0C) & 0x1F, (u + 0x18) & 0x1F, \ + (u + 0x03) & 0x1F, (u + 0x07) & 0x1F, \ + (u + 0x0B) & 0x1F, (u + 0x0F) & 0x1F); \ + RBTT(u + 0x04, (u + 0x05) & 0x1F, a, \ + (u + 0x06) & 0x1F, (u + 0x0A) & 0x1F, \ + (u + 0x0E) & 0x1F, (u + 0x1A) & 0x1F, \ + (u + 0x05) & 0x1F, (u + 0x09) & 0x1F, \ + (u + 0x0D) & 0x1F, (u + 0x11) & 0x1F); \ + RBTT(u + 0x06, (u + 0x07) & 0x1F, a, \ + (u + 0x08) & 0x1F, (u + 0x0C) & 0x1F, \ + (u + 0x10) & 0x1F, (u + 0x1C) & 0x1F, \ + (u + 0x07) & 0x1F, (u + 0x0B) & 0x1F, \ + (u + 0x0F) & 0x1F, (u + 0x13) & 0x1F); \ + } \ + memcpy(a, t, sizeof t); \ + } while (0) + +#else + +#define ROUND_BIG_P(a, r) do { \ + sph_u32 t[32]; \ + a[0x00] ^= PC32up(0x00, r); \ + a[0x01] ^= PC32dn(0x00, r); \ + a[0x02] ^= PC32up(0x10, r); \ + a[0x03] ^= PC32dn(0x10, r); \ + a[0x04] ^= PC32up(0x20, r); \ + a[0x05] ^= PC32dn(0x20, r); \ + a[0x06] ^= PC32up(0x30, r); \ + a[0x07] ^= PC32dn(0x30, r); \ + a[0x08] ^= PC32up(0x40, r); \ + a[0x09] ^= PC32dn(0x40, r); \ + a[0x0A] ^= PC32up(0x50, r); \ + a[0x0B] ^= PC32dn(0x50, r); \ + a[0x0C] ^= PC32up(0x60, r); \ + a[0x0D] ^= PC32dn(0x60, r); \ + a[0x0E] ^= PC32up(0x70, r); \ + a[0x0F] ^= PC32dn(0x70, r); \ + a[0x10] ^= PC32up(0x80, r); \ + a[0x11] ^= PC32dn(0x80, r); \ + a[0x12] ^= PC32up(0x90, r); \ + a[0x13] ^= PC32dn(0x90, r); \ + a[0x14] ^= PC32up(0xA0, r); \ + a[0x15] ^= PC32dn(0xA0, r); \ + a[0x16] ^= PC32up(0xB0, r); \ + a[0x17] ^= PC32dn(0xB0, r); \ + a[0x18] ^= PC32up(0xC0, r); \ + a[0x19] ^= PC32dn(0xC0, r); \ + a[0x1A] ^= PC32up(0xD0, r); \ + a[0x1B] ^= PC32dn(0xD0, r); \ + a[0x1C] ^= PC32up(0xE0, r); \ + a[0x1D] ^= PC32dn(0xE0, r); \ + a[0x1E] ^= PC32up(0xF0, r); \ + a[0x1F] ^= PC32dn(0xF0, r); \ + RBTT(0x00, 0x01, a, \ + 0x00, 0x02, 0x04, 0x06, 0x09, 0x0B, 0x0D, 0x17); \ + RBTT(0x02, 0x03, a, \ + 0x02, 0x04, 0x06, 0x08, 0x0B, 0x0D, 0x0F, 0x19); \ + RBTT(0x04, 0x05, a, \ + 0x04, 0x06, 0x08, 0x0A, 0x0D, 0x0F, 0x11, 0x1B); \ + RBTT(0x06, 0x07, a, \ + 0x06, 0x08, 0x0A, 0x0C, 0x0F, 0x11, 0x13, 0x1D); \ + RBTT(0x08, 0x09, a, \ + 0x08, 0x0A, 0x0C, 0x0E, 0x11, 0x13, 0x15, 0x1F); \ + RBTT(0x0A, 0x0B, a, \ + 0x0A, 0x0C, 0x0E, 0x10, 0x13, 0x15, 0x17, 0x01); \ + RBTT(0x0C, 0x0D, a, \ + 0x0C, 0x0E, 0x10, 0x12, 0x15, 0x17, 0x19, 0x03); \ + RBTT(0x0E, 0x0F, a, \ + 0x0E, 0x10, 0x12, 0x14, 0x17, 0x19, 0x1B, 0x05); \ + RBTT(0x10, 0x11, a, \ + 0x10, 0x12, 0x14, 0x16, 0x19, 0x1B, 0x1D, 0x07); \ + RBTT(0x12, 0x13, a, \ + 0x12, 0x14, 0x16, 0x18, 0x1B, 0x1D, 0x1F, 0x09); \ + RBTT(0x14, 0x15, a, \ + 0x14, 0x16, 0x18, 0x1A, 0x1D, 0x1F, 0x01, 0x0B); \ + RBTT(0x16, 0x17, a, \ + 0x16, 0x18, 0x1A, 0x1C, 0x1F, 0x01, 0x03, 0x0D); \ + RBTT(0x18, 0x19, a, \ + 0x18, 0x1A, 0x1C, 0x1E, 0x01, 0x03, 0x05, 0x0F); \ + RBTT(0x1A, 0x1B, a, \ + 0x1A, 0x1C, 0x1E, 0x00, 0x03, 0x05, 0x07, 0x11); \ + RBTT(0x1C, 0x1D, a, \ + 0x1C, 0x1E, 0x00, 0x02, 0x05, 0x07, 0x09, 0x13); \ + RBTT(0x1E, 0x1F, a, \ + 0x1E, 0x00, 0x02, 0x04, 0x07, 0x09, 0x0B, 0x15); \ + memcpy(a, t, sizeof t); \ + } while (0) + +#define ROUND_BIG_Q(a, r) do { \ + sph_u32 t[32]; \ + a[0x00] ^= QC32up(0x00, r); \ + a[0x01] ^= QC32dn(0x00, r); \ + a[0x02] ^= QC32up(0x10, r); \ + a[0x03] ^= QC32dn(0x10, r); \ + a[0x04] ^= QC32up(0x20, r); \ + a[0x05] ^= QC32dn(0x20, r); \ + a[0x06] ^= QC32up(0x30, r); \ + a[0x07] ^= QC32dn(0x30, r); \ + a[0x08] ^= QC32up(0x40, r); \ + a[0x09] ^= QC32dn(0x40, r); \ + a[0x0A] ^= QC32up(0x50, r); \ + a[0x0B] ^= QC32dn(0x50, r); \ + a[0x0C] ^= QC32up(0x60, r); \ + a[0x0D] ^= QC32dn(0x60, r); \ + a[0x0E] ^= QC32up(0x70, r); \ + a[0x0F] ^= QC32dn(0x70, r); \ + a[0x10] ^= QC32up(0x80, r); \ + a[0x11] ^= QC32dn(0x80, r); \ + a[0x12] ^= QC32up(0x90, r); \ + a[0x13] ^= QC32dn(0x90, r); \ + a[0x14] ^= QC32up(0xA0, r); \ + a[0x15] ^= QC32dn(0xA0, r); \ + a[0x16] ^= QC32up(0xB0, r); \ + a[0x17] ^= QC32dn(0xB0, r); \ + a[0x18] ^= QC32up(0xC0, r); \ + a[0x19] ^= QC32dn(0xC0, r); \ + a[0x1A] ^= QC32up(0xD0, r); \ + a[0x1B] ^= QC32dn(0xD0, r); \ + a[0x1C] ^= QC32up(0xE0, r); \ + a[0x1D] ^= QC32dn(0xE0, r); \ + a[0x1E] ^= QC32up(0xF0, r); \ + a[0x1F] ^= QC32dn(0xF0, r); \ + RBTT(0x00, 0x01, a, \ + 0x02, 0x06, 0x0A, 0x16, 0x01, 0x05, 0x09, 0x0D); \ + RBTT(0x02, 0x03, a, \ + 0x04, 0x08, 0x0C, 0x18, 0x03, 0x07, 0x0B, 0x0F); \ + RBTT(0x04, 0x05, a, \ + 0x06, 0x0A, 0x0E, 0x1A, 0x05, 0x09, 0x0D, 0x11); \ + RBTT(0x06, 0x07, a, \ + 0x08, 0x0C, 0x10, 0x1C, 0x07, 0x0B, 0x0F, 0x13); \ + RBTT(0x08, 0x09, a, \ + 0x0A, 0x0E, 0x12, 0x1E, 0x09, 0x0D, 0x11, 0x15); \ + RBTT(0x0A, 0x0B, a, \ + 0x0C, 0x10, 0x14, 0x00, 0x0B, 0x0F, 0x13, 0x17); \ + RBTT(0x0C, 0x0D, a, \ + 0x0E, 0x12, 0x16, 0x02, 0x0D, 0x11, 0x15, 0x19); \ + RBTT(0x0E, 0x0F, a, \ + 0x10, 0x14, 0x18, 0x04, 0x0F, 0x13, 0x17, 0x1B); \ + RBTT(0x10, 0x11, a, \ + 0x12, 0x16, 0x1A, 0x06, 0x11, 0x15, 0x19, 0x1D); \ + RBTT(0x12, 0x13, a, \ + 0x14, 0x18, 0x1C, 0x08, 0x13, 0x17, 0x1B, 0x1F); \ + RBTT(0x14, 0x15, a, \ + 0x16, 0x1A, 0x1E, 0x0A, 0x15, 0x19, 0x1D, 0x01); \ + RBTT(0x16, 0x17, a, \ + 0x18, 0x1C, 0x00, 0x0C, 0x17, 0x1B, 0x1F, 0x03); \ + RBTT(0x18, 0x19, a, \ + 0x1A, 0x1E, 0x02, 0x0E, 0x19, 0x1D, 0x01, 0x05); \ + RBTT(0x1A, 0x1B, a, \ + 0x1C, 0x00, 0x04, 0x10, 0x1B, 0x1F, 0x03, 0x07); \ + RBTT(0x1C, 0x1D, a, \ + 0x1E, 0x02, 0x06, 0x12, 0x1D, 0x01, 0x05, 0x09); \ + RBTT(0x1E, 0x1F, a, \ + 0x00, 0x04, 0x08, 0x14, 0x1F, 0x03, 0x07, 0x0B); \ + memcpy(a, t, sizeof t); \ + } while (0) + +#endif + +#if SPH_SMALL_FOOTPRINT_GROESTL + +#define PERM_BIG_P(a) do { \ + int r; \ + for (r = 0; r < 14; r ++) \ + ROUND_BIG_P(a, r); \ + } while (0) + +#define PERM_BIG_Q(a) do { \ + int r; \ + for (r = 0; r < 14; r ++) \ + ROUND_BIG_Q(a, r); \ + } while (0) + +#else + +#define PERM_BIG_P(a) do { \ + int r; \ + for (r = 0; r < 14; r += 2) { \ + ROUND_BIG_P(a, r + 0); \ + ROUND_BIG_P(a, r + 1); \ + } \ + } while (0) + +#define PERM_BIG_Q(a) do { \ + int r; \ + for (r = 0; r < 14; r += 2) { \ + ROUND_BIG_Q(a, r + 0); \ + ROUND_BIG_Q(a, r + 1); \ + } \ + } while (0) + +#endif + +#define COMPRESS_BIG do { \ + sph_u32 g[32], m[32]; \ + size_t u; \ + for (u = 0; u < 32; u ++) { \ + m[u] = dec32e_aligned(buf + (u << 2)); \ + g[u] = m[u] ^ H[u]; \ + } \ + PERM_BIG_P(g); \ + PERM_BIG_Q(m); \ + for (u = 0; u < 32; u ++) \ + H[u] ^= g[u] ^ m[u]; \ + } while (0) + +#define FINAL_BIG do { \ + sph_u32 x[32]; \ + size_t u; \ + memcpy(x, H, sizeof x); \ + PERM_BIG_P(x); \ + for (u = 0; u < 32; u ++) \ + H[u] ^= x[u]; \ + } while (0) + +#endif + +static void +groestl_small_init(sph_groestl_small_context *sc, unsigned out_size) +{ + size_t u; + + sc->ptr = 0; +#if SPH_GROESTL_64 + for (u = 0; u < 7; u ++) + sc->state.wide[u] = 0; +#if USE_LE + sc->state.wide[7] = ((sph_u64)(out_size & 0xFF) << 56) + | ((sph_u64)(out_size & 0xFF00) << 40); +#else + sc->state.wide[7] = (sph_u64)out_size; +#endif +#else + for (u = 0; u < 15; u ++) + sc->state.narrow[u] = 0; +#if USE_LE + sc->state.narrow[15] = ((sph_u32)(out_size & 0xFF) << 24) + | ((sph_u32)(out_size & 0xFF00) << 8); +#else + sc->state.narrow[15] = (sph_u32)out_size; +#endif +#endif +#if SPH_64 + sc->count = 0; +#else + sc->count_high = 0; + sc->count_low = 0; +#endif +} + +static void +groestl_small_core(sph_groestl_small_context *sc, const void *data, size_t len) +{ + unsigned char *buf; + size_t ptr; + DECL_STATE_SMALL + + buf = sc->buf; + ptr = sc->ptr; + if (len < (sizeof sc->buf) - ptr) { + memcpy(buf + ptr, data, len); + ptr += len; + sc->ptr = ptr; + return; + } + + READ_STATE_SMALL(sc); + while (len > 0) { + size_t clen; + + clen = (sizeof sc->buf) - ptr; + if (clen > len) + clen = len; + memcpy(buf + ptr, data, clen); + ptr += clen; + data = (const unsigned char *)data + clen; + len -= clen; + if (ptr == sizeof sc->buf) { + COMPRESS_SMALL; +#if SPH_64 + sc->count ++; +#else + if ((sc->count_low = SPH_T32(sc->count_low + 1)) == 0) + sc->count_high = SPH_T32(sc->count_high + 1); +#endif + ptr = 0; + } + } + WRITE_STATE_SMALL(sc); + sc->ptr = ptr; +} + +static void +groestl_small_close(sph_groestl_small_context *sc, + unsigned ub, unsigned n, void *dst, size_t out_len) +{ + unsigned char *buf; + unsigned char pad[72]; + size_t u, ptr, pad_len; +#if SPH_64 + sph_u64 count; +#else + sph_u32 count_high, count_low; +#endif + unsigned z; + DECL_STATE_SMALL + + buf = sc->buf; + ptr = sc->ptr; + z = 0x80 >> n; + pad[0] = ((ub & -z) | z) & 0xFF; + if (ptr < 56) { + pad_len = 64 - ptr; +#if SPH_64 + count = SPH_T64(sc->count + 1); +#else + count_low = SPH_T32(sc->count_low + 1); + count_high = SPH_T32(sc->count_high); + if (count_low == 0) + count_high = SPH_T32(count_high + 1); +#endif + } else { + pad_len = 128 - ptr; +#if SPH_64 + count = SPH_T64(sc->count + 2); +#else + count_low = SPH_T32(sc->count_low + 2); + count_high = SPH_T32(sc->count_high); + if (count_low <= 1) + count_high = SPH_T32(count_high + 1); +#endif + } + memset(pad + 1, 0, pad_len - 9); +#if SPH_64 + sph_enc64be(pad + pad_len - 8, count); +#else + sph_enc64be(pad + pad_len - 8, count_high); + sph_enc64be(pad + pad_len - 4, count_low); +#endif + groestl_small_core(sc, pad, pad_len); + READ_STATE_SMALL(sc); + FINAL_SMALL; +#if SPH_GROESTL_64 + for (u = 0; u < 4; u ++) + enc64e(pad + (u << 3), H[u + 4]); +#else + for (u = 0; u < 8; u ++) + enc32e(pad + (u << 2), H[u + 8]); +#endif + memcpy(dst, pad + 32 - out_len, out_len); + groestl_small_init(sc, (unsigned)out_len << 3); +} + +static void +groestl_big_init(sph_groestl_big_context *sc, unsigned out_size) +{ + size_t u; + + sc->ptr = 0; +#if SPH_GROESTL_64 + for (u = 0; u < 15; u ++) + sc->state.wide[u] = 0; +#if USE_LE + sc->state.wide[15] = ((sph_u64)(out_size & 0xFF) << 56) + | ((sph_u64)(out_size & 0xFF00) << 40); +#else + sc->state.wide[15] = (sph_u64)out_size; +#endif +#else + for (u = 0; u < 31; u ++) + sc->state.narrow[u] = 0; +#if USE_LE + sc->state.narrow[31] = ((sph_u32)(out_size & 0xFF) << 24) + | ((sph_u32)(out_size & 0xFF00) << 8); +#else + sc->state.narrow[31] = (sph_u32)out_size; +#endif +#endif +#if SPH_64 + sc->count = 0; +#else + sc->count_high = 0; + sc->count_low = 0; +#endif +} + +static void +groestl_big_core(sph_groestl_big_context *sc, const void *data, size_t len) +{ + unsigned char *buf; + size_t ptr; + DECL_STATE_BIG + + buf = sc->buf; + ptr = sc->ptr; + if (len < (sizeof sc->buf) - ptr) { + memcpy(buf + ptr, data, len); + ptr += len; + sc->ptr = ptr; + return; + } + + READ_STATE_BIG(sc); + while (len > 0) { + size_t clen; + + clen = (sizeof sc->buf) - ptr; + if (clen > len) + clen = len; + memcpy(buf + ptr, data, clen); + ptr += clen; + data = (const unsigned char *)data + clen; + len -= clen; + if (ptr == sizeof sc->buf) { + COMPRESS_BIG; +#if SPH_64 + sc->count ++; +#else + if ((sc->count_low = SPH_T32(sc->count_low + 1)) == 0) + sc->count_high = SPH_T32(sc->count_high + 1); +#endif + ptr = 0; + } + } + WRITE_STATE_BIG(sc); + sc->ptr = ptr; +} + +static void +groestl_big_close(sph_groestl_big_context *sc, + unsigned ub, unsigned n, void *dst, size_t out_len) +{ + unsigned char *buf; + unsigned char pad[136]; + size_t ptr, pad_len, u; +#if SPH_64 + sph_u64 count; +#else + sph_u32 count_high, count_low; +#endif + unsigned z; + DECL_STATE_BIG + + buf = sc->buf; + ptr = sc->ptr; + z = 0x80 >> n; + pad[0] = ((ub & -z) | z) & 0xFF; + if (ptr < 120) { + pad_len = 128 - ptr; +#if SPH_64 + count = SPH_T64(sc->count + 1); +#else + count_low = SPH_T32(sc->count_low + 1); + count_high = SPH_T32(sc->count_high); + if (count_low == 0) + count_high = SPH_T32(count_high + 1); +#endif + } else { + pad_len = 256 - ptr; +#if SPH_64 + count = SPH_T64(sc->count + 2); +#else + count_low = SPH_T32(sc->count_low + 2); + count_high = SPH_T32(sc->count_high); + if (count_low <= 1) + count_high = SPH_T32(count_high + 1); +#endif + } + memset(pad + 1, 0, pad_len - 9); +#if SPH_64 + sph_enc64be(pad + pad_len - 8, count); +#else + sph_enc64be(pad + pad_len - 8, count_high); + sph_enc64be(pad + pad_len - 4, count_low); +#endif + groestl_big_core(sc, pad, pad_len); + READ_STATE_BIG(sc); + FINAL_BIG; +#if SPH_GROESTL_64 + for (u = 0; u < 8; u ++) + enc64e(pad + (u << 3), H[u + 8]); +#else + for (u = 0; u < 16; u ++) + enc32e(pad + (u << 2), H[u + 16]); +#endif + memcpy(dst, pad + 64 - out_len, out_len); + groestl_big_init(sc, (unsigned)out_len << 3); +} + +/* see sph_groestl.h */ +void +sph_groestl224_init(void *cc) +{ + groestl_small_init(cc, 224); +} + +/* see sph_groestl.h */ +void +sph_groestl224(void *cc, const void *data, size_t len) +{ + groestl_small_core(cc, data, len); +} + +/* see sph_groestl.h */ +void +sph_groestl224_close(void *cc, void *dst) +{ + groestl_small_close(cc, 0, 0, dst, 28); +} + +/* see sph_groestl.h */ +void +sph_groestl224_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + groestl_small_close(cc, ub, n, dst, 28); +} + +/* see sph_groestl.h */ +void +sph_groestl256_init(void *cc) +{ + groestl_small_init(cc, 256); +} + +/* see sph_groestl.h */ +void +sph_groestl256(void *cc, const void *data, size_t len) +{ + groestl_small_core(cc, data, len); +} + +/* see sph_groestl.h */ +void +sph_groestl256_close(void *cc, void *dst) +{ + groestl_small_close(cc, 0, 0, dst, 32); +} + +/* see sph_groestl.h */ +void +sph_groestl256_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + groestl_small_close(cc, ub, n, dst, 32); +} + +/* see sph_groestl.h */ +void +sph_groestl384_init(void *cc) +{ + groestl_big_init(cc, 384); +} + +/* see sph_groestl.h */ +void +sph_groestl384(void *cc, const void *data, size_t len) +{ + groestl_big_core(cc, data, len); +} + +/* see sph_groestl.h */ +void +sph_groestl384_close(void *cc, void *dst) +{ + groestl_big_close(cc, 0, 0, dst, 48); +} + +/* see sph_groestl.h */ +void +sph_groestl384_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + groestl_big_close(cc, ub, n, dst, 48); +} + +/* see sph_groestl.h */ +void +sph_groestl512_init(void *cc) +{ + groestl_big_init(cc, 512); +} + +/* see sph_groestl.h */ +void +sph_groestl512(void *cc, const void *data, size_t len) +{ + groestl_big_core(cc, data, len); +} + +/* see sph_groestl.h */ +void +sph_groestl512_close(void *cc, void *dst) +{ + groestl_big_close(cc, 0, 0, dst, 64); +} + +/* see sph_groestl.h */ +void +sph_groestl512_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + groestl_big_close(cc, ub, n, dst, 64); +} + +#ifdef __cplusplus +} +#endif diff --git a/src/hash.cpp b/src/hash.cpp new file mode 100644 index 0000000..bddd8ab --- /dev/null +++ b/src/hash.cpp @@ -0,0 +1,58 @@ +#include "hash.h" + +inline uint32_t ROTL32 ( uint32_t x, int8_t r ) +{ + return (x << r) | (x >> (32 - r)); +} + +unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector& vDataToHash) +{ + // The following is MurmurHash3 (x86_32), see http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp + uint32_t h1 = nHashSeed; + const uint32_t c1 = 0xcc9e2d51; + const uint32_t c2 = 0x1b873593; + + const int nblocks = vDataToHash.size() / 4; + + //---------- + // body + const uint32_t * blocks = (const uint32_t *)(&vDataToHash[0] + nblocks*4); + + for(int i = -nblocks; i; i++) + { + uint32_t k1 = blocks[i]; + + k1 *= c1; + k1 = ROTL32(k1,15); + k1 *= c2; + + h1 ^= k1; + h1 = ROTL32(h1,13); + h1 = h1*5+0xe6546b64; + } + + //---------- + // tail + const uint8_t * tail = (const uint8_t*)(&vDataToHash[0] + nblocks*4); + + uint32_t k1 = 0; + + switch(vDataToHash.size() & 3) + { + case 3: k1 ^= tail[2] << 16; + case 2: k1 ^= tail[1] << 8; + case 1: k1 ^= tail[0]; + k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; + }; + + //---------- + // finalization + h1 ^= vDataToHash.size(); + h1 ^= h1 >> 16; + h1 *= 0x85ebca6b; + h1 ^= h1 >> 13; + h1 *= 0xc2b2ae35; + h1 ^= h1 >> 16; + + return h1; +} diff --git a/src/hash.h b/src/hash.h new file mode 100644 index 0000000..e3c4cc7 --- /dev/null +++ b/src/hash.h @@ -0,0 +1,120 @@ +// 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_HASH_H +#define BITCOIN_HASH_H + +#include "uint256.h" +#include "serialize.h" + +#include +#include +#include + + +template +inline uint256 Hash2(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(); +} + +inline uint160 Hash160(const std::vector& vch) +{ + uint256 hash1; + SHA256(&vch[0], vch.size(), (unsigned char*)&hash1); + uint160 hash2; + RIPEMD160((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); + return hash2; +} + +unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector& vDataToHash); + +#endif diff --git a/src/hashblock.h b/src/hashblock.h new file mode 100644 index 0000000..a5d2829 --- /dev/null +++ b/src/hashblock.h @@ -0,0 +1,150 @@ +#ifndef HASHBLOCK_H +#define HASHBLOCK_H + +#include "uint256.h" +#include "sph_blake.h" +#include "sph_bmw.h" +#include "sph_groestl.h" +#include "sph_jh.h" +#include "sph_keccak.h" +#include "sph_skein.h" + +#ifndef QT_NO_DEBUG +#include +#endif + +#ifdef GLOBALDEFINED +#define GLOBAL +#else +#define GLOBAL extern +#endif + +GLOBAL sph_blake512_context z_blake; +GLOBAL sph_bmw512_context z_bmw; +GLOBAL sph_groestl512_context z_groestl; +GLOBAL sph_jh512_context z_jh; +GLOBAL sph_keccak512_context z_keccak; +GLOBAL sph_skein512_context z_skein; + +#define fillz() do { \ + sph_blake512_init(&z_blake); \ + sph_bmw512_init(&z_bmw); \ + sph_groestl512_init(&z_groestl); \ + sph_jh512_init(&z_jh); \ + sph_keccak512_init(&z_keccak); \ + sph_skein512_init(&z_skein); \ +} while (0) + +#define ZBLAKE (memcpy(&ctx_blake, &z_blake, sizeof(z_blake))) +#define ZBMW (memcpy(&ctx_bmw, &z_bmw, sizeof(z_bmw))) +#define ZGROESTL (memcpy(&ctx_groestl, &z_groestl, sizeof(z_groestl))) +#define ZJH (memcpy(&ctx_jh, &z_jh, sizeof(z_jh))) +#define ZKECCAK (memcpy(&ctx_keccak, &z_keccak, sizeof(z_keccak))) +#define ZSKEIN (memcpy(&ctx_skein, &z_skein, sizeof(z_skein))) + +template +inline uint256 Hash9(const T1 pbegin, const T1 pend) + +{ + sph_blake512_context ctx_blake; + sph_bmw512_context ctx_bmw; + sph_groestl512_context ctx_groestl; + sph_jh512_context ctx_jh; + sph_keccak512_context ctx_keccak; + sph_skein512_context ctx_skein; + static unsigned char pblank[1]; + +#ifndef QT_NO_DEBUG + //std::string strhash; + //strhash = ""; +#endif + + uint512 mask = 8; + uint512 zero = 0; + + uint512 hash[9]; + + sph_blake512_init(&ctx_blake); + // ZBLAKE; + sph_blake512 (&ctx_blake, (pbegin == pend ? pblank : static_cast(&pbegin[0])), (pend - pbegin) * sizeof(pbegin[0])); + sph_blake512_close(&ctx_blake, static_cast(&hash[0])); + + sph_bmw512_init(&ctx_bmw); + // ZBMW; + sph_bmw512 (&ctx_bmw, static_cast(&hash[0]), 64); + sph_bmw512_close(&ctx_bmw, static_cast(&hash[1])); + + if ((hash[1] & mask) != zero) + { + sph_groestl512_init(&ctx_groestl); + // ZGROESTL; + sph_groestl512 (&ctx_groestl, static_cast(&hash[1]), 64); + sph_groestl512_close(&ctx_groestl, static_cast(&hash[2])); + } + else + { + sph_skein512_init(&ctx_skein); + // ZSKEIN; + sph_skein512 (&ctx_skein, static_cast(&hash[1]), 64); + sph_skein512_close(&ctx_skein, static_cast(&hash[2])); + } + + sph_groestl512_init(&ctx_groestl); + // ZGROESTL; + sph_groestl512 (&ctx_groestl, static_cast(&hash[2]), 64); + sph_groestl512_close(&ctx_groestl, static_cast(&hash[3])); + + sph_jh512_init(&ctx_jh); + // ZJH; + sph_jh512 (&ctx_jh, static_cast(&hash[3]), 64); + sph_jh512_close(&ctx_jh, static_cast(&hash[4])); + + if ((hash[4] & mask) != zero) + { + sph_blake512_init(&ctx_blake); + // ZBLAKE; + sph_blake512 (&ctx_blake, static_cast(&hash[4]), 64); + sph_blake512_close(&ctx_blake, static_cast(&hash[5])); + } + else + { + sph_bmw512_init(&ctx_bmw); + // ZBMW; + sph_bmw512 (&ctx_bmw, static_cast(&hash[4]), 64); + sph_bmw512_close(&ctx_bmw, static_cast(&hash[5])); + } + + sph_keccak512_init(&ctx_keccak); + // ZKECCAK; + sph_keccak512 (&ctx_keccak, static_cast(&hash[5]), 64); + sph_keccak512_close(&ctx_keccak, static_cast(&hash[6])); + + sph_skein512_init(&ctx_skein); + // SKEIN; + sph_skein512 (&ctx_skein, static_cast(&hash[6]), 64); + sph_skein512_close(&ctx_skein, static_cast(&hash[7])); + + if ((hash[7] & mask) != zero) + { + sph_keccak512_init(&ctx_keccak); + // ZKECCAK; + sph_keccak512 (&ctx_keccak, static_cast(&hash[7]), 64); + sph_keccak512_close(&ctx_keccak, static_cast(&hash[8])); + } + else + { + sph_jh512_init(&ctx_jh); + // ZJH; + sph_jh512 (&ctx_jh, static_cast(&hash[7]), 64); + sph_jh512_close(&ctx_jh, static_cast(&hash[8])); + } + + return hash[8].trim256(); +} + + + + + + +#endif // HASHBLOCK_H diff --git a/src/init.cpp b/src/init.cpp new file mode 100644 index 0000000..33b0549 --- /dev/null +++ b/src/init.cpp @@ -0,0 +1,1114 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2017 Greenchain developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#define GLOBALDEFINED + +#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 + +#ifndef WIN32 +#include +#endif + +using namespace std; +using namespace boost; + +CWallet* pwalletMain; +CClientUIInterface uiInterface; + +#ifdef WIN32 +// Win32 LevelDB doesn't use filedescriptors, and the ones used for +// accessing block files, don't count towards to fd_set size limit +// anyway. +#define MIN_CORE_FILEDESCRIPTORS 0 +#else +#define MIN_CORE_FILEDESCRIPTORS 150 +#endif + + +// Used to pass flags to the Bind() function +enum BindFlags { + BF_NONE = 0, + BF_EXPLICIT = (1U << 0), + BF_REPORT_ERROR = (1U << 1) +}; + +////////////////////////////////////////////////////////////////////////////// +// +// Shutdown +// + +// +// Thread management and startup/shutdown: +// +// The network-processing threads are all part of a thread group +// created by AppInit() or the Qt main() function. +// +// A clean exit happens when StartShutdown() or the SIGTERM +// signal handler sets fRequestShutdown, which triggers +// the DetectShutdownThread(), which interrupts the main thread group. +// DetectShutdownThread() then exits, which causes AppInit() to +// continue (it .joins the shutdown thread). +// Shutdown() is then +// called to clean up database connections, and stop other +// threads that should only be stopped after the main network-processing +// threads have exited. +// +// Note that if running -daemon the parent process returns from AppInit2 +// before adding any threads to the threadGroup, so .join_all() returns +// immediately and the parent exits from main(). +// +// Shutdown for Qt is very similar, only it uses a QTimer to detect +// fRequestShutdown getting set, and then does the normal Qt +// shutdown thing. +// + +volatile bool fRequestShutdown = false; + +void StartShutdown() +{ + fRequestShutdown = true; +} + +bool ShutdownRequested() +{ + return fRequestShutdown; +} + +static CCoinsViewDB *pcoinsdbview; + +void Shutdown() +{ + static CCriticalSection cs_Shutdown; + TRY_LOCK(cs_Shutdown, lockShutdown); + if (!lockShutdown) return; + + RenameThread("bitcoin-shutoff"); + nTransactionsUpdated++; + StopRPCThreads(); + bitdb.Flush(false); + StopNode(); + { + LOCK(cs_main); + if (pwalletMain) + pwalletMain->SetBestChain(CBlockLocator(pindexBest)); + if (pblocktree) + pblocktree->Flush(); + if (pcoinsTip) + pcoinsTip->Flush(); + delete pcoinsTip; pcoinsTip = NULL; + delete pcoinsdbview; pcoinsdbview = NULL; + delete pblocktree; pblocktree = NULL; + } + bitdb.Flush(true); + boost::filesystem::remove(GetPidFile()); + UnregisterWallet(pwalletMain); + delete pwalletMain; +} + +// +// Signal handlers are very limited in what they are allowed to do, so: +// +void DetectShutdownThread(boost::thread_group* threadGroup) +{ + // Tell the main threads to shutdown. + while (!fRequestShutdown) + { + MilliSleep(200); + if (fRequestShutdown) + threadGroup->interrupt_all(); + } +} + +void HandleSIGTERM(int) +{ + fRequestShutdown = true; +} + +void HandleSIGHUP(int) +{ + fReopenDebugLog = true; +} + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// Start +// +#if !defined(QT_GUI) +bool AppInit(int argc, char* argv[]) +{ + boost::thread_group threadGroup; + boost::thread* detectShutdownThread = NULL; + fillz(); + + bool fRet = false; + try + { + // + // Parameters + // + // If Qt is used, parameters/Greenchain.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(); + } + ReadConfigFile(mapArgs, mapMultiArgs); + + if (mapArgs.count("-?") || mapArgs.count("--help")) + { + // First part of help message is specific to Greenchaind / RPC client + std::string strUsage = _("Greenchain version") + " " + FormatFullVersion() + "\n\n" + + _("Usage:") + "\n" + + " Greenchaind [options] " + "\n" + + " Greenchaind [options] [params] " + _("Send command to -server or Greenchaind") + "\n" + + " Greenchaind [options] help " + _("List commands") + "\n" + + " Greenchaind [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], "Greenchain:")) + fCommandLine = true; + + if (fCommandLine) + { + int ret = CommandLineRPC(argc, argv); + exit(ret); + } +#if !defined(WIN32) + fDaemon = GetBoolArg("-daemon"); + 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) // Parent process, pid is child process id + { + CreatePidFile(GetPidFile(), pid); + return true; + } + // Child process falls through to rest of initialization + + pid_t sid = setsid(); + if (sid < 0) + fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno); + } +#endif + + detectShutdownThread = new boost::thread(boost::bind(&DetectShutdownThread, &threadGroup)); + fRet = AppInit2(threadGroup); + } + catch (std::exception& e) { + PrintExceptionContinue(&e, "AppInit()"); + } catch (...) { + PrintExceptionContinue(NULL, "AppInit()"); + } + if (!fRet) { + if (detectShutdownThread) + detectShutdownThread->interrupt(); + threadGroup.interrupt_all(); + } + + if (detectShutdownThread) + { + detectShutdownThread->join(); + delete detectShutdownThread; + detectShutdownThread = NULL; + } + Shutdown(); + + return fRet; +} + +extern void noui_connect(); +int main(int argc, char* argv[]) +{ + bool fRet = false; + + // Connect Greenchaind signal handlers + noui_connect(); + + fRet = AppInit(argc, argv); + + if (fRet && fDaemon) + return 0; + + return (fRet ? 0 : 1); +} +#endif + +bool static InitError(const std::string &str) +{ + uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_ERROR); + return false; +} + +bool static InitWarning(const std::string &str) +{ + uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_WARNING); + return true; +} + +bool static Bind(const CService &addr, unsigned int flags) { + if (!(flags & BF_EXPLICIT) && IsLimited(addr)) + return false; + std::string strError; + if (!BindListenPort(addr, strError)) { + if (flags & BF_REPORT_ERROR) + 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: Greenchain.conf)") + "\n" + + " -pid= " + _("Specify pid file (default: Greenchaind.pid)") + "\n" + + " -gen " + _("Generate coins (default: 0)") + "\n" + + " -datadir= " + _("Specify data directory") + "\n" + + " -dbcache= " + _("Set database cache size in megabytes (default: 25)") + "\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" + " -dns " + _("Allow DNS lookups for -addnode, -seednode and -connect") + "\n" + + " -port= " + _("Listen for connections on (default: 31743 or testnet: 41743)") + "\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 Tor)") + "\n" + + " -discover " + _("Discover own IP address (default: 1 when listening and no -externalip)") + "\n" + + " -checkpoints " + _("Only accept block chain matching built-in checkpoints (default: 1)") + "\n" + + " -listen " + _("Accept connections from outside (default: 1 if no -proxy or -connect)") + "\n" + + " -bind= " + _("Bind to given address and always listen on it. Use [host]:port notation for IPv6") + "\n" + + " -dnsseed " + _("Find peers using DNS lookup (default: 1 unless -connect)") + "\n" + + " -banscore= " + _("Threshold for disconnecting misbehaving peers (default: 100)") + "\n" + + " -bantime= " + _("Number of seconds to keep misbehaving peers from reconnecting (default: 129600)") + "\n" + + " -maxreceivebuffer= " + _("Maximum per-connection receive buffer, *1000 bytes (default: 5000)") + "\n" + + " -maxsendbuffer= " + _("Maximum per-connection send buffer, *1000 bytes (default: 1000)") + "\n" + +#ifdef USE_UPNP +#if USE_UPNP + " -upnp " + _("Use UPnP to map the listening port (default: 1 when listening)") + "\n" + +#else + " -upnp " + _("Use UPnP to map the listening port (default: 0)") + "\n" + +#endif +#endif + " -paytxfee= " + _("Fee per KB to add to transactions you send") + "\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: 31742 or testnet: 41742)") + "\n" + + " -rpcallowip= " + _("Allow JSON-RPC connections from specified IP address") + "\n" + +#ifndef QT_GUI + " -rpcconnect= " + _("Send commands to node running on (default: 127.0.0.1)") + "\n" + +#endif + " -rpcthreads= " + _("Set the number of threads to service RPC calls (default: 4)") + "\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" + + " -alertnotify= " + _("Execute command when a relevant alert is received (%s in cmd is replaced by message)") + "\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" + + " -salvagewallet " + _("Attempt to recover private keys from a corrupt wallet.dat") + "\n" + + " -checkblocks= " + _("How many blocks to check at startup (default: 288, 0 = all)") + "\n" + + " -checklevel= " + _("How thorough the block verification is (0-4, default: 3)") + "\n" + + " -txindex " + _("Maintain a full transaction index (default: 0)") + "\n" + + " -loadblock= " + _("Imports blocks from external blk000??.dat file") + "\n" + + " -reindex " + _("Rebuild block chain index from current blk000??.dat files") + "\n" + + " -par= " + _("Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0)") + "\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 Greenchain 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; +} + +struct CImportingNow +{ + CImportingNow() { + assert(fImporting == false); + fImporting = true; + } + + ~CImportingNow() { + assert(fImporting == true); + fImporting = false; + } +}; + +void ThreadImport(std::vector vImportFiles) +{ + RenameThread("bitcoin-loadblk"); + + // -reindex + if (fReindex) { + CImportingNow imp; + int nFile = 0; + while (true) { + CDiskBlockPos pos(nFile, 0); + FILE *file = OpenBlockFile(pos, true); + if (!file) + break; + printf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile); + LoadExternalBlockFile(file, &pos); + nFile++; + } + pblocktree->WriteReindexing(false); + fReindex = false; + printf("Reindexing finished\n"); + // To avoid ending up in a situation without genesis block, re-try initializing (no-op if reindexing worked): + InitBlockIndex(); + } + + // hardcoded $DATADIR/bootstrap.dat + filesystem::path pathBootstrap = GetDataDir() / "bootstrap.dat"; + if (filesystem::exists(pathBootstrap)) { + FILE *file = fopen(pathBootstrap.string().c_str(), "rb"); + if (file) { + CImportingNow imp; + filesystem::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old"; + printf("Importing bootstrap.dat...\n"); + LoadExternalBlockFile(file); + RenameOver(pathBootstrap, pathBootstrapOld); + } + } + + // -loadblock= + BOOST_FOREACH(boost::filesystem::path &path, vImportFiles) { + FILE *file = fopen(path.string().c_str(), "rb"); + if (file) { + CImportingNow imp; + printf("Importing %s...\n", path.string().c_str()); + LoadExternalBlockFile(file); + } + } +} + +/** Initialize bitcoin. + * @pre Parameters should be parsed and config file should be read. + */ +bool AppInit2(boost::thread_group& threadGroup) +{ + // ********************************************************* 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 || LOBYTE(wsadata.wVersion ) != 2 || HIBYTE(wsadata.wVersion) != 2) + { + return InitError(strprintf("Error: Winsock 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 + + fTestNet = GetBoolArg("-testnet"); + + 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("-upnp", false); + 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); + } + + // Make sure enough file descriptors are available + int nBind = std::max((int)mapArgs.count("-bind"), 1); + nMaxConnections = GetArg("-maxconnections", 125); + nMaxConnections = std::max(std::min(nMaxConnections, FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS), 0); + int nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS); + if (nFD < MIN_CORE_FILEDESCRIPTORS) + return InitError(_("Not enough file descriptors available.")); + if (nFD - MIN_CORE_FILEDESCRIPTORS < nMaxConnections) + nMaxConnections = nFD - MIN_CORE_FILEDESCRIPTORS; + + // ********************************************************* Step 3: parameter-to-internal-flags + + fDebug = GetBoolArg("-debug"); + fBenchmark = GetBoolArg("-benchmark"); + + // -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency + nScriptCheckThreads = GetArg("-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; + + // -debug implies fDebug* + if (fDebug) + fDebugNet = true; + else + fDebugNet = GetBoolArg("-debugnet"); + + 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 = GetArg("-timeout", 5000); + if (nNewTimeout > 0 && nNewTimeout < 600000) + nConnectTimeout = nNewTimeout; + } + + // Continue to put "/P2SH/" in the coinbase to monitor + // BIP16 support. + // This can be removed eventually... + const char* pszP2SH = "/P2SH/"; + COINBASE_FLAGS << std::vector(pszP2SH, pszP2SH+strlen(pszP2SH)); + + // Fee-per-kilobyte amount considered the same as "free" + // If you are mining, 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. + if (mapArgs.count("-mintxfee")) + { + int64 n = 0; + if (ParseMoney(mapArgs["-mintxfee"], n) && n > 0) + CTransaction::nMinTxFee = n; + else + return InitError(strprintf(_("Invalid amount for -mintxfee=: '%s'"), mapArgs["-mintxfee"].c_str())); + } + if (mapArgs.count("-minrelaytxfee")) + { + int64 n = 0; + if (ParseMoney(mapArgs["-minrelaytxfee"], n) && n > 0) + CTransaction::nMinRelayTxFee = n; + else + return InitError(strprintf(_("Invalid amount for -minrelaytxfee=: '%s'"), mapArgs["-minrelaytxfee"].c_str())); + } + + 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.")); + } + + if (mapArgs.count("-checkpointkey")) // ppcoin: checkpoint master priv key + { + if (!Checkpoints::SetCheckpointPrivKey(GetArg("-checkpointkey", ""))) + return InitError(_("Unable to sign checkpoint, wrong checkpointkey?")); + } + + // ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log + + std::string strDataDir = GetDataDir().string(); + + // Make sure only a single Greenchain 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. Greenchain is probably already running."), strDataDir.c_str())); + + 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("Greenchain 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("%Y-%m-%d %H:%M:%S", GetTime()).c_str()); + printf("Default data directory %s\n", GetDefaultDataDir().string().c_str()); + printf("Using data directory %s\n", strDataDir.c_str()); + printf("Using at most %i connections (%i file descriptors available)\n", nMaxConnections, nFD); + std::ostringstream strErrors; + + if (fDaemon) + fprintf(stdout, "Greenchain 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"], 9070); + 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"], 9070); + 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 + fNoListen = !GetBoolArg("-listen", true); + fDiscover = GetBoolArg("-discover", true); + fNameLookup = GetBoolArg("-dns", true); + + bool fBound = false; + if (!fNoListen) { + 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, (BF_EXPLICIT | BF_REPORT_ERROR)); + } + } + else { + struct in_addr inaddr_any; + inaddr_any.s_addr = INADDR_ANY; +#ifdef USE_IPV6 + fBound |= Bind(CService(in6addr_any, GetListenPort()), BF_NONE); +#endif + fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE); + } + if (!fBound) + return InitError(_("Failed to listen on any port. Use -listen=0 if you want this.")); + } + + 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); + } + } + + BOOST_FOREACH(string strDest, mapMultiArgs["-seednode"]) + AddOneShot(strDest); + + // ********************************************************* Step 7: load block chain + + fReindex = GetBoolArg("-reindex"); + + // Upgrading to 0.8; hard-link the old blknnnn.dat files into /blocks/ + filesystem::path blocksDir = GetDataDir() / "blocks"; + if (!filesystem::exists(blocksDir)) + { + filesystem::create_directories(blocksDir); + bool linked = false; + for (unsigned int i = 1; i < 10000; i++) { + filesystem::path source = GetDataDir() / strprintf("blk%04u.dat", i); + if (!filesystem::exists(source)) break; + filesystem::path dest = blocksDir / strprintf("blk%05u.dat", i-1); + try { + filesystem::create_hard_link(source, dest); + printf("Hardlinked %s -> %s\n", source.string().c_str(), dest.string().c_str()); + linked = true; + } catch (filesystem::filesystem_error & e) { + // Note: hardlink creation failing is not a disaster, it just means + // blocks will get re-downloaded from peers. + printf("Error hardlinking blk%04u.dat : %s\n", i, e.what()); + break; + } + } + if (linked) + { + fReindex = true; + } + } + + // cache size calculations + size_t nTotalCache = GetArg("-dbcache", 25) << 20; + if (nTotalCache < (1 << 22)) + nTotalCache = (1 << 22); // total cache cannot be less than 4 MiB + size_t nBlockTreeDBCache = nTotalCache / 8; + if (nBlockTreeDBCache > (1 << 21) && !GetBoolArg("-txindex", false)) + nBlockTreeDBCache = (1 << 21); // block tree db cache shouldn't be larger than 2 MiB + nTotalCache -= nBlockTreeDBCache; + size_t nCoinDBCache = nTotalCache / 2; // use half of the remaining cache for coindb cache + nTotalCache -= nCoinDBCache; + nCoinCacheSize = nTotalCache / 300; // coins in memory require around 300 bytes + + bool fLoaded = false; + while (!fLoaded) { + bool fReset = fReindex; + std::string strLoadError; + + uiInterface.InitMessage(_("Loading block index...")); + + nStart = GetTimeMillis(); + do { + try { + UnloadBlockIndex(); + delete pcoinsTip; + delete pcoinsdbview; + delete pblocktree; + + pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex); + pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex); + pcoinsTip = new CCoinsViewCache(*pcoinsdbview); + + if (fReindex) + pblocktree->WriteReindexing(true); + + if (!LoadBlockIndex()) { + strLoadError = _("Error loading block database"); + break; + } + + // Initialize the block index (no-op if non-empty database was already loaded) + if (!InitBlockIndex()) { + strLoadError = _("Error initializing block database"); + break; + } + + uiInterface.InitMessage(_("Verifying blocks...")); + if (!VerifyDB()) { + strLoadError = _("Corrupted block database detected"); + break; + } + } catch(std::exception &e) { + strLoadError = _("Error opening block database"); + break; + } + + fLoaded = true; + } while(false); + + if (!fLoaded) { + // first suggest a reindex + if (!fReset) { + bool fRet = uiInterface.ThreadSafeMessageBox( + strLoadError + ".\n" + _("Do you want to rebuild the block database now?"), + "", CClientUIInterface::MSG_ERROR | CClientUIInterface::BTN_ABORT); + if (fRet) { + fReindex = true; + fRequestShutdown = false; + } else { + return false; + } + } else { + return InitError(strLoadError); + } + } + } + + if (mapArgs.count("-txindex") && fTxIndex != GetBoolArg("-txindex", false)) + return InitError(_("You need to rebuild the databases using -reindex to change -txindex")); + + // 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"PRI64d"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 + + uiInterface.InitMessage(_("Loading wallet...")); + + nStart = GetTimeMillis(); + bool fFirstRun = true; + pwalletMain = new CWallet("wallet.dat"); + 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.")); + InitWarning(msg); + } + else if (nLoadWalletRet == DB_TOO_NEW) + strErrors << _("Error loading wallet.dat: Wallet requires newer version of Greenchain") << "\n"; + else if (nLoadWalletRet == DB_NEED_REWRITE) + { + strErrors << _("Wallet needed to be rewritten: restart Greenchain 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 = GetArg("-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)) { + pwalletMain->SetDefaultKey(newDefaultKey); + if (!pwalletMain->SetAddressBookName(pwalletMain->vchDefaultKey.GetID(), "")) + strErrors << _("Cannot write default address") << "\n"; + } + + pwalletMain->SetBestChain(CBlockLocator(pindexBest)); + } + + printf("%s", strErrors.str().c_str()); + printf(" wallet %15"PRI64d"ms\n", GetTimeMillis() - nStart); + + RegisterWallet(pwalletMain); + + CBlockIndex *pindexRescan = pindexBest; + if (GetBoolArg("-rescan")) + pindexRescan = pindexGenesisBlock; + else + { + CWalletDB walletdb("wallet.dat"); + CBlockLocator locator; + if (walletdb.ReadBestBlock(locator)) + pindexRescan = locator.GetBlockIndex(); + else + pindexRescan = pindexGenesisBlock; + } + if (pindexBest && pindexBest != pindexRescan) + { + 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"PRI64d"ms\n", GetTimeMillis() - nStart); + pwalletMain->SetBestChain(CBlockLocator(pindexBest)); + nWalletDBUpdated++; + } + + // ********************************************************* Step 9: import blocks + + // scan for better chains in the block chain database, that are not yet connected in the active best chain + CValidationState state; + if (!ConnectBestBlock(state)) + strErrors << "Failed to connect best block"; + + std::vector vImportFiles; + if (mapArgs.count("-loadblock")) + { + BOOST_FOREACH(string strFile, mapMultiArgs["-loadblock"]) + vImportFiles.push_back(strFile); + } + threadGroup.create_thread(boost::bind(&ThreadImport, vImportFiles)); + + // ********************************************************* Step 10: load peers + + uiInterface.InitMessage(_("Loading addresses...")); + + nStart = GetTimeMillis(); + + { + CAddrDB adb; + if (!adb.Read(addrman)) + printf("Invalid or missing peers.dat; recreating\n"); + } + + printf("Loaded %i addresses from peers.dat %"PRI64d"ms\n", + addrman.size(), GetTimeMillis() - nStart); + + // ********************************************************* Step 11: start node + + if (!CheckDiskSpace()) + return false; + + if (!strErrors.str().empty()) + return InitError(strErrors.str()); + + 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()); + + StartNode(threadGroup); + + if (fServer) + StartRPCThreads(); + + // Generate coins in the background + GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain); + + // ********************************************************* Step 12: finished + + uiInterface.InitMessage(_("Done loading")); + + // Add wallet transactions that aren't already in a block to mapTransactions + pwalletMain->ReacceptWalletTransactions(); + + // Run a thread to flush wallet periodically + threadGroup.create_thread(boost::bind(&ThreadFlushWalletDB, boost::ref(pwalletMain->strWalletFile))); + + return !fRequestShutdown; +} diff --git a/src/init.h b/src/init.h new file mode 100644 index 0000000..5927670 --- /dev/null +++ b/src/init.h @@ -0,0 +1,18 @@ +// 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; + +void StartShutdown(); +bool ShutdownRequested(); +void Shutdown(); +bool AppInit2(boost::thread_group& threadGroup); +std::string HelpMessage(); + +#endif diff --git a/src/jh.c b/src/jh.c new file mode 100644 index 0000000..41487a5 --- /dev/null +++ b/src/jh.c @@ -0,0 +1,1116 @@ +/* $Id: jh.c 255 2011-06-07 19:50:20Z tp $ */ +/* + * JH implementation. + * + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * 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 END)============================= + * + * @author Thomas Pornin + */ + +#include +#include + +#include "sph_jh.h" + +#ifdef __cplusplus +extern "C"{ +#endif + + +#if SPH_SMALL_FOOTPRINT && !defined SPH_SMALL_FOOTPRINT_JH +#define SPH_SMALL_FOOTPRINT_JH 1 +#endif + +#if !defined SPH_JH_64 && SPH_64_TRUE +#define SPH_JH_64 1 +#endif + +#if !SPH_64 +#undef SPH_JH_64 +#endif + +#ifdef _MSC_VER +#pragma warning (disable: 4146) +#endif + +/* + * The internal bitslice representation may use either big-endian or + * little-endian (true bitslice operations do not care about the bit + * ordering, and the bit-swapping linear operations in JH happen to + * be invariant through endianness-swapping). The constants must be + * defined according to the chosen endianness; we use some + * byte-swapping macros for that. + */ + +#if SPH_LITTLE_ENDIAN + +#define C32e(x) ((SPH_C32(x) >> 24) \ + | ((SPH_C32(x) >> 8) & SPH_C32(0x0000FF00)) \ + | ((SPH_C32(x) << 8) & SPH_C32(0x00FF0000)) \ + | ((SPH_C32(x) << 24) & SPH_C32(0xFF000000))) +#define dec32e_aligned sph_dec32le_aligned +#define enc32e sph_enc32le + +#if SPH_64 +#define C64e(x) ((SPH_C64(x) >> 56) \ + | ((SPH_C64(x) >> 40) & SPH_C64(0x000000000000FF00)) \ + | ((SPH_C64(x) >> 24) & SPH_C64(0x0000000000FF0000)) \ + | ((SPH_C64(x) >> 8) & SPH_C64(0x00000000FF000000)) \ + | ((SPH_C64(x) << 8) & SPH_C64(0x000000FF00000000)) \ + | ((SPH_C64(x) << 24) & SPH_C64(0x0000FF0000000000)) \ + | ((SPH_C64(x) << 40) & SPH_C64(0x00FF000000000000)) \ + | ((SPH_C64(x) << 56) & SPH_C64(0xFF00000000000000))) +#define dec64e_aligned sph_dec64le_aligned +#define enc64e sph_enc64le +#endif + +#else + +#define C32e(x) SPH_C32(x) +#define dec32e_aligned sph_dec32be_aligned +#define enc32e sph_enc32be +#if SPH_64 +#define C64e(x) SPH_C64(x) +#define dec64e_aligned sph_dec64be_aligned +#define enc64e sph_enc64be +#endif + +#endif + +#define Sb(x0, x1, x2, x3, c) do { \ + x3 = ~x3; \ + x0 ^= (c) & ~x2; \ + tmp = (c) ^ (x0 & x1); \ + x0 ^= x2 & x3; \ + x3 ^= ~x1 & x2; \ + x1 ^= x0 & x2; \ + x2 ^= x0 & ~x3; \ + x0 ^= x1 | x3; \ + x3 ^= x1 & x2; \ + x1 ^= tmp & x0; \ + x2 ^= tmp; \ + } while (0) + +#define Lb(x0, x1, x2, x3, x4, x5, x6, x7) do { \ + x4 ^= x1; \ + x5 ^= x2; \ + x6 ^= x3 ^ x0; \ + x7 ^= x0; \ + x0 ^= x5; \ + x1 ^= x6; \ + x2 ^= x7 ^ x4; \ + x3 ^= x4; \ + } while (0) + +#if SPH_JH_64 + +static const sph_u64 C[] = { + C64e(0x72d5dea2df15f867), C64e(0x7b84150ab7231557), + C64e(0x81abd6904d5a87f6), C64e(0x4e9f4fc5c3d12b40), + C64e(0xea983ae05c45fa9c), C64e(0x03c5d29966b2999a), + C64e(0x660296b4f2bb538a), C64e(0xb556141a88dba231), + C64e(0x03a35a5c9a190edb), C64e(0x403fb20a87c14410), + C64e(0x1c051980849e951d), C64e(0x6f33ebad5ee7cddc), + C64e(0x10ba139202bf6b41), C64e(0xdc786515f7bb27d0), + C64e(0x0a2c813937aa7850), C64e(0x3f1abfd2410091d3), + C64e(0x422d5a0df6cc7e90), C64e(0xdd629f9c92c097ce), + C64e(0x185ca70bc72b44ac), C64e(0xd1df65d663c6fc23), + C64e(0x976e6c039ee0b81a), C64e(0x2105457e446ceca8), + C64e(0xeef103bb5d8e61fa), C64e(0xfd9697b294838197), + C64e(0x4a8e8537db03302f), C64e(0x2a678d2dfb9f6a95), + C64e(0x8afe7381f8b8696c), C64e(0x8ac77246c07f4214), + C64e(0xc5f4158fbdc75ec4), C64e(0x75446fa78f11bb80), + C64e(0x52de75b7aee488bc), C64e(0x82b8001e98a6a3f4), + C64e(0x8ef48f33a9a36315), C64e(0xaa5f5624d5b7f989), + C64e(0xb6f1ed207c5ae0fd), C64e(0x36cae95a06422c36), + C64e(0xce2935434efe983d), C64e(0x533af974739a4ba7), + C64e(0xd0f51f596f4e8186), C64e(0x0e9dad81afd85a9f), + C64e(0xa7050667ee34626a), C64e(0x8b0b28be6eb91727), + C64e(0x47740726c680103f), C64e(0xe0a07e6fc67e487b), + C64e(0x0d550aa54af8a4c0), C64e(0x91e3e79f978ef19e), + C64e(0x8676728150608dd4), C64e(0x7e9e5a41f3e5b062), + C64e(0xfc9f1fec4054207a), C64e(0xe3e41a00cef4c984), + C64e(0x4fd794f59dfa95d8), C64e(0x552e7e1124c354a5), + C64e(0x5bdf7228bdfe6e28), C64e(0x78f57fe20fa5c4b2), + C64e(0x05897cefee49d32e), C64e(0x447e9385eb28597f), + C64e(0x705f6937b324314a), C64e(0x5e8628f11dd6e465), + C64e(0xc71b770451b920e7), C64e(0x74fe43e823d4878a), + C64e(0x7d29e8a3927694f2), C64e(0xddcb7a099b30d9c1), + C64e(0x1d1b30fb5bdc1be0), C64e(0xda24494ff29c82bf), + C64e(0xa4e7ba31b470bfff), C64e(0x0d324405def8bc48), + C64e(0x3baefc3253bbd339), C64e(0x459fc3c1e0298ba0), + C64e(0xe5c905fdf7ae090f), C64e(0x947034124290f134), + C64e(0xa271b701e344ed95), C64e(0xe93b8e364f2f984a), + C64e(0x88401d63a06cf615), C64e(0x47c1444b8752afff), + C64e(0x7ebb4af1e20ac630), C64e(0x4670b6c5cc6e8ce6), + C64e(0xa4d5a456bd4fca00), C64e(0xda9d844bc83e18ae), + C64e(0x7357ce453064d1ad), C64e(0xe8a6ce68145c2567), + C64e(0xa3da8cf2cb0ee116), C64e(0x33e906589a94999a), + C64e(0x1f60b220c26f847b), C64e(0xd1ceac7fa0d18518), + C64e(0x32595ba18ddd19d3), C64e(0x509a1cc0aaa5b446), + C64e(0x9f3d6367e4046bba), C64e(0xf6ca19ab0b56ee7e), + C64e(0x1fb179eaa9282174), C64e(0xe9bdf7353b3651ee), + C64e(0x1d57ac5a7550d376), C64e(0x3a46c2fea37d7001), + C64e(0xf735c1af98a4d842), C64e(0x78edec209e6b6779), + C64e(0x41836315ea3adba8), C64e(0xfac33b4d32832c83), + C64e(0xa7403b1f1c2747f3), C64e(0x5940f034b72d769a), + C64e(0xe73e4e6cd2214ffd), C64e(0xb8fd8d39dc5759ef), + C64e(0x8d9b0c492b49ebda), C64e(0x5ba2d74968f3700d), + C64e(0x7d3baed07a8d5584), C64e(0xf5a5e9f0e4f88e65), + C64e(0xa0b8a2f436103b53), C64e(0x0ca8079e753eec5a), + C64e(0x9168949256e8884f), C64e(0x5bb05c55f8babc4c), + C64e(0xe3bb3b99f387947b), C64e(0x75daf4d6726b1c5d), + C64e(0x64aeac28dc34b36d), C64e(0x6c34a550b828db71), + C64e(0xf861e2f2108d512a), C64e(0xe3db643359dd75fc), + C64e(0x1cacbcf143ce3fa2), C64e(0x67bbd13c02e843b0), + C64e(0x330a5bca8829a175), C64e(0x7f34194db416535c), + C64e(0x923b94c30e794d1e), C64e(0x797475d7b6eeaf3f), + C64e(0xeaa8d4f7be1a3921), C64e(0x5cf47e094c232751), + C64e(0x26a32453ba323cd2), C64e(0x44a3174a6da6d5ad), + C64e(0xb51d3ea6aff2c908), C64e(0x83593d98916b3c56), + C64e(0x4cf87ca17286604d), C64e(0x46e23ecc086ec7f6), + C64e(0x2f9833b3b1bc765e), C64e(0x2bd666a5efc4e62a), + C64e(0x06f4b6e8bec1d436), C64e(0x74ee8215bcef2163), + C64e(0xfdc14e0df453c969), C64e(0xa77d5ac406585826), + C64e(0x7ec1141606e0fa16), C64e(0x7e90af3d28639d3f), + C64e(0xd2c9f2e3009bd20c), C64e(0x5faace30b7d40c30), + C64e(0x742a5116f2e03298), C64e(0x0deb30d8e3cef89a), + C64e(0x4bc59e7bb5f17992), C64e(0xff51e66e048668d3), + C64e(0x9b234d57e6966731), C64e(0xcce6a6f3170a7505), + C64e(0xb17681d913326cce), C64e(0x3c175284f805a262), + C64e(0xf42bcbb378471547), C64e(0xff46548223936a48), + C64e(0x38df58074e5e6565), C64e(0xf2fc7c89fc86508e), + C64e(0x31702e44d00bca86), C64e(0xf04009a23078474e), + C64e(0x65a0ee39d1f73883), C64e(0xf75ee937e42c3abd), + C64e(0x2197b2260113f86f), C64e(0xa344edd1ef9fdee7), + C64e(0x8ba0df15762592d9), C64e(0x3c85f7f612dc42be), + C64e(0xd8a7ec7cab27b07e), C64e(0x538d7ddaaa3ea8de), + C64e(0xaa25ce93bd0269d8), C64e(0x5af643fd1a7308f9), + C64e(0xc05fefda174a19a5), C64e(0x974d66334cfd216a), + C64e(0x35b49831db411570), C64e(0xea1e0fbbedcd549b), + C64e(0x9ad063a151974072), C64e(0xf6759dbf91476fe2) +}; + +#define Ceven_hi(r) (C[((r) << 2) + 0]) +#define Ceven_lo(r) (C[((r) << 2) + 1]) +#define Codd_hi(r) (C[((r) << 2) + 2]) +#define Codd_lo(r) (C[((r) << 2) + 3]) + +#define S(x0, x1, x2, x3, cb, r) do { \ + Sb(x0 ## h, x1 ## h, x2 ## h, x3 ## h, cb ## hi(r)); \ + Sb(x0 ## l, x1 ## l, x2 ## l, x3 ## l, cb ## lo(r)); \ + } while (0) + +#define L(x0, x1, x2, x3, x4, x5, x6, x7) do { \ + Lb(x0 ## h, x1 ## h, x2 ## h, x3 ## h, \ + x4 ## h, x5 ## h, x6 ## h, x7 ## h); \ + Lb(x0 ## l, x1 ## l, x2 ## l, x3 ## l, \ + x4 ## l, x5 ## l, x6 ## l, x7 ## l); \ + } while (0) + +#define Wz(x, c, n) do { \ + sph_u64 t = (x ## h & (c)) << (n); \ + x ## h = ((x ## h >> (n)) & (c)) | t; \ + t = (x ## l & (c)) << (n); \ + x ## l = ((x ## l >> (n)) & (c)) | t; \ + } while (0) + +#define W0(x) Wz(x, SPH_C64(0x5555555555555555), 1) +#define W1(x) Wz(x, SPH_C64(0x3333333333333333), 2) +#define W2(x) Wz(x, SPH_C64(0x0F0F0F0F0F0F0F0F), 4) +#define W3(x) Wz(x, SPH_C64(0x00FF00FF00FF00FF), 8) +#define W4(x) Wz(x, SPH_C64(0x0000FFFF0000FFFF), 16) +#define W5(x) Wz(x, SPH_C64(0x00000000FFFFFFFF), 32) +#define W6(x) do { \ + sph_u64 t = x ## h; \ + x ## h = x ## l; \ + x ## l = t; \ + } while (0) + +#define DECL_STATE \ + sph_u64 h0h, h1h, h2h, h3h, h4h, h5h, h6h, h7h; \ + sph_u64 h0l, h1l, h2l, h3l, h4l, h5l, h6l, h7l; \ + sph_u64 tmp; + +#define READ_STATE(state) do { \ + h0h = (state)->H.wide[ 0]; \ + h0l = (state)->H.wide[ 1]; \ + h1h = (state)->H.wide[ 2]; \ + h1l = (state)->H.wide[ 3]; \ + h2h = (state)->H.wide[ 4]; \ + h2l = (state)->H.wide[ 5]; \ + h3h = (state)->H.wide[ 6]; \ + h3l = (state)->H.wide[ 7]; \ + h4h = (state)->H.wide[ 8]; \ + h4l = (state)->H.wide[ 9]; \ + h5h = (state)->H.wide[10]; \ + h5l = (state)->H.wide[11]; \ + h6h = (state)->H.wide[12]; \ + h6l = (state)->H.wide[13]; \ + h7h = (state)->H.wide[14]; \ + h7l = (state)->H.wide[15]; \ + } while (0) + +#define WRITE_STATE(state) do { \ + (state)->H.wide[ 0] = h0h; \ + (state)->H.wide[ 1] = h0l; \ + (state)->H.wide[ 2] = h1h; \ + (state)->H.wide[ 3] = h1l; \ + (state)->H.wide[ 4] = h2h; \ + (state)->H.wide[ 5] = h2l; \ + (state)->H.wide[ 6] = h3h; \ + (state)->H.wide[ 7] = h3l; \ + (state)->H.wide[ 8] = h4h; \ + (state)->H.wide[ 9] = h4l; \ + (state)->H.wide[10] = h5h; \ + (state)->H.wide[11] = h5l; \ + (state)->H.wide[12] = h6h; \ + (state)->H.wide[13] = h6l; \ + (state)->H.wide[14] = h7h; \ + (state)->H.wide[15] = h7l; \ + } while (0) + +#define INPUT_BUF1 \ + sph_u64 m0h = dec64e_aligned(buf + 0); \ + sph_u64 m0l = dec64e_aligned(buf + 8); \ + sph_u64 m1h = dec64e_aligned(buf + 16); \ + sph_u64 m1l = dec64e_aligned(buf + 24); \ + sph_u64 m2h = dec64e_aligned(buf + 32); \ + sph_u64 m2l = dec64e_aligned(buf + 40); \ + sph_u64 m3h = dec64e_aligned(buf + 48); \ + sph_u64 m3l = dec64e_aligned(buf + 56); \ + h0h ^= m0h; \ + h0l ^= m0l; \ + h1h ^= m1h; \ + h1l ^= m1l; \ + h2h ^= m2h; \ + h2l ^= m2l; \ + h3h ^= m3h; \ + h3l ^= m3l; + +#define INPUT_BUF2 \ + h4h ^= m0h; \ + h4l ^= m0l; \ + h5h ^= m1h; \ + h5l ^= m1l; \ + h6h ^= m2h; \ + h6l ^= m2l; \ + h7h ^= m3h; \ + h7l ^= m3l; + +static const sph_u64 IV224[] = { + C64e(0x2dfedd62f99a98ac), C64e(0xae7cacd619d634e7), + C64e(0xa4831005bc301216), C64e(0xb86038c6c9661494), + C64e(0x66d9899f2580706f), C64e(0xce9ea31b1d9b1adc), + C64e(0x11e8325f7b366e10), C64e(0xf994857f02fa06c1), + C64e(0x1b4f1b5cd8c840b3), C64e(0x97f6a17f6e738099), + C64e(0xdcdf93a5adeaa3d3), C64e(0xa431e8dec9539a68), + C64e(0x22b4a98aec86a1e4), C64e(0xd574ac959ce56cf0), + C64e(0x15960deab5ab2bbf), C64e(0x9611dcf0dd64ea6e) +}; + +static const sph_u64 IV256[] = { + C64e(0xeb98a3412c20d3eb), C64e(0x92cdbe7b9cb245c1), + C64e(0x1c93519160d4c7fa), C64e(0x260082d67e508a03), + C64e(0xa4239e267726b945), C64e(0xe0fb1a48d41a9477), + C64e(0xcdb5ab26026b177a), C64e(0x56f024420fff2fa8), + C64e(0x71a396897f2e4d75), C64e(0x1d144908f77de262), + C64e(0x277695f776248f94), C64e(0x87d5b6574780296c), + C64e(0x5c5e272dac8e0d6c), C64e(0x518450c657057a0f), + C64e(0x7be4d367702412ea), C64e(0x89e3ab13d31cd769) +}; + +static const sph_u64 IV384[] = { + C64e(0x481e3bc6d813398a), C64e(0x6d3b5e894ade879b), + C64e(0x63faea68d480ad2e), C64e(0x332ccb21480f8267), + C64e(0x98aec84d9082b928), C64e(0xd455ea3041114249), + C64e(0x36f555b2924847ec), C64e(0xc7250a93baf43ce1), + C64e(0x569b7f8a27db454c), C64e(0x9efcbd496397af0e), + C64e(0x589fc27d26aa80cd), C64e(0x80c08b8c9deb2eda), + C64e(0x8a7981e8f8d5373a), C64e(0xf43967adddd17a71), + C64e(0xa9b4d3bda475d394), C64e(0x976c3fba9842737f) +}; + +static const sph_u64 IV512[] = { + C64e(0x6fd14b963e00aa17), C64e(0x636a2e057a15d543), + C64e(0x8a225e8d0c97ef0b), C64e(0xe9341259f2b3c361), + C64e(0x891da0c1536f801e), C64e(0x2aa9056bea2b6d80), + C64e(0x588eccdb2075baa6), C64e(0xa90f3a76baf83bf7), + C64e(0x0169e60541e34a69), C64e(0x46b58a8e2e6fe65a), + C64e(0x1047a7d0c1843c24), C64e(0x3b6e71b12d5ac199), + C64e(0xcf57f6ec9db1f856), C64e(0xa706887c5716b156), + C64e(0xe3c2fcdfe68517fb), C64e(0x545a4678cc8cdd4b) +}; + +#else + +static const sph_u32 C[] = { + C32e(0x72d5dea2), C32e(0xdf15f867), C32e(0x7b84150a), + C32e(0xb7231557), C32e(0x81abd690), C32e(0x4d5a87f6), + C32e(0x4e9f4fc5), C32e(0xc3d12b40), C32e(0xea983ae0), + C32e(0x5c45fa9c), C32e(0x03c5d299), C32e(0x66b2999a), + C32e(0x660296b4), C32e(0xf2bb538a), C32e(0xb556141a), + C32e(0x88dba231), C32e(0x03a35a5c), C32e(0x9a190edb), + C32e(0x403fb20a), C32e(0x87c14410), C32e(0x1c051980), + C32e(0x849e951d), C32e(0x6f33ebad), C32e(0x5ee7cddc), + C32e(0x10ba1392), C32e(0x02bf6b41), C32e(0xdc786515), + C32e(0xf7bb27d0), C32e(0x0a2c8139), C32e(0x37aa7850), + C32e(0x3f1abfd2), C32e(0x410091d3), C32e(0x422d5a0d), + C32e(0xf6cc7e90), C32e(0xdd629f9c), C32e(0x92c097ce), + C32e(0x185ca70b), C32e(0xc72b44ac), C32e(0xd1df65d6), + C32e(0x63c6fc23), C32e(0x976e6c03), C32e(0x9ee0b81a), + C32e(0x2105457e), C32e(0x446ceca8), C32e(0xeef103bb), + C32e(0x5d8e61fa), C32e(0xfd9697b2), C32e(0x94838197), + C32e(0x4a8e8537), C32e(0xdb03302f), C32e(0x2a678d2d), + C32e(0xfb9f6a95), C32e(0x8afe7381), C32e(0xf8b8696c), + C32e(0x8ac77246), C32e(0xc07f4214), C32e(0xc5f4158f), + C32e(0xbdc75ec4), C32e(0x75446fa7), C32e(0x8f11bb80), + C32e(0x52de75b7), C32e(0xaee488bc), C32e(0x82b8001e), + C32e(0x98a6a3f4), C32e(0x8ef48f33), C32e(0xa9a36315), + C32e(0xaa5f5624), C32e(0xd5b7f989), C32e(0xb6f1ed20), + C32e(0x7c5ae0fd), C32e(0x36cae95a), C32e(0x06422c36), + C32e(0xce293543), C32e(0x4efe983d), C32e(0x533af974), + C32e(0x739a4ba7), C32e(0xd0f51f59), C32e(0x6f4e8186), + C32e(0x0e9dad81), C32e(0xafd85a9f), C32e(0xa7050667), + C32e(0xee34626a), C32e(0x8b0b28be), C32e(0x6eb91727), + C32e(0x47740726), C32e(0xc680103f), C32e(0xe0a07e6f), + C32e(0xc67e487b), C32e(0x0d550aa5), C32e(0x4af8a4c0), + C32e(0x91e3e79f), C32e(0x978ef19e), C32e(0x86767281), + C32e(0x50608dd4), C32e(0x7e9e5a41), C32e(0xf3e5b062), + C32e(0xfc9f1fec), C32e(0x4054207a), C32e(0xe3e41a00), + C32e(0xcef4c984), C32e(0x4fd794f5), C32e(0x9dfa95d8), + C32e(0x552e7e11), C32e(0x24c354a5), C32e(0x5bdf7228), + C32e(0xbdfe6e28), C32e(0x78f57fe2), C32e(0x0fa5c4b2), + C32e(0x05897cef), C32e(0xee49d32e), C32e(0x447e9385), + C32e(0xeb28597f), C32e(0x705f6937), C32e(0xb324314a), + C32e(0x5e8628f1), C32e(0x1dd6e465), C32e(0xc71b7704), + C32e(0x51b920e7), C32e(0x74fe43e8), C32e(0x23d4878a), + C32e(0x7d29e8a3), C32e(0x927694f2), C32e(0xddcb7a09), + C32e(0x9b30d9c1), C32e(0x1d1b30fb), C32e(0x5bdc1be0), + C32e(0xda24494f), C32e(0xf29c82bf), C32e(0xa4e7ba31), + C32e(0xb470bfff), C32e(0x0d324405), C32e(0xdef8bc48), + C32e(0x3baefc32), C32e(0x53bbd339), C32e(0x459fc3c1), + C32e(0xe0298ba0), C32e(0xe5c905fd), C32e(0xf7ae090f), + C32e(0x94703412), C32e(0x4290f134), C32e(0xa271b701), + C32e(0xe344ed95), C32e(0xe93b8e36), C32e(0x4f2f984a), + C32e(0x88401d63), C32e(0xa06cf615), C32e(0x47c1444b), + C32e(0x8752afff), C32e(0x7ebb4af1), C32e(0xe20ac630), + C32e(0x4670b6c5), C32e(0xcc6e8ce6), C32e(0xa4d5a456), + C32e(0xbd4fca00), C32e(0xda9d844b), C32e(0xc83e18ae), + C32e(0x7357ce45), C32e(0x3064d1ad), C32e(0xe8a6ce68), + C32e(0x145c2567), C32e(0xa3da8cf2), C32e(0xcb0ee116), + C32e(0x33e90658), C32e(0x9a94999a), C32e(0x1f60b220), + C32e(0xc26f847b), C32e(0xd1ceac7f), C32e(0xa0d18518), + C32e(0x32595ba1), C32e(0x8ddd19d3), C32e(0x509a1cc0), + C32e(0xaaa5b446), C32e(0x9f3d6367), C32e(0xe4046bba), + C32e(0xf6ca19ab), C32e(0x0b56ee7e), C32e(0x1fb179ea), + C32e(0xa9282174), C32e(0xe9bdf735), C32e(0x3b3651ee), + C32e(0x1d57ac5a), C32e(0x7550d376), C32e(0x3a46c2fe), + C32e(0xa37d7001), C32e(0xf735c1af), C32e(0x98a4d842), + C32e(0x78edec20), C32e(0x9e6b6779), C32e(0x41836315), + C32e(0xea3adba8), C32e(0xfac33b4d), C32e(0x32832c83), + C32e(0xa7403b1f), C32e(0x1c2747f3), C32e(0x5940f034), + C32e(0xb72d769a), C32e(0xe73e4e6c), C32e(0xd2214ffd), + C32e(0xb8fd8d39), C32e(0xdc5759ef), C32e(0x8d9b0c49), + C32e(0x2b49ebda), C32e(0x5ba2d749), C32e(0x68f3700d), + C32e(0x7d3baed0), C32e(0x7a8d5584), C32e(0xf5a5e9f0), + C32e(0xe4f88e65), C32e(0xa0b8a2f4), C32e(0x36103b53), + C32e(0x0ca8079e), C32e(0x753eec5a), C32e(0x91689492), + C32e(0x56e8884f), C32e(0x5bb05c55), C32e(0xf8babc4c), + C32e(0xe3bb3b99), C32e(0xf387947b), C32e(0x75daf4d6), + C32e(0x726b1c5d), C32e(0x64aeac28), C32e(0xdc34b36d), + C32e(0x6c34a550), C32e(0xb828db71), C32e(0xf861e2f2), + C32e(0x108d512a), C32e(0xe3db6433), C32e(0x59dd75fc), + C32e(0x1cacbcf1), C32e(0x43ce3fa2), C32e(0x67bbd13c), + C32e(0x02e843b0), C32e(0x330a5bca), C32e(0x8829a175), + C32e(0x7f34194d), C32e(0xb416535c), C32e(0x923b94c3), + C32e(0x0e794d1e), C32e(0x797475d7), C32e(0xb6eeaf3f), + C32e(0xeaa8d4f7), C32e(0xbe1a3921), C32e(0x5cf47e09), + C32e(0x4c232751), C32e(0x26a32453), C32e(0xba323cd2), + C32e(0x44a3174a), C32e(0x6da6d5ad), C32e(0xb51d3ea6), + C32e(0xaff2c908), C32e(0x83593d98), C32e(0x916b3c56), + C32e(0x4cf87ca1), C32e(0x7286604d), C32e(0x46e23ecc), + C32e(0x086ec7f6), C32e(0x2f9833b3), C32e(0xb1bc765e), + C32e(0x2bd666a5), C32e(0xefc4e62a), C32e(0x06f4b6e8), + C32e(0xbec1d436), C32e(0x74ee8215), C32e(0xbcef2163), + C32e(0xfdc14e0d), C32e(0xf453c969), C32e(0xa77d5ac4), + C32e(0x06585826), C32e(0x7ec11416), C32e(0x06e0fa16), + C32e(0x7e90af3d), C32e(0x28639d3f), C32e(0xd2c9f2e3), + C32e(0x009bd20c), C32e(0x5faace30), C32e(0xb7d40c30), + C32e(0x742a5116), C32e(0xf2e03298), C32e(0x0deb30d8), + C32e(0xe3cef89a), C32e(0x4bc59e7b), C32e(0xb5f17992), + C32e(0xff51e66e), C32e(0x048668d3), C32e(0x9b234d57), + C32e(0xe6966731), C32e(0xcce6a6f3), C32e(0x170a7505), + C32e(0xb17681d9), C32e(0x13326cce), C32e(0x3c175284), + C32e(0xf805a262), C32e(0xf42bcbb3), C32e(0x78471547), + C32e(0xff465482), C32e(0x23936a48), C32e(0x38df5807), + C32e(0x4e5e6565), C32e(0xf2fc7c89), C32e(0xfc86508e), + C32e(0x31702e44), C32e(0xd00bca86), C32e(0xf04009a2), + C32e(0x3078474e), C32e(0x65a0ee39), C32e(0xd1f73883), + C32e(0xf75ee937), C32e(0xe42c3abd), C32e(0x2197b226), + C32e(0x0113f86f), C32e(0xa344edd1), C32e(0xef9fdee7), + C32e(0x8ba0df15), C32e(0x762592d9), C32e(0x3c85f7f6), + C32e(0x12dc42be), C32e(0xd8a7ec7c), C32e(0xab27b07e), + C32e(0x538d7dda), C32e(0xaa3ea8de), C32e(0xaa25ce93), + C32e(0xbd0269d8), C32e(0x5af643fd), C32e(0x1a7308f9), + C32e(0xc05fefda), C32e(0x174a19a5), C32e(0x974d6633), + C32e(0x4cfd216a), C32e(0x35b49831), C32e(0xdb411570), + C32e(0xea1e0fbb), C32e(0xedcd549b), C32e(0x9ad063a1), + C32e(0x51974072), C32e(0xf6759dbf), C32e(0x91476fe2) +}; + +#define Ceven_w3(r) (C[((r) << 3) + 0]) +#define Ceven_w2(r) (C[((r) << 3) + 1]) +#define Ceven_w1(r) (C[((r) << 3) + 2]) +#define Ceven_w0(r) (C[((r) << 3) + 3]) +#define Codd_w3(r) (C[((r) << 3) + 4]) +#define Codd_w2(r) (C[((r) << 3) + 5]) +#define Codd_w1(r) (C[((r) << 3) + 6]) +#define Codd_w0(r) (C[((r) << 3) + 7]) + +#define S(x0, x1, x2, x3, cb, r) do { \ + Sb(x0 ## 3, x1 ## 3, x2 ## 3, x3 ## 3, cb ## w3(r)); \ + Sb(x0 ## 2, x1 ## 2, x2 ## 2, x3 ## 2, cb ## w2(r)); \ + Sb(x0 ## 1, x1 ## 1, x2 ## 1, x3 ## 1, cb ## w1(r)); \ + Sb(x0 ## 0, x1 ## 0, x2 ## 0, x3 ## 0, cb ## w0(r)); \ + } while (0) + +#define L(x0, x1, x2, x3, x4, x5, x6, x7) do { \ + Lb(x0 ## 3, x1 ## 3, x2 ## 3, x3 ## 3, \ + x4 ## 3, x5 ## 3, x6 ## 3, x7 ## 3); \ + Lb(x0 ## 2, x1 ## 2, x2 ## 2, x3 ## 2, \ + x4 ## 2, x5 ## 2, x6 ## 2, x7 ## 2); \ + Lb(x0 ## 1, x1 ## 1, x2 ## 1, x3 ## 1, \ + x4 ## 1, x5 ## 1, x6 ## 1, x7 ## 1); \ + Lb(x0 ## 0, x1 ## 0, x2 ## 0, x3 ## 0, \ + x4 ## 0, x5 ## 0, x6 ## 0, x7 ## 0); \ + } while (0) + +#define Wz(x, c, n) do { \ + sph_u32 t = (x ## 3 & (c)) << (n); \ + x ## 3 = ((x ## 3 >> (n)) & (c)) | t; \ + t = (x ## 2 & (c)) << (n); \ + x ## 2 = ((x ## 2 >> (n)) & (c)) | t; \ + t = (x ## 1 & (c)) << (n); \ + x ## 1 = ((x ## 1 >> (n)) & (c)) | t; \ + t = (x ## 0 & (c)) << (n); \ + x ## 0 = ((x ## 0 >> (n)) & (c)) | t; \ + } while (0) + +#define W0(x) Wz(x, SPH_C32(0x55555555), 1) +#define W1(x) Wz(x, SPH_C32(0x33333333), 2) +#define W2(x) Wz(x, SPH_C32(0x0F0F0F0F), 4) +#define W3(x) Wz(x, SPH_C32(0x00FF00FF), 8) +#define W4(x) Wz(x, SPH_C32(0x0000FFFF), 16) +#define W5(x) do { \ + sph_u32 t = x ## 3; \ + x ## 3 = x ## 2; \ + x ## 2 = t; \ + t = x ## 1; \ + x ## 1 = x ## 0; \ + x ## 0 = t; \ + } while (0) +#define W6(x) do { \ + sph_u32 t = x ## 3; \ + x ## 3 = x ## 1; \ + x ## 1 = t; \ + t = x ## 2; \ + x ## 2 = x ## 0; \ + x ## 0 = t; \ + } while (0) + +#define DECL_STATE \ + sph_u32 h03, h02, h01, h00, h13, h12, h11, h10; \ + sph_u32 h23, h22, h21, h20, h33, h32, h31, h30; \ + sph_u32 h43, h42, h41, h40, h53, h52, h51, h50; \ + sph_u32 h63, h62, h61, h60, h73, h72, h71, h70; \ + sph_u32 tmp; + +#define READ_STATE(state) do { \ + h03 = (state)->H.narrow[ 0]; \ + h02 = (state)->H.narrow[ 1]; \ + h01 = (state)->H.narrow[ 2]; \ + h00 = (state)->H.narrow[ 3]; \ + h13 = (state)->H.narrow[ 4]; \ + h12 = (state)->H.narrow[ 5]; \ + h11 = (state)->H.narrow[ 6]; \ + h10 = (state)->H.narrow[ 7]; \ + h23 = (state)->H.narrow[ 8]; \ + h22 = (state)->H.narrow[ 9]; \ + h21 = (state)->H.narrow[10]; \ + h20 = (state)->H.narrow[11]; \ + h33 = (state)->H.narrow[12]; \ + h32 = (state)->H.narrow[13]; \ + h31 = (state)->H.narrow[14]; \ + h30 = (state)->H.narrow[15]; \ + h43 = (state)->H.narrow[16]; \ + h42 = (state)->H.narrow[17]; \ + h41 = (state)->H.narrow[18]; \ + h40 = (state)->H.narrow[19]; \ + h53 = (state)->H.narrow[20]; \ + h52 = (state)->H.narrow[21]; \ + h51 = (state)->H.narrow[22]; \ + h50 = (state)->H.narrow[23]; \ + h63 = (state)->H.narrow[24]; \ + h62 = (state)->H.narrow[25]; \ + h61 = (state)->H.narrow[26]; \ + h60 = (state)->H.narrow[27]; \ + h73 = (state)->H.narrow[28]; \ + h72 = (state)->H.narrow[29]; \ + h71 = (state)->H.narrow[30]; \ + h70 = (state)->H.narrow[31]; \ + } while (0) + +#define WRITE_STATE(state) do { \ + (state)->H.narrow[ 0] = h03; \ + (state)->H.narrow[ 1] = h02; \ + (state)->H.narrow[ 2] = h01; \ + (state)->H.narrow[ 3] = h00; \ + (state)->H.narrow[ 4] = h13; \ + (state)->H.narrow[ 5] = h12; \ + (state)->H.narrow[ 6] = h11; \ + (state)->H.narrow[ 7] = h10; \ + (state)->H.narrow[ 8] = h23; \ + (state)->H.narrow[ 9] = h22; \ + (state)->H.narrow[10] = h21; \ + (state)->H.narrow[11] = h20; \ + (state)->H.narrow[12] = h33; \ + (state)->H.narrow[13] = h32; \ + (state)->H.narrow[14] = h31; \ + (state)->H.narrow[15] = h30; \ + (state)->H.narrow[16] = h43; \ + (state)->H.narrow[17] = h42; \ + (state)->H.narrow[18] = h41; \ + (state)->H.narrow[19] = h40; \ + (state)->H.narrow[20] = h53; \ + (state)->H.narrow[21] = h52; \ + (state)->H.narrow[22] = h51; \ + (state)->H.narrow[23] = h50; \ + (state)->H.narrow[24] = h63; \ + (state)->H.narrow[25] = h62; \ + (state)->H.narrow[26] = h61; \ + (state)->H.narrow[27] = h60; \ + (state)->H.narrow[28] = h73; \ + (state)->H.narrow[29] = h72; \ + (state)->H.narrow[30] = h71; \ + (state)->H.narrow[31] = h70; \ + } while (0) + +#define INPUT_BUF1 \ + sph_u32 m03 = dec32e_aligned(buf + 0); \ + sph_u32 m02 = dec32e_aligned(buf + 4); \ + sph_u32 m01 = dec32e_aligned(buf + 8); \ + sph_u32 m00 = dec32e_aligned(buf + 12); \ + sph_u32 m13 = dec32e_aligned(buf + 16); \ + sph_u32 m12 = dec32e_aligned(buf + 20); \ + sph_u32 m11 = dec32e_aligned(buf + 24); \ + sph_u32 m10 = dec32e_aligned(buf + 28); \ + sph_u32 m23 = dec32e_aligned(buf + 32); \ + sph_u32 m22 = dec32e_aligned(buf + 36); \ + sph_u32 m21 = dec32e_aligned(buf + 40); \ + sph_u32 m20 = dec32e_aligned(buf + 44); \ + sph_u32 m33 = dec32e_aligned(buf + 48); \ + sph_u32 m32 = dec32e_aligned(buf + 52); \ + sph_u32 m31 = dec32e_aligned(buf + 56); \ + sph_u32 m30 = dec32e_aligned(buf + 60); \ + h03 ^= m03; \ + h02 ^= m02; \ + h01 ^= m01; \ + h00 ^= m00; \ + h13 ^= m13; \ + h12 ^= m12; \ + h11 ^= m11; \ + h10 ^= m10; \ + h23 ^= m23; \ + h22 ^= m22; \ + h21 ^= m21; \ + h20 ^= m20; \ + h33 ^= m33; \ + h32 ^= m32; \ + h31 ^= m31; \ + h30 ^= m30; + +#define INPUT_BUF2 \ + h43 ^= m03; \ + h42 ^= m02; \ + h41 ^= m01; \ + h40 ^= m00; \ + h53 ^= m13; \ + h52 ^= m12; \ + h51 ^= m11; \ + h50 ^= m10; \ + h63 ^= m23; \ + h62 ^= m22; \ + h61 ^= m21; \ + h60 ^= m20; \ + h73 ^= m33; \ + h72 ^= m32; \ + h71 ^= m31; \ + h70 ^= m30; + +static const sph_u32 IV224[] = { + C32e(0x2dfedd62), C32e(0xf99a98ac), C32e(0xae7cacd6), C32e(0x19d634e7), + C32e(0xa4831005), C32e(0xbc301216), C32e(0xb86038c6), C32e(0xc9661494), + C32e(0x66d9899f), C32e(0x2580706f), C32e(0xce9ea31b), C32e(0x1d9b1adc), + C32e(0x11e8325f), C32e(0x7b366e10), C32e(0xf994857f), C32e(0x02fa06c1), + C32e(0x1b4f1b5c), C32e(0xd8c840b3), C32e(0x97f6a17f), C32e(0x6e738099), + C32e(0xdcdf93a5), C32e(0xadeaa3d3), C32e(0xa431e8de), C32e(0xc9539a68), + C32e(0x22b4a98a), C32e(0xec86a1e4), C32e(0xd574ac95), C32e(0x9ce56cf0), + C32e(0x15960dea), C32e(0xb5ab2bbf), C32e(0x9611dcf0), C32e(0xdd64ea6e) +}; + +static const sph_u32 IV256[] = { + C32e(0xeb98a341), C32e(0x2c20d3eb), C32e(0x92cdbe7b), C32e(0x9cb245c1), + C32e(0x1c935191), C32e(0x60d4c7fa), C32e(0x260082d6), C32e(0x7e508a03), + C32e(0xa4239e26), C32e(0x7726b945), C32e(0xe0fb1a48), C32e(0xd41a9477), + C32e(0xcdb5ab26), C32e(0x026b177a), C32e(0x56f02442), C32e(0x0fff2fa8), + C32e(0x71a39689), C32e(0x7f2e4d75), C32e(0x1d144908), C32e(0xf77de262), + C32e(0x277695f7), C32e(0x76248f94), C32e(0x87d5b657), C32e(0x4780296c), + C32e(0x5c5e272d), C32e(0xac8e0d6c), C32e(0x518450c6), C32e(0x57057a0f), + C32e(0x7be4d367), C32e(0x702412ea), C32e(0x89e3ab13), C32e(0xd31cd769) +}; + +static const sph_u32 IV384[] = { + C32e(0x481e3bc6), C32e(0xd813398a), C32e(0x6d3b5e89), C32e(0x4ade879b), + C32e(0x63faea68), C32e(0xd480ad2e), C32e(0x332ccb21), C32e(0x480f8267), + C32e(0x98aec84d), C32e(0x9082b928), C32e(0xd455ea30), C32e(0x41114249), + C32e(0x36f555b2), C32e(0x924847ec), C32e(0xc7250a93), C32e(0xbaf43ce1), + C32e(0x569b7f8a), C32e(0x27db454c), C32e(0x9efcbd49), C32e(0x6397af0e), + C32e(0x589fc27d), C32e(0x26aa80cd), C32e(0x80c08b8c), C32e(0x9deb2eda), + C32e(0x8a7981e8), C32e(0xf8d5373a), C32e(0xf43967ad), C32e(0xddd17a71), + C32e(0xa9b4d3bd), C32e(0xa475d394), C32e(0x976c3fba), C32e(0x9842737f) +}; + +static const sph_u32 IV512[] = { + C32e(0x6fd14b96), C32e(0x3e00aa17), C32e(0x636a2e05), C32e(0x7a15d543), + C32e(0x8a225e8d), C32e(0x0c97ef0b), C32e(0xe9341259), C32e(0xf2b3c361), + C32e(0x891da0c1), C32e(0x536f801e), C32e(0x2aa9056b), C32e(0xea2b6d80), + C32e(0x588eccdb), C32e(0x2075baa6), C32e(0xa90f3a76), C32e(0xbaf83bf7), + C32e(0x0169e605), C32e(0x41e34a69), C32e(0x46b58a8e), C32e(0x2e6fe65a), + C32e(0x1047a7d0), C32e(0xc1843c24), C32e(0x3b6e71b1), C32e(0x2d5ac199), + C32e(0xcf57f6ec), C32e(0x9db1f856), C32e(0xa706887c), C32e(0x5716b156), + C32e(0xe3c2fcdf), C32e(0xe68517fb), C32e(0x545a4678), C32e(0xcc8cdd4b) +}; + +#endif + +#define SL(ro) SLu(r + ro, ro) + +#define SLu(r, ro) do { \ + S(h0, h2, h4, h6, Ceven_, r); \ + S(h1, h3, h5, h7, Codd_, r); \ + L(h0, h2, h4, h6, h1, h3, h5, h7); \ + W ## ro(h1); \ + W ## ro(h3); \ + W ## ro(h5); \ + W ## ro(h7); \ + } while (0) + +#if SPH_SMALL_FOOTPRINT_JH + +#if SPH_JH_64 + +/* + * The "small footprint" 64-bit version just uses a partially unrolled + * loop. + */ + +#define E8 do { \ + unsigned r; \ + for (r = 0; r < 42; r += 7) { \ + SL(0); \ + SL(1); \ + SL(2); \ + SL(3); \ + SL(4); \ + SL(5); \ + SL(6); \ + } \ + } while (0) + +#else + +#define E8 do { \ + unsigned r, g; \ + for (r = g = 0; r < 42; r ++) { \ + S(h0, h2, h4, h6, Ceven_, r); \ + S(h1, h3, h5, h7, Codd_, r); \ + L(h0, h2, h4, h6, h1, h3, h5, h7); \ + switch (g) { \ + case 0: \ + W0(h1); \ + W0(h3); \ + W0(h5); \ + W0(h7); \ + break; \ + case 1: \ + W1(h1); \ + W1(h3); \ + W1(h5); \ + W1(h7); \ + break; \ + case 2: \ + W2(h1); \ + W2(h3); \ + W2(h5); \ + W2(h7); \ + break; \ + case 3: \ + W3(h1); \ + W3(h3); \ + W3(h5); \ + W3(h7); \ + break; \ + case 4: \ + W4(h1); \ + W4(h3); \ + W4(h5); \ + W4(h7); \ + break; \ + case 5: \ + W5(h1); \ + W5(h3); \ + W5(h5); \ + W5(h7); \ + break; \ + case 6: \ + W6(h1); \ + W6(h3); \ + W6(h5); \ + W6(h7); \ + break; \ + } \ + if (++ g == 7) \ + g = 0; \ + } \ + } while (0) + +#endif + +#else + +#if SPH_JH_64 + +/* + * On a "true 64-bit" architecture, we can unroll at will. + */ + +#define E8 do { \ + SLu( 0, 0); \ + SLu( 1, 1); \ + SLu( 2, 2); \ + SLu( 3, 3); \ + SLu( 4, 4); \ + SLu( 5, 5); \ + SLu( 6, 6); \ + SLu( 7, 0); \ + SLu( 8, 1); \ + SLu( 9, 2); \ + SLu(10, 3); \ + SLu(11, 4); \ + SLu(12, 5); \ + SLu(13, 6); \ + SLu(14, 0); \ + SLu(15, 1); \ + SLu(16, 2); \ + SLu(17, 3); \ + SLu(18, 4); \ + SLu(19, 5); \ + SLu(20, 6); \ + SLu(21, 0); \ + SLu(22, 1); \ + SLu(23, 2); \ + SLu(24, 3); \ + SLu(25, 4); \ + SLu(26, 5); \ + SLu(27, 6); \ + SLu(28, 0); \ + SLu(29, 1); \ + SLu(30, 2); \ + SLu(31, 3); \ + SLu(32, 4); \ + SLu(33, 5); \ + SLu(34, 6); \ + SLu(35, 0); \ + SLu(36, 1); \ + SLu(37, 2); \ + SLu(38, 3); \ + SLu(39, 4); \ + SLu(40, 5); \ + SLu(41, 6); \ + } while (0) + +#else + +/* + * We are not aiming at a small footprint, but we are still using a + * 32-bit implementation. Full loop unrolling would smash the L1 + * cache on some "big" architectures (32 kB L1 cache). + */ + +#define E8 do { \ + unsigned r; \ + for (r = 0; r < 42; r += 7) { \ + SL(0); \ + SL(1); \ + SL(2); \ + SL(3); \ + SL(4); \ + SL(5); \ + SL(6); \ + } \ + } while (0) + +#endif + +#endif + +static void +jh_init(sph_jh_context *sc, const void *iv) +{ + sc->ptr = 0; +#if SPH_JH_64 + memcpy(sc->H.wide, iv, sizeof sc->H.wide); +#else + memcpy(sc->H.narrow, iv, sizeof sc->H.narrow); +#endif +#if SPH_64 + sc->block_count = 0; +#else + sc->block_count_high = 0; + sc->block_count_low = 0; +#endif +} + +static void +jh_core(sph_jh_context *sc, const void *data, size_t len) +{ + unsigned char *buf; + size_t ptr; + DECL_STATE + + buf = sc->buf; + ptr = sc->ptr; + if (len < (sizeof sc->buf) - ptr) { + memcpy(buf + ptr, data, len); + ptr += len; + sc->ptr = ptr; + return; + } + + READ_STATE(sc); + while (len > 0) { + size_t clen; + + clen = (sizeof sc->buf) - ptr; + if (clen > len) + clen = len; + memcpy(buf + ptr, data, clen); + ptr += clen; + data = (const unsigned char *)data + clen; + len -= clen; + if (ptr == sizeof sc->buf) { + INPUT_BUF1; + E8; + INPUT_BUF2; +#if SPH_64 + sc->block_count ++; +#else + if ((sc->block_count_low = SPH_T32( + sc->block_count_low + 1)) == 0) + sc->block_count_high ++; +#endif + ptr = 0; + } + } + WRITE_STATE(sc); + sc->ptr = ptr; +} + +static void +jh_close(sph_jh_context *sc, unsigned ub, unsigned n, + void *dst, size_t out_size_w32, const void *iv) +{ + unsigned z; + unsigned char buf[128]; + size_t numz, u; +#if SPH_64 + sph_u64 l0, l1; +#else + sph_u32 l0, l1, l2, l3; +#endif + + z = 0x80 >> n; + buf[0] = ((ub & -z) | z) & 0xFF; + if (sc->ptr == 0 && n == 0) { + numz = 47; + } else { + numz = 111 - sc->ptr; + } + memset(buf + 1, 0, numz); +#if SPH_64 + l0 = SPH_T64(sc->block_count << 9) + (sc->ptr << 3) + n; + l1 = SPH_T64(sc->block_count >> 55); + sph_enc64be(buf + numz + 1, l1); + sph_enc64be(buf + numz + 9, l0); +#else + l0 = SPH_T32(sc->block_count_low << 9) + (sc->ptr << 3) + n; + l1 = SPH_T32(sc->block_count_low >> 23) + + SPH_T32(sc->block_count_high << 9); + l2 = SPH_T32(sc->block_count_high >> 23); + l3 = 0; + sph_enc32be(buf + numz + 1, l3); + sph_enc32be(buf + numz + 5, l2); + sph_enc32be(buf + numz + 9, l1); + sph_enc32be(buf + numz + 13, l0); +#endif + jh_core(sc, buf, numz + 17); +#if SPH_JH_64 + for (u = 0; u < 8; u ++) + enc64e(buf + (u << 3), sc->H.wide[u + 8]); +#else + for (u = 0; u < 16; u ++) + enc32e(buf + (u << 2), sc->H.narrow[u + 16]); +#endif + memcpy(dst, buf + ((16 - out_size_w32) << 2), out_size_w32 << 2); + jh_init(sc, iv); +} + +/* see sph_jh.h */ +void +sph_jh224_init(void *cc) +{ + jh_init(cc, IV224); +} + +/* see sph_jh.h */ +void +sph_jh224(void *cc, const void *data, size_t len) +{ + jh_core(cc, data, len); +} + +/* see sph_jh.h */ +void +sph_jh224_close(void *cc, void *dst) +{ + jh_close(cc, 0, 0, dst, 7, IV224); +} + +/* see sph_jh.h */ +void +sph_jh224_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + jh_close(cc, ub, n, dst, 7, IV224); +} + +/* see sph_jh.h */ +void +sph_jh256_init(void *cc) +{ + jh_init(cc, IV256); +} + +/* see sph_jh.h */ +void +sph_jh256(void *cc, const void *data, size_t len) +{ + jh_core(cc, data, len); +} + +/* see sph_jh.h */ +void +sph_jh256_close(void *cc, void *dst) +{ + jh_close(cc, 0, 0, dst, 8, IV256); +} + +/* see sph_jh.h */ +void +sph_jh256_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + jh_close(cc, ub, n, dst, 8, IV256); +} + +/* see sph_jh.h */ +void +sph_jh384_init(void *cc) +{ + jh_init(cc, IV384); +} + +/* see sph_jh.h */ +void +sph_jh384(void *cc, const void *data, size_t len) +{ + jh_core(cc, data, len); +} + +/* see sph_jh.h */ +void +sph_jh384_close(void *cc, void *dst) +{ + jh_close(cc, 0, 0, dst, 12, IV384); +} + +/* see sph_jh.h */ +void +sph_jh384_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + jh_close(cc, ub, n, dst, 12, IV384); +} + +/* see sph_jh.h */ +void +sph_jh512_init(void *cc) +{ + jh_init(cc, IV512); +} + +/* see sph_jh.h */ +void +sph_jh512(void *cc, const void *data, size_t len) +{ + jh_core(cc, data, len); +} + +/* see sph_jh.h */ +void +sph_jh512_close(void *cc, void *dst) +{ + jh_close(cc, 0, 0, dst, 16, IV512); +} + +/* see sph_jh.h */ +void +sph_jh512_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + jh_close(cc, ub, n, dst, 16, IV512); +} + +#ifdef __cplusplus +} +#endif diff --git a/src/json/LICENSE.txt b/src/json/LICENSE.txt new file mode 100644 index 0000000..797d536 --- /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 0000000..ac1879d --- /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 0000000..1720850 --- /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 0000000..aa4f637 --- /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 0000000..96494a9 --- /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 0000000..4dec00e --- /dev/null +++ b/src/json/json_spirit_reader_template.h @@ -0,0 +1,612 @@ +#ifndef JSON_SPIRIT_READER_TEMPLATE +#define JSON_SPIRIT_READER_TEMPLATE + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#include "json_spirit_value.h" +#include "json_spirit_error_position.h" + +//#define BOOST_SPIRIT_THREADSAFE // uncomment for multithreaded use, requires linking to boost.thread + +#include +#include +#include + +#if BOOST_VERSION >= 103800 + #include + #include + #include + #include + #include + #define spirit_namespace boost::spirit::classic +#else + #include + #include + #include + #include + #include + #define spirit_namespace boost::spirit +#endif + +namespace json_spirit +{ + const spirit_namespace::int_parser < boost::int64_t > int64_p = spirit_namespace::int_parser < boost::int64_t >(); + const spirit_namespace::uint_parser< boost::uint64_t > uint64_p = spirit_namespace::uint_parser< boost::uint64_t >(); + + template< class Iter_type > + bool is_eq( Iter_type first, Iter_type last, const char* c_str ) + { + for( Iter_type i = first; i != last; ++i, ++c_str ) + { + if( *c_str == 0 ) return false; + + if( *i != *c_str ) return false; + } + + return true; + } + + template< class Char_type > + Char_type hex_to_num( const Char_type c ) + { + if( ( c >= '0' ) && ( c <= '9' ) ) return c - '0'; + if( ( c >= 'a' ) && ( c <= 'f' ) ) return c - 'a' + 10; + if( ( c >= 'A' ) && ( c <= 'F' ) ) return c - 'A' + 10; + return 0; + } + + template< class Char_type, class Iter_type > + Char_type hex_str_to_char( Iter_type& begin ) + { + const Char_type c1( *( ++begin ) ); + const Char_type c2( *( ++begin ) ); + + return ( hex_to_num( c1 ) << 4 ) + hex_to_num( c2 ); + } + + template< class Char_type, class Iter_type > + Char_type unicode_str_to_char( Iter_type& begin ) + { + const Char_type c1( *( ++begin ) ); + const Char_type c2( *( ++begin ) ); + const Char_type c3( *( ++begin ) ); + const Char_type c4( *( ++begin ) ); + + return ( hex_to_num( c1 ) << 12 ) + + ( hex_to_num( c2 ) << 8 ) + + ( hex_to_num( c3 ) << 4 ) + + hex_to_num( c4 ); + } + + template< class String_type > + void append_esc_char_and_incr_iter( String_type& s, + typename String_type::const_iterator& begin, + typename String_type::const_iterator end ) + { + typedef typename String_type::value_type Char_type; + + const Char_type c2( *begin ); + + switch( c2 ) + { + case 't': s += '\t'; break; + case 'b': s += '\b'; break; + case 'f': s += '\f'; break; + case 'n': s += '\n'; break; + case 'r': s += '\r'; break; + case '\\': s += '\\'; break; + case '/': s += '/'; break; + case '"': s += '"'; break; + case 'x': + { + if( end - begin >= 3 ) // expecting "xHH..." + { + s += hex_str_to_char< Char_type >( begin ); + } + break; + } + case 'u': + { + if( end - begin >= 5 ) // expecting "uHHHH..." + { + s += unicode_str_to_char< Char_type >( begin ); + } + break; + } + } + } + + template< class String_type > + String_type substitute_esc_chars( typename String_type::const_iterator begin, + typename String_type::const_iterator end ) + { + typedef typename String_type::const_iterator Iter_type; + + if( end - begin < 2 ) return String_type( begin, end ); + + String_type result; + + result.reserve( end - begin ); + + const Iter_type end_minus_1( end - 1 ); + + Iter_type substr_start = begin; + Iter_type i = begin; + + for( ; i < end_minus_1; ++i ) + { + if( *i == '\\' ) + { + result.append( substr_start, i ); + + ++i; // skip the '\' + + append_esc_char_and_incr_iter( result, i, end ); + + substr_start = i + 1; + } + } + + result.append( substr_start, end ); + + return result; + } + + template< class String_type > + String_type get_str_( typename String_type::const_iterator begin, + typename String_type::const_iterator end ) + { + assert( end - begin >= 2 ); + + typedef typename String_type::const_iterator Iter_type; + + Iter_type str_without_quotes( ++begin ); + Iter_type end_without_quotes( --end ); + + return substitute_esc_chars< String_type >( str_without_quotes, end_without_quotes ); + } + + inline std::string get_str( std::string::const_iterator begin, std::string::const_iterator end ) + { + return get_str_< std::string >( begin, end ); + } + + inline std::wstring get_str( std::wstring::const_iterator begin, std::wstring::const_iterator end ) + { + return get_str_< std::wstring >( begin, end ); + } + + template< class String_type, class Iter_type > + String_type get_str( Iter_type begin, Iter_type end ) + { + const String_type tmp( begin, end ); // convert multipass iterators to string iterators + + return get_str( tmp.begin(), tmp.end() ); + } + + // this class's methods get called by the spirit parse resulting + // in the creation of a JSON object or array + // + // NB Iter_type could be a std::string iterator, wstring iterator, a position iterator or a multipass iterator + // + template< class Value_type, class Iter_type > + class Semantic_actions + { + public: + + typedef typename Value_type::Config_type Config_type; + typedef typename Config_type::String_type String_type; + typedef typename Config_type::Object_type Object_type; + typedef typename Config_type::Array_type Array_type; + typedef typename String_type::value_type Char_type; + + Semantic_actions( Value_type& value ) + : value_( value ) + , current_p_( 0 ) + { + } + + void begin_obj( Char_type c ) + { + assert( c == '{' ); + + begin_compound< Object_type >(); + } + + void end_obj( Char_type c ) + { + assert( c == '}' ); + + end_compound(); + } + + void begin_array( Char_type c ) + { + assert( c == '[' ); + + begin_compound< Array_type >(); + } + + void end_array( Char_type c ) + { + assert( c == ']' ); + + end_compound(); + } + + void new_name( Iter_type begin, Iter_type end ) + { + assert( current_p_->type() == obj_type ); + + name_ = get_str< String_type >( begin, end ); + } + + void new_str( Iter_type begin, Iter_type end ) + { + add_to_current( get_str< String_type >( begin, end ) ); + } + + void new_true( Iter_type begin, Iter_type end ) + { + assert( is_eq( begin, end, "true" ) ); + + add_to_current( true ); + } + + void new_false( Iter_type begin, Iter_type end ) + { + assert( is_eq( begin, end, "false" ) ); + + add_to_current( false ); + } + + void new_null( Iter_type begin, Iter_type end ) + { + assert( is_eq( begin, end, "null" ) ); + + add_to_current( Value_type() ); + } + + void new_int( boost::int64_t i ) + { + add_to_current( i ); + } + + void new_uint64( boost::uint64_t ui ) + { + add_to_current( ui ); + } + + void new_real( double d ) + { + add_to_current( d ); + } + + private: + + Semantic_actions& operator=( const Semantic_actions& ); + // to prevent "assignment operator could not be generated" warning + + Value_type* add_first( const Value_type& value ) + { + assert( current_p_ == 0 ); + + value_ = value; + current_p_ = &value_; + return current_p_; + } + + template< class Array_or_obj > + void begin_compound() + { + if( current_p_ == 0 ) + { + add_first( Array_or_obj() ); + } + else + { + stack_.push_back( current_p_ ); + + Array_or_obj new_array_or_obj; // avoid copy by building new array or object in place + + current_p_ = add_to_current( new_array_or_obj ); + } + } + + void end_compound() + { + if( current_p_ != &value_ ) + { + current_p_ = stack_.back(); + + stack_.pop_back(); + } + } + + Value_type* add_to_current( const Value_type& value ) + { + if( current_p_ == 0 ) + { + return add_first( value ); + } + else if( current_p_->type() == array_type ) + { + current_p_->get_array().push_back( value ); + + return ¤t_p_->get_array().back(); + } + + assert( current_p_->type() == obj_type ); + + return &Config_type::add( current_p_->get_obj(), name_, value ); + } + + Value_type& value_; // this is the object or array that is being created + Value_type* current_p_; // the child object or array that is currently being constructed + + std::vector< Value_type* > stack_; // previous child objects and arrays + + String_type name_; // of current name/value pair + }; + + template< typename Iter_type > + void throw_error( spirit_namespace::position_iterator< Iter_type > i, const std::string& reason ) + { + throw Error_position( i.get_position().line, i.get_position().column, reason ); + } + + template< typename Iter_type > + void throw_error( Iter_type i, const std::string& reason ) + { + throw reason; + } + + // the spirit grammer + // + template< class Value_type, class Iter_type > + class Json_grammer : public spirit_namespace::grammar< Json_grammer< Value_type, Iter_type > > + { + public: + + typedef Semantic_actions< Value_type, Iter_type > Semantic_actions_t; + + Json_grammer( Semantic_actions_t& semantic_actions ) + : actions_( semantic_actions ) + { + } + + static void throw_not_value( Iter_type begin, Iter_type end ) + { + throw_error( begin, "not a value" ); + } + + static void throw_not_array( Iter_type begin, Iter_type end ) + { + throw_error( begin, "not an array" ); + } + + static void throw_not_object( Iter_type begin, Iter_type end ) + { + throw_error( begin, "not an object" ); + } + + static void throw_not_pair( Iter_type begin, Iter_type end ) + { + throw_error( begin, "not a pair" ); + } + + static void throw_not_colon( Iter_type begin, Iter_type end ) + { + throw_error( begin, "no colon in pair" ); + } + + static void throw_not_string( Iter_type begin, Iter_type end ) + { + throw_error( begin, "not a string" ); + } + + template< typename ScannerT > + class definition + { + public: + + definition( const Json_grammer& self ) + { + using namespace spirit_namespace; + + typedef typename Value_type::String_type::value_type Char_type; + + // first we convert the semantic action class methods to functors with the + // parameter signature expected by spirit + + typedef boost::function< void( Char_type ) > Char_action; + typedef boost::function< void( Iter_type, Iter_type ) > Str_action; + typedef boost::function< void( double ) > Real_action; + typedef boost::function< void( boost::int64_t ) > Int_action; + typedef boost::function< void( boost::uint64_t ) > Uint64_action; + + Char_action begin_obj ( boost::bind( &Semantic_actions_t::begin_obj, &self.actions_, _1 ) ); + Char_action end_obj ( boost::bind( &Semantic_actions_t::end_obj, &self.actions_, _1 ) ); + Char_action begin_array( boost::bind( &Semantic_actions_t::begin_array, &self.actions_, _1 ) ); + Char_action end_array ( boost::bind( &Semantic_actions_t::end_array, &self.actions_, _1 ) ); + Str_action new_name ( boost::bind( &Semantic_actions_t::new_name, &self.actions_, _1, _2 ) ); + Str_action new_str ( boost::bind( &Semantic_actions_t::new_str, &self.actions_, _1, _2 ) ); + Str_action new_true ( boost::bind( &Semantic_actions_t::new_true, &self.actions_, _1, _2 ) ); + Str_action new_false ( boost::bind( &Semantic_actions_t::new_false, &self.actions_, _1, _2 ) ); + Str_action new_null ( boost::bind( &Semantic_actions_t::new_null, &self.actions_, _1, _2 ) ); + Real_action new_real ( boost::bind( &Semantic_actions_t::new_real, &self.actions_, _1 ) ); + Int_action new_int ( boost::bind( &Semantic_actions_t::new_int, &self.actions_, _1 ) ); + Uint64_action new_uint64 ( boost::bind( &Semantic_actions_t::new_uint64, &self.actions_, _1 ) ); + + // actual grammer + + json_ + = value_ | eps_p[ &throw_not_value ] + ; + + value_ + = string_[ new_str ] + | number_ + | object_ + | array_ + | str_p( "true" ) [ new_true ] + | str_p( "false" )[ new_false ] + | str_p( "null" ) [ new_null ] + ; + + object_ + = ch_p('{')[ begin_obj ] + >> !members_ + >> ( ch_p('}')[ end_obj ] | eps_p[ &throw_not_object ] ) + ; + + members_ + = pair_ >> *( ',' >> pair_ ) + ; + + pair_ + = string_[ new_name ] + >> ( ':' | eps_p[ &throw_not_colon ] ) + >> ( value_ | eps_p[ &throw_not_value ] ) + ; + + array_ + = ch_p('[')[ begin_array ] + >> !elements_ + >> ( ch_p(']')[ end_array ] | eps_p[ &throw_not_array ] ) + ; + + elements_ + = value_ >> *( ',' >> value_ ) + ; + + string_ + = lexeme_d // this causes white space inside a string to be retained + [ + confix_p + ( + '"', + *lex_escape_ch_p, + '"' + ) + ] + ; + + number_ + = strict_real_p[ new_real ] + | int64_p [ new_int ] + | uint64_p [ new_uint64 ] + ; + } + + spirit_namespace::rule< ScannerT > json_, object_, members_, pair_, array_, elements_, value_, string_, number_; + + const spirit_namespace::rule< ScannerT >& start() const { return json_; } + }; + + private: + + Json_grammer& operator=( const Json_grammer& ); // to prevent "assignment operator could not be generated" warning + + Semantic_actions_t& actions_; + }; + + template< class Iter_type, class Value_type > + Iter_type read_range_or_throw( Iter_type begin, Iter_type end, Value_type& value ) + { + Semantic_actions< Value_type, Iter_type > semantic_actions( value ); + + const spirit_namespace::parse_info< Iter_type > info = + spirit_namespace::parse( begin, end, + Json_grammer< Value_type, Iter_type >( semantic_actions ), + spirit_namespace::space_p ); + + if( !info.hit ) + { + assert( false ); // in theory exception should already have been thrown + throw_error( info.stop, "error" ); + } + + return info.stop; + } + + template< class Iter_type, class Value_type > + void add_posn_iter_and_read_range_or_throw( Iter_type begin, Iter_type end, Value_type& value ) + { + typedef spirit_namespace::position_iterator< Iter_type > Posn_iter_t; + + const Posn_iter_t posn_begin( begin, end ); + const Posn_iter_t posn_end( end, end ); + + read_range_or_throw( posn_begin, posn_end, value ); + } + + template< class Iter_type, class Value_type > + bool read_range( Iter_type& begin, Iter_type end, Value_type& value ) + { + try + { + begin = read_range_or_throw( begin, end, value ); + + return true; + } + catch( ... ) + { + return false; + } + } + + template< class String_type, class Value_type > + void read_string_or_throw( const String_type& s, Value_type& value ) + { + add_posn_iter_and_read_range_or_throw( s.begin(), s.end(), value ); + } + + template< class String_type, class Value_type > + bool read_string( const String_type& s, Value_type& value ) + { + typename String_type::const_iterator begin = s.begin(); + + return read_range( begin, s.end(), value ); + } + + template< class Istream_type > + struct Multi_pass_iters + { + typedef typename Istream_type::char_type Char_type; + typedef std::istream_iterator< Char_type, Char_type > istream_iter; + typedef spirit_namespace::multi_pass< istream_iter > Mp_iter; + + Multi_pass_iters( Istream_type& is ) + { + is.unsetf( std::ios::skipws ); + + begin_ = spirit_namespace::make_multi_pass( istream_iter( is ) ); + end_ = spirit_namespace::make_multi_pass( istream_iter() ); + } + + Mp_iter begin_; + Mp_iter end_; + }; + + template< class Istream_type, class Value_type > + bool read_stream( Istream_type& is, Value_type& value ) + { + Multi_pass_iters< Istream_type > mp_iters( is ); + + return read_range( mp_iters.begin_, mp_iters.end_, value ); + } + + template< class Istream_type, class Value_type > + void read_stream_or_throw( Istream_type& is, Value_type& value ) + { + const Multi_pass_iters< Istream_type > mp_iters( is ); + + add_posn_iter_and_read_range_or_throw( mp_iters.begin_, mp_iters.end_, value ); + } +} + +#endif diff --git a/src/json/json_spirit_stream_reader.h b/src/json/json_spirit_stream_reader.h new file mode 100644 index 0000000..7e59c9a --- /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 0000000..553e3b9 --- /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 0000000..44d2f06 --- /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 0000000..7e83a2a --- /dev/null +++ b/src/json/json_spirit_value.h @@ -0,0 +1,534 @@ +#ifndef JSON_SPIRIT_VALUE +#define JSON_SPIRIT_VALUE + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace json_spirit +{ + enum Value_type{ obj_type, array_type, str_type, bool_type, int_type, real_type, null_type }; + static const char* Value_type_name[]={"obj", "array", "str", "bool", "int", "real", "null"}; + + template< class Config > // Config determines whether the value uses std::string or std::wstring and + // whether JSON Objects are represented as vectors or maps + class Value_impl + { + public: + + typedef Config Config_type; + typedef typename Config::String_type String_type; + typedef typename Config::Object_type Object; + typedef typename Config::Array_type Array; + typedef typename String_type::const_pointer Const_str_ptr; // eg const char* + + Value_impl(); // creates null value + Value_impl( Const_str_ptr value ); + Value_impl( const String_type& value ); + Value_impl( const Object& value ); + Value_impl( const Array& value ); + Value_impl( bool value ); + Value_impl( int value ); + Value_impl( boost::int64_t value ); + Value_impl( boost::uint64_t value ); + Value_impl( double value ); + + Value_impl( const Value_impl& other ); + + bool operator==( const Value_impl& lhs ) const; + + Value_impl& operator=( const Value_impl& lhs ); + + Value_type type() const; + + bool is_uint64() const; + bool is_null() const; + + const String_type& get_str() const; + const Object& get_obj() const; + const Array& get_array() const; + bool get_bool() const; + int get_int() const; + boost::int64_t get_int64() const; + boost::uint64_t get_uint64() const; + double get_real() const; + + Object& get_obj(); + Array& get_array(); + + template< typename T > T get_value() const; // example usage: int i = value.get_value< int >(); + // or double d = value.get_value< double >(); + + static const Value_impl null; + + private: + + void check_type( const Value_type vtype ) const; + + typedef boost::variant< String_type, + boost::recursive_wrapper< Object >, boost::recursive_wrapper< Array >, + bool, boost::int64_t, double > Variant; + + Value_type type_; + Variant v_; + bool is_uint64_; + }; + + // vector objects + + template< class Config > + struct Pair_impl + { + typedef typename Config::String_type String_type; + typedef typename Config::Value_type Value_type; + + Pair_impl( const String_type& name, const Value_type& value ); + + bool operator==( const Pair_impl& lhs ) const; + + String_type name_; + Value_type value_; + }; + + template< class String > + struct Config_vector + { + typedef String String_type; + typedef Value_impl< Config_vector > Value_type; + typedef Pair_impl < Config_vector > Pair_type; + typedef std::vector< Value_type > Array_type; + typedef std::vector< Pair_type > Object_type; + + static Value_type& add( Object_type& obj, const String_type& name, const Value_type& value ) + { + obj.push_back( Pair_type( name , value ) ); + + return obj.back().value_; + } + + static String_type get_name( const Pair_type& pair ) + { + return pair.name_; + } + + static Value_type get_value( const Pair_type& pair ) + { + return pair.value_; + } + }; + + // typedefs for ASCII + + typedef Config_vector< std::string > Config; + + typedef Config::Value_type Value; + typedef Config::Pair_type Pair; + typedef Config::Object_type Object; + typedef Config::Array_type Array; + + // typedefs for Unicode + +#ifndef BOOST_NO_STD_WSTRING + + typedef Config_vector< std::wstring > wConfig; + + typedef wConfig::Value_type wValue; + typedef wConfig::Pair_type wPair; + typedef wConfig::Object_type wObject; + typedef wConfig::Array_type wArray; +#endif + + // map objects + + template< class String > + struct Config_map + { + typedef String String_type; + typedef Value_impl< Config_map > Value_type; + typedef std::vector< Value_type > Array_type; + typedef std::map< String_type, Value_type > Object_type; + typedef typename Object_type::value_type Pair_type; + + static Value_type& add( Object_type& obj, const String_type& name, const Value_type& value ) + { + return obj[ name ] = value; + } + + static String_type get_name( const Pair_type& pair ) + { + return pair.first; + } + + static Value_type get_value( const Pair_type& pair ) + { + return pair.second; + } + }; + + // typedefs for ASCII + + typedef Config_map< std::string > mConfig; + + typedef mConfig::Value_type mValue; + typedef mConfig::Object_type mObject; + typedef mConfig::Array_type mArray; + + // typedefs for Unicode + +#ifndef BOOST_NO_STD_WSTRING + + typedef Config_map< std::wstring > wmConfig; + + typedef wmConfig::Value_type wmValue; + typedef wmConfig::Object_type wmObject; + typedef wmConfig::Array_type wmArray; + +#endif + + /////////////////////////////////////////////////////////////////////////////////////////////// + // + // implementation + + template< class Config > + const Value_impl< Config > Value_impl< Config >::null; + + template< class Config > + Value_impl< Config >::Value_impl() + : type_( null_type ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( const Const_str_ptr value ) + : type_( str_type ) + , v_( String_type( value ) ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( const String_type& value ) + : type_( str_type ) + , v_( value ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( const Object& value ) + : type_( obj_type ) + , v_( value ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( const Array& value ) + : type_( array_type ) + , v_( value ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( bool value ) + : type_( bool_type ) + , v_( value ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( int value ) + : type_( int_type ) + , v_( static_cast< boost::int64_t >( value ) ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( boost::int64_t value ) + : type_( int_type ) + , v_( value ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( boost::uint64_t value ) + : type_( int_type ) + , v_( static_cast< boost::int64_t >( value ) ) + , is_uint64_( true ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( double value ) + : type_( real_type ) + , v_( value ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( const Value_impl< Config >& other ) + : type_( other.type() ) + , v_( other.v_ ) + , is_uint64_( other.is_uint64_ ) + { + } + + template< class Config > + Value_impl< Config >& Value_impl< Config >::operator=( const Value_impl& lhs ) + { + Value_impl tmp( lhs ); + + std::swap( type_, tmp.type_ ); + std::swap( v_, tmp.v_ ); + std::swap( is_uint64_, tmp.is_uint64_ ); + + return *this; + } + + template< class Config > + bool Value_impl< Config >::operator==( const Value_impl& lhs ) const + { + if( this == &lhs ) return true; + + if( type() != lhs.type() ) return false; + + return v_ == lhs.v_; + } + + template< class Config > + Value_type Value_impl< Config >::type() const + { + return type_; + } + + template< class Config > + bool Value_impl< Config >::is_uint64() const + { + return is_uint64_; + } + + template< class Config > + bool Value_impl< Config >::is_null() const + { + return type() == null_type; + } + + template< class Config > + void Value_impl< Config >::check_type( const Value_type vtype ) const + { + if( type() != vtype ) + { + std::ostringstream os; + + ///// Bitcoin: Tell the types by name instead of by number + os << "value is type " << Value_type_name[type()] << ", expected " << Value_type_name[vtype]; + + throw std::runtime_error( os.str() ); + } + } + + template< class Config > + const typename Config::String_type& Value_impl< Config >::get_str() const + { + check_type( str_type ); + + return *boost::get< String_type >( &v_ ); + } + + template< class Config > + const typename Value_impl< Config >::Object& Value_impl< Config >::get_obj() const + { + check_type( obj_type ); + + return *boost::get< Object >( &v_ ); + } + + template< class Config > + const typename Value_impl< Config >::Array& Value_impl< Config >::get_array() const + { + check_type( array_type ); + + return *boost::get< Array >( &v_ ); + } + + template< class Config > + bool Value_impl< Config >::get_bool() const + { + check_type( bool_type ); + + return boost::get< bool >( v_ ); + } + + template< class Config > + int Value_impl< Config >::get_int() const + { + check_type( int_type ); + + return static_cast< int >( get_int64() ); + } + + template< class Config > + boost::int64_t Value_impl< Config >::get_int64() const + { + check_type( int_type ); + + return boost::get< boost::int64_t >( v_ ); + } + + template< class Config > + boost::uint64_t Value_impl< Config >::get_uint64() const + { + check_type( int_type ); + + return static_cast< boost::uint64_t >( get_int64() ); + } + + template< class Config > + double Value_impl< Config >::get_real() const + { + if( type() == int_type ) + { + return is_uint64() ? static_cast< double >( get_uint64() ) + : static_cast< double >( get_int64() ); + } + + check_type( real_type ); + + return boost::get< double >( v_ ); + } + + template< class Config > + typename Value_impl< Config >::Object& Value_impl< Config >::get_obj() + { + check_type( obj_type ); + + return *boost::get< Object >( &v_ ); + } + + template< class Config > + typename Value_impl< Config >::Array& Value_impl< Config >::get_array() + { + check_type( array_type ); + + return *boost::get< Array >( &v_ ); + } + + template< class Config > + Pair_impl< Config >::Pair_impl( const String_type& name, const Value_type& value ) + : name_( name ) + , value_( value ) + { + } + + template< class Config > + bool Pair_impl< Config >::operator==( const Pair_impl< Config >& lhs ) const + { + if( this == &lhs ) return true; + + return ( name_ == lhs.name_ ) && ( value_ == lhs.value_ ); + } + + // converts a C string, ie. 8 bit char array, to a string object + // + template < class String_type > + String_type to_str( const char* c_str ) + { + String_type result; + + for( const char* p = c_str; *p != 0; ++p ) + { + result += *p; + } + + return result; + } + + // + + namespace internal_ + { + template< typename T > + struct Type_to_type + { + }; + + template< class Value > + int get_value( const Value& value, Type_to_type< int > ) + { + return value.get_int(); + } + + template< class Value > + boost::int64_t get_value( const Value& value, Type_to_type< boost::int64_t > ) + { + return value.get_int64(); + } + + template< class Value > + boost::uint64_t get_value( const Value& value, Type_to_type< boost::uint64_t > ) + { + return value.get_uint64(); + } + + template< class Value > + double get_value( const Value& value, Type_to_type< double > ) + { + return value.get_real(); + } + + template< class Value > + typename Value::String_type get_value( const Value& value, Type_to_type< typename Value::String_type > ) + { + return value.get_str(); + } + + template< class Value > + typename Value::Array get_value( const Value& value, Type_to_type< typename Value::Array > ) + { + return value.get_array(); + } + + template< class Value > + typename Value::Object get_value( const Value& value, Type_to_type< typename Value::Object > ) + { + return value.get_obj(); + } + + template< class Value > + bool get_value( const Value& value, Type_to_type< bool > ) + { + return value.get_bool(); + } + } + + template< class Config > + template< typename T > + T Value_impl< Config >::get_value() const + { + return internal_::get_value( *this, internal_::Type_to_type< T >() ); + } +} + +#endif diff --git a/src/json/json_spirit_writer.cpp b/src/json/json_spirit_writer.cpp new file mode 100644 index 0000000..d24a632 --- /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 0000000..52e1406 --- /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 0000000..28c49dd --- /dev/null +++ b/src/json/json_spirit_writer_template.h @@ -0,0 +1,248 @@ +#ifndef JSON_SPIRIT_WRITER_TEMPLATE +#define JSON_SPIRIT_WRITER_TEMPLATE + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#include "json_spirit_value.h" + +#include +#include +#include + +namespace json_spirit +{ + inline char to_hex_char( unsigned int c ) + { + assert( c <= 0xF ); + + const char ch = static_cast< char >( c ); + + if( ch < 10 ) return '0' + ch; + + return 'A' - 10 + ch; + } + + template< class String_type > + String_type non_printable_to_string( unsigned int c ) + { + typedef typename String_type::value_type Char_type; + + String_type result( 6, '\\' ); + + result[1] = 'u'; + + result[ 5 ] = to_hex_char( c & 0x000F ); c >>= 4; + result[ 4 ] = to_hex_char( c & 0x000F ); c >>= 4; + result[ 3 ] = to_hex_char( c & 0x000F ); c >>= 4; + result[ 2 ] = to_hex_char( c & 0x000F ); + + return result; + } + + template< typename Char_type, class String_type > + bool add_esc_char( Char_type c, String_type& s ) + { + switch( c ) + { + case '"': s += to_str< String_type >( "\\\"" ); return true; + case '\\': s += to_str< String_type >( "\\\\" ); return true; + case '\b': s += to_str< String_type >( "\\b" ); return true; + case '\f': s += to_str< String_type >( "\\f" ); return true; + case '\n': s += to_str< String_type >( "\\n" ); return true; + case '\r': s += to_str< String_type >( "\\r" ); return true; + case '\t': s += to_str< String_type >( "\\t" ); return true; + } + + return false; + } + + template< class String_type > + String_type add_esc_chars( const String_type& s ) + { + typedef typename String_type::const_iterator Iter_type; + typedef typename String_type::value_type Char_type; + + String_type result; + + const Iter_type end( s.end() ); + + for( Iter_type i = s.begin(); i != end; ++i ) + { + const Char_type c( *i ); + + if( add_esc_char( c, result ) ) continue; + + const wint_t unsigned_c( ( c >= 0 ) ? c : 256 + c ); + + if( iswprint( unsigned_c ) ) + { + result += c; + } + else + { + result += non_printable_to_string< String_type >( unsigned_c ); + } + } + + return result; + } + + // this class generates the JSON text, + // it keeps track of the indentation level etc. + // + template< class Value_type, class Ostream_type > + class Generator + { + typedef typename Value_type::Config_type Config_type; + typedef typename Config_type::String_type String_type; + typedef typename Config_type::Object_type Object_type; + typedef typename Config_type::Array_type Array_type; + typedef typename String_type::value_type Char_type; + typedef typename Object_type::value_type Obj_member_type; + + public: + + Generator( const Value_type& value, Ostream_type& os, bool pretty ) + : os_( os ) + , indentation_level_( 0 ) + , pretty_( pretty ) + { + output( value ); + } + + private: + + void output( const Value_type& value ) + { + switch( value.type() ) + { + case obj_type: output( value.get_obj() ); break; + case array_type: output( value.get_array() ); break; + case str_type: output( value.get_str() ); break; + case bool_type: output( value.get_bool() ); break; + case int_type: output_int( value ); break; + + /// Bitcoin: Added std::fixed and changed precision from 16 to 8 + case real_type: os_ << std::showpoint << std::fixed << std::setprecision(8) + << value.get_real(); break; + + case null_type: os_ << "null"; break; + default: assert( false ); + } + } + + void output( const Object_type& obj ) + { + output_array_or_obj( obj, '{', '}' ); + } + + void output( const Array_type& arr ) + { + output_array_or_obj( arr, '[', ']' ); + } + + void output( const Obj_member_type& member ) + { + output( Config_type::get_name( member ) ); space(); + os_ << ':'; space(); + output( Config_type::get_value( member ) ); + } + + void output_int( const Value_type& value ) + { + if( value.is_uint64() ) + { + os_ << value.get_uint64(); + } + else + { + os_ << value.get_int64(); + } + } + + void output( const String_type& s ) + { + os_ << '"' << add_esc_chars( s ) << '"'; + } + + void output( bool b ) + { + os_ << to_str< String_type >( b ? "true" : "false" ); + } + + template< class T > + void output_array_or_obj( const T& t, Char_type start_char, Char_type end_char ) + { + os_ << start_char; new_line(); + + ++indentation_level_; + + for( typename T::const_iterator i = t.begin(); i != t.end(); ++i ) + { + indent(); output( *i ); + + typename T::const_iterator next = i; + + if( ++next != t.end()) + { + os_ << ','; + } + + new_line(); + } + + --indentation_level_; + + indent(); os_ << end_char; + } + + void indent() + { + if( !pretty_ ) return; + + for( int i = 0; i < indentation_level_; ++i ) + { + os_ << " "; + } + } + + void space() + { + if( pretty_ ) os_ << ' '; + } + + void new_line() + { + if( pretty_ ) os_ << '\n'; + } + + Generator& operator=( const Generator& ); // to prevent "assignment operator could not be generated" warning + + Ostream_type& os_; + int indentation_level_; + bool pretty_; + }; + + template< class Value_type, class Ostream_type > + void write_stream( const Value_type& value, Ostream_type& os, bool pretty ) + { + Generator< Value_type, Ostream_type >( value, os, pretty ); + } + + template< class Value_type > + typename Value_type::String_type write_string( const Value_type& value, bool pretty ) + { + typedef typename Value_type::String_type::value_type Char_type; + + std::basic_ostringstream< Char_type > os; + + write_stream( value, os, pretty ); + + return os.str(); + } +} + +#endif diff --git a/src/keccak.c b/src/keccak.c new file mode 100644 index 0000000..cff9f87 --- /dev/null +++ b/src/keccak.c @@ -0,0 +1,1824 @@ +/* $Id: keccak.c 259 2011-07-19 22:11:27Z tp $ */ +/* + * Keccak implementation. + * + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * 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 END)============================= + * + * @author Thomas Pornin + */ + +#include +#include + +#include "sph_keccak.h" + +#ifdef __cplusplus +extern "C"{ +#endif + +/* + * Parameters: + * + * SPH_KECCAK_64 use a 64-bit type + * SPH_KECCAK_UNROLL number of loops to unroll (0/undef for full unroll) + * SPH_KECCAK_INTERLEAVE use bit-interleaving (32-bit type only) + * SPH_KECCAK_NOCOPY do not copy the state into local variables + * + * If there is no usable 64-bit type, the code automatically switches + * back to the 32-bit implementation. + * + * Some tests on an Intel Core2 Q6600 (both 64-bit and 32-bit, 32 kB L1 + * code cache), a PowerPC (G3, 32 kB L1 code cache), an ARM920T core + * (16 kB L1 code cache), and a small MIPS-compatible CPU (Broadcom BCM3302, + * 8 kB L1 code cache), seem to show that the following are optimal: + * + * -- x86, 64-bit: use the 64-bit implementation, unroll 8 rounds, + * do not copy the state; unrolling 2, 6 or all rounds also provides + * near-optimal performance. + * -- x86, 32-bit: use the 32-bit implementation, unroll 6 rounds, + * interleave, do not copy the state. Unrolling 1, 2, 4 or 8 rounds + * also provides near-optimal performance. + * -- PowerPC: use the 64-bit implementation, unroll 8 rounds, + * copy the state. Unrolling 4 or 6 rounds is near-optimal. + * -- ARM: use the 64-bit implementation, unroll 2 or 4 rounds, + * copy the state. + * -- MIPS: use the 64-bit implementation, unroll 2 rounds, copy + * the state. Unrolling only 1 round is also near-optimal. + * + * Also, interleaving does not always yield actual improvements when + * using a 32-bit implementation; in particular when the architecture + * does not offer a native rotation opcode (interleaving replaces one + * 64-bit rotation with two 32-bit rotations, which is a gain only if + * there is a native 32-bit rotation opcode and not a native 64-bit + * rotation opcode; also, interleaving implies a small overhead when + * processing input words). + * + * To sum up: + * -- when possible, use the 64-bit code + * -- exception: on 32-bit x86, use 32-bit code + * -- when using 32-bit code, use interleaving + * -- copy the state, except on x86 + * -- unroll 8 rounds on "big" machine, 2 rounds on "small" machines + */ + +#if SPH_SMALL_FOOTPRINT && !defined SPH_SMALL_FOOTPRINT_KECCAK +#define SPH_SMALL_FOOTPRINT_KECCAK 1 +#endif + +/* + * By default, we select the 64-bit implementation if a 64-bit type + * is available, unless a 32-bit x86 is detected. + */ +#if !defined SPH_KECCAK_64 && SPH_64 \ + && !(defined __i386__ || SPH_I386_GCC || SPH_I386_MSVC) +#define SPH_KECCAK_64 1 +#endif + +/* + * If using a 32-bit implementation, we prefer to interleave. + */ +#if !SPH_KECCAK_64 && !defined SPH_KECCAK_INTERLEAVE +#define SPH_KECCAK_INTERLEAVE 1 +#endif + +/* + * Unroll 8 rounds on big systems, 2 rounds on small systems. + */ +#ifndef SPH_KECCAK_UNROLL +#if SPH_SMALL_FOOTPRINT_KECCAK +#define SPH_KECCAK_UNROLL 2 +#else +#define SPH_KECCAK_UNROLL 8 +#endif +#endif + +/* + * We do not want to copy the state to local variables on x86 (32-bit + * and 64-bit alike). + */ +#ifndef SPH_KECCAK_NOCOPY +#if defined __i386__ || defined __x86_64 || SPH_I386_MSVC || SPH_I386_GCC +#define SPH_KECCAK_NOCOPY 1 +#else +#define SPH_KECCAK_NOCOPY 0 +#endif +#endif + +#ifdef _MSC_VER +#pragma warning (disable: 4146) +#endif + +#if SPH_KECCAK_64 + +static const sph_u64 RC[] = { + SPH_C64(0x0000000000000001), SPH_C64(0x0000000000008082), + SPH_C64(0x800000000000808A), SPH_C64(0x8000000080008000), + SPH_C64(0x000000000000808B), SPH_C64(0x0000000080000001), + SPH_C64(0x8000000080008081), SPH_C64(0x8000000000008009), + SPH_C64(0x000000000000008A), SPH_C64(0x0000000000000088), + SPH_C64(0x0000000080008009), SPH_C64(0x000000008000000A), + SPH_C64(0x000000008000808B), SPH_C64(0x800000000000008B), + SPH_C64(0x8000000000008089), SPH_C64(0x8000000000008003), + SPH_C64(0x8000000000008002), SPH_C64(0x8000000000000080), + SPH_C64(0x000000000000800A), SPH_C64(0x800000008000000A), + SPH_C64(0x8000000080008081), SPH_C64(0x8000000000008080), + SPH_C64(0x0000000080000001), SPH_C64(0x8000000080008008) +}; + +#if SPH_KECCAK_NOCOPY + +#define a00 (kc->u.wide[ 0]) +#define a10 (kc->u.wide[ 1]) +#define a20 (kc->u.wide[ 2]) +#define a30 (kc->u.wide[ 3]) +#define a40 (kc->u.wide[ 4]) +#define a01 (kc->u.wide[ 5]) +#define a11 (kc->u.wide[ 6]) +#define a21 (kc->u.wide[ 7]) +#define a31 (kc->u.wide[ 8]) +#define a41 (kc->u.wide[ 9]) +#define a02 (kc->u.wide[10]) +#define a12 (kc->u.wide[11]) +#define a22 (kc->u.wide[12]) +#define a32 (kc->u.wide[13]) +#define a42 (kc->u.wide[14]) +#define a03 (kc->u.wide[15]) +#define a13 (kc->u.wide[16]) +#define a23 (kc->u.wide[17]) +#define a33 (kc->u.wide[18]) +#define a43 (kc->u.wide[19]) +#define a04 (kc->u.wide[20]) +#define a14 (kc->u.wide[21]) +#define a24 (kc->u.wide[22]) +#define a34 (kc->u.wide[23]) +#define a44 (kc->u.wide[24]) + +#define DECL_STATE +#define READ_STATE(sc) +#define WRITE_STATE(sc) + +#define INPUT_BUF(size) do { \ + size_t j; \ + for (j = 0; j < (size); j += 8) { \ + kc->u.wide[j >> 3] ^= sph_dec64le_aligned(buf + j); \ + } \ + } while (0) + +#define INPUT_BUF144 INPUT_BUF(144) +#define INPUT_BUF136 INPUT_BUF(136) +#define INPUT_BUF104 INPUT_BUF(104) +#define INPUT_BUF72 INPUT_BUF(72) + +#else + +#define DECL_STATE \ + sph_u64 a00, a01, a02, a03, a04; \ + sph_u64 a10, a11, a12, a13, a14; \ + sph_u64 a20, a21, a22, a23, a24; \ + sph_u64 a30, a31, a32, a33, a34; \ + sph_u64 a40, a41, a42, a43, a44; + +#define READ_STATE(state) do { \ + a00 = (state)->u.wide[ 0]; \ + a10 = (state)->u.wide[ 1]; \ + a20 = (state)->u.wide[ 2]; \ + a30 = (state)->u.wide[ 3]; \ + a40 = (state)->u.wide[ 4]; \ + a01 = (state)->u.wide[ 5]; \ + a11 = (state)->u.wide[ 6]; \ + a21 = (state)->u.wide[ 7]; \ + a31 = (state)->u.wide[ 8]; \ + a41 = (state)->u.wide[ 9]; \ + a02 = (state)->u.wide[10]; \ + a12 = (state)->u.wide[11]; \ + a22 = (state)->u.wide[12]; \ + a32 = (state)->u.wide[13]; \ + a42 = (state)->u.wide[14]; \ + a03 = (state)->u.wide[15]; \ + a13 = (state)->u.wide[16]; \ + a23 = (state)->u.wide[17]; \ + a33 = (state)->u.wide[18]; \ + a43 = (state)->u.wide[19]; \ + a04 = (state)->u.wide[20]; \ + a14 = (state)->u.wide[21]; \ + a24 = (state)->u.wide[22]; \ + a34 = (state)->u.wide[23]; \ + a44 = (state)->u.wide[24]; \ + } while (0) + +#define WRITE_STATE(state) do { \ + (state)->u.wide[ 0] = a00; \ + (state)->u.wide[ 1] = a10; \ + (state)->u.wide[ 2] = a20; \ + (state)->u.wide[ 3] = a30; \ + (state)->u.wide[ 4] = a40; \ + (state)->u.wide[ 5] = a01; \ + (state)->u.wide[ 6] = a11; \ + (state)->u.wide[ 7] = a21; \ + (state)->u.wide[ 8] = a31; \ + (state)->u.wide[ 9] = a41; \ + (state)->u.wide[10] = a02; \ + (state)->u.wide[11] = a12; \ + (state)->u.wide[12] = a22; \ + (state)->u.wide[13] = a32; \ + (state)->u.wide[14] = a42; \ + (state)->u.wide[15] = a03; \ + (state)->u.wide[16] = a13; \ + (state)->u.wide[17] = a23; \ + (state)->u.wide[18] = a33; \ + (state)->u.wide[19] = a43; \ + (state)->u.wide[20] = a04; \ + (state)->u.wide[21] = a14; \ + (state)->u.wide[22] = a24; \ + (state)->u.wide[23] = a34; \ + (state)->u.wide[24] = a44; \ + } while (0) + +#define INPUT_BUF144 do { \ + a00 ^= sph_dec64le_aligned(buf + 0); \ + a10 ^= sph_dec64le_aligned(buf + 8); \ + a20 ^= sph_dec64le_aligned(buf + 16); \ + a30 ^= sph_dec64le_aligned(buf + 24); \ + a40 ^= sph_dec64le_aligned(buf + 32); \ + a01 ^= sph_dec64le_aligned(buf + 40); \ + a11 ^= sph_dec64le_aligned(buf + 48); \ + a21 ^= sph_dec64le_aligned(buf + 56); \ + a31 ^= sph_dec64le_aligned(buf + 64); \ + a41 ^= sph_dec64le_aligned(buf + 72); \ + a02 ^= sph_dec64le_aligned(buf + 80); \ + a12 ^= sph_dec64le_aligned(buf + 88); \ + a22 ^= sph_dec64le_aligned(buf + 96); \ + a32 ^= sph_dec64le_aligned(buf + 104); \ + a42 ^= sph_dec64le_aligned(buf + 112); \ + a03 ^= sph_dec64le_aligned(buf + 120); \ + a13 ^= sph_dec64le_aligned(buf + 128); \ + a23 ^= sph_dec64le_aligned(buf + 136); \ + } while (0) + +#define INPUT_BUF136 do { \ + a00 ^= sph_dec64le_aligned(buf + 0); \ + a10 ^= sph_dec64le_aligned(buf + 8); \ + a20 ^= sph_dec64le_aligned(buf + 16); \ + a30 ^= sph_dec64le_aligned(buf + 24); \ + a40 ^= sph_dec64le_aligned(buf + 32); \ + a01 ^= sph_dec64le_aligned(buf + 40); \ + a11 ^= sph_dec64le_aligned(buf + 48); \ + a21 ^= sph_dec64le_aligned(buf + 56); \ + a31 ^= sph_dec64le_aligned(buf + 64); \ + a41 ^= sph_dec64le_aligned(buf + 72); \ + a02 ^= sph_dec64le_aligned(buf + 80); \ + a12 ^= sph_dec64le_aligned(buf + 88); \ + a22 ^= sph_dec64le_aligned(buf + 96); \ + a32 ^= sph_dec64le_aligned(buf + 104); \ + a42 ^= sph_dec64le_aligned(buf + 112); \ + a03 ^= sph_dec64le_aligned(buf + 120); \ + a13 ^= sph_dec64le_aligned(buf + 128); \ + } while (0) + +#define INPUT_BUF104 do { \ + a00 ^= sph_dec64le_aligned(buf + 0); \ + a10 ^= sph_dec64le_aligned(buf + 8); \ + a20 ^= sph_dec64le_aligned(buf + 16); \ + a30 ^= sph_dec64le_aligned(buf + 24); \ + a40 ^= sph_dec64le_aligned(buf + 32); \ + a01 ^= sph_dec64le_aligned(buf + 40); \ + a11 ^= sph_dec64le_aligned(buf + 48); \ + a21 ^= sph_dec64le_aligned(buf + 56); \ + a31 ^= sph_dec64le_aligned(buf + 64); \ + a41 ^= sph_dec64le_aligned(buf + 72); \ + a02 ^= sph_dec64le_aligned(buf + 80); \ + a12 ^= sph_dec64le_aligned(buf + 88); \ + a22 ^= sph_dec64le_aligned(buf + 96); \ + } while (0) + +#define INPUT_BUF72 do { \ + a00 ^= sph_dec64le_aligned(buf + 0); \ + a10 ^= sph_dec64le_aligned(buf + 8); \ + a20 ^= sph_dec64le_aligned(buf + 16); \ + a30 ^= sph_dec64le_aligned(buf + 24); \ + a40 ^= sph_dec64le_aligned(buf + 32); \ + a01 ^= sph_dec64le_aligned(buf + 40); \ + a11 ^= sph_dec64le_aligned(buf + 48); \ + a21 ^= sph_dec64le_aligned(buf + 56); \ + a31 ^= sph_dec64le_aligned(buf + 64); \ + } while (0) + +#define INPUT_BUF(lim) do { \ + a00 ^= sph_dec64le_aligned(buf + 0); \ + a10 ^= sph_dec64le_aligned(buf + 8); \ + a20 ^= sph_dec64le_aligned(buf + 16); \ + a30 ^= sph_dec64le_aligned(buf + 24); \ + a40 ^= sph_dec64le_aligned(buf + 32); \ + a01 ^= sph_dec64le_aligned(buf + 40); \ + a11 ^= sph_dec64le_aligned(buf + 48); \ + a21 ^= sph_dec64le_aligned(buf + 56); \ + a31 ^= sph_dec64le_aligned(buf + 64); \ + if ((lim) == 72) \ + break; \ + a41 ^= sph_dec64le_aligned(buf + 72); \ + a02 ^= sph_dec64le_aligned(buf + 80); \ + a12 ^= sph_dec64le_aligned(buf + 88); \ + a22 ^= sph_dec64le_aligned(buf + 96); \ + if ((lim) == 104) \ + break; \ + a32 ^= sph_dec64le_aligned(buf + 104); \ + a42 ^= sph_dec64le_aligned(buf + 112); \ + a03 ^= sph_dec64le_aligned(buf + 120); \ + a13 ^= sph_dec64le_aligned(buf + 128); \ + if ((lim) == 136) \ + break; \ + a23 ^= sph_dec64le_aligned(buf + 136); \ + } while (0) + +#endif + +#define DECL64(x) sph_u64 x +#define MOV64(d, s) (d = s) +#define XOR64(d, a, b) (d = a ^ b) +#define AND64(d, a, b) (d = a & b) +#define OR64(d, a, b) (d = a | b) +#define NOT64(d, s) (d = SPH_T64(~s)) +#define ROL64(d, v, n) (d = SPH_ROTL64(v, n)) +#define XOR64_IOTA XOR64 + +#else + +static const struct { + sph_u32 high, low; +} RC[] = { +#if SPH_KECCAK_INTERLEAVE + { SPH_C32(0x00000000), SPH_C32(0x00000001) }, + { SPH_C32(0x00000089), SPH_C32(0x00000000) }, + { SPH_C32(0x8000008B), SPH_C32(0x00000000) }, + { SPH_C32(0x80008080), SPH_C32(0x00000000) }, + { SPH_C32(0x0000008B), SPH_C32(0x00000001) }, + { SPH_C32(0x00008000), SPH_C32(0x00000001) }, + { SPH_C32(0x80008088), SPH_C32(0x00000001) }, + { SPH_C32(0x80000082), SPH_C32(0x00000001) }, + { SPH_C32(0x0000000B), SPH_C32(0x00000000) }, + { SPH_C32(0x0000000A), SPH_C32(0x00000000) }, + { SPH_C32(0x00008082), SPH_C32(0x00000001) }, + { SPH_C32(0x00008003), SPH_C32(0x00000000) }, + { SPH_C32(0x0000808B), SPH_C32(0x00000001) }, + { SPH_C32(0x8000000B), SPH_C32(0x00000001) }, + { SPH_C32(0x8000008A), SPH_C32(0x00000001) }, + { SPH_C32(0x80000081), SPH_C32(0x00000001) }, + { SPH_C32(0x80000081), SPH_C32(0x00000000) }, + { SPH_C32(0x80000008), SPH_C32(0x00000000) }, + { SPH_C32(0x00000083), SPH_C32(0x00000000) }, + { SPH_C32(0x80008003), SPH_C32(0x00000000) }, + { SPH_C32(0x80008088), SPH_C32(0x00000001) }, + { SPH_C32(0x80000088), SPH_C32(0x00000000) }, + { SPH_C32(0x00008000), SPH_C32(0x00000001) }, + { SPH_C32(0x80008082), SPH_C32(0x00000000) } +#else + { SPH_C32(0x00000000), SPH_C32(0x00000001) }, + { SPH_C32(0x00000000), SPH_C32(0x00008082) }, + { SPH_C32(0x80000000), SPH_C32(0x0000808A) }, + { SPH_C32(0x80000000), SPH_C32(0x80008000) }, + { SPH_C32(0x00000000), SPH_C32(0x0000808B) }, + { SPH_C32(0x00000000), SPH_C32(0x80000001) }, + { SPH_C32(0x80000000), SPH_C32(0x80008081) }, + { SPH_C32(0x80000000), SPH_C32(0x00008009) }, + { SPH_C32(0x00000000), SPH_C32(0x0000008A) }, + { SPH_C32(0x00000000), SPH_C32(0x00000088) }, + { SPH_C32(0x00000000), SPH_C32(0x80008009) }, + { SPH_C32(0x00000000), SPH_C32(0x8000000A) }, + { SPH_C32(0x00000000), SPH_C32(0x8000808B) }, + { SPH_C32(0x80000000), SPH_C32(0x0000008B) }, + { SPH_C32(0x80000000), SPH_C32(0x00008089) }, + { SPH_C32(0x80000000), SPH_C32(0x00008003) }, + { SPH_C32(0x80000000), SPH_C32(0x00008002) }, + { SPH_C32(0x80000000), SPH_C32(0x00000080) }, + { SPH_C32(0x00000000), SPH_C32(0x0000800A) }, + { SPH_C32(0x80000000), SPH_C32(0x8000000A) }, + { SPH_C32(0x80000000), SPH_C32(0x80008081) }, + { SPH_C32(0x80000000), SPH_C32(0x00008080) }, + { SPH_C32(0x00000000), SPH_C32(0x80000001) }, + { SPH_C32(0x80000000), SPH_C32(0x80008008) } +#endif +}; + +#if SPH_KECCAK_INTERLEAVE + +#define INTERLEAVE(xl, xh) do { \ + sph_u32 l, h, t; \ + l = (xl); h = (xh); \ + t = (l ^ (l >> 1)) & SPH_C32(0x22222222); l ^= t ^ (t << 1); \ + t = (h ^ (h >> 1)) & SPH_C32(0x22222222); h ^= t ^ (t << 1); \ + t = (l ^ (l >> 2)) & SPH_C32(0x0C0C0C0C); l ^= t ^ (t << 2); \ + t = (h ^ (h >> 2)) & SPH_C32(0x0C0C0C0C); h ^= t ^ (t << 2); \ + t = (l ^ (l >> 4)) & SPH_C32(0x00F000F0); l ^= t ^ (t << 4); \ + t = (h ^ (h >> 4)) & SPH_C32(0x00F000F0); h ^= t ^ (t << 4); \ + t = (l ^ (l >> 8)) & SPH_C32(0x0000FF00); l ^= t ^ (t << 8); \ + t = (h ^ (h >> 8)) & SPH_C32(0x0000FF00); h ^= t ^ (t << 8); \ + t = (l ^ SPH_T32(h << 16)) & SPH_C32(0xFFFF0000); \ + l ^= t; h ^= t >> 16; \ + (xl) = l; (xh) = h; \ + } while (0) + +#define UNINTERLEAVE(xl, xh) do { \ + sph_u32 l, h, t; \ + l = (xl); h = (xh); \ + t = (l ^ SPH_T32(h << 16)) & SPH_C32(0xFFFF0000); \ + l ^= t; h ^= t >> 16; \ + t = (l ^ (l >> 8)) & SPH_C32(0x0000FF00); l ^= t ^ (t << 8); \ + t = (h ^ (h >> 8)) & SPH_C32(0x0000FF00); h ^= t ^ (t << 8); \ + t = (l ^ (l >> 4)) & SPH_C32(0x00F000F0); l ^= t ^ (t << 4); \ + t = (h ^ (h >> 4)) & SPH_C32(0x00F000F0); h ^= t ^ (t << 4); \ + t = (l ^ (l >> 2)) & SPH_C32(0x0C0C0C0C); l ^= t ^ (t << 2); \ + t = (h ^ (h >> 2)) & SPH_C32(0x0C0C0C0C); h ^= t ^ (t << 2); \ + t = (l ^ (l >> 1)) & SPH_C32(0x22222222); l ^= t ^ (t << 1); \ + t = (h ^ (h >> 1)) & SPH_C32(0x22222222); h ^= t ^ (t << 1); \ + (xl) = l; (xh) = h; \ + } while (0) + +#else + +#define INTERLEAVE(l, h) +#define UNINTERLEAVE(l, h) + +#endif + +#if SPH_KECCAK_NOCOPY + +#define a00l (kc->u.narrow[2 * 0 + 0]) +#define a00h (kc->u.narrow[2 * 0 + 1]) +#define a10l (kc->u.narrow[2 * 1 + 0]) +#define a10h (kc->u.narrow[2 * 1 + 1]) +#define a20l (kc->u.narrow[2 * 2 + 0]) +#define a20h (kc->u.narrow[2 * 2 + 1]) +#define a30l (kc->u.narrow[2 * 3 + 0]) +#define a30h (kc->u.narrow[2 * 3 + 1]) +#define a40l (kc->u.narrow[2 * 4 + 0]) +#define a40h (kc->u.narrow[2 * 4 + 1]) +#define a01l (kc->u.narrow[2 * 5 + 0]) +#define a01h (kc->u.narrow[2 * 5 + 1]) +#define a11l (kc->u.narrow[2 * 6 + 0]) +#define a11h (kc->u.narrow[2 * 6 + 1]) +#define a21l (kc->u.narrow[2 * 7 + 0]) +#define a21h (kc->u.narrow[2 * 7 + 1]) +#define a31l (kc->u.narrow[2 * 8 + 0]) +#define a31h (kc->u.narrow[2 * 8 + 1]) +#define a41l (kc->u.narrow[2 * 9 + 0]) +#define a41h (kc->u.narrow[2 * 9 + 1]) +#define a02l (kc->u.narrow[2 * 10 + 0]) +#define a02h (kc->u.narrow[2 * 10 + 1]) +#define a12l (kc->u.narrow[2 * 11 + 0]) +#define a12h (kc->u.narrow[2 * 11 + 1]) +#define a22l (kc->u.narrow[2 * 12 + 0]) +#define a22h (kc->u.narrow[2 * 12 + 1]) +#define a32l (kc->u.narrow[2 * 13 + 0]) +#define a32h (kc->u.narrow[2 * 13 + 1]) +#define a42l (kc->u.narrow[2 * 14 + 0]) +#define a42h (kc->u.narrow[2 * 14 + 1]) +#define a03l (kc->u.narrow[2 * 15 + 0]) +#define a03h (kc->u.narrow[2 * 15 + 1]) +#define a13l (kc->u.narrow[2 * 16 + 0]) +#define a13h (kc->u.narrow[2 * 16 + 1]) +#define a23l (kc->u.narrow[2 * 17 + 0]) +#define a23h (kc->u.narrow[2 * 17 + 1]) +#define a33l (kc->u.narrow[2 * 18 + 0]) +#define a33h (kc->u.narrow[2 * 18 + 1]) +#define a43l (kc->u.narrow[2 * 19 + 0]) +#define a43h (kc->u.narrow[2 * 19 + 1]) +#define a04l (kc->u.narrow[2 * 20 + 0]) +#define a04h (kc->u.narrow[2 * 20 + 1]) +#define a14l (kc->u.narrow[2 * 21 + 0]) +#define a14h (kc->u.narrow[2 * 21 + 1]) +#define a24l (kc->u.narrow[2 * 22 + 0]) +#define a24h (kc->u.narrow[2 * 22 + 1]) +#define a34l (kc->u.narrow[2 * 23 + 0]) +#define a34h (kc->u.narrow[2 * 23 + 1]) +#define a44l (kc->u.narrow[2 * 24 + 0]) +#define a44h (kc->u.narrow[2 * 24 + 1]) + +#define DECL_STATE +#define READ_STATE(state) +#define WRITE_STATE(state) + +#define INPUT_BUF(size) do { \ + size_t j; \ + for (j = 0; j < (size); j += 8) { \ + sph_u32 tl, th; \ + tl = sph_dec32le_aligned(buf + j + 0); \ + th = sph_dec32le_aligned(buf + j + 4); \ + INTERLEAVE(tl, th); \ + kc->u.narrow[(j >> 2) + 0] ^= tl; \ + kc->u.narrow[(j >> 2) + 1] ^= th; \ + } \ + } while (0) + +#define INPUT_BUF144 INPUT_BUF(144) +#define INPUT_BUF136 INPUT_BUF(136) +#define INPUT_BUF104 INPUT_BUF(104) +#define INPUT_BUF72 INPUT_BUF(72) + +#else + +#define DECL_STATE \ + sph_u32 a00l, a00h, a01l, a01h, a02l, a02h, a03l, a03h, a04l, a04h; \ + sph_u32 a10l, a10h, a11l, a11h, a12l, a12h, a13l, a13h, a14l, a14h; \ + sph_u32 a20l, a20h, a21l, a21h, a22l, a22h, a23l, a23h, a24l, a24h; \ + sph_u32 a30l, a30h, a31l, a31h, a32l, a32h, a33l, a33h, a34l, a34h; \ + sph_u32 a40l, a40h, a41l, a41h, a42l, a42h, a43l, a43h, a44l, a44h; + +#define READ_STATE(state) do { \ + a00l = (state)->u.narrow[2 * 0 + 0]; \ + a00h = (state)->u.narrow[2 * 0 + 1]; \ + a10l = (state)->u.narrow[2 * 1 + 0]; \ + a10h = (state)->u.narrow[2 * 1 + 1]; \ + a20l = (state)->u.narrow[2 * 2 + 0]; \ + a20h = (state)->u.narrow[2 * 2 + 1]; \ + a30l = (state)->u.narrow[2 * 3 + 0]; \ + a30h = (state)->u.narrow[2 * 3 + 1]; \ + a40l = (state)->u.narrow[2 * 4 + 0]; \ + a40h = (state)->u.narrow[2 * 4 + 1]; \ + a01l = (state)->u.narrow[2 * 5 + 0]; \ + a01h = (state)->u.narrow[2 * 5 + 1]; \ + a11l = (state)->u.narrow[2 * 6 + 0]; \ + a11h = (state)->u.narrow[2 * 6 + 1]; \ + a21l = (state)->u.narrow[2 * 7 + 0]; \ + a21h = (state)->u.narrow[2 * 7 + 1]; \ + a31l = (state)->u.narrow[2 * 8 + 0]; \ + a31h = (state)->u.narrow[2 * 8 + 1]; \ + a41l = (state)->u.narrow[2 * 9 + 0]; \ + a41h = (state)->u.narrow[2 * 9 + 1]; \ + a02l = (state)->u.narrow[2 * 10 + 0]; \ + a02h = (state)->u.narrow[2 * 10 + 1]; \ + a12l = (state)->u.narrow[2 * 11 + 0]; \ + a12h = (state)->u.narrow[2 * 11 + 1]; \ + a22l = (state)->u.narrow[2 * 12 + 0]; \ + a22h = (state)->u.narrow[2 * 12 + 1]; \ + a32l = (state)->u.narrow[2 * 13 + 0]; \ + a32h = (state)->u.narrow[2 * 13 + 1]; \ + a42l = (state)->u.narrow[2 * 14 + 0]; \ + a42h = (state)->u.narrow[2 * 14 + 1]; \ + a03l = (state)->u.narrow[2 * 15 + 0]; \ + a03h = (state)->u.narrow[2 * 15 + 1]; \ + a13l = (state)->u.narrow[2 * 16 + 0]; \ + a13h = (state)->u.narrow[2 * 16 + 1]; \ + a23l = (state)->u.narrow[2 * 17 + 0]; \ + a23h = (state)->u.narrow[2 * 17 + 1]; \ + a33l = (state)->u.narrow[2 * 18 + 0]; \ + a33h = (state)->u.narrow[2 * 18 + 1]; \ + a43l = (state)->u.narrow[2 * 19 + 0]; \ + a43h = (state)->u.narrow[2 * 19 + 1]; \ + a04l = (state)->u.narrow[2 * 20 + 0]; \ + a04h = (state)->u.narrow[2 * 20 + 1]; \ + a14l = (state)->u.narrow[2 * 21 + 0]; \ + a14h = (state)->u.narrow[2 * 21 + 1]; \ + a24l = (state)->u.narrow[2 * 22 + 0]; \ + a24h = (state)->u.narrow[2 * 22 + 1]; \ + a34l = (state)->u.narrow[2 * 23 + 0]; \ + a34h = (state)->u.narrow[2 * 23 + 1]; \ + a44l = (state)->u.narrow[2 * 24 + 0]; \ + a44h = (state)->u.narrow[2 * 24 + 1]; \ + } while (0) + +#define WRITE_STATE(state) do { \ + (state)->u.narrow[2 * 0 + 0] = a00l; \ + (state)->u.narrow[2 * 0 + 1] = a00h; \ + (state)->u.narrow[2 * 1 + 0] = a10l; \ + (state)->u.narrow[2 * 1 + 1] = a10h; \ + (state)->u.narrow[2 * 2 + 0] = a20l; \ + (state)->u.narrow[2 * 2 + 1] = a20h; \ + (state)->u.narrow[2 * 3 + 0] = a30l; \ + (state)->u.narrow[2 * 3 + 1] = a30h; \ + (state)->u.narrow[2 * 4 + 0] = a40l; \ + (state)->u.narrow[2 * 4 + 1] = a40h; \ + (state)->u.narrow[2 * 5 + 0] = a01l; \ + (state)->u.narrow[2 * 5 + 1] = a01h; \ + (state)->u.narrow[2 * 6 + 0] = a11l; \ + (state)->u.narrow[2 * 6 + 1] = a11h; \ + (state)->u.narrow[2 * 7 + 0] = a21l; \ + (state)->u.narrow[2 * 7 + 1] = a21h; \ + (state)->u.narrow[2 * 8 + 0] = a31l; \ + (state)->u.narrow[2 * 8 + 1] = a31h; \ + (state)->u.narrow[2 * 9 + 0] = a41l; \ + (state)->u.narrow[2 * 9 + 1] = a41h; \ + (state)->u.narrow[2 * 10 + 0] = a02l; \ + (state)->u.narrow[2 * 10 + 1] = a02h; \ + (state)->u.narrow[2 * 11 + 0] = a12l; \ + (state)->u.narrow[2 * 11 + 1] = a12h; \ + (state)->u.narrow[2 * 12 + 0] = a22l; \ + (state)->u.narrow[2 * 12 + 1] = a22h; \ + (state)->u.narrow[2 * 13 + 0] = a32l; \ + (state)->u.narrow[2 * 13 + 1] = a32h; \ + (state)->u.narrow[2 * 14 + 0] = a42l; \ + (state)->u.narrow[2 * 14 + 1] = a42h; \ + (state)->u.narrow[2 * 15 + 0] = a03l; \ + (state)->u.narrow[2 * 15 + 1] = a03h; \ + (state)->u.narrow[2 * 16 + 0] = a13l; \ + (state)->u.narrow[2 * 16 + 1] = a13h; \ + (state)->u.narrow[2 * 17 + 0] = a23l; \ + (state)->u.narrow[2 * 17 + 1] = a23h; \ + (state)->u.narrow[2 * 18 + 0] = a33l; \ + (state)->u.narrow[2 * 18 + 1] = a33h; \ + (state)->u.narrow[2 * 19 + 0] = a43l; \ + (state)->u.narrow[2 * 19 + 1] = a43h; \ + (state)->u.narrow[2 * 20 + 0] = a04l; \ + (state)->u.narrow[2 * 20 + 1] = a04h; \ + (state)->u.narrow[2 * 21 + 0] = a14l; \ + (state)->u.narrow[2 * 21 + 1] = a14h; \ + (state)->u.narrow[2 * 22 + 0] = a24l; \ + (state)->u.narrow[2 * 22 + 1] = a24h; \ + (state)->u.narrow[2 * 23 + 0] = a34l; \ + (state)->u.narrow[2 * 23 + 1] = a34h; \ + (state)->u.narrow[2 * 24 + 0] = a44l; \ + (state)->u.narrow[2 * 24 + 1] = a44h; \ + } while (0) + +#define READ64(d, off) do { \ + sph_u32 tl, th; \ + tl = sph_dec32le_aligned(buf + (off)); \ + th = sph_dec32le_aligned(buf + (off) + 4); \ + INTERLEAVE(tl, th); \ + d ## l ^= tl; \ + d ## h ^= th; \ + } while (0) + +#define INPUT_BUF144 do { \ + READ64(a00, 0); \ + READ64(a10, 8); \ + READ64(a20, 16); \ + READ64(a30, 24); \ + READ64(a40, 32); \ + READ64(a01, 40); \ + READ64(a11, 48); \ + READ64(a21, 56); \ + READ64(a31, 64); \ + READ64(a41, 72); \ + READ64(a02, 80); \ + READ64(a12, 88); \ + READ64(a22, 96); \ + READ64(a32, 104); \ + READ64(a42, 112); \ + READ64(a03, 120); \ + READ64(a13, 128); \ + READ64(a23, 136); \ + } while (0) + +#define INPUT_BUF136 do { \ + READ64(a00, 0); \ + READ64(a10, 8); \ + READ64(a20, 16); \ + READ64(a30, 24); \ + READ64(a40, 32); \ + READ64(a01, 40); \ + READ64(a11, 48); \ + READ64(a21, 56); \ + READ64(a31, 64); \ + READ64(a41, 72); \ + READ64(a02, 80); \ + READ64(a12, 88); \ + READ64(a22, 96); \ + READ64(a32, 104); \ + READ64(a42, 112); \ + READ64(a03, 120); \ + READ64(a13, 128); \ + } while (0) + +#define INPUT_BUF104 do { \ + READ64(a00, 0); \ + READ64(a10, 8); \ + READ64(a20, 16); \ + READ64(a30, 24); \ + READ64(a40, 32); \ + READ64(a01, 40); \ + READ64(a11, 48); \ + READ64(a21, 56); \ + READ64(a31, 64); \ + READ64(a41, 72); \ + READ64(a02, 80); \ + READ64(a12, 88); \ + READ64(a22, 96); \ + } while (0) + +#define INPUT_BUF72 do { \ + READ64(a00, 0); \ + READ64(a10, 8); \ + READ64(a20, 16); \ + READ64(a30, 24); \ + READ64(a40, 32); \ + READ64(a01, 40); \ + READ64(a11, 48); \ + READ64(a21, 56); \ + READ64(a31, 64); \ + } while (0) + +#define INPUT_BUF(lim) do { \ + READ64(a00, 0); \ + READ64(a10, 8); \ + READ64(a20, 16); \ + READ64(a30, 24); \ + READ64(a40, 32); \ + READ64(a01, 40); \ + READ64(a11, 48); \ + READ64(a21, 56); \ + READ64(a31, 64); \ + if ((lim) == 72) \ + break; \ + READ64(a41, 72); \ + READ64(a02, 80); \ + READ64(a12, 88); \ + READ64(a22, 96); \ + if ((lim) == 104) \ + break; \ + READ64(a32, 104); \ + READ64(a42, 112); \ + READ64(a03, 120); \ + READ64(a13, 128); \ + if ((lim) == 136) \ + break; \ + READ64(a23, 136); \ + } while (0) + +#endif + +#define DECL64(x) sph_u64 x ## l, x ## h +#define MOV64(d, s) (d ## l = s ## l, d ## h = s ## h) +#define XOR64(d, a, b) (d ## l = a ## l ^ b ## l, d ## h = a ## h ^ b ## h) +#define AND64(d, a, b) (d ## l = a ## l & b ## l, d ## h = a ## h & b ## h) +#define OR64(d, a, b) (d ## l = a ## l | b ## l, d ## h = a ## h | b ## h) +#define NOT64(d, s) (d ## l = SPH_T32(~s ## l), d ## h = SPH_T32(~s ## h)) +#define ROL64(d, v, n) ROL64_ ## n(d, v) + +#if SPH_KECCAK_INTERLEAVE + +#define ROL64_odd1(d, v) do { \ + sph_u32 tmp; \ + tmp = v ## l; \ + d ## l = SPH_T32(v ## h << 1) | (v ## h >> 31); \ + d ## h = tmp; \ + } while (0) + +#define ROL64_odd63(d, v) do { \ + sph_u32 tmp; \ + tmp = SPH_T32(v ## l << 31) | (v ## l >> 1); \ + d ## l = v ## h; \ + d ## h = tmp; \ + } while (0) + +#define ROL64_odd(d, v, n) do { \ + sph_u32 tmp; \ + tmp = SPH_T32(v ## l << (n - 1)) | (v ## l >> (33 - n)); \ + d ## l = SPH_T32(v ## h << n) | (v ## h >> (32 - n)); \ + d ## h = tmp; \ + } while (0) + +#define ROL64_even(d, v, n) do { \ + d ## l = SPH_T32(v ## l << n) | (v ## l >> (32 - n)); \ + d ## h = SPH_T32(v ## h << n) | (v ## h >> (32 - n)); \ + } while (0) + +#define ROL64_0(d, v) +#define ROL64_1(d, v) ROL64_odd1(d, v) +#define ROL64_2(d, v) ROL64_even(d, v, 1) +#define ROL64_3(d, v) ROL64_odd( d, v, 2) +#define ROL64_4(d, v) ROL64_even(d, v, 2) +#define ROL64_5(d, v) ROL64_odd( d, v, 3) +#define ROL64_6(d, v) ROL64_even(d, v, 3) +#define ROL64_7(d, v) ROL64_odd( d, v, 4) +#define ROL64_8(d, v) ROL64_even(d, v, 4) +#define ROL64_9(d, v) ROL64_odd( d, v, 5) +#define ROL64_10(d, v) ROL64_even(d, v, 5) +#define ROL64_11(d, v) ROL64_odd( d, v, 6) +#define ROL64_12(d, v) ROL64_even(d, v, 6) +#define ROL64_13(d, v) ROL64_odd( d, v, 7) +#define ROL64_14(d, v) ROL64_even(d, v, 7) +#define ROL64_15(d, v) ROL64_odd( d, v, 8) +#define ROL64_16(d, v) ROL64_even(d, v, 8) +#define ROL64_17(d, v) ROL64_odd( d, v, 9) +#define ROL64_18(d, v) ROL64_even(d, v, 9) +#define ROL64_19(d, v) ROL64_odd( d, v, 10) +#define ROL64_20(d, v) ROL64_even(d, v, 10) +#define ROL64_21(d, v) ROL64_odd( d, v, 11) +#define ROL64_22(d, v) ROL64_even(d, v, 11) +#define ROL64_23(d, v) ROL64_odd( d, v, 12) +#define ROL64_24(d, v) ROL64_even(d, v, 12) +#define ROL64_25(d, v) ROL64_odd( d, v, 13) +#define ROL64_26(d, v) ROL64_even(d, v, 13) +#define ROL64_27(d, v) ROL64_odd( d, v, 14) +#define ROL64_28(d, v) ROL64_even(d, v, 14) +#define ROL64_29(d, v) ROL64_odd( d, v, 15) +#define ROL64_30(d, v) ROL64_even(d, v, 15) +#define ROL64_31(d, v) ROL64_odd( d, v, 16) +#define ROL64_32(d, v) ROL64_even(d, v, 16) +#define ROL64_33(d, v) ROL64_odd( d, v, 17) +#define ROL64_34(d, v) ROL64_even(d, v, 17) +#define ROL64_35(d, v) ROL64_odd( d, v, 18) +#define ROL64_36(d, v) ROL64_even(d, v, 18) +#define ROL64_37(d, v) ROL64_odd( d, v, 19) +#define ROL64_38(d, v) ROL64_even(d, v, 19) +#define ROL64_39(d, v) ROL64_odd( d, v, 20) +#define ROL64_40(d, v) ROL64_even(d, v, 20) +#define ROL64_41(d, v) ROL64_odd( d, v, 21) +#define ROL64_42(d, v) ROL64_even(d, v, 21) +#define ROL64_43(d, v) ROL64_odd( d, v, 22) +#define ROL64_44(d, v) ROL64_even(d, v, 22) +#define ROL64_45(d, v) ROL64_odd( d, v, 23) +#define ROL64_46(d, v) ROL64_even(d, v, 23) +#define ROL64_47(d, v) ROL64_odd( d, v, 24) +#define ROL64_48(d, v) ROL64_even(d, v, 24) +#define ROL64_49(d, v) ROL64_odd( d, v, 25) +#define ROL64_50(d, v) ROL64_even(d, v, 25) +#define ROL64_51(d, v) ROL64_odd( d, v, 26) +#define ROL64_52(d, v) ROL64_even(d, v, 26) +#define ROL64_53(d, v) ROL64_odd( d, v, 27) +#define ROL64_54(d, v) ROL64_even(d, v, 27) +#define ROL64_55(d, v) ROL64_odd( d, v, 28) +#define ROL64_56(d, v) ROL64_even(d, v, 28) +#define ROL64_57(d, v) ROL64_odd( d, v, 29) +#define ROL64_58(d, v) ROL64_even(d, v, 29) +#define ROL64_59(d, v) ROL64_odd( d, v, 30) +#define ROL64_60(d, v) ROL64_even(d, v, 30) +#define ROL64_61(d, v) ROL64_odd( d, v, 31) +#define ROL64_62(d, v) ROL64_even(d, v, 31) +#define ROL64_63(d, v) ROL64_odd63(d, v) + +#else + +#define ROL64_small(d, v, n) do { \ + sph_u32 tmp; \ + tmp = SPH_T32(v ## l << n) | (v ## h >> (32 - n)); \ + d ## h = SPH_T32(v ## h << n) | (v ## l >> (32 - n)); \ + d ## l = tmp; \ + } while (0) + +#define ROL64_0(d, v) 0 +#define ROL64_1(d, v) ROL64_small(d, v, 1) +#define ROL64_2(d, v) ROL64_small(d, v, 2) +#define ROL64_3(d, v) ROL64_small(d, v, 3) +#define ROL64_4(d, v) ROL64_small(d, v, 4) +#define ROL64_5(d, v) ROL64_small(d, v, 5) +#define ROL64_6(d, v) ROL64_small(d, v, 6) +#define ROL64_7(d, v) ROL64_small(d, v, 7) +#define ROL64_8(d, v) ROL64_small(d, v, 8) +#define ROL64_9(d, v) ROL64_small(d, v, 9) +#define ROL64_10(d, v) ROL64_small(d, v, 10) +#define ROL64_11(d, v) ROL64_small(d, v, 11) +#define ROL64_12(d, v) ROL64_small(d, v, 12) +#define ROL64_13(d, v) ROL64_small(d, v, 13) +#define ROL64_14(d, v) ROL64_small(d, v, 14) +#define ROL64_15(d, v) ROL64_small(d, v, 15) +#define ROL64_16(d, v) ROL64_small(d, v, 16) +#define ROL64_17(d, v) ROL64_small(d, v, 17) +#define ROL64_18(d, v) ROL64_small(d, v, 18) +#define ROL64_19(d, v) ROL64_small(d, v, 19) +#define ROL64_20(d, v) ROL64_small(d, v, 20) +#define ROL64_21(d, v) ROL64_small(d, v, 21) +#define ROL64_22(d, v) ROL64_small(d, v, 22) +#define ROL64_23(d, v) ROL64_small(d, v, 23) +#define ROL64_24(d, v) ROL64_small(d, v, 24) +#define ROL64_25(d, v) ROL64_small(d, v, 25) +#define ROL64_26(d, v) ROL64_small(d, v, 26) +#define ROL64_27(d, v) ROL64_small(d, v, 27) +#define ROL64_28(d, v) ROL64_small(d, v, 28) +#define ROL64_29(d, v) ROL64_small(d, v, 29) +#define ROL64_30(d, v) ROL64_small(d, v, 30) +#define ROL64_31(d, v) ROL64_small(d, v, 31) + +#define ROL64_32(d, v) do { \ + sph_u32 tmp; \ + tmp = v ## l; \ + d ## l = v ## h; \ + d ## h = tmp; \ + } while (0) + +#define ROL64_big(d, v, n) do { \ + sph_u32 trl, trh; \ + ROL64_small(tr, v, n); \ + d ## h = trl; \ + d ## l = trh; \ + } while (0) + +#define ROL64_33(d, v) ROL64_big(d, v, 1) +#define ROL64_34(d, v) ROL64_big(d, v, 2) +#define ROL64_35(d, v) ROL64_big(d, v, 3) +#define ROL64_36(d, v) ROL64_big(d, v, 4) +#define ROL64_37(d, v) ROL64_big(d, v, 5) +#define ROL64_38(d, v) ROL64_big(d, v, 6) +#define ROL64_39(d, v) ROL64_big(d, v, 7) +#define ROL64_40(d, v) ROL64_big(d, v, 8) +#define ROL64_41(d, v) ROL64_big(d, v, 9) +#define ROL64_42(d, v) ROL64_big(d, v, 10) +#define ROL64_43(d, v) ROL64_big(d, v, 11) +#define ROL64_44(d, v) ROL64_big(d, v, 12) +#define ROL64_45(d, v) ROL64_big(d, v, 13) +#define ROL64_46(d, v) ROL64_big(d, v, 14) +#define ROL64_47(d, v) ROL64_big(d, v, 15) +#define ROL64_48(d, v) ROL64_big(d, v, 16) +#define ROL64_49(d, v) ROL64_big(d, v, 17) +#define ROL64_50(d, v) ROL64_big(d, v, 18) +#define ROL64_51(d, v) ROL64_big(d, v, 19) +#define ROL64_52(d, v) ROL64_big(d, v, 20) +#define ROL64_53(d, v) ROL64_big(d, v, 21) +#define ROL64_54(d, v) ROL64_big(d, v, 22) +#define ROL64_55(d, v) ROL64_big(d, v, 23) +#define ROL64_56(d, v) ROL64_big(d, v, 24) +#define ROL64_57(d, v) ROL64_big(d, v, 25) +#define ROL64_58(d, v) ROL64_big(d, v, 26) +#define ROL64_59(d, v) ROL64_big(d, v, 27) +#define ROL64_60(d, v) ROL64_big(d, v, 28) +#define ROL64_61(d, v) ROL64_big(d, v, 29) +#define ROL64_62(d, v) ROL64_big(d, v, 30) +#define ROL64_63(d, v) ROL64_big(d, v, 31) + +#endif + +#define XOR64_IOTA(d, s, k) \ + (d ## l = s ## l ^ k.low, d ## h = s ## h ^ k.high) + +#endif + +#define TH_ELT(t, c0, c1, c2, c3, c4, d0, d1, d2, d3, d4) do { \ + DECL64(tt0); \ + DECL64(tt1); \ + DECL64(tt2); \ + DECL64(tt3); \ + XOR64(tt0, d0, d1); \ + XOR64(tt1, d2, d3); \ + XOR64(tt0, tt0, d4); \ + XOR64(tt0, tt0, tt1); \ + ROL64(tt0, tt0, 1); \ + XOR64(tt2, c0, c1); \ + XOR64(tt3, c2, c3); \ + XOR64(tt0, tt0, c4); \ + XOR64(tt2, tt2, tt3); \ + XOR64(t, tt0, tt2); \ + } while (0) + +#define THETA(b00, b01, b02, b03, b04, b10, b11, b12, b13, b14, \ + b20, b21, b22, b23, b24, b30, b31, b32, b33, b34, \ + b40, b41, b42, b43, b44) \ + do { \ + DECL64(t0); \ + DECL64(t1); \ + DECL64(t2); \ + DECL64(t3); \ + DECL64(t4); \ + TH_ELT(t0, b40, b41, b42, b43, b44, b10, b11, b12, b13, b14); \ + TH_ELT(t1, b00, b01, b02, b03, b04, b20, b21, b22, b23, b24); \ + TH_ELT(t2, b10, b11, b12, b13, b14, b30, b31, b32, b33, b34); \ + TH_ELT(t3, b20, b21, b22, b23, b24, b40, b41, b42, b43, b44); \ + TH_ELT(t4, b30, b31, b32, b33, b34, b00, b01, b02, b03, b04); \ + XOR64(b00, b00, t0); \ + XOR64(b01, b01, t0); \ + XOR64(b02, b02, t0); \ + XOR64(b03, b03, t0); \ + XOR64(b04, b04, t0); \ + XOR64(b10, b10, t1); \ + XOR64(b11, b11, t1); \ + XOR64(b12, b12, t1); \ + XOR64(b13, b13, t1); \ + XOR64(b14, b14, t1); \ + XOR64(b20, b20, t2); \ + XOR64(b21, b21, t2); \ + XOR64(b22, b22, t2); \ + XOR64(b23, b23, t2); \ + XOR64(b24, b24, t2); \ + XOR64(b30, b30, t3); \ + XOR64(b31, b31, t3); \ + XOR64(b32, b32, t3); \ + XOR64(b33, b33, t3); \ + XOR64(b34, b34, t3); \ + XOR64(b40, b40, t4); \ + XOR64(b41, b41, t4); \ + XOR64(b42, b42, t4); \ + XOR64(b43, b43, t4); \ + XOR64(b44, b44, t4); \ + } while (0) + +#define RHO(b00, b01, b02, b03, b04, b10, b11, b12, b13, b14, \ + b20, b21, b22, b23, b24, b30, b31, b32, b33, b34, \ + b40, b41, b42, b43, b44) \ + do { \ + /* ROL64(b00, b00, 0); */ \ + ROL64(b01, b01, 36); \ + ROL64(b02, b02, 3); \ + ROL64(b03, b03, 41); \ + ROL64(b04, b04, 18); \ + ROL64(b10, b10, 1); \ + ROL64(b11, b11, 44); \ + ROL64(b12, b12, 10); \ + ROL64(b13, b13, 45); \ + ROL64(b14, b14, 2); \ + ROL64(b20, b20, 62); \ + ROL64(b21, b21, 6); \ + ROL64(b22, b22, 43); \ + ROL64(b23, b23, 15); \ + ROL64(b24, b24, 61); \ + ROL64(b30, b30, 28); \ + ROL64(b31, b31, 55); \ + ROL64(b32, b32, 25); \ + ROL64(b33, b33, 21); \ + ROL64(b34, b34, 56); \ + ROL64(b40, b40, 27); \ + ROL64(b41, b41, 20); \ + ROL64(b42, b42, 39); \ + ROL64(b43, b43, 8); \ + ROL64(b44, b44, 14); \ + } while (0) + +/* + * The KHI macro integrates the "lane complement" optimization. On input, + * some words are complemented: + * a00 a01 a02 a04 a13 a20 a21 a22 a30 a33 a34 a43 + * On output, the following words are complemented: + * a04 a10 a20 a22 a23 a31 + * + * The (implicit) permutation and the theta expansion will bring back + * the input mask for the next round. + */ + +#define KHI_XO(d, a, b, c) do { \ + DECL64(kt); \ + OR64(kt, b, c); \ + XOR64(d, a, kt); \ + } while (0) + +#define KHI_XA(d, a, b, c) do { \ + DECL64(kt); \ + AND64(kt, b, c); \ + XOR64(d, a, kt); \ + } while (0) + +#define KHI(b00, b01, b02, b03, b04, b10, b11, b12, b13, b14, \ + b20, b21, b22, b23, b24, b30, b31, b32, b33, b34, \ + b40, b41, b42, b43, b44) \ + do { \ + DECL64(c0); \ + DECL64(c1); \ + DECL64(c2); \ + DECL64(c3); \ + DECL64(c4); \ + DECL64(bnn); \ + NOT64(bnn, b20); \ + KHI_XO(c0, b00, b10, b20); \ + KHI_XO(c1, b10, bnn, b30); \ + KHI_XA(c2, b20, b30, b40); \ + KHI_XO(c3, b30, b40, b00); \ + KHI_XA(c4, b40, b00, b10); \ + MOV64(b00, c0); \ + MOV64(b10, c1); \ + MOV64(b20, c2); \ + MOV64(b30, c3); \ + MOV64(b40, c4); \ + NOT64(bnn, b41); \ + KHI_XO(c0, b01, b11, b21); \ + KHI_XA(c1, b11, b21, b31); \ + KHI_XO(c2, b21, b31, bnn); \ + KHI_XO(c3, b31, b41, b01); \ + KHI_XA(c4, b41, b01, b11); \ + MOV64(b01, c0); \ + MOV64(b11, c1); \ + MOV64(b21, c2); \ + MOV64(b31, c3); \ + MOV64(b41, c4); \ + NOT64(bnn, b32); \ + KHI_XO(c0, b02, b12, b22); \ + KHI_XA(c1, b12, b22, b32); \ + KHI_XA(c2, b22, bnn, b42); \ + KHI_XO(c3, bnn, b42, b02); \ + KHI_XA(c4, b42, b02, b12); \ + MOV64(b02, c0); \ + MOV64(b12, c1); \ + MOV64(b22, c2); \ + MOV64(b32, c3); \ + MOV64(b42, c4); \ + NOT64(bnn, b33); \ + KHI_XA(c0, b03, b13, b23); \ + KHI_XO(c1, b13, b23, b33); \ + KHI_XO(c2, b23, bnn, b43); \ + KHI_XA(c3, bnn, b43, b03); \ + KHI_XO(c4, b43, b03, b13); \ + MOV64(b03, c0); \ + MOV64(b13, c1); \ + MOV64(b23, c2); \ + MOV64(b33, c3); \ + MOV64(b43, c4); \ + NOT64(bnn, b14); \ + KHI_XA(c0, b04, bnn, b24); \ + KHI_XO(c1, bnn, b24, b34); \ + KHI_XA(c2, b24, b34, b44); \ + KHI_XO(c3, b34, b44, b04); \ + KHI_XA(c4, b44, b04, b14); \ + MOV64(b04, c0); \ + MOV64(b14, c1); \ + MOV64(b24, c2); \ + MOV64(b34, c3); \ + MOV64(b44, c4); \ + } while (0) + +#define IOTA(r) XOR64_IOTA(a00, a00, r) + +#define P0 a00, a01, a02, a03, a04, a10, a11, a12, a13, a14, a20, a21, \ + a22, a23, a24, a30, a31, a32, a33, a34, a40, a41, a42, a43, a44 +#define P1 a00, a30, a10, a40, a20, a11, a41, a21, a01, a31, a22, a02, \ + a32, a12, a42, a33, a13, a43, a23, a03, a44, a24, a04, a34, a14 +#define P2 a00, a33, a11, a44, a22, a41, a24, a02, a30, a13, a32, a10, \ + a43, a21, a04, a23, a01, a34, a12, a40, a14, a42, a20, a03, a31 +#define P3 a00, a23, a41, a14, a32, a24, a42, a10, a33, a01, a43, a11, \ + a34, a02, a20, a12, a30, a03, a21, a44, a31, a04, a22, a40, a13 +#define P4 a00, a12, a24, a31, a43, a42, a04, a11, a23, a30, a34, a41, \ + a03, a10, a22, a21, a33, a40, a02, a14, a13, a20, a32, a44, a01 +#define P5 a00, a21, a42, a13, a34, a04, a20, a41, a12, a33, a03, a24, \ + a40, a11, a32, a02, a23, a44, a10, a31, a01, a22, a43, a14, a30 +#define P6 a00, a02, a04, a01, a03, a20, a22, a24, a21, a23, a40, a42, \ + a44, a41, a43, a10, a12, a14, a11, a13, a30, a32, a34, a31, a33 +#define P7 a00, a10, a20, a30, a40, a22, a32, a42, a02, a12, a44, a04, \ + a14, a24, a34, a11, a21, a31, a41, a01, a33, a43, a03, a13, a23 +#define P8 a00, a11, a22, a33, a44, a32, a43, a04, a10, a21, a14, a20, \ + a31, a42, a03, a41, a02, a13, a24, a30, a23, a34, a40, a01, a12 +#define P9 a00, a41, a32, a23, a14, a43, a34, a20, a11, a02, a31, a22, \ + a13, a04, a40, a24, a10, a01, a42, a33, a12, a03, a44, a30, a21 +#define P10 a00, a24, a43, a12, a31, a34, a03, a22, a41, a10, a13, a32, \ + a01, a20, a44, a42, a11, a30, a04, a23, a21, a40, a14, a33, a02 +#define P11 a00, a42, a34, a21, a13, a03, a40, a32, a24, a11, a01, a43, \ + a30, a22, a14, a04, a41, a33, a20, a12, a02, a44, a31, a23, a10 +#define P12 a00, a04, a03, a02, a01, a40, a44, a43, a42, a41, a30, a34, \ + a33, a32, a31, a20, a24, a23, a22, a21, a10, a14, a13, a12, a11 +#define P13 a00, a20, a40, a10, a30, a44, a14, a34, a04, a24, a33, a03, \ + a23, a43, a13, a22, a42, a12, a32, a02, a11, a31, a01, a21, a41 +#define P14 a00, a22, a44, a11, a33, a14, a31, a03, a20, a42, a23, a40, \ + a12, a34, a01, a32, a04, a21, a43, a10, a41, a13, a30, a02, a24 +#define P15 a00, a32, a14, a41, a23, a31, a13, a40, a22, a04, a12, a44, \ + a21, a03, a30, a43, a20, a02, a34, a11, a24, a01, a33, a10, a42 +#define P16 a00, a43, a31, a24, a12, a13, a01, a44, a32, a20, a21, a14, \ + a02, a40, a33, a34, a22, a10, a03, a41, a42, a30, a23, a11, a04 +#define P17 a00, a34, a13, a42, a21, a01, a30, a14, a43, a22, a02, a31, \ + a10, a44, a23, a03, a32, a11, a40, a24, a04, a33, a12, a41, a20 +#define P18 a00, a03, a01, a04, a02, a30, a33, a31, a34, a32, a10, a13, \ + a11, a14, a12, a40, a43, a41, a44, a42, a20, a23, a21, a24, a22 +#define P19 a00, a40, a30, a20, a10, a33, a23, a13, a03, a43, a11, a01, \ + a41, a31, a21, a44, a34, a24, a14, a04, a22, a12, a02, a42, a32 +#define P20 a00, a44, a33, a22, a11, a23, a12, a01, a40, a34, a41, a30, \ + a24, a13, a02, a14, a03, a42, a31, a20, a32, a21, a10, a04, a43 +#define P21 a00, a14, a23, a32, a41, a12, a21, a30, a44, a03, a24, a33, \ + a42, a01, a10, a31, a40, a04, a13, a22, a43, a02, a11, a20, a34 +#define P22 a00, a31, a12, a43, a24, a21, a02, a33, a14, a40, a42, a23, \ + a04, a30, a11, a13, a44, a20, a01, a32, a34, a10, a41, a22, a03 +#define P23 a00, a13, a21, a34, a42, a02, a10, a23, a31, a44, a04, a12, \ + a20, a33, a41, a01, a14, a22, a30, a43, a03, a11, a24, a32, a40 + +#define P1_TO_P0 do { \ + DECL64(t); \ + MOV64(t, a01); \ + MOV64(a01, a30); \ + MOV64(a30, a33); \ + MOV64(a33, a23); \ + MOV64(a23, a12); \ + MOV64(a12, a21); \ + MOV64(a21, a02); \ + MOV64(a02, a10); \ + MOV64(a10, a11); \ + MOV64(a11, a41); \ + MOV64(a41, a24); \ + MOV64(a24, a42); \ + MOV64(a42, a04); \ + MOV64(a04, a20); \ + MOV64(a20, a22); \ + MOV64(a22, a32); \ + MOV64(a32, a43); \ + MOV64(a43, a34); \ + MOV64(a34, a03); \ + MOV64(a03, a40); \ + MOV64(a40, a44); \ + MOV64(a44, a14); \ + MOV64(a14, a31); \ + MOV64(a31, a13); \ + MOV64(a13, t); \ + } while (0) + +#define P2_TO_P0 do { \ + DECL64(t); \ + MOV64(t, a01); \ + MOV64(a01, a33); \ + MOV64(a33, a12); \ + MOV64(a12, a02); \ + MOV64(a02, a11); \ + MOV64(a11, a24); \ + MOV64(a24, a04); \ + MOV64(a04, a22); \ + MOV64(a22, a43); \ + MOV64(a43, a03); \ + MOV64(a03, a44); \ + MOV64(a44, a31); \ + MOV64(a31, t); \ + MOV64(t, a10); \ + MOV64(a10, a41); \ + MOV64(a41, a42); \ + MOV64(a42, a20); \ + MOV64(a20, a32); \ + MOV64(a32, a34); \ + MOV64(a34, a40); \ + MOV64(a40, a14); \ + MOV64(a14, a13); \ + MOV64(a13, a30); \ + MOV64(a30, a23); \ + MOV64(a23, a21); \ + MOV64(a21, t); \ + } while (0) + +#define P4_TO_P0 do { \ + DECL64(t); \ + MOV64(t, a01); \ + MOV64(a01, a12); \ + MOV64(a12, a11); \ + MOV64(a11, a04); \ + MOV64(a04, a43); \ + MOV64(a43, a44); \ + MOV64(a44, t); \ + MOV64(t, a02); \ + MOV64(a02, a24); \ + MOV64(a24, a22); \ + MOV64(a22, a03); \ + MOV64(a03, a31); \ + MOV64(a31, a33); \ + MOV64(a33, t); \ + MOV64(t, a10); \ + MOV64(a10, a42); \ + MOV64(a42, a32); \ + MOV64(a32, a40); \ + MOV64(a40, a13); \ + MOV64(a13, a23); \ + MOV64(a23, t); \ + MOV64(t, a14); \ + MOV64(a14, a30); \ + MOV64(a30, a21); \ + MOV64(a21, a41); \ + MOV64(a41, a20); \ + MOV64(a20, a34); \ + MOV64(a34, t); \ + } while (0) + +#define P6_TO_P0 do { \ + DECL64(t); \ + MOV64(t, a01); \ + MOV64(a01, a02); \ + MOV64(a02, a04); \ + MOV64(a04, a03); \ + MOV64(a03, t); \ + MOV64(t, a10); \ + MOV64(a10, a20); \ + MOV64(a20, a40); \ + MOV64(a40, a30); \ + MOV64(a30, t); \ + MOV64(t, a11); \ + MOV64(a11, a22); \ + MOV64(a22, a44); \ + MOV64(a44, a33); \ + MOV64(a33, t); \ + MOV64(t, a12); \ + MOV64(a12, a24); \ + MOV64(a24, a43); \ + MOV64(a43, a31); \ + MOV64(a31, t); \ + MOV64(t, a13); \ + MOV64(a13, a21); \ + MOV64(a21, a42); \ + MOV64(a42, a34); \ + MOV64(a34, t); \ + MOV64(t, a14); \ + MOV64(a14, a23); \ + MOV64(a23, a41); \ + MOV64(a41, a32); \ + MOV64(a32, t); \ + } while (0) + +#define P8_TO_P0 do { \ + DECL64(t); \ + MOV64(t, a01); \ + MOV64(a01, a11); \ + MOV64(a11, a43); \ + MOV64(a43, t); \ + MOV64(t, a02); \ + MOV64(a02, a22); \ + MOV64(a22, a31); \ + MOV64(a31, t); \ + MOV64(t, a03); \ + MOV64(a03, a33); \ + MOV64(a33, a24); \ + MOV64(a24, t); \ + MOV64(t, a04); \ + MOV64(a04, a44); \ + MOV64(a44, a12); \ + MOV64(a12, t); \ + MOV64(t, a10); \ + MOV64(a10, a32); \ + MOV64(a32, a13); \ + MOV64(a13, t); \ + MOV64(t, a14); \ + MOV64(a14, a21); \ + MOV64(a21, a20); \ + MOV64(a20, t); \ + MOV64(t, a23); \ + MOV64(a23, a42); \ + MOV64(a42, a40); \ + MOV64(a40, t); \ + MOV64(t, a30); \ + MOV64(a30, a41); \ + MOV64(a41, a34); \ + MOV64(a34, t); \ + } while (0) + +#define P12_TO_P0 do { \ + DECL64(t); \ + MOV64(t, a01); \ + MOV64(a01, a04); \ + MOV64(a04, t); \ + MOV64(t, a02); \ + MOV64(a02, a03); \ + MOV64(a03, t); \ + MOV64(t, a10); \ + MOV64(a10, a40); \ + MOV64(a40, t); \ + MOV64(t, a11); \ + MOV64(a11, a44); \ + MOV64(a44, t); \ + MOV64(t, a12); \ + MOV64(a12, a43); \ + MOV64(a43, t); \ + MOV64(t, a13); \ + MOV64(a13, a42); \ + MOV64(a42, t); \ + MOV64(t, a14); \ + MOV64(a14, a41); \ + MOV64(a41, t); \ + MOV64(t, a20); \ + MOV64(a20, a30); \ + MOV64(a30, t); \ + MOV64(t, a21); \ + MOV64(a21, a34); \ + MOV64(a34, t); \ + MOV64(t, a22); \ + MOV64(a22, a33); \ + MOV64(a33, t); \ + MOV64(t, a23); \ + MOV64(a23, a32); \ + MOV64(a32, t); \ + MOV64(t, a24); \ + MOV64(a24, a31); \ + MOV64(a31, t); \ + } while (0) + +#define LPAR ( +#define RPAR ) + +#define KF_ELT(r, s, k) do { \ + THETA LPAR P ## r RPAR; \ + RHO LPAR P ## r RPAR; \ + KHI LPAR P ## s RPAR; \ + IOTA(k); \ + } while (0) + +#define DO(x) x + +#define KECCAK_F_1600 DO(KECCAK_F_1600_) + +#if SPH_KECCAK_UNROLL == 1 + +#define KECCAK_F_1600_ do { \ + int j; \ + for (j = 0; j < 24; j ++) { \ + KF_ELT( 0, 1, RC[j + 0]); \ + P1_TO_P0; \ + } \ + } while (0) + +#elif SPH_KECCAK_UNROLL == 2 + +#define KECCAK_F_1600_ do { \ + int j; \ + for (j = 0; j < 24; j += 2) { \ + KF_ELT( 0, 1, RC[j + 0]); \ + KF_ELT( 1, 2, RC[j + 1]); \ + P2_TO_P0; \ + } \ + } while (0) + +#elif SPH_KECCAK_UNROLL == 4 + +#define KECCAK_F_1600_ do { \ + int j; \ + for (j = 0; j < 24; j += 4) { \ + KF_ELT( 0, 1, RC[j + 0]); \ + KF_ELT( 1, 2, RC[j + 1]); \ + KF_ELT( 2, 3, RC[j + 2]); \ + KF_ELT( 3, 4, RC[j + 3]); \ + P4_TO_P0; \ + } \ + } while (0) + +#elif SPH_KECCAK_UNROLL == 6 + +#define KECCAK_F_1600_ do { \ + int j; \ + for (j = 0; j < 24; j += 6) { \ + KF_ELT( 0, 1, RC[j + 0]); \ + KF_ELT( 1, 2, RC[j + 1]); \ + KF_ELT( 2, 3, RC[j + 2]); \ + KF_ELT( 3, 4, RC[j + 3]); \ + KF_ELT( 4, 5, RC[j + 4]); \ + KF_ELT( 5, 6, RC[j + 5]); \ + P6_TO_P0; \ + } \ + } while (0) + +#elif SPH_KECCAK_UNROLL == 8 + +#define KECCAK_F_1600_ do { \ + int j; \ + for (j = 0; j < 24; j += 8) { \ + KF_ELT( 0, 1, RC[j + 0]); \ + KF_ELT( 1, 2, RC[j + 1]); \ + KF_ELT( 2, 3, RC[j + 2]); \ + KF_ELT( 3, 4, RC[j + 3]); \ + KF_ELT( 4, 5, RC[j + 4]); \ + KF_ELT( 5, 6, RC[j + 5]); \ + KF_ELT( 6, 7, RC[j + 6]); \ + KF_ELT( 7, 8, RC[j + 7]); \ + P8_TO_P0; \ + } \ + } while (0) + +#elif SPH_KECCAK_UNROLL == 12 + +#define KECCAK_F_1600_ do { \ + int j; \ + for (j = 0; j < 24; j += 12) { \ + KF_ELT( 0, 1, RC[j + 0]); \ + KF_ELT( 1, 2, RC[j + 1]); \ + KF_ELT( 2, 3, RC[j + 2]); \ + KF_ELT( 3, 4, RC[j + 3]); \ + KF_ELT( 4, 5, RC[j + 4]); \ + KF_ELT( 5, 6, RC[j + 5]); \ + KF_ELT( 6, 7, RC[j + 6]); \ + KF_ELT( 7, 8, RC[j + 7]); \ + KF_ELT( 8, 9, RC[j + 8]); \ + KF_ELT( 9, 10, RC[j + 9]); \ + KF_ELT(10, 11, RC[j + 10]); \ + KF_ELT(11, 12, RC[j + 11]); \ + P12_TO_P0; \ + } \ + } while (0) + +#elif SPH_KECCAK_UNROLL == 0 + +#define KECCAK_F_1600_ do { \ + KF_ELT( 0, 1, RC[ 0]); \ + KF_ELT( 1, 2, RC[ 1]); \ + KF_ELT( 2, 3, RC[ 2]); \ + KF_ELT( 3, 4, RC[ 3]); \ + KF_ELT( 4, 5, RC[ 4]); \ + KF_ELT( 5, 6, RC[ 5]); \ + KF_ELT( 6, 7, RC[ 6]); \ + KF_ELT( 7, 8, RC[ 7]); \ + KF_ELT( 8, 9, RC[ 8]); \ + KF_ELT( 9, 10, RC[ 9]); \ + KF_ELT(10, 11, RC[10]); \ + KF_ELT(11, 12, RC[11]); \ + KF_ELT(12, 13, RC[12]); \ + KF_ELT(13, 14, RC[13]); \ + KF_ELT(14, 15, RC[14]); \ + KF_ELT(15, 16, RC[15]); \ + KF_ELT(16, 17, RC[16]); \ + KF_ELT(17, 18, RC[17]); \ + KF_ELT(18, 19, RC[18]); \ + KF_ELT(19, 20, RC[19]); \ + KF_ELT(20, 21, RC[20]); \ + KF_ELT(21, 22, RC[21]); \ + KF_ELT(22, 23, RC[22]); \ + KF_ELT(23, 0, RC[23]); \ + } while (0) + +#else + +#error Unimplemented unroll count for Keccak. + +#endif + +static void +keccak_init(sph_keccak_context *kc, unsigned out_size) +{ + int i; + +#if SPH_KECCAK_64 + for (i = 0; i < 25; i ++) + kc->u.wide[i] = 0; + /* + * Initialization for the "lane complement". + */ + kc->u.wide[ 1] = SPH_C64(0xFFFFFFFFFFFFFFFF); + kc->u.wide[ 2] = SPH_C64(0xFFFFFFFFFFFFFFFF); + kc->u.wide[ 8] = SPH_C64(0xFFFFFFFFFFFFFFFF); + kc->u.wide[12] = SPH_C64(0xFFFFFFFFFFFFFFFF); + kc->u.wide[17] = SPH_C64(0xFFFFFFFFFFFFFFFF); + kc->u.wide[20] = SPH_C64(0xFFFFFFFFFFFFFFFF); +#else + + for (i = 0; i < 50; i ++) + kc->u.narrow[i] = 0; + /* + * Initialization for the "lane complement". + * Note: since we set to all-one full 64-bit words, + * interleaving (if applicable) is a no-op. + */ + kc->u.narrow[ 2] = SPH_C32(0xFFFFFFFF); + kc->u.narrow[ 3] = SPH_C32(0xFFFFFFFF); + kc->u.narrow[ 4] = SPH_C32(0xFFFFFFFF); + kc->u.narrow[ 5] = SPH_C32(0xFFFFFFFF); + kc->u.narrow[16] = SPH_C32(0xFFFFFFFF); + kc->u.narrow[17] = SPH_C32(0xFFFFFFFF); + kc->u.narrow[24] = SPH_C32(0xFFFFFFFF); + kc->u.narrow[25] = SPH_C32(0xFFFFFFFF); + kc->u.narrow[34] = SPH_C32(0xFFFFFFFF); + kc->u.narrow[35] = SPH_C32(0xFFFFFFFF); + kc->u.narrow[40] = SPH_C32(0xFFFFFFFF); + kc->u.narrow[41] = SPH_C32(0xFFFFFFFF); +#endif + kc->ptr = 0; + kc->lim = 200 - (out_size >> 2); +} + +static void +keccak_core(sph_keccak_context *kc, const void *data, size_t len, size_t lim) +{ + unsigned char *buf; + size_t ptr; + DECL_STATE + + buf = kc->buf; + ptr = kc->ptr; + + if (len < (lim - ptr)) { + memcpy(buf + ptr, data, len); + kc->ptr = ptr + len; + return; + } + + READ_STATE(kc); + while (len > 0) { + size_t clen; + + clen = (lim - ptr); + if (clen > len) + clen = len; + memcpy(buf + ptr, data, clen); + ptr += clen; + data = (const unsigned char *)data + clen; + len -= clen; + if (ptr == lim) { + INPUT_BUF(lim); + KECCAK_F_1600; + ptr = 0; + } + } + WRITE_STATE(kc); + kc->ptr = ptr; +} + +#if SPH_KECCAK_64 + +#define DEFCLOSE(d, lim) \ + static void keccak_close ## d( \ + sph_keccak_context *kc, unsigned ub, unsigned n, void *dst) \ + { \ + unsigned eb; \ + union { \ + unsigned char tmp[lim + 1]; \ + sph_u64 dummy; /* for alignment */ \ + } u; \ + size_t j; \ + \ + eb = (0x100 | (ub & 0xFF)) >> (8 - n); \ + if (kc->ptr == (lim - 1)) { \ + if (n == 7) { \ + u.tmp[0] = eb; \ + memset(u.tmp + 1, 0, lim - 1); \ + u.tmp[lim] = 0x80; \ + j = 1 + lim; \ + } else { \ + u.tmp[0] = eb | 0x80; \ + j = 1; \ + } \ + } else { \ + j = lim - kc->ptr; \ + u.tmp[0] = eb; \ + memset(u.tmp + 1, 0, j - 2); \ + u.tmp[j - 1] = 0x80; \ + } \ + keccak_core(kc, u.tmp, j, lim); \ + /* Finalize the "lane complement" */ \ + kc->u.wide[ 1] = ~kc->u.wide[ 1]; \ + kc->u.wide[ 2] = ~kc->u.wide[ 2]; \ + kc->u.wide[ 8] = ~kc->u.wide[ 8]; \ + kc->u.wide[12] = ~kc->u.wide[12]; \ + kc->u.wide[17] = ~kc->u.wide[17]; \ + kc->u.wide[20] = ~kc->u.wide[20]; \ + for (j = 0; j < d; j += 8) \ + sph_enc64le_aligned(u.tmp + j, kc->u.wide[j >> 3]); \ + memcpy(dst, u.tmp, d); \ + keccak_init(kc, (unsigned)d << 3); \ + } \ + +#else + +#define DEFCLOSE(d, lim) \ + static void keccak_close ## d( \ + sph_keccak_context *kc, unsigned ub, unsigned n, void *dst) \ + { \ + unsigned eb; \ + union { \ + unsigned char tmp[lim + 1]; \ + sph_u64 dummy; /* for alignment */ \ + } u; \ + size_t j; \ + \ + eb = (0x100 | (ub & 0xFF)) >> (8 - n); \ + if (kc->ptr == (lim - 1)) { \ + if (n == 7) { \ + u.tmp[0] = eb; \ + memset(u.tmp + 1, 0, lim - 1); \ + u.tmp[lim] = 0x80; \ + j = 1 + lim; \ + } else { \ + u.tmp[0] = eb | 0x80; \ + j = 1; \ + } \ + } else { \ + j = lim - kc->ptr; \ + u.tmp[0] = eb; \ + memset(u.tmp + 1, 0, j - 2); \ + u.tmp[j - 1] = 0x80; \ + } \ + keccak_core(kc, u.tmp, j, lim); \ + /* Finalize the "lane complement" */ \ + kc->u.narrow[ 2] = ~kc->u.narrow[ 2]; \ + kc->u.narrow[ 3] = ~kc->u.narrow[ 3]; \ + kc->u.narrow[ 4] = ~kc->u.narrow[ 4]; \ + kc->u.narrow[ 5] = ~kc->u.narrow[ 5]; \ + kc->u.narrow[16] = ~kc->u.narrow[16]; \ + kc->u.narrow[17] = ~kc->u.narrow[17]; \ + kc->u.narrow[24] = ~kc->u.narrow[24]; \ + kc->u.narrow[25] = ~kc->u.narrow[25]; \ + kc->u.narrow[34] = ~kc->u.narrow[34]; \ + kc->u.narrow[35] = ~kc->u.narrow[35]; \ + kc->u.narrow[40] = ~kc->u.narrow[40]; \ + kc->u.narrow[41] = ~kc->u.narrow[41]; \ + /* un-interleave */ \ + for (j = 0; j < 50; j += 2) \ + UNINTERLEAVE(kc->u.narrow[j], kc->u.narrow[j + 1]); \ + for (j = 0; j < d; j += 4) \ + sph_enc32le_aligned(u.tmp + j, kc->u.narrow[j >> 2]); \ + memcpy(dst, u.tmp, d); \ + keccak_init(kc, (unsigned)d << 3); \ + } \ + +#endif + +DEFCLOSE(28, 144) +DEFCLOSE(32, 136) +DEFCLOSE(48, 104) +DEFCLOSE(64, 72) + +/* see sph_keccak.h */ +void +sph_keccak224_init(void *cc) +{ + keccak_init(cc, 224); +} + +/* see sph_keccak.h */ +void +sph_keccak224(void *cc, const void *data, size_t len) +{ + keccak_core(cc, data, len, 144); +} + +/* see sph_keccak.h */ +void +sph_keccak224_close(void *cc, void *dst) +{ + sph_keccak224_addbits_and_close(cc, 0, 0, dst); +} + +/* see sph_keccak.h */ +void +sph_keccak224_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + keccak_close28(cc, ub, n, dst); +} + +/* see sph_keccak.h */ +void +sph_keccak256_init(void *cc) +{ + keccak_init(cc, 256); +} + +/* see sph_keccak.h */ +void +sph_keccak256(void *cc, const void *data, size_t len) +{ + keccak_core(cc, data, len, 136); +} + +/* see sph_keccak.h */ +void +sph_keccak256_close(void *cc, void *dst) +{ + sph_keccak256_addbits_and_close(cc, 0, 0, dst); +} + +/* see sph_keccak.h */ +void +sph_keccak256_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + keccak_close32(cc, ub, n, dst); +} + +/* see sph_keccak.h */ +void +sph_keccak384_init(void *cc) +{ + keccak_init(cc, 384); +} + +/* see sph_keccak.h */ +void +sph_keccak384(void *cc, const void *data, size_t len) +{ + keccak_core(cc, data, len, 104); +} + +/* see sph_keccak.h */ +void +sph_keccak384_close(void *cc, void *dst) +{ + sph_keccak384_addbits_and_close(cc, 0, 0, dst); +} + +/* see sph_keccak.h */ +void +sph_keccak384_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + keccak_close48(cc, ub, n, dst); +} + +/* see sph_keccak.h */ +void +sph_keccak512_init(void *cc) +{ + keccak_init(cc, 512); +} + +/* see sph_keccak.h */ +void +sph_keccak512(void *cc, const void *data, size_t len) +{ + keccak_core(cc, data, len, 72); +} + +/* see sph_keccak.h */ +void +sph_keccak512_close(void *cc, void *dst) +{ + sph_keccak512_addbits_and_close(cc, 0, 0, dst); +} + +/* see sph_keccak.h */ +void +sph_keccak512_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + keccak_close64(cc, ub, n, dst); +} + + +#ifdef __cplusplus +} +#endif diff --git a/src/key.cpp b/src/key.cpp new file mode 100644 index 0000000..20114e6 --- /dev/null +++ b/src/key.cpp @@ -0,0 +1,406 @@ +// 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; +} + +void CKey::SetCompressedPubKey(bool fCompressed) +{ + EC_KEY_set_conv_form(pkey, fCompressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED); + 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; +} + +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; + return (*this); +} + +CKey::~CKey() +{ + EC_KEY_free(pkey); +} + +bool CKey::IsNull() const +{ + return !fSet; +} + +bool CKey::IsCompressed() const +{ + return fCompressedPubKey; +} + +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) +{ + unsigned int nSize = ECDSA_size(pkey); + vchSig.resize(nSize); // Make sure it is big enough + if (!ECDSA_sign(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], &nSize, pkey)) + { + vchSig.clear(); + return false; + } + vchSig.resize(nSize); // Shrink to fit actual size + 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; + 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) + { + int nRecId = -1; + for (int 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) + 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; + } + return false; +} + +bool CKey::Verify(uint256 hash, const std::vector& vchSig) +{ + // -1 = error, 0 = bad sig, 1 = good + if (ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], vchSig.size(), pkey) != 1) + return false; + + return true; +} + +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 0000000..9602f72 --- /dev/null +++ b/src/key.h @@ -0,0 +1,161 @@ +// 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 // 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 Hash2(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; + +public: + void SetCompressedPubKey(bool fCompressed = true); + + 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(); +}; + +#endif diff --git a/src/keystore.cpp b/src/keystore.cpp new file mode 100644 index 0000000..e0cf805 --- /dev/null +++ b/src/keystore.cpp @@ -0,0 +1,221 @@ +// 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" + +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) +{ + { + 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 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(); + } + + 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); + 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; +} diff --git a/src/keystore.h b/src/keystore.h new file mode 100644 index 0000000..ab369bb --- /dev/null +++ b/src/keystore.h @@ -0,0 +1,184 @@ +// 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 + +class CScript; + +/** 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; + + 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; + +/** Basic key store, that keeps keys in an address->secret map */ +class CBasicKeyStore : public CKeyStore +{ +protected: + KeyMap mapKeys; + ScriptMap mapScripts; + +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; +}; + +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 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; + } + return false; + } + 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.cpp b/src/leveldb.cpp new file mode 100644 index 0000000..e66f851 --- /dev/null +++ b/src/leveldb.cpp @@ -0,0 +1,81 @@ +// 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 "leveldb.h" +#include "util.h" + +#include +#include +#include +#include + +#include + +void HandleError(const leveldb::Status &status) throw(leveldb_error) { + if (status.ok()) + return; + if (status.IsCorruption()) + throw leveldb_error("Database corrupted"); + if (status.IsIOError()) + throw leveldb_error("Database I/O error"); + if (status.IsNotFound()) + throw leveldb_error("Database entry missing"); + throw leveldb_error("Unknown database error"); +} + +static leveldb::Options GetOptions(size_t nCacheSize) { + leveldb::Options options; + options.block_cache = leveldb::NewLRUCache(nCacheSize / 2); + options.write_buffer_size = nCacheSize / 4; // up to two write buffers may be held in memory simultaneously + options.filter_policy = leveldb::NewBloomFilterPolicy(10); + options.compression = leveldb::kNoCompression; + options.max_open_files = 64; + return options; +} + +CLevelDB::CLevelDB(const boost::filesystem::path &path, size_t nCacheSize, bool fMemory, bool fWipe) { + penv = NULL; + readoptions.verify_checksums = true; + iteroptions.verify_checksums = true; + iteroptions.fill_cache = false; + syncoptions.sync = true; + options = GetOptions(nCacheSize); + options.create_if_missing = true; + if (fMemory) { + penv = leveldb::NewMemEnv(leveldb::Env::Default()); + options.env = penv; + } else { + if (fWipe) { + printf("Wiping LevelDB in %s\n", path.string().c_str()); + leveldb::DestroyDB(path.string(), options); + } + boost::filesystem::create_directory(path); + printf("Opening LevelDB in %s\n", path.string().c_str()); + } + leveldb::Status status = leveldb::DB::Open(options, path.string(), &pdb); + if (!status.ok()) + throw std::runtime_error(strprintf("CLevelDB(): error opening database environment %s", status.ToString().c_str())); + printf("Opened LevelDB successfully\n"); +} + +CLevelDB::~CLevelDB() { + delete pdb; + pdb = NULL; + delete options.filter_policy; + options.filter_policy = NULL; + delete options.block_cache; + options.block_cache = NULL; + delete penv; + options.env = NULL; +} + +bool CLevelDB::WriteBatch(CLevelDBBatch &batch, bool fSync) throw(leveldb_error) { + leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch); + if (!status.ok()) { + printf("LevelDB write failure: %s\n", status.ToString().c_str()); + HandleError(status); + return false; + } + return true; +} diff --git a/src/leveldb.h b/src/leveldb.h new file mode 100644 index 0000000..79262ed --- /dev/null +++ b/src/leveldb.h @@ -0,0 +1,153 @@ +// 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_LEVELDB_H +#define BITCOIN_LEVELDB_H + +#include "serialize.h" + +#include +#include + +#include + +class leveldb_error : public std::runtime_error +{ +public: + leveldb_error(const std::string &msg) : std::runtime_error(msg) {} +}; + +void HandleError(const leveldb::Status &status) throw(leveldb_error); + +// Batch of changes queued to be written to a CLevelDB +class CLevelDBBatch +{ + friend class CLevelDB; + +private: + leveldb::WriteBatch batch; + +public: + template void Write(const K& key, const V& value) { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(ssKey.GetSerializeSize(key)); + ssKey << key; + leveldb::Slice slKey(&ssKey[0], ssKey.size()); + + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + ssValue.reserve(ssValue.GetSerializeSize(value)); + ssValue << value; + leveldb::Slice slValue(&ssValue[0], ssValue.size()); + + batch.Put(slKey, slValue); + } + + template void Erase(const K& key) { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(ssKey.GetSerializeSize(key)); + ssKey << key; + leveldb::Slice slKey(&ssKey[0], ssKey.size()); + + batch.Delete(slKey); + } +}; + +class CLevelDB +{ +private: + // custom environment this database is using (may be NULL in case of default environment) + leveldb::Env *penv; + + // database options used + leveldb::Options options; + + // options used when reading from the database + leveldb::ReadOptions readoptions; + + // options used when iterating over values of the database + leveldb::ReadOptions iteroptions; + + // options used when writing to the database + leveldb::WriteOptions writeoptions; + + // options used when sync writing to the database + leveldb::WriteOptions syncoptions; + + // the database itself + leveldb::DB *pdb; + +public: + CLevelDB(const boost::filesystem::path &path, size_t nCacheSize, bool fMemory = false, bool fWipe = false); + ~CLevelDB(); + + template bool Read(const K& key, V& value) throw(leveldb_error) { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(ssKey.GetSerializeSize(key)); + ssKey << key; + leveldb::Slice slKey(&ssKey[0], ssKey.size()); + + std::string strValue; + leveldb::Status status = pdb->Get(readoptions, slKey, &strValue); + if (!status.ok()) { + if (status.IsNotFound()) + return false; + printf("LevelDB read failure: %s\n", status.ToString().c_str()); + HandleError(status); + } + try { + CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION); + ssValue >> value; + } catch(std::exception &e) { + return false; + } + return true; + } + + template bool Write(const K& key, const V& value, bool fSync = false) throw(leveldb_error) { + CLevelDBBatch batch; + batch.Write(key, value); + return WriteBatch(batch, fSync); + } + + template bool Exists(const K& key) throw(leveldb_error) { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(ssKey.GetSerializeSize(key)); + ssKey << key; + leveldb::Slice slKey(&ssKey[0], ssKey.size()); + + std::string strValue; + leveldb::Status status = pdb->Get(readoptions, slKey, &strValue); + if (!status.ok()) { + if (status.IsNotFound()) + return false; + printf("LevelDB read failure: %s\n", status.ToString().c_str()); + HandleError(status); + } + return true; + } + + template bool Erase(const K& key, bool fSync = false) throw(leveldb_error) { + CLevelDBBatch batch; + batch.Erase(key); + return WriteBatch(batch, fSync); + } + + bool WriteBatch(CLevelDBBatch &batch, bool fSync = false) throw(leveldb_error); + + // not available for LevelDB; provide for compatibility with BDB + bool Flush() { + return true; + } + + bool Sync() throw(leveldb_error) { + CLevelDBBatch batch; + return WriteBatch(batch, true); + } + + // not exactly clean encapsulation, but it's easiest for now + leveldb::Iterator *NewIterator() { + return pdb->NewIterator(iteroptions); + } +}; + +#endif // BITCOIN_LEVELDB_H diff --git a/src/limitedmap.h b/src/limitedmap.h new file mode 100644 index 0000000..7049d68 --- /dev/null +++ b/src/limitedmap.h @@ -0,0 +1,100 @@ +// 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_LIMITEDMAP_H +#define BITCOIN_LIMITEDMAP_H + +#include +#include + +/** STL-like map container that only keeps the N elements with the highest value. */ +template class limitedmap +{ +public: + typedef K key_type; + typedef V mapped_type; + typedef std::pair value_type; + typedef typename std::map::const_iterator const_iterator; + typedef typename std::map::size_type size_type; + +protected: + std::map map; + typedef typename std::map::iterator iterator; + std::multimap rmap; + typedef typename std::multimap::iterator rmap_iterator; + size_type nMaxSize; + +public: + limitedmap(size_type nMaxSizeIn = 0) { nMaxSize = nMaxSizeIn; } + const_iterator begin() const { return map.begin(); } + const_iterator end() const { return map.end(); } + size_type size() const { return map.size(); } + bool empty() const { return map.empty(); } + const_iterator find(const key_type& k) const { return map.find(k); } + size_type count(const key_type& k) const { return map.count(k); } + void insert(const value_type& x) + { + std::pair ret = map.insert(x); + if (ret.second) + { + if (nMaxSize && map.size() == nMaxSize) + { + map.erase(rmap.begin()->second); + rmap.erase(rmap.begin()); + } + rmap.insert(make_pair(x.second, ret.first)); + } + return; + } + void erase(const key_type& k) + { + iterator itTarget = map.find(k); + if (itTarget == map.end()) + return; + std::pair itPair = rmap.equal_range(itTarget->second); + for (rmap_iterator it = itPair.first; it != itPair.second; ++it) + if (it->second == itTarget) + { + rmap.erase(it); + map.erase(itTarget); + return; + } + // Shouldn't ever get here + assert(0); //TODO remove me + map.erase(itTarget); + } + void update(const_iterator itIn, const mapped_type& v) + { + //TODO: When we switch to C++11, use map.erase(itIn, itIn) to get the non-const iterator + iterator itTarget = map.find(itIn->first); + if (itTarget == map.end()) + return; + std::pair itPair = rmap.equal_range(itTarget->second); + for (rmap_iterator it = itPair.first; it != itPair.second; ++it) + if (it->second == itTarget) + { + rmap.erase(it); + itTarget->second = v; + rmap.insert(make_pair(v, itTarget)); + return; + } + // Shouldn't ever get here + assert(0); //TODO remove me + itTarget->second = v; + rmap.insert(make_pair(v, itTarget)); + } + size_type max_size() const { return nMaxSize; } + size_type max_size(size_type s) + { + if (s) + while (map.size() > s) + { + map.erase(rmap.begin()->second); + rmap.erase(rmap.begin()); + } + nMaxSize = s; + return nMaxSize; + } +}; + +#endif diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..f21fdaf --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,4883 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2013 The Bitcoin developers +// Copyright (c) 2013 The Sifcoin developers +// Copyright (c) 2017 The Greenchain developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#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 +#include +#include + +using namespace std; +using namespace boost; + +// +// Global state +// + +CCriticalSection cs_setpwalletRegistered; +set setpwalletRegistered; + +CCriticalSection cs_main; + +CTxMemPool mempool; +unsigned int nTransactionsUpdated = 0; + +map mapBlockIndex; +uint256 hashGenesisBlock("0x00000283a023cb26be74f92492652abbb058c990d9a9970cf2d8771749081957"); +static const unsigned int timeGenesisBlock = 1533981061; +static CBigNum bnProofOfWorkLimit(~uint256(0) >> 20); // joy joy +CBlockIndex* pindexGenesisBlock = NULL; +int nBestHeight = -1; +uint256 nBestChainWork = 0; +uint256 nBestInvalidWork = 0; +uint256 hashBestChain = 0; +CBlockIndex* pindexBest = NULL; +set setBlockIndexValid; // may contain all CBlockIndex*'s that have validness >=BLOCK_VALID_TRANSACTIONS, and must contain those who aren't failed +int64 nTimeBestReceived = 0; +int nScriptCheckThreads = 0; +bool fImporting = false; +bool fReindex = false; +bool fBenchmark = false; +bool fTxIndex = false; +unsigned int nCoinCacheSize = 5000; + +/** Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) */ +int64 CTransaction::nMinTxFee = 10; // Override with -mintxfee +/** Fees smaller than this (in satoshi) are considered zero fee (for relaying) */ +int64 CTransaction::nMinRelayTxFee = 100; + +CMedianFilter cPeerBlockCounts(8, 0); // Amount of blocks that other nodes claim to have + +map mapOrphanBlocks; +multimap mapOrphanBlocksByPrev; + +map mapOrphanTransactions; +map > mapOrphanTransactionsByPrev; + +// Constant stuff for coinbase transactions we create: +CScript COINBASE_FLAGS; + +const string strMessageMagic = "Greenchain Signed Message:\n"; + +double dHashesPerSec = 0.0; +int64 nHPSTimerStart = 0; + +// Settings +int64 nTransactionFee = 0; + + + +////////////////////////////////////////////////////////////////////////////// +// +// 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); + } +} + +// 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 uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate) +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->AddToWalletIfInvolvingMe(hash, 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 static ResendWalletTransactions() +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->ResendWalletTransactions(); +} + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// CCoinsView implementations +// + +bool CCoinsView::GetCoins(const uint256 &txid, CCoins &coins) { return false; } +bool CCoinsView::SetCoins(const uint256 &txid, const CCoins &coins) { return false; } +bool CCoinsView::HaveCoins(const uint256 &txid) { return false; } +CBlockIndex *CCoinsView::GetBestBlock() { return NULL; } +bool CCoinsView::SetBestBlock(CBlockIndex *pindex) { return false; } +bool CCoinsView::BatchWrite(const std::map &mapCoins, CBlockIndex *pindex) { return false; } +bool CCoinsView::GetStats(CCoinsStats &stats) { return false; } + + +CCoinsViewBacked::CCoinsViewBacked(CCoinsView &viewIn) : base(&viewIn) { } +bool CCoinsViewBacked::GetCoins(const uint256 &txid, CCoins &coins) { return base->GetCoins(txid, coins); } +bool CCoinsViewBacked::SetCoins(const uint256 &txid, const CCoins &coins) { return base->SetCoins(txid, coins); } +bool CCoinsViewBacked::HaveCoins(const uint256 &txid) { return base->HaveCoins(txid); } +CBlockIndex *CCoinsViewBacked::GetBestBlock() { return base->GetBestBlock(); } +bool CCoinsViewBacked::SetBestBlock(CBlockIndex *pindex) { return base->SetBestBlock(pindex); } +void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; } +bool CCoinsViewBacked::BatchWrite(const std::map &mapCoins, CBlockIndex *pindex) { return base->BatchWrite(mapCoins, pindex); } +bool CCoinsViewBacked::GetStats(CCoinsStats &stats) { return base->GetStats(stats); } + +CCoinsViewCache::CCoinsViewCache(CCoinsView &baseIn, bool fDummy) : CCoinsViewBacked(baseIn), pindexTip(NULL) { } + +bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) { + if (cacheCoins.count(txid)) { + coins = cacheCoins[txid]; + return true; + } + if (base->GetCoins(txid, coins)) { + cacheCoins[txid] = coins; + return true; + } + return false; +} + +std::map::iterator CCoinsViewCache::FetchCoins(const uint256 &txid) { + std::map::iterator it = cacheCoins.lower_bound(txid); + if (it != cacheCoins.end() && it->first == txid) + return it; + CCoins tmp; + if (!base->GetCoins(txid,tmp)) + return cacheCoins.end(); + std::map::iterator ret = cacheCoins.insert(it, std::make_pair(txid, CCoins())); + tmp.swap(ret->second); + return ret; +} + +CCoins &CCoinsViewCache::GetCoins(const uint256 &txid) { + std::map::iterator it = FetchCoins(txid); + assert(it != cacheCoins.end()); + return it->second; +} + +bool CCoinsViewCache::SetCoins(const uint256 &txid, const CCoins &coins) { + cacheCoins[txid] = coins; + return true; +} + +bool CCoinsViewCache::HaveCoins(const uint256 &txid) { + return FetchCoins(txid) != cacheCoins.end(); +} + +CBlockIndex *CCoinsViewCache::GetBestBlock() { + if (pindexTip == NULL) + pindexTip = base->GetBestBlock(); + return pindexTip; +} + +bool CCoinsViewCache::SetBestBlock(CBlockIndex *pindex) { + pindexTip = pindex; + return true; +} + +bool CCoinsViewCache::BatchWrite(const std::map &mapCoins, CBlockIndex *pindex) { + for (std::map::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++) + cacheCoins[it->first] = it->second; + pindexTip = pindex; + return true; +} + +bool CCoinsViewCache::Flush() { + bool fOk = base->BatchWrite(cacheCoins, pindexTip); + if (fOk) + cacheCoins.clear(); + return fOk; +} + +unsigned int CCoinsViewCache::GetCacheSize() { + return cacheCoins.size(); +} + +/** CCoinsView that brings transactions from a memorypool into view. + It does not check for spendings by memory pool transactions. */ +CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { } + +bool CCoinsViewMemPool::GetCoins(const uint256 &txid, CCoins &coins) { + if (base->GetCoins(txid, coins)) + return true; + if (mempool.exists(txid)) { + const CTransaction &tx = mempool.lookup(txid); + coins = CCoins(tx, MEMPOOL_HEIGHT); + return true; + } + return false; +} + +bool CCoinsViewMemPool::HaveCoins(const uint256 &txid) { + return mempool.exists(txid) || base->HaveCoins(txid); +} + +CCoinsViewCache *pcoinsTip = NULL; +CBlockTreeDB *pblocktree = NULL; + +////////////////////////////////////////////////////////////////////////////// +// +// mapOrphanTransactions +// + +bool AddOrphanTx(const CDataStream& vMsg) +{ + CTransaction tx; + CDataStream(vMsg) >> tx; + uint256 hash = tx.GetHash(); + if (mapOrphanTransactions.count(hash)) + return false; + + CDataStream* pvMsg = new CDataStream(vMsg); + + // 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: + if (pvMsg->size() > 5000) + { + printf("ignoring large orphan tx (size: %"PRIszu", hash: %s)\n", pvMsg->size(), hash.ToString().c_str()); + delete pvMsg; + return false; + } + + mapOrphanTransactions[hash] = pvMsg; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + mapOrphanTransactionsByPrev[txin.prevout.hash].insert(make_pair(hash, pvMsg)); + + printf("stored orphan tx %s (mapsz %"PRIszu")\n", hash.ToString().c_str(), + mapOrphanTransactions.size()); + return true; +} + +void static EraseOrphanTx(uint256 hash) +{ + if (!mapOrphanTransactions.count(hash)) + return; + const CDataStream* pvMsg = mapOrphanTransactions[hash]; + CTransaction tx; + CDataStream(*pvMsg) >> tx; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + mapOrphanTransactionsByPrev[txin.prevout.hash].erase(hash); + if (mapOrphanTransactionsByPrev[txin.prevout.hash].empty()) + mapOrphanTransactionsByPrev.erase(txin.prevout.hash); + } + delete pvMsg; + 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 / CTxOut +// + +bool CTxOut::IsDust() const +{ + // "Dust" is defined in terms of CTransaction::nMinRelayTxFee, + // which has units satoshis-per-kilobyte. + // If you'd pay more than 1/3 in fees + // to spend something, then we consider it dust. + // A typical txout is 33 bytes big, and will + // need a CTxIn of at least 148 bytes to spend, + // so dust is a txout less than 54 uBTC + // (5430 satoshis) with default nMinRelayTxFee + return ((nValue*1000)/(3*((int)GetSerializeSize(SER_DISK,0)+148)) < CTransaction::nMinRelayTxFee); +} + +bool CTransaction::IsStandard() const +{ + if (nVersion > CTransaction::TXCOMMENT_VERSION) + return false; + + if (!IsFinal()) + return false; + + // Disallow large transaction comments + if (strTxComment.length() > MAX_TX_COMMENT_LEN) + return false; + + // Extremely large transactions with lots of inputs can cost the network + // almost as much to process as they cost the sender in fees, because + // computing signature hashes is O(ninputs*txsize). Limiting transactions + // to MAX_STANDARD_TX_SIZE mitigates CPU exhaustion attacks. + unsigned int sz = this->GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); + if (sz >= MAX_STANDARD_TX_SIZE) + return false; + + BOOST_FOREACH(const CTxIn& txin, vin) + { + // Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG + // pay-to-script-hash, which is 3 ~80-byte signatures, 3 + // ~65-byte public keys, plus a few script ops. + if (txin.scriptSig.size() > 500) + return false; + if (!txin.scriptSig.IsPushOnly()) + return false; + } + BOOST_FOREACH(const CTxOut& txout, vout) { + if (!::IsStandard(txout.scriptPubKey)) + return false; + if (txout.IsDust()) + 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(CCoinsViewCache& 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; + 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) +{ + CBlock blockTmp; + + if (pblock == NULL) { + CCoins coins; + if (pcoinsTip->GetCoins(GetHash(), coins)) { + CBlockIndex *pindex = FindBlockByHeight(coins.nHeight); + if (pindex) { + if (!blockTmp.ReadFromDisk(pindex)) + return 0; + pblock = &blockTmp; + } + } + } + + if (pblock) { + // 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(CValidationState &state) const +{ + // Basic checks that don't depend on any context + if (vin.empty()) + return state.DoS(10, error("CTransaction::CheckTransaction() : vin empty")); + if (vout.empty()) + return state.DoS(10, error("CTransaction::CheckTransaction() : vout empty")); + // Size limits + if (::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) + return state.DoS(100, error("CTransaction::CheckTransaction() : size limits failed")); + + // Check for negative or overflow output values + int64 nValueOut = 0; + BOOST_FOREACH(const CTxOut& txout, vout) + { + if (txout.nValue < 0) + return state.DoS(100, error("CTransaction::CheckTransaction() : txout.nValue negative")); + if (txout.nValue > MAX_MONEY) + return state.DoS(100, error("CTransaction::CheckTransaction() : txout.nValue too high")); + nValueOut += txout.nValue; + if (!MoneyRange(nValueOut)) + return state.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 state.DoS(100, error("CTransaction::CheckTransaction() : duplicate inputs")); + vInOutPoints.insert(txin.prevout); + } + + if (IsCoinBase()) + { + if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100) + return state.DoS(100, error("CTransaction::CheckTransaction() : coinbase script size")); + } + else + { + BOOST_FOREACH(const CTxIn& txin, vin) + if (txin.prevout.IsNull()) + return state.DoS(10, error("CTransaction::CheckTransaction() : prevout is null")); + } + + return true; +} + +int64 CTransaction::GetMinFee(unsigned int nBlockSize, bool fAllowFree, + enum GetMinFee_mode mode) const +{ + // Base fee is either nMinTxFee or nMinRelayTxFee + int64 nBaseFee = (mode == GMF_RELAY) ? nMinRelayTxFee : nMinTxFee; + + unsigned int nBytes = ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION); + unsigned int nNewBlockSize = nBlockSize + nBytes; + int64 nMinFee = (1 + (int64)nBytes / 1000) * nBaseFee; + + if (fAllowFree) + { + if (nBlockSize == 1) + { + // Transactions under 10K are free + // (about 4500 BTC if made of 50 BTC inputs) + if (nBytes < 10000) + nMinFee = 0; + } + else + { + // Free transaction area + if (nNewBlockSize < 27000) + nMinFee = 0; + } + } + + // To limit dust spam, require base fee if any output is less than 0.01 + if (nMinFee < nBaseFee) + { + BOOST_FOREACH(const CTxOut& txout, vout) + if (txout.nValue < CENT) + nMinFee = nBaseFee; + } + + // Raise the price as the block approaches full + if (nBlockSize != 1 && nNewBlockSize >= MAX_BLOCK_SIZE_GEN/2) + { + if (nNewBlockSize >= MAX_BLOCK_SIZE_GEN) + return uint64(-1); + nMinFee *= MAX_BLOCK_SIZE_GEN / (MAX_BLOCK_SIZE_GEN - nNewBlockSize); + } + + if (!MoneyRange(nMinFee)) + nMinFee = uint64(-1); + return nMinFee; +} + +void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins) +{ + LOCK(cs); + + std::map::iterator it = mapNextTx.lower_bound(COutPoint(hashTx, 0)); + + // iterate over all COutPoints in mapNextTx whose hash equals the provided hashTx + while (it != mapNextTx.end() && it->first.hash == hashTx) { + coins.Spend(it->first.n); // and remove those outputs from coins + it++; + } +} + +bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckInputs, bool fLimitFree, + bool* pfMissingInputs) +{ + if (pfMissingInputs) + *pfMissingInputs = false; + + if (!tx.CheckTransaction(state)) + return error("CTxMemPool::accept() : CheckTransaction failed"); + + // Coinbase is only valid in a block, not as a loose transaction + if (tx.IsCoinBase()) + return state.DoS(100, error("CTxMemPool::accept() : coinbase as individual tx")); + + // To help v0.1.5 clients who would see it as a negative number + if ((int64)tx.nLockTime > std::numeric_limits::max()) + return error("CTxMemPool::accept() : not accepting nLockTime beyond 2038 yet"); + + // Rather not work on nonstandard transactions (unless -testnet) + if (!fTestNet && !tx.IsStandard()) + return error("CTxMemPool::accept() : nonstandard transaction type"); + + // is it already in the memory pool? + uint256 hash = tx.GetHash(); + { + LOCK(cs); + if (mapTx.count(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) + { + CCoinsView dummy; + CCoinsViewCache view(dummy); + + { + LOCK(cs); + CCoinsViewMemPool viewMemPool(*pcoinsTip, *this); + view.SetBackend(viewMemPool); + + // do we already have it? + if (view.HaveCoins(hash)) + return false; + + // do all inputs exist? + // Note that this does not check for the presence of actual outputs (see the next check for that), + // only helps filling in pfMissingInputs (to determine missing vs spent). + BOOST_FOREACH(const CTxIn txin, tx.vin) { + if (!view.HaveCoins(txin.prevout.hash)) { + if (pfMissingInputs) + *pfMissingInputs = true; + return false; + } + } + + // are the actual inputs available? + if (!tx.HaveInputs(view)) + return state.Invalid(error("CTxMemPool::accept() : inputs already spent")); + + // Bring the best block into scope + view.GetBestBlock(); + + // we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool + view.SetBackend(dummy); + } + + // Check for non-standard pay-to-script-hash in inputs + if (!tx.AreInputsStandard(view) && !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 nFees = tx.GetValueIn(view)-tx.GetValueOut(); + unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); + + // Don't accept it if it can't get into a block + int64 txMinFee = tx.GetMinFee(1000, true, GMF_RELAY); + if (fLimitFree && nFees < txMinFee) + return error("CTxMemPool::accept() : not enough fees %s, %"PRI64d" < %"PRI64d, + 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 (fLimitFree && nFees < CTransaction::nMinRelayTxFee) + { + static double dFreeCount; + static int64 nLastTime; + int64 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) + 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.CheckInputs(state, view, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC)) + { + return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().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()); + SyncWithWallets(hash, tx, NULL, true); + + printf("CTxMemPool::accept() : accepted %s (poolsz %"PRIszu")\n", + hash.ToString().c_str(), + mapTx.size()); + return true; +} + +bool CTransaction::AcceptToMemoryPool(CValidationState &state, bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs) +{ + try { + return mempool.accept(state, *this, fCheckInputs, fLimitFree, pfMissingInputs); + } catch(std::runtime_error &e) { + return state.Abort(_("System error: ") + e.what()); + } +} + +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(const CTransaction &tx, bool fRecursive) +{ + // Remove transaction from memory pool + { + LOCK(cs); + uint256 hash = tx.GetHash(); + if (mapTx.count(hash)) + { + if (fRecursive) { + for (unsigned int i = 0; i < tx.vout.size(); i++) { + std::map::iterator it = mapNextTx.find(COutPoint(hash, i)); + if (it != mapNextTx.end()) + remove(*it->second.ptx, true); + } + } + BOOST_FOREACH(const CTxIn& txin, tx.vin) + mapNextTx.erase(txin.prevout); + mapTx.erase(hash); + nTransactionsUpdated++; + } + } + return true; +} + +bool CTxMemPool::removeConflicts(const CTransaction &tx) +{ + // Remove transactions which depend on inputs of tx, recursively + LOCK(cs); + BOOST_FOREACH(const CTxIn &txin, tx.vin) { + std::map::iterator it = mapNextTx.find(txin.prevout); + if (it != mapNextTx.end()) { + const CTransaction &txConflict = *it->second.ptx; + if (txConflict != tx) + remove(txConflict, true); + } + } + 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()) + return 0; + return max(0, (COINBASE_MATURITY+20) - GetDepthInMainChain()); +} + + +bool CMerkleTx::AcceptToMemoryPool(bool fCheckInputs, bool fLimitFree) +{ + CValidationState state; + return CTransaction::AcceptToMemoryPool(state, fCheckInputs, fLimitFree); +} + + + +bool CWalletTx::AcceptWalletTransaction(bool fCheckInputs) +{ + { + LOCK(mempool.cs); + // Add previous supporting transactions first + BOOST_FOREACH(CMerkleTx& tx, vtxPrev) + { + if (!tx.IsCoinBase()) + { + uint256 hash = tx.GetHash(); + if (!mempool.exists(hash) && pcoinsTip->HaveCoins(hash)) + tx.AcceptToMemoryPool(fCheckInputs, false); + } + } + return AcceptToMemoryPool(fCheckInputs, false); + } + return false; +} + +int CTxIndex::GetDepthInMainChain() const +{ + // Read block header + CDiskBlockPos blockpos(pos.nFile, pos.nPos); + CBlock block; + if (!block.ReadFromDisk(blockpos)) + 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 &txOut, uint256 &hashBlock, bool fAllowSlow) +{ + CBlockIndex *pindexSlow = NULL; + { + LOCK(cs_main); + { + LOCK(mempool.cs); + if (mempool.exists(hash)) + { + txOut = mempool.lookup(hash); + return true; + } + } + + if (fTxIndex) { + CDiskTxPos postx; + if (pblocktree->ReadTxIndex(hash, postx)) { + CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION); + CBlockHeader header; + try { + file >> header; + fseek(file, postx.nTxOffset, SEEK_CUR); + file >> txOut; + } catch (std::exception &e) { + return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__); + } + hashBlock = header.GetHash(); + if (txOut.GetHash() != hash) + return error("%s() : txid mismatch", __PRETTY_FUNCTION__); + return true; + } + } + + if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it + int nHeight = -1; + { + CCoinsViewCache &view = *pcoinsTip; + CCoins coins; + if (view.GetCoins(hash, coins)) + nHeight = coins.nHeight; + } + if (nHeight > 0) + pindexSlow = FindBlockByHeight(nHeight); + } + } + + if (pindexSlow) { + CBlock block; + if (block.ReadFromDisk(pindexSlow)) { + BOOST_FOREACH(const CTransaction &tx, block.vtx) { + if (tx.GetHash() == hash) { + txOut = tx; + hashBlock = pindexSlow->GetBlockHash(); + 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) +{ + if (!ReadFromDisk(pindex->GetBlockPos())) + return false; + if (GetHash() != pindex->GetBlockHash()) + return error("CBlock::ReadFromDisk() : GetHash() doesn't match index"); + return true; +} + +uint256 static GetOrphanRoot(const CBlockHeader* pblock) +{ + // Work back to the first block in the orphan chain + while (mapOrphanBlocks.count(pblock->hashPrevBlock)) + pblock = mapOrphanBlocks[pblock->hashPrevBlock]; + return pblock->GetHash(); +} + + + +static const int64 nTargetTimespan = 20 * 60; // readjust difficulty once per hour +static const int64 nTargetSpacing = 60; // 30 seconds one block +static const int64 nInterval = nTargetTimespan / nTargetSpacing; // 10 blocks +static const int YEARLY_POS_BLOCK_COUNT = 1440 * 365; + +int64 static GetBlockValue(int nHeight, int64 nFees, unsigned int nBits) +{ + /* 1440 blocks/day , 3153600 blocks/year */ + int64 nSubsidy = 0 * COIN; + if (nHeight == 1 ) + { + nSubsidy = 1000000000 * COIN; + } + else if (nHeight > 1 ) + + { + nSubsidy = 0 * COIN; + + } + return nSubsidy + nFees; +} +// +// minimum amount of work that could possibly be required nTime after +// minimum work required was nBase +// +unsigned int ComputeMinWork(unsigned int nBase, int64 nTime) +{ + // Testnet has min-difficulty blocks + // after nTargetSpacing*2 time between blocks: + if (fTestNet && nTime > nTargetSpacing*2) + return bnProofOfWorkLimit.GetCompact(); + + CBigNum bnResult; + bnResult.SetCompact(nBase); + while (nTime > 0 && bnResult < bnProofOfWorkLimit) + { + // Maximum 200% adjustment... + bnResult *= 2; + // ... per timespan + nTime -= nTargetTimespan; + } + if (bnResult > bnProofOfWorkLimit) + bnResult = bnProofOfWorkLimit; + return bnResult.GetCompact(); +} + +unsigned int static GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock) +{ + unsigned int nProofOfWorkLimit = bnProofOfWorkLimit.GetCompact(); + + // Genesis block + if (pindexLast == NULL) + return nProofOfWorkLimit; + + // Only change once per interval + if ((pindexLast->nHeight+1) % nInterval != 0) + { + // Special difficulty rule for testnet: + if (fTestNet) + { + // If the new block's timestamp is more than 2 * nTargetSpacing + // then allow mining of a min-difficulty block. + if (pblock->nTime > pindexLast->nTime + nTargetSpacing*2) + return nProofOfWorkLimit; + else + { + // Return the last non-special-min-difficulty-rules-block + const CBlockIndex* pindex = pindexLast; + while (pindex->pprev && pindex->nHeight % nInterval != 0 && pindex->nBits == nProofOfWorkLimit) + pindex = pindex->pprev; + return pindex->nBits; + } + } + + return pindexLast->nBits; + } + + // Go back by what we want to be nInterval blocks + const CBlockIndex* pindexFirst = pindexLast; + for (int i = 0; pindexFirst && i < nInterval-1; i++) + pindexFirst = pindexFirst->pprev; + assert(pindexFirst); + + // Limit adjustment step + int64 nActualTimespan = pindexLast->GetBlockTime() - pindexFirst->GetBlockTime(); + printf(" nActualTimespan = %"PRI64d" before bounds\n", nActualTimespan); + //int64 LimUp = nTargetTimespan * 100 / 110; // 110% up + //int64 LimDown = nTargetTimespan * 2; // 200% down + //if (nActualTimespan < LimUp) + // nActualTimespan = LimUp; + //if (nActualTimespan > LimDown) + // nActualTimespan = LimDown; + int nActualTimespanLong = (pindexLast->GetBlockTime() - pindexFirst->GetBlockTime())/4; + int nActualTimespanAvg = (nActualTimespan + nActualTimespanLong)/2; // Average between short and long windows + nActualTimespan = nActualTimespanAvg + 3*nTargetTimespan; + nActualTimespan /= 4; + + printf("RETARGET: nActualTimespanLong = %d, nActualTimeSpanAvg = %d, nActualTimespan (with funk) = %"PRI64d"\n", nActualTimespanLong, nActualTimespanAvg, nActualTimespan); + int nActualTimespanMax = nTargetTimespan*98/77; // add crack + int nActualTimespanMin = nTargetTimespan*77/98; + + if(nActualTimespan < nActualTimespanMin) nActualTimespan = nActualTimespanMin; + if(nActualTimespan > nActualTimespanMax) nActualTimespan = nActualTimespanMax; + + // Retarget + CBigNum bnNew; + bnNew.SetCompact(pindexLast->nBits); + bnNew *= nActualTimespan; + bnNew /= nTargetTimespan; + + if (bnNew > bnProofOfWorkLimit) + bnNew = bnProofOfWorkLimit; + + /// debug print + printf("GetNextWorkRequired RETARGET\n"); + printf("nTargetTimespan = %"PRI64d" nActualTimespan = %"PRI64d"\n", nTargetTimespan, nActualTimespan); + printf("Before: %08x %s\n", pindexLast->nBits, CBigNum().SetCompact(pindexLast->nBits).getuint256().ToString().c_str()); + printf("After: %08x %s\n", bnNew.GetCompact(), bnNew.getuint256().ToString().c_str()); + + return bnNew.GetCompact(); +} + +bool CheckProofOfWork(uint256 hash, unsigned int nBits) +{ + CBigNum bnTarget; + bnTarget.SetCompact(nBits); + + // Check range + if (bnTarget <= 0 || bnTarget > bnProofOfWorkLimit) + return error("CheckProofOfWork() : nBits below minimum work"); + + // Check proof of work matches claimed amount + if (hash > bnTarget.getuint256()) + return error("CheckProofOfWork() : hash doesn't match nBits"); + + return true; +} + +// 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 || fImporting || fReindex || nBestHeight < Checkpoints::GetTotalBlocksEstimate()) + return true; + static int64 nLastUpdate; + static CBlockIndex* pindexLastBest; + if (pindexBest != pindexLastBest) + { + pindexLastBest = pindexBest; + nLastUpdate = GetTime(); + } + return (GetTime() - nLastUpdate < 10 && + pindexBest->GetBlockTime() < GetTime() - 96 * 60 * 60); +} + +void static InvalidChainFound(CBlockIndex* pindexNew) +{ + if (pindexNew->nChainWork > nBestInvalidWork) + { + nBestInvalidWork = pindexNew->nChainWork; + pblocktree->WriteBestInvalidWork(CBigNum(nBestInvalidWork)); + uiInterface.NotifyBlocksChanged(); + } + printf("InvalidChainFound: invalid block=%s height=%d log2_work=%.8g date=%s\n", + pindexNew->GetBlockHash().ToString().c_str(), pindexNew->nHeight, + log(pindexNew->nChainWork.getdouble())/log(2.0), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", + pindexNew->GetBlockTime()).c_str()); + printf("InvalidChainFound: current best=%s height=%d log2_work=%.8g date=%s\n", + hashBestChain.ToString().c_str(), nBestHeight, log(nBestChainWork.getdouble())/log(2.0), + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexBest->GetBlockTime()).c_str()); + if (pindexBest && nBestInvalidWork > nBestChainWork + (pindexBest->GetBlockWork() * 6).getuint256()) + printf("InvalidChainFound: Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.\n"); +} + +void static InvalidBlockFound(CBlockIndex *pindex) { + pindex->nStatus |= BLOCK_FAILED_VALID; + pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex)); + setBlockIndexValid.erase(pindex); + InvalidChainFound(pindex); + if (pindex->pnext) { + CValidationState stateDummy; + ConnectBestBlock(stateDummy); // reorganise away from the failed block + } +} + +bool ConnectBestBlock(CValidationState &state) { + do { + CBlockIndex *pindexNewBest; + + { + std::set::reverse_iterator it = setBlockIndexValid.rbegin(); + if (it == setBlockIndexValid.rend()) + return true; + pindexNewBest = *it; + } + + if (pindexNewBest == pindexBest || (pindexBest && pindexNewBest->nChainWork == pindexBest->nChainWork)) + return true; // nothing to do + + // check ancestry + CBlockIndex *pindexTest = pindexNewBest; + std::vector vAttach; + do { + if (pindexTest->nStatus & BLOCK_FAILED_MASK) { + // mark descendants failed + CBlockIndex *pindexFailed = pindexNewBest; + while (pindexTest != pindexFailed) { + pindexFailed->nStatus |= BLOCK_FAILED_CHILD; + setBlockIndexValid.erase(pindexFailed); + pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexFailed)); + pindexFailed = pindexFailed->pprev; + } + InvalidChainFound(pindexNewBest); + break; + } + + if (pindexBest == NULL || pindexTest->nChainWork > pindexBest->nChainWork) + vAttach.push_back(pindexTest); + + if (pindexTest->pprev == NULL || pindexTest->pnext != NULL) { + reverse(vAttach.begin(), vAttach.end()); + BOOST_FOREACH(CBlockIndex *pindexSwitch, vAttach) { + boost::this_thread::interruption_point(); + try { + if (!SetBestChain(state, pindexSwitch)) + return false; + } catch(std::runtime_error &e) { + return state.Abort(_("System error: ") + e.what()); + } + } + return true; + } + pindexTest = pindexTest->pprev; + } while(true); + } while(true); +} + +void CBlockHeader::UpdateTime(const CBlockIndex* pindexPrev) +{ + nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + + // Updating time can change work required on testnet: +// if (fTestNet) +// nBits = GetNextWorkRequired(pindexPrev, this); +} + + + + + + + + + + + +const CTxOut &CTransaction::GetOutputFor(const CTxIn& input, CCoinsViewCache& view) +{ + const CCoins &coins = view.GetCoins(input.prevout.hash); + assert(coins.IsAvailable(input.prevout.n)); + return coins.vout[input.prevout.n]; +} + +int64 CTransaction::GetValueIn(CCoinsViewCache& inputs) const +{ + if (IsCoinBase()) + return 0; + + int64 nResult = 0; + for (unsigned int i = 0; i < vin.size(); i++) + nResult += GetOutputFor(vin[i], inputs).nValue; + + return nResult; +} + +unsigned int CTransaction::GetP2SHSigOpCount(CCoinsViewCache& 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; +} + +void CTransaction::UpdateCoins(CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, const uint256 &txhash) const +{ + // mark inputs spent + if (!IsCoinBase()) { + BOOST_FOREACH(const CTxIn &txin, vin) { + CCoins &coins = inputs.GetCoins(txin.prevout.hash); + CTxInUndo undo; + assert(coins.Spend(txin.prevout, undo)); + txundo.vprevout.push_back(undo); + } + } + + // add outputs + assert(inputs.SetCoins(txhash, CCoins(*this, nHeight))); +} + +bool CTransaction::HaveInputs(CCoinsViewCache &inputs) const +{ + if (!IsCoinBase()) { + // first check whether information about the prevout hash is available + for (unsigned int i = 0; i < vin.size(); i++) { + const COutPoint &prevout = vin[i].prevout; + if (!inputs.HaveCoins(prevout.hash)) + return false; + } + + // then check whether the actual outputs are available + for (unsigned int i = 0; i < vin.size(); i++) { + const COutPoint &prevout = vin[i].prevout; + const CCoins &coins = inputs.GetCoins(prevout.hash); + if (!coins.IsAvailable(prevout.n)) + return false; + } + } + return true; +} + +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().c_str()); + return true; +} + +bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType) +{ + return CScriptCheck(txFrom, txTo, nIn, flags, nHashType)(); +} + +bool CTransaction::CheckInputs(CValidationState &state, CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, std::vector *pvChecks) const +{ + if (!IsCoinBase()) + { + if (pvChecks) + pvChecks->reserve(vin.size()); + + // 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 (!HaveInputs(inputs)) + return state.Invalid(error("CheckInputs() : %s inputs unavailable", GetHash().ToString().c_str())); + + // While checking, GetBestBlock() refers to the parent block. + // This is also true for mempool checks. + int nSpendHeight = inputs.GetBestBlock()->nHeight + 1; + int64 nValueIn = 0; + int64 nFees = 0; + for (unsigned int i = 0; i < vin.size(); i++) + { + const COutPoint &prevout = vin[i].prevout; + const CCoins &coins = inputs.GetCoins(prevout.hash); + + // If prev is coinbase, check that it's matured + if (coins.IsCoinBase()) { + if (nSpendHeight - coins.nHeight < COINBASE_MATURITY) + return state.Invalid(error("CheckInputs() : tried to spend coinbase at depth %d", nSpendHeight - coins.nHeight)); + } + + // Check for negative or overflow input values + nValueIn += coins.vout[prevout.n].nValue; + if (!MoneyRange(coins.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) + return state.DoS(100, error("CheckInputs() : txin values out of range")); + + } + + if (nValueIn < GetValueOut()) + return state.DoS(100, error("CheckInputs() : %s value in < value out", GetHash().ToString().c_str())); + + // Tally transaction fees + int64 nTxFee = nValueIn - GetValueOut(); + if (nTxFee < 0) + return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", GetHash().ToString().c_str())); + nFees += nTxFee; + if (!MoneyRange(nFees)) + return state.DoS(100, error("CheckInputs() : nFees out of range")); + + // 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. + + // Skip ECDSA signature verification when connecting blocks + // before the last block chain 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) { + for (unsigned int i = 0; i < vin.size(); i++) { + const COutPoint &prevout = vin[i].prevout; + const CCoins &coins = inputs.GetCoins(prevout.hash); + + // Verify signature + CScriptCheck check(coins, *this, i, flags, 0); + if (pvChecks) { + pvChecks->push_back(CScriptCheck()); + check.swap(pvChecks->back()); + } else if (!check()) { + if (flags & SCRIPT_VERIFY_STRICTENC) { + // For now, check whether the failure was caused by non-canonical + // encodings or not; if so, don't trigger DoS protection. + CScriptCheck check(coins, *this, i, flags & (~SCRIPT_VERIFY_STRICTENC), 0); + if (check()) + return state.Invalid(); + } + return state.DoS(100,false); + } + } + } + } + + return true; +} + + + + +bool CBlock::DisconnectBlock(CValidationState &state, CBlockIndex *pindex, CCoinsViewCache &view, bool *pfClean) +{ + assert(pindex == view.GetBestBlock()); + + if (pfClean) + *pfClean = false; + + bool fClean = true; + + CBlockUndo blockUndo; + CDiskBlockPos pos = pindex->GetUndoPos(); + if (pos.IsNull()) + return error("DisconnectBlock() : no undo data available"); + if (!blockUndo.ReadFromDisk(pos, pindex->pprev->GetBlockHash())) + return error("DisconnectBlock() : failure reading undo data"); + + if (blockUndo.vtxundo.size() + 1 != vtx.size()) + return error("DisconnectBlock() : block and undo data inconsistent"); + + // undo transactions in reverse order + for (int i = vtx.size() - 1; i >= 0; i--) { + const CTransaction &tx = vtx[i]; + uint256 hash = tx.GetHash(); + + // check that all outputs are available + if (!view.HaveCoins(hash)) { + fClean = fClean && error("DisconnectBlock() : outputs still spent? database corrupted"); + view.SetCoins(hash, CCoins()); + } + CCoins &outs = view.GetCoins(hash); + + CCoins outsBlock = CCoins(tx, pindex->nHeight); + if (outs != outsBlock) + fClean = fClean && error("DisconnectBlock() : added transaction mismatch? database corrupted"); + + // remove outputs + outs = CCoins(); + + // restore inputs + if (i > 0) { // not coinbases + const CTxUndo &txundo = blockUndo.vtxundo[i-1]; + if (txundo.vprevout.size() != tx.vin.size()) + return error("DisconnectBlock() : transaction and undo data inconsistent"); + for (unsigned int j = tx.vin.size(); j-- > 0;) { + const COutPoint &out = tx.vin[j].prevout; + const CTxInUndo &undo = txundo.vprevout[j]; + CCoins coins; + view.GetCoins(out.hash, coins); // this can fail if the prevout was already entirely spent + if (undo.nHeight != 0) { + // undo data contains height: this is the last output of the prevout tx being spent + if (!coins.IsPruned()) + fClean = fClean && error("DisconnectBlock() : undo data overwriting existing transaction"); + coins = CCoins(); + coins.fCoinBase = undo.fCoinBase; + coins.nHeight = undo.nHeight; + coins.nVersion = undo.nVersion; + } else { + if (coins.IsPruned()) + fClean = fClean && error("DisconnectBlock() : undo data adding output to missing transaction"); + } + if (coins.IsAvailable(out.n)) + fClean = fClean && error("DisconnectBlock() : undo data overwriting existing output"); + if (coins.vout.size() < out.n+1) + coins.vout.resize(out.n+1); + coins.vout[out.n] = undo.txout; + if (!view.SetCoins(out.hash, coins)) + return error("DisconnectBlock() : cannot restore coin inputs"); + } + } + } + + // move best block pointer to prevout block + view.SetBestBlock(pindex->pprev); + + if (pfClean) { + *pfClean = fClean; + return true; + } else { + return fClean; + } +} + +void static FlushBlockFile(bool fFinalize = false) +{ + LOCK(cs_LastBlockFile); + + CDiskBlockPos posOld(nLastBlockFile, 0); + + FILE *fileOld = OpenBlockFile(posOld); + if (fileOld) { + if (fFinalize) + TruncateFile(fileOld, infoLastBlockFile.nSize); + FileCommit(fileOld); + fclose(fileOld); + } + + fileOld = OpenUndoFile(posOld); + if (fileOld) { + if (fFinalize) + TruncateFile(fileOld, infoLastBlockFile.nUndoSize); + FileCommit(fileOld); + fclose(fileOld); + } +} + +bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize); + +static CCheckQueue scriptcheckqueue(128); + +void ThreadScriptCheck() { + RenameThread("bitcoin-scriptch"); + scriptcheckqueue.Thread(); +} + +bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsViewCache &view, bool fJustCheck) +{ + // Check it again in case a previous version let a bad block in + if (!CheckBlock(state, !fJustCheck, !fJustCheck)) + return false; + + // verify that the view's current state corresponds to the previous block + assert(pindex->pprev == view.GetBestBlock()); + + // Special case for the genesis block, skipping connection of its transactions + // (its coinbase is unspendable) + if (GetHash() == hashGenesisBlock) { + view.SetBestBlock(pindex); + pindexGenesisBlock = pindex; + return true; + } + + bool fScriptChecks = pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate(); + + // 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 = !pindex->phashBlock; +// bool fEnforceBIP30 = (!pindex->phashBlock) || // Enforce on CreateNewBlock invocations which don't have a hash. +// !((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) || +// (pindex->nHeight==91880 && pindex->GetBlockHash() == uint256("0x00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721"))); + if (fEnforceBIP30) { + for (unsigned int i=0; inTime >= nBIP16SwitchTime); + + unsigned int flags = SCRIPT_VERIFY_NOCACHE | + (fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE); + + CBlockUndo blockundo; + + CCheckQueueControl control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); + + int64 nStart = GetTimeMicros(); + int64 nFees = 0; + int nInputs = 0; + unsigned int nSigOps = 0; + CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(vtx.size())); + std::vector > vPos; + vPos.reserve(vtx.size()); + for (unsigned int i=0; i MAX_BLOCK_SIGOPS) + return state.DoS(100, error("ConnectBlock() : too many sigops")); + + if (!tx.IsCoinBase()) + { + if (!tx.HaveInputs(view)) + return state.DoS(100, error("ConnectBlock() : inputs missing/spent")); + + if (fStrictPayToScriptHash) + { + // 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(view); + if (nSigOps > MAX_BLOCK_SIGOPS) + return state.DoS(100, error("ConnectBlock() : too many sigops")); + } + + nFees += tx.GetValueIn(view)-tx.GetValueOut(); + + std::vector vChecks; + if (!tx.CheckInputs(state, view, fScriptChecks, flags, nScriptCheckThreads ? &vChecks : NULL)) + return false; + control.Add(vChecks); + } + + CTxUndo txundo; + tx.UpdateCoins(state, view, txundo, pindex->nHeight, GetTxHash(i)); + if (!tx.IsCoinBase()) + blockundo.vtxundo.push_back(txundo); + + vPos.push_back(std::make_pair(GetTxHash(i), pos)); + pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); + } + int64 nTime = GetTimeMicros() - nStart; + if (fBenchmark) + printf("- Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin)\n", (unsigned)vtx.size(), 0.001 * nTime, 0.001 * nTime / vtx.size(), nInputs <= 1 ? 0 : 0.001 * nTime / (nInputs-1)); + + if (vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees, pindex->nBits)) + return state.DoS(100, error("ConnectBlock() : coinbase pays too much (actual=%"PRI64d" vs limit=%"PRI64d")", vtx[0].GetValueOut(), GetBlockValue(pindex->nHeight, nFees, pindex->nBits))); + + if (!control.Wait()) + return state.DoS(100, false); + int64 nTime2 = GetTimeMicros() - nStart; + if (fBenchmark) + printf("- Verify %u txins: %.2fms (%.3fms/txin)\n", nInputs - 1, 0.001 * nTime2, nInputs <= 1 ? 0 : 0.001 * nTime2 / (nInputs-1)); + + if (fJustCheck) + return true; + + // Write undo information to disk + if (pindex->GetUndoPos().IsNull() || (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS) + { + if (pindex->GetUndoPos().IsNull()) { + CDiskBlockPos pos; + if (!FindUndoPos(state, pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40)) + return error("ConnectBlock() : FindUndoPos failed"); + if (!blockundo.WriteToDisk(pos, pindex->pprev->GetBlockHash())) + return state.Abort(_("Failed to write undo data")); + + // update nUndoPos in block index + pindex->nUndoPos = pos.nPos; + pindex->nStatus |= BLOCK_HAVE_UNDO; + } + + pindex->nStatus = (pindex->nStatus & ~BLOCK_VALID_MASK) | BLOCK_VALID_SCRIPTS; + + CDiskBlockIndex blockindex(pindex); + if (!pblocktree->WriteBlockIndex(blockindex)) + return state.Abort(_("Failed to write block index")); + } + + if (fTxIndex) + if (!pblocktree->WriteTxIndex(vPos)) + return state.Abort(_("Failed to write transaction index")); + + // add this block to the view's block chain + assert(view.SetBestBlock(pindex)); + + // Watch for transactions paying to me + for (unsigned int i=0; inHeight > pfork->nHeight) { + plonger = plonger->pprev; + assert(plonger != NULL); + } + if (pfork == plonger) + break; + pfork = pfork->pprev; + assert(pfork != NULL); + } + + // List of what to disconnect (typically nothing) + vector vDisconnect; + for (CBlockIndex* pindex = view.GetBestBlock(); pindex != pfork; pindex = pindex->pprev) + vDisconnect.push_back(pindex); + + // List of what to connect (typically only pindexNew) + vector vConnect; + for (CBlockIndex* pindex = pindexNew; pindex != pfork; pindex = pindex->pprev) + vConnect.push_back(pindex); + reverse(vConnect.begin(), vConnect.end()); + + if (vDisconnect.size() > 0) { + printf("REORGANIZE: Disconnect %"PRIszu" blocks; %s..\n", vDisconnect.size(), pfork->GetBlockHash().ToString().c_str()); + printf("REORGANIZE: Connect %"PRIszu" blocks; ..%s\n", vConnect.size(), pindexNew->GetBlockHash().ToString().c_str()); + } + + // Disconnect shorter branch + vector vResurrect; + BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) { + CBlock block; + if (!block.ReadFromDisk(pindex)) + return state.Abort(_("Failed to read block")); + int64 nStart = GetTimeMicros(); + if (!block.DisconnectBlock(state, pindex, view)) + return error("SetBestBlock() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().c_str()); + if (fBenchmark) + printf("- Disconnect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); + + // Queue memory transactions to resurrect. + // We only do this for blocks after the last checkpoint (reorganisation before that + // point should only happen with -reindex/-loadblock, or a misbehaving peer. + BOOST_FOREACH(const CTransaction& tx, block.vtx) + if (!tx.IsCoinBase() && pindex->nHeight > Checkpoints::GetTotalBlocksEstimate()) + vResurrect.push_back(tx); + } + + // Connect longer branch + vector vDelete; + BOOST_FOREACH(CBlockIndex *pindex, vConnect) { + CBlock block; + if (!block.ReadFromDisk(pindex)) + return state.Abort(_("Failed to read block")); + int64 nStart = GetTimeMicros(); + if (!block.ConnectBlock(state, pindex, view)) { + if (state.IsInvalid()) { + InvalidChainFound(pindexNew); + InvalidBlockFound(pindex); + } + return error("SetBestBlock() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().c_str()); + } + if (fBenchmark) + printf("- Connect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); + + // Queue memory transactions to delete + BOOST_FOREACH(const CTransaction& tx, block.vtx) + vDelete.push_back(tx); + } + + // Flush changes to global coin state + int64 nStart = GetTimeMicros(); + int nModified = view.GetCacheSize(); + assert(view.Flush()); + int64 nTime = GetTimeMicros() - nStart; + if (fBenchmark) + printf("- Flush %i transactions: %.2fms (%.4fms/tx)\n", nModified, 0.001 * nTime, 0.001 * nTime / nModified); + + // Make sure it's successfully written to disk before changing memory structure + bool fIsInitialDownload = IsInitialBlockDownload(); + if (!fIsInitialDownload || pcoinsTip->GetCacheSize() > nCoinCacheSize) { + // Typical CCoins structures on disk are around 100 bytes in size. + // Pushing a new one to the database can cause it to be written + // twice (once in the log, and once in the tables). This is already + // an overestimation, as most will delete an existing entry or + // overwrite one. Still, use a conservative safety factor of 2. + if (!CheckDiskSpace(100 * 2 * 2 * pcoinsTip->GetCacheSize())) + return state.Error(); + FlushBlockFile(); + pblocktree->Sync(); + if (!pcoinsTip->Flush()) + return state.Abort(_("Failed to write to coin database")); + } + + // At this point, all changes have been done to the database. + // Proceed by updating the memory structures. + + // 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) { + // ignore validation errors in resurrected transactions + CValidationState stateDummy; + tx.AcceptToMemoryPool(stateDummy, true, false); + } + + // Delete redundant memory transactions that are in the connected branch + BOOST_FOREACH(CTransaction& tx, vDelete) { + mempool.remove(tx); + mempool.removeConflicts(tx); + } + + // Update best block in wallet (so we can detect restored wallets) + if ((pindexNew->nHeight % 9600) == 0 || (!fIsInitialDownload && (pindexNew->nHeight % 240) == 0)) + { + const CBlockLocator locator(pindexNew); + ::SetBestChain(locator); + } + + // New best block + hashBestChain = pindexNew->GetBlockHash(); + pindexBest = pindexNew; + pblockindexFBBHLast = NULL; + nBestHeight = pindexBest->nHeight; + nBestChainWork = pindexNew->nChainWork; + nTimeBestReceived = GetTime(); + nTransactionsUpdated++; + printf("SetBestChain: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f\n", + hashBestChain.ToString().c_str(), nBestHeight, log(nBestChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx, + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexBest->GetBlockTime()).c_str(), + Checkpoints::GuessVerificationProgress(pindexBest)); + + // 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!"); + } + + if (!Checkpoints::IsSyncCheckpointEnforced()) // checkpoint advisory mode + { + if (pindexBest->pprev && !Checkpoints::CheckSyncCheckpoint(pindexBest->GetBlockHash(), pindexBest->pprev)) + Checkpoints::strCheckpointWarning = _("Warning: checkpoint on different blockchain fork, contact developers to resolve the issue"); + else + Checkpoints::strCheckpointWarning = ""; + } + + 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; +} + + +bool CBlock::AddToBlockIndex(CValidationState &state, const CDiskBlockPos &pos) +{ + // Check for duplicate + uint256 hash = GetHash(); + if (mapBlockIndex.count(hash)) + return state.Invalid(error("AddToBlockIndex() : %s already exists", hash.ToString().c_str())); + + // Construct new block index object + CBlockIndex* pindexNew = new CBlockIndex(*this); + assert(pindexNew); + map::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; + pindexNew->phashBlock = &((*mi).first); + map::iterator miPrev = mapBlockIndex.find(hashPrevBlock); + if (miPrev != mapBlockIndex.end()) + { + pindexNew->pprev = (*miPrev).second; + pindexNew->nHeight = pindexNew->pprev->nHeight + 1; + } + pindexNew->nTx = vtx.size(); + pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + pindexNew->GetBlockWork().getuint256(); + pindexNew->nChainTx = (pindexNew->pprev ? pindexNew->pprev->nChainTx : 0) + pindexNew->nTx; + pindexNew->nFile = pos.nFile; + pindexNew->nDataPos = pos.nPos; + pindexNew->nUndoPos = 0; + pindexNew->nStatus = BLOCK_VALID_TRANSACTIONS | BLOCK_HAVE_DATA; + setBlockIndexValid.insert(pindexNew); + + if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew))) + return state.Abort(_("Failed to write block index")); + + // New best? + if (!ConnectBestBlock(state)) + return false; + + if (pindexNew == pindexBest) + { + // Notify UI to display prev block's coinbase if it was ours + static uint256 hashPrevBestCoinBase; + UpdatedTransaction(hashPrevBestCoinBase); + hashPrevBestCoinBase = GetTxHash(0); + } + + if (!pblocktree->Flush()) + return state.Abort(_("Failed to sync block index")); + + uiInterface.NotifyBlocksChanged(); + return true; +} + + +bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64 nTime, bool fKnown = false) +{ + bool fUpdatedLast = false; + + LOCK(cs_LastBlockFile); + + if (fKnown) { + if (nLastBlockFile != pos.nFile) { + nLastBlockFile = pos.nFile; + infoLastBlockFile.SetNull(); + pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile); + fUpdatedLast = true; + } + } else { + while (infoLastBlockFile.nSize + nAddSize >= MAX_BLOCKFILE_SIZE) { + printf("Leaving block file %i: %s\n", nLastBlockFile, infoLastBlockFile.ToString().c_str()); + FlushBlockFile(true); + nLastBlockFile++; + infoLastBlockFile.SetNull(); + pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile); // check whether data for the new file somehow already exist; can fail just fine + fUpdatedLast = true; + } + pos.nFile = nLastBlockFile; + pos.nPos = infoLastBlockFile.nSize; + } + + infoLastBlockFile.nSize += nAddSize; + infoLastBlockFile.AddBlock(nHeight, nTime); + + if (!fKnown) { + unsigned int nOldChunks = (pos.nPos + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; + unsigned int nNewChunks = (infoLastBlockFile.nSize + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; + if (nNewChunks > nOldChunks) { + if (CheckDiskSpace(nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos)) { + FILE *file = OpenBlockFile(pos); + if (file) { + printf("Pre-allocating up to position 0x%x in blk%05u.dat\n", nNewChunks * BLOCKFILE_CHUNK_SIZE, pos.nFile); + AllocateFileRange(file, pos.nPos, nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos); + fclose(file); + } + } + else + return state.Error(); + } + } + + if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile)) + return state.Abort(_("Failed to write file info")); + if (fUpdatedLast) + pblocktree->WriteLastBlockFile(nLastBlockFile); + + return true; +} + +bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize) +{ + pos.nFile = nFile; + + LOCK(cs_LastBlockFile); + + unsigned int nNewSize; + if (nFile == nLastBlockFile) { + pos.nPos = infoLastBlockFile.nUndoSize; + nNewSize = (infoLastBlockFile.nUndoSize += nAddSize); + if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile)) + return state.Abort(_("Failed to write block info")); + } else { + CBlockFileInfo info; + if (!pblocktree->ReadBlockFileInfo(nFile, info)) + return state.Abort(_("Failed to read block info")); + pos.nPos = info.nUndoSize; + nNewSize = (info.nUndoSize += nAddSize); + if (!pblocktree->WriteBlockFileInfo(nFile, info)) + return state.Abort(_("Failed to write block info")); + } + + unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; + unsigned int nNewChunks = (nNewSize + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; + if (nNewChunks > nOldChunks) { + if (CheckDiskSpace(nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos)) { + FILE *file = OpenUndoFile(pos); + if (file) { + printf("Pre-allocating up to position 0x%x in rev%05u.dat\n", nNewChunks * UNDOFILE_CHUNK_SIZE, pos.nFile); + AllocateFileRange(file, pos.nPos, nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos); + fclose(file); + } + } + else + return state.Error(); + } + + return true; +} + + +bool CBlock::CheckBlock(CValidationState &state, bool fCheckPOW, bool fCheckMerkleRoot) const +{ + // These are checks that are independent of context + // that can be verified before saving an orphan block. + + // Size limits + if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) + return state.DoS(100, error("CheckBlock() : size limits failed")); + + // Special short-term limits to avoid 10,000 BDB lock limit: +/* if (GetBlockTime() >= 1363867200 && // start enforcing 21 March 2013, noon GMT + GetBlockTime() < 1368576000) // stop enforcing 15 May 2013 00:00:00 + { + // Rule is: #unique txids referenced <= 4,500 + // ... to prevent 10,000 BDB lock exhaustion on old clients + set setTxIn; + for (size_t i = 0; i < vtx.size(); i++) + { + setTxIn.insert(vtx[i].GetHash()); + if (i == 0) continue; // skip coinbase txin + BOOST_FOREACH(const CTxIn& txin, vtx[i].vin) + setTxIn.insert(txin.prevout.hash); + } + size_t nTxids = setTxIn.size(); + if (nTxids > 4500) + return error("CheckBlock() : 15 May maxlocks violation"); + } +*/ + // Check proof of work matches claimed amount + if (fCheckPOW && !CheckProofOfWork(GetHash(), nBits)) + return state.DoS(50, error("CheckBlock() : proof of work failed")); + + // Check timestamp + if (GetBlockTime() > GetAdjustedTime() + 1 * 60 * 60) + return state.Invalid(error("CheckBlock() : block timestamp too far in the future")); + + // First transaction must be coinbase, the rest must not be + if (vtx.empty() || !vtx[0].IsCoinBase()) + return state.DoS(100, error("CheckBlock() : first tx is not coinbase")); + for (unsigned int i = 1; i < vtx.size(); i++) + if (vtx[i].IsCoinBase()) + return state.DoS(100, error("CheckBlock() : more than one coinbase")); + + // Check transactions + BOOST_FOREACH(const CTransaction& tx, vtx) + if (!tx.CheckTransaction(state)) + return error("CheckBlock() : CheckTransaction failed"); + + // Build the merkle tree already. We need it anyway later, and it makes the + // block cache the transaction hashes, which means they don't need to be + // recalculated many times during this block's validation. + BuildMerkleTree(); + + // Check for duplicate txids. This is caught by ConnectInputs(), + // but catching it earlier avoids a potential DoS attack: + set uniqueTx; + for (unsigned int i=0; i MAX_BLOCK_SIGOPS) + return state.DoS(100, error("CheckBlock() : out-of-bounds SigOpCount")); + + // Check merkle root + if (fCheckMerkleRoot && hashMerkleRoot != BuildMerkleTree()) + return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch")); + + return true; +} + +bool CBlock::AcceptBlock(CValidationState &state, CDiskBlockPos *dbp) +{ + // Check for duplicate + uint256 hash = GetHash(); + if (mapBlockIndex.count(hash)) + return state.Invalid(error("AcceptBlock() : block already in mapBlockIndex")); + + // Get prev block index + CBlockIndex* pindexPrev = NULL; + int nHeight = 0; + if (hash != hashGenesisBlock) { + map::iterator mi = mapBlockIndex.find(hashPrevBlock); + if (mi == mapBlockIndex.end()) + return state.DoS(10, error("AcceptBlock() : prev block not found")); + pindexPrev = (*mi).second; + nHeight = pindexPrev->nHeight+1; + + // Check proof of work + if (nBits != GetNextWorkRequired(pindexPrev, this)) + return state.DoS(100, error("AcceptBlock() : incorrect proof of work")); + + // Check timestamp against prev + if (GetBlockTime() <= pindexPrev->GetMedianTimePast()) + return state.Invalid(error("AcceptBlock() : block's timestamp is too early")); + + // Check that all transactions are finalized + BOOST_FOREACH(const CTransaction& tx, vtx) + if (!tx.IsFinal(nHeight, GetBlockTime())) + return state.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::CheckBlock(nHeight, hash)) + return state.DoS(100, error("AcceptBlock() : rejected by checkpoint lock-in at %d", nHeight)); + + // ppcoin: check that the block satisfies synchronized checkpoint + if (Checkpoints::IsSyncCheckpointEnforced() // checkpoint enforce mode + && !Checkpoints::CheckSyncCheckpoint(hash, pindexPrev)) + return error("AcceptBlock() : rejected by synchronized checkpoint"); + + // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: +// if (nVersion < 2) +// { +// if ((!fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 950, 1000)) || +// (fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 75, 100))) +// { +// return state.Invalid(error("AcceptBlock() : rejected nVersion=1 block")); +// } +// } + // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height + if (nVersion >= 2) + { + // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet): + if ((!fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 750, 1000)) || + (fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 51, 100))) + { + CScript expect = CScript() << nHeight; + if (!std::equal(expect.begin(), expect.end(), vtx[0].vin[0].scriptSig.begin())) + return state.DoS(100, error("AcceptBlock() : block height mismatch in coinbase")); + } + } + } + + // Write block to history file + try { + unsigned int nBlockSize = ::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION); + CDiskBlockPos blockPos; + if (dbp != NULL) + blockPos = *dbp; + if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, nTime, dbp != NULL)) + return error("AcceptBlock() : FindBlockPos failed"); + if (dbp == NULL) + if (!WriteToDisk(blockPos)) + return state.Abort(_("Failed to write block")); + if (!AddToBlockIndex(state, blockPos)) + return error("AcceptBlock() : AddToBlockIndex failed"); + } catch(std::runtime_error &e) { + return state.Abort(_("System error: ") + e.what()); + } + + // 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; +} + +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 ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) +{ + // Check for duplicate + uint256 hash = pblock->GetHash(); + if (mapBlockIndex.count(hash)) + return state.Invalid(error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, hash.ToString().c_str())); + if (mapOrphanBlocks.count(hash)) + return state.Invalid(error("ProcessBlock() : already have block (orphan) %s", hash.ToString().c_str())); + + // Preliminary checks + if (!pblock->CheckBlock(state)) + return error("ProcessBlock() : CheckBlock FAILED"); + + CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); + if (pcheckpoint && pblock->hashPrevBlock != hashBestChain) + { + // Extra checks to prevent "fill up memory by spamming with bogus blocks" + int64 deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime; + if (deltaTime < 0) + { + return state.DoS(100, error("ProcessBlock() : block with timestamp before last checkpoint")); + } + CBigNum bnNewBlock; + bnNewBlock.SetCompact(pblock->nBits); + CBigNum bnRequired; + bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime)); + if (bnNewBlock > bnRequired) + { + return state.DoS(100, error("ProcessBlock() : block with too little proof-of-work")); + } + } + + + // ppcoin: ask for pending sync-checkpoint if any + if (!IsInitialBlockDownload()) + Checkpoints::AskForPendingSyncCheckpoint(pfrom); + + // If we don't already have its previous block, shunt it off to holding area until we get it + if (pblock->hashPrevBlock != 0 && !mapBlockIndex.count(pblock->hashPrevBlock)) + { + printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().c_str()); + + // Accept orphans as long as there is a node to request its parents from + if (pfrom) { + 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 + pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(pblock2)); + } + return true; + } + + // Store to disk + if (!pblock->AcceptBlock(state, dbp)) + 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; + // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan resolution (that is, feeding people an invalid block based on LegitBlockX in order to get anyone relaying LegitBlockX banned) + CValidationState stateDummy; + if (pblockOrphan->AcceptBlock(stateDummy)) + vWorkQueue.push_back(pblockOrphan->GetHash()); + mapOrphanBlocks.erase(pblockOrphan->GetHash()); + delete pblockOrphan; + } + mapOrphanBlocksByPrev.erase(hashPrev); + } + + printf("ProcessBlock: ACCEPTED\n"); + + // ppcoin: if responsible for sync-checkpoint send it + if (pfrom && !CSyncCheckpoint::strMasterPrivKey.empty() && + (int)GetArg("-checkpointdepth", -1) >= 0) + Checkpoints::SendSyncCheckpoint(Checkpoints::AutoSelectSyncCheckpoint()); + + return true; +} + + + + + + + + +CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter& filter) +{ + header = block.GetBlockHeader(); + + vector vMatch; + vector vHashes; + + vMatch.reserve(block.vtx.size()); + vHashes.reserve(block.vtx.size()); + + for (unsigned int i = 0; i < block.vtx.size(); i++) + { + uint256 hash = block.vtx[i].GetHash(); + if (filter.IsRelevantAndUpdate(block.vtx[i], hash)) + { + vMatch.push_back(true); + vMatchedTxn.push_back(make_pair(i, hash)); + } + else + vMatch.push_back(false); + vHashes.push_back(hash); + } + + txn = CPartialMerkleTree(vHashes, vMatch); +} + + + + + + + + +uint256 CPartialMerkleTree::CalcHash(int height, unsigned int pos, const std::vector &vTxid) { + if (height == 0) { + // hash at height 0 is the txids themself + return vTxid[pos]; + } else { + // calculate left hash + uint256 left = CalcHash(height-1, pos*2, vTxid), right; + // calculate right hash if not beyong the end of the array - copy left hash otherwise1 + if (pos*2+1 < CalcTreeWidth(height-1)) + right = CalcHash(height-1, pos*2+1, vTxid); + else + right = left; + // combine subhashes + return Hash(BEGIN(left), END(left), BEGIN(right), END(right)); + } +} + +void CPartialMerkleTree::TraverseAndBuild(int height, unsigned int pos, const std::vector &vTxid, const std::vector &vMatch) { + // determine whether this node is the parent of at least one matched txid + bool fParentOfMatch = false; + for (unsigned int p = pos << height; p < (pos+1) << height && p < nTransactions; p++) + fParentOfMatch |= vMatch[p]; + // store as flag bit + vBits.push_back(fParentOfMatch); + if (height==0 || !fParentOfMatch) { + // if at height 0, or nothing interesting below, store hash and stop + vHash.push_back(CalcHash(height, pos, vTxid)); + } else { + // otherwise, don't store any hash, but descend into the subtrees + TraverseAndBuild(height-1, pos*2, vTxid, vMatch); + if (pos*2+1 < CalcTreeWidth(height-1)) + TraverseAndBuild(height-1, pos*2+1, vTxid, vMatch); + } +} + +uint256 CPartialMerkleTree::TraverseAndExtract(int height, unsigned int pos, unsigned int &nBitsUsed, unsigned int &nHashUsed, std::vector &vMatch) { + if (nBitsUsed >= vBits.size()) { + // overflowed the bits array - failure + fBad = true; + return 0; + } + bool fParentOfMatch = vBits[nBitsUsed++]; + if (height==0 || !fParentOfMatch) { + // if at height 0, or nothing interesting below, use stored hash and do not descend + if (nHashUsed >= vHash.size()) { + // overflowed the hash array - failure + fBad = true; + return 0; + } + const uint256 &hash = vHash[nHashUsed++]; + if (height==0 && fParentOfMatch) // in case of height 0, we have a matched txid + vMatch.push_back(hash); + return hash; + } else { + // otherwise, descend into the subtrees to extract matched txids and hashes + uint256 left = TraverseAndExtract(height-1, pos*2, nBitsUsed, nHashUsed, vMatch), right; + if (pos*2+1 < CalcTreeWidth(height-1)) + right = TraverseAndExtract(height-1, pos*2+1, nBitsUsed, nHashUsed, vMatch); + else + right = left; + // and combine them before returning + return Hash(BEGIN(left), END(left), BEGIN(right), END(right)); + } +} + +CPartialMerkleTree::CPartialMerkleTree(const std::vector &vTxid, const std::vector &vMatch) : nTransactions(vTxid.size()), fBad(false) { + // reset state + vBits.clear(); + vHash.clear(); + + // calculate height of tree + int nHeight = 0; + while (CalcTreeWidth(nHeight) > 1) + nHeight++; + + // traverse the partial tree + TraverseAndBuild(nHeight, 0, vTxid, vMatch); +} + +CPartialMerkleTree::CPartialMerkleTree() : nTransactions(0), fBad(true) {} + +uint256 CPartialMerkleTree::ExtractMatches(std::vector &vMatch) { + vMatch.clear(); + // An empty set will not work + if (nTransactions == 0) + return 0; + // check for excessively high numbers of transactions + if (nTransactions > MAX_BLOCK_SIZE / 60) // 60 is the lower bound for the size of a serialized CTransaction + return 0; + // there can never be more hashes provided than one for every txid + if (vHash.size() > nTransactions) + return 0; + // there must be at least one bit per node in the partial tree, and at least one node per hash + if (vBits.size() < vHash.size()) + return 0; + // calculate height of tree + int nHeight = 0; + while (CalcTreeWidth(nHeight) > 1) + nHeight++; + // traverse the partial tree + unsigned int nBitsUsed = 0, nHashUsed = 0; + uint256 hashMerkleRoot = TraverseAndExtract(nHeight, 0, nBitsUsed, nHashUsed, vMatch); + // verify that no problems occured during the tree traversal + if (fBad) + return 0; + // verify that all bits were consumed (except for the padding caused by serializing it as a byte sequence) + if ((nBitsUsed+7)/8 != (vBits.size()+7)/8) + return 0; + // verify that all hashes were consumed + if (nHashUsed != vHash.size()) + return 0; + return hashMerkleRoot; +} + + + + + + + +bool AbortNode(const std::string &strMessage) { + strMiscWarning = strMessage; + printf("*** %s\n", strMessage.c_str()); + uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_ERROR); + StartShutdown(); + return false; +} + +bool CheckDiskSpace(uint64 nAdditionalBytes) +{ + uint64 nFreeBytesAvailable = filesystem::space(GetDataDir()).available; + + // Check for nMinDiskSpace bytes (currently 50MB) + if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes) + return AbortNode(_("Error: Disk space is low!")); + + return true; +} + +CCriticalSection cs_LastBlockFile; +CBlockFileInfo infoLastBlockFile; +int nLastBlockFile = 0; + +FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly) +{ + if (pos.IsNull()) + return NULL; + boost::filesystem::path path = GetDataDir() / "blocks" / strprintf("%s%05u.dat", prefix, pos.nFile); + boost::filesystem::create_directories(path.parent_path()); + FILE* file = fopen(path.string().c_str(), "rb+"); + if (!file && !fReadOnly) + file = fopen(path.string().c_str(), "wb+"); + if (!file) { + printf("Unable to open file %s\n", path.string().c_str()); + return NULL; + } + if (pos.nPos) { + if (fseek(file, pos.nPos, SEEK_SET)) { + printf("Unable to seek to position %u of %s\n", pos.nPos, path.string().c_str()); + fclose(file); + return NULL; + } + } + return file; +} + +FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly) { + return OpenDiskFile(pos, "blk", fReadOnly); +} + +FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly) { + return OpenDiskFile(pos, "rev", fReadOnly); +} + +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 static LoadBlockIndexDB() +{ + if (!pblocktree->LoadBlockIndexGuts()) + return false; + + boost::this_thread::interruption_point(); + + // Calculate nChainWork + 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->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + pindex->GetBlockWork().getuint256(); + pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx; + if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS && !(pindex->nStatus & BLOCK_FAILED_MASK)) + setBlockIndexValid.insert(pindex); + } + + // Load block file info + pblocktree->ReadLastBlockFile(nLastBlockFile); + printf("LoadBlockIndexDB(): last block file = %i\n", nLastBlockFile); + if (pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile)) + printf("LoadBlockIndexDB(): last block file info: %s\n", infoLastBlockFile.ToString().c_str()); + + // Load nBestInvalidWork, OK if it doesn't exist + CBigNum bnBestInvalidWork; + pblocktree->ReadBestInvalidWork(bnBestInvalidWork); + nBestInvalidWork = bnBestInvalidWork.getuint256(); + + // Check whether we need to continue reindexing + bool fReindexing = false; + pblocktree->ReadReindexing(fReindexing); + fReindex |= fReindexing; + + // Check whether we have a transaction index + pblocktree->ReadFlag("txindex", fTxIndex); + printf("LoadBlockIndexDB(): transaction index %s\n", fTxIndex ? "enabled" : "disabled"); + + // Load hashBestChain pointer to end of best chain + pindexBest = pcoinsTip->GetBestBlock(); + if (pindexBest == NULL) + return true; + hashBestChain = pindexBest->GetBlockHash(); + nBestHeight = pindexBest->nHeight; + nBestChainWork = pindexBest->nChainWork; + + // set 'next' pointers in best chain + CBlockIndex *pindex = pindexBest; + while(pindex != NULL && pindex->pprev != NULL) { + CBlockIndex *pindexPrev = pindex->pprev; + pindexPrev->pnext = pindex; + pindex = pindexPrev; + } + printf("LoadBlockIndexDB(): hashBestChain=%s height=%d date=%s\n", + hashBestChain.ToString().c_str(), nBestHeight, + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexBest->GetBlockTime()).c_str()); + + return true; +} + +bool VerifyDB() { + if (pindexBest == NULL || pindexBest->pprev == NULL) + return true; + + // Verify blocks in the best chain + int nCheckLevel = GetArg("-checklevel", 3); + int nCheckDepth = GetArg( "-checkblocks", 288); + if (nCheckDepth == 0) + nCheckDepth = 1000000000; // suffices until the year 19000 + if (nCheckDepth > nBestHeight) + nCheckDepth = nBestHeight; + nCheckLevel = std::max(0, std::min(4, nCheckLevel)); + printf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); + CCoinsViewCache coins(*pcoinsTip, true); + CBlockIndex* pindexState = pindexBest; + CBlockIndex* pindexFailure = NULL; + int nGoodTransactions = 0; + CValidationState state; + for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev) + { + boost::this_thread::interruption_point(); + if (pindex->nHeight < nBestHeight-nCheckDepth) + break; + CBlock block; + // check level 0: read from disk + if (!block.ReadFromDisk(pindex)) + return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + // check level 1: verify block validity + if (nCheckLevel >= 1 && !block.CheckBlock(state)) + return error("VerifyDB() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + // check level 2: verify undo validity + if (nCheckLevel >= 2 && pindex) { + CBlockUndo undo; + CDiskBlockPos pos = pindex->GetUndoPos(); + if (!pos.IsNull()) { + if (!undo.ReadFromDisk(pos, pindex->pprev->GetBlockHash())) + return error("VerifyDB() : *** found bad undo data at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + } + } + // check level 3: check for inconsistencies during memory-only disconnect of tip blocks + if (nCheckLevel >= 3 && pindex == pindexState && (coins.GetCacheSize() + pcoinsTip->GetCacheSize()) <= 2*nCoinCacheSize + 32000) { + bool fClean = true; + if (!block.DisconnectBlock(state, pindex, coins, &fClean)) + return error("VerifyDB() : *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + pindexState = pindex->pprev; + if (!fClean) { + nGoodTransactions = 0; + pindexFailure = pindex; + } else + nGoodTransactions += block.vtx.size(); + } + } + if (pindexFailure) + return error("VerifyDB() : *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", pindexBest->nHeight - pindexFailure->nHeight + 1, nGoodTransactions); + + // check level 4: try reconnecting blocks + if (nCheckLevel >= 4) { + CBlockIndex *pindex = pindexState; + while (pindex != pindexBest) { + boost::this_thread::interruption_point(); + pindex = pindex->pnext; + CBlock block; + if (!block.ReadFromDisk(pindex)) + return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + if (!block.ConnectBlock(state, pindex, coins)) + return error("VerifyDB() : *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + } + } + + printf("No coin database inconsistencies in last %i blocks (%i transactions)\n", pindexBest->nHeight - pindexState->nHeight, nGoodTransactions); + + return true; +} + +void UnloadBlockIndex() +{ + mapBlockIndex.clear(); + setBlockIndexValid.clear(); + pindexGenesisBlock = NULL; + nBestHeight = 0; + nBestChainWork = 0; + nBestInvalidWork = 0; + hashBestChain = 0; + pindexBest = NULL; +} + +bool LoadBlockIndex() +{ + if (fTestNet) + { + pchMessageStart[0] = 0xFE; + pchMessageStart[1] = 0xEE; + pchMessageStart[2] = 0xA9; + pchMessageStart[3] = 0xF7; + hashGenesisBlock = uint256("0x0000028542fd4edd1fae38410e16afbfe6edbb0155d7d71ed56330926c3933fc"); + } + + // + // Load block index from databases + // + if (!fReindex && !LoadBlockIndexDB()) + return false; + + return true; +} + + +bool InitBlockIndex() { + // Check whether we're already initialized + if (pindexGenesisBlock != NULL) + return true; + + // Use the provided setting for -txindex in the new database + fTxIndex = GetBoolArg("-txindex", false); + pblocktree->WriteFlag("txindex", fTxIndex); + printf("Initializing databases...\n"); + + // Only add the genesis block if not reindexing (in which case we reuse the one already on disk) + if (!fReindex) { + + // Genesis block + const char* pszTimestamp = "August 15, 2017. The Greenchain release. "; + CTransaction txNew; + txNew.vin.resize(1); + txNew.vout.resize(1); + txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(4) << vector((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp)); + txNew.vout[0].nValue = 0; + txNew.vout[0].scriptPubKey = CScript() << ParseHex("04f435367958d44e34be2d6d90711946f42a81452ddfcbef22f356266a085b58c923770b514e188253d14eb126501be50d4ea8a36b72c862259b60b85fa4d5364b") << OP_CHECKSIG; + CBlock block; + block.vtx.push_back(txNew); + block.hashPrevBlock = 0; + block.hashMerkleRoot = block.BuildMerkleTree(); + block.nVersion = 112; + block.nTime = timeGenesisBlock; + block.nBits = bnProofOfWorkLimit.GetCompact(); + block.nNonce = 390577; + + if (fTestNet) + { + block.nTime = 1492164000; + block.nNonce = 122965; + } + + + //// debug print + uint256 hash = block.GetHash(); + while (hash > bnProofOfWorkLimit.getuint256()){ + if (++block.nNonce==0) break; + hash = block.GetHash(); + } + + printf("%s\n", hash.ToString().c_str()); + printf("%s\n", hashGenesisBlock.ToString().c_str()); + printf("%s\n", block.hashMerkleRoot.ToString().c_str()); + block.print(); + assert(block.hashMerkleRoot == uint256("0x871b51bb9ac9dba4625d50f0a94323fd47352fe8e377a1973cfa9430fb8e0dd5")); + assert(hash == hashGenesisBlock); + + // Start new block file + try { + unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); + CDiskBlockPos blockPos; + CValidationState state; + if (!FindBlockPos(state, blockPos, nBlockSize+8, 0, block.nTime)) + return error("LoadBlockIndex() : FindBlockPos failed"); + if (!block.WriteToDisk(blockPos)) + return error("LoadBlockIndex() : writing genesis block to disk failed"); + if (!block.AddToBlockIndex(state, blockPos)) + return error("LoadBlockIndex() : genesis block not accepted"); + + // ppcoin: initialize synchronized checkpoint + printf("InitBlockIndex\n"); + if (!Checkpoints::WriteSyncCheckpoint(hashGenesisBlock)) + return error("LoadBlockIndex() : failed to init sync checkpoint"); + printf("InitBlockIndex_Pass\n"); + } catch(std::runtime_error &e) { + return error("LoadBlockIndex() : failed to initialize block database: %s", e.what()); + } + } + + // ppcoin: if checkpoint master key changed must reset sync-checkpoint + printf("InitBlockIndex_CheckCheckpointPubKey\n"); + if (!Checkpoints::CheckCheckpointPubKey()) + return error("LoadBlockIndex() : failed to reset checkpoint master pubkey"); + + 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 (blk%05u.dat:0x%x) %s tx %"PRIszu"", + pindex->nHeight, + pindex->GetBlockPos().nFile, pindex->GetBlockPos().nPos, + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", block.GetBlockTime()).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, CDiskBlockPos *dbp) +{ + int64 nStart = GetTimeMillis(); + + int nLoaded = 0; + try { + CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SIZE, MAX_BLOCK_SIZE+8, SER_DISK, CLIENT_VERSION); + uint64 nStartByte = 0; + if (dbp) { + // (try to) skip already indexed part + CBlockFileInfo info; + if (pblocktree->ReadBlockFileInfo(dbp->nFile, info)) { + nStartByte = info.nSize; + blkdat.Seek(info.nSize); + } + } + uint64 nRewind = blkdat.GetPos(); + while (blkdat.good() && !blkdat.eof()) { + boost::this_thread::interruption_point(); + + blkdat.SetPos(nRewind); + nRewind++; // start one byte further next time, in case of failure + blkdat.SetLimit(); // remove former limit + unsigned int nSize = 0; + try { + // locate a header + unsigned char buf[4]; + blkdat.FindByte(pchMessageStart[0]); + nRewind = blkdat.GetPos()+1; + blkdat >> FLATDATA(buf); + if (memcmp(buf, pchMessageStart, 4)) + continue; + // read size + blkdat >> nSize; + if (nSize < 80 || nSize > MAX_BLOCK_SIZE) + continue; + } catch (std::exception &e) { + // no valid block header found; don't complain + break; + } + try { + // read block + uint64 nBlockPos = blkdat.GetPos(); + blkdat.SetLimit(nBlockPos + nSize); + CBlock block; + blkdat >> block; + nRewind = blkdat.GetPos(); + + // process block + if (nBlockPos >= nStartByte) { + LOCK(cs_main); + if (dbp) + dbp->nPos = nBlockPos; + CValidationState state; + if (ProcessBlock(state, NULL, &block, dbp)) + nLoaded++; + if (state.IsError()) + break; + } + } catch (std::exception &e) { + printf("%s() : Deserialize or I/O error caught during load\n", __PRETTY_FUNCTION__); + } + } + fclose(fileIn); + } catch(std::runtime_error &e) { + AbortNode(_("Error: system error: ") + e.what()); + } + if (nLoaded > 0) + printf("Loaded %i blocks from external file in %"PRI64d"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"; + + if (!CLIENT_VERSION_IS_RELEASE) + strStatusBar = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications"); + + // Checkpoint warning + if (Checkpoints::strCheckpointWarning != "") + { + nPriority = 900; + strStatusBar = Checkpoints::strCheckpointWarning; + } + + // Misc warnings like out of disk space and clock is wrong + if (strMiscWarning != "") + { + nPriority = 1000; + strStatusBar = strMiscWarning; + } + + // Longer invalid proof-of-work chain + if (pindexBest && nBestInvalidWork > nBestChainWork + (pindexBest->GetBlockWork() * 6).getuint256()) + { + nPriority = 2000; + strStatusBar = strRPC = _("Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade."); + } + + // ppcoin: if detected invalid checkpoint enter safe mode + if (Checkpoints::hashInvalidCheckpoint != 0) + { + nPriority = 3000; + strStatusBar = strRPC = "WARNING: Inconsistent checkpoint found! Stop enforcing checkpoints and notify developers to resolve the issue."; + } + + + // 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 (strFor == "statusbar") + return strStatusBar; + else if (strFor == "rpc") + return strRPC; + assert(!"GetWarnings() : invalid parameter"); + return "error"; +} + + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// Messages +// + + +bool static AlreadyHave(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) || + pcoinsTip->HaveCoins(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] = { 0xdf, 0xef, 0xad, 0x2f }; + +void static ProcessGetData(CNode* pfrom) +{ + std::deque::iterator it = pfrom->vRecvGetData.begin(); + + vector vNotFound; + + while (it != pfrom->vRecvGetData.end()) { + // Don't bother if send buffer is too full to respond anyway + if (pfrom->nSendSize >= SendBufferSize()) + break; + + const CInv &inv = *it; + { + boost::this_thread::interruption_point(); + it++; + + if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) + { + // Send block from disk + map::iterator mi = mapBlockIndex.find(inv.hash); + if (mi != mapBlockIndex.end()) + { + CBlock block; + block.ReadFromDisk((*mi).second); + if (inv.type == MSG_BLOCK) + pfrom->PushMessage("block", block); + else // MSG_FILTERED_BLOCK) + { + LOCK(pfrom->cs_filter); + if (pfrom->pfilter) + { + CMerkleBlock merkleBlock(block, *pfrom->pfilter); + pfrom->PushMessage("merkleblock", merkleBlock); + // CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see + // This avoids hurting performance by pointlessly requiring a round-trip + // Note that there is currently no way for a node to request any single transactions we didnt send here - + // they must either disconnect and retry or request the full block. + // Thus, the protocol spec specified allows for us to provide duplicate txn here, + // however we MUST always provide at least what the remote peer needs + typedef std::pair PairType; + BOOST_FOREACH(PairType& pair, merkleBlock.vMatchedTxn) + if (!pfrom->setInventoryKnown.count(CInv(MSG_TX, pair.second))) + pfrom->PushMessage("tx", block.vtx[pair.first]); + } + // else + // no response + } + + // Trigger them to send a getblocks request for the next batch of inventory + if (inv.hash == pfrom->hashContinue) + { + // Bypass PushInventory, this must send even if redundant, + // and we want it right after the last block so they don't + // wait for other stuff first. + vector vInv; + vInv.push_back(CInv(MSG_BLOCK, hashBestChain)); + pfrom->PushMessage("inv", vInv); + pfrom->hashContinue = 0; + } + } + } + else if (inv.IsKnownType()) + { + // Send stream from relay memory + 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); + pushed = true; + } + } + if (!pushed) { + vNotFound.push_back(inv); + } + } + + // Track requests for our stuff. + Inventory(inv.hash); + } + } + + pfrom->vRecvGetData.erase(pfrom->vRecvGetData.begin(), it); + + if (!vNotFound.empty()) { + // Let the peer know that we didn't find what it asked for, so it doesn't + // have to wait around forever. Currently only SPV clients actually care + // about this message: it's needed when they are recursively walking the + // dependencies of relevant unconfirmed transactions. SPV clients want to + // do that because they want to know about (and store and rebroadcast and + // risk analyze) the dependencies of transactions relevant to them, without + // having to download the entire memory pool. + pfrom->PushMessage("notfound", vNotFound); + } +} + +bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) +{ + 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 nTime; + CAddress addrMe; + CAddress addrFrom; + uint64 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 (!vRecv.empty()) + vRecv >> pfrom->fRelayTxes; // set to true after we get the first filter* message + else + pfrom->fRelayTxes = true; + + 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; + } + + // 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->ssSend.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); + } + } + + // Relay alerts + { + LOCK(cs_mapAlerts); + BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) + item.second.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->SetRecvVersion(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 nNow = GetAdjustedTime(); + int64 nSince = nNow - 10 * 60; + BOOST_FOREACH(CAddress& addr, vAddr) + { + boost::this_thread::interruption_point(); + + if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) + addr.nTime = nNow - 5 * 24 * 60 * 60; + 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 hashAddr = addr.GetHash(); + uint256 hashRand = hashSalt ^ (hashAddr<<32) ^ ((GetTime()+hashAddr)/(24*60*60)); + hashRand = Hash2(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 = Hash2(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 * 60 * 60); + 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 = (unsigned int)(-1); + for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) { + if (vInv[vInv.size() - 1 - nInv].type == MSG_BLOCK) { + nLastBlock = vInv.size() - 1 - nInv; + break; + } + } + for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) + { + const CInv &inv = vInv[nInv]; + + boost::this_thread::interruption_point(); + pfrom->AddInventoryKnown(inv); + + bool fAlreadyHave = AlreadyHave(inv); + if (fDebug) + printf(" got inventory: %s %s\n", inv.ToString().c_str(), fAlreadyHave ? "have" : "new"); + + if (!fAlreadyHave) { + if (!fImporting && !fReindex) + 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()); + + if ((fDebugNet && vInv.size() > 0) || (vInv.size() == 1)) + printf("received getdata for: %s\n", vInv[0].ToString().c_str()); + + pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end()); + ProcessGetData(pfrom); + } + + + 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().c_str(), nLimit); + for (; pindex; pindex = pindex->pnext) + { + if (pindex->GetBlockHash() == hashStop) + { + printf(" getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + break; + } + pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash())); + if (--nLimit <= 0) + { + // When this block is requested, we'll send an inv that'll make them + // getblocks the next batch of inventory. + printf(" getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + pfrom->hashContinue = pindex->GetBlockHash(); + break; + } + } + } + + + else if (strCommand == "getheaders") + { + CBlockLocator locator; + uint256 hashStop; + vRecv >> locator >> hashStop; + + CBlockIndex* pindex = NULL; + if (locator.IsNull()) + { + // If locator is null, return the hashStop block + map::iterator mi = mapBlockIndex.find(hashStop); + if (mi == mapBlockIndex.end()) + return true; + pindex = (*mi).second; + } + else + { + // Find the last block the caller has in the main chain + pindex = locator.GetBlockIndex(); + if (pindex) + pindex = pindex->pnext; + } + + // we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end + vector vHeaders; + int nLimit = 2000; + printf("getheaders %d to %s\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().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); + CTransaction tx; + vRecv >> tx; + + CInv inv(MSG_TX, tx.GetHash()); + pfrom->AddInventoryKnown(inv); + + // Truncate messages to the size of the tx in them + unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); + unsigned int oldSize = vMsg.size(); + if (nSize < oldSize) { + vMsg.resize(nSize); + printf("truncating oversized TX %s (%u -> %u)\n", + tx.GetHash().ToString().c_str(), + oldSize, nSize); + } + + bool fMissingInputs = false; + CValidationState state; + if (tx.AcceptToMemoryPool(state, true, true, &fMissingInputs)) + { + RelayTransaction(tx, inv.hash, vMsg); + 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 (map::iterator mi = mapOrphanTransactionsByPrev[hashPrev].begin(); + mi != mapOrphanTransactionsByPrev[hashPrev].end(); + ++mi) + { + const CDataStream& vMsg = *((*mi).second); + CTransaction tx; + CDataStream(vMsg) >> tx; + CInv inv(MSG_TX, tx.GetHash()); + bool fMissingInputs2 = false; + // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get anyone relaying LegitTxX banned) + CValidationState stateDummy; + + if (tx.AcceptToMemoryPool(stateDummy, true, true, &fMissingInputs2)) + { + printf(" accepted orphan tx %s\n", inv.hash.ToString().c_str()); + RelayTransaction(tx, inv.hash, vMsg); + mapAlreadyAskedFor.erase(inv); + vWorkQueue.push_back(inv.hash); + vEraseQueue.push_back(inv.hash); + } + else if (!fMissingInputs2) + { + // invalid or too-little-fee orphan + vEraseQueue.push_back(inv.hash); + printf(" removed orphan tx %s\n", inv.hash.ToString().c_str()); + } + } + } + + BOOST_FOREACH(uint256 hash, vEraseQueue) + EraseOrphanTx(hash); + } + else if (fMissingInputs) + { + AddOrphanTx(vMsg); + + // 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); + } + int nDoS; + if (state.IsInvalid(nDoS)) + pfrom->Misbehaving(nDoS); + } + + + else if (strCommand == "block" && !fImporting && !fReindex) // Ignore blocks received while importing + { + CBlock block; + vRecv >> block; + + printf("received block %s\n", block.GetHash().ToString().c_str()); + // block.print(); + + CInv inv(MSG_BLOCK, block.GetHash()); + pfrom->AddInventoryKnown(inv); + + CValidationState state; + if (ProcessBlock(state, pfrom, &block)) + mapAlreadyAskedFor.erase(inv); + int nDoS; + if (state.IsInvalid(nDoS)) + pfrom->Misbehaving(nDoS); + } + + + else if (strCommand == "getaddr") + { + pfrom->vAddrToSend.clear(); + vector vAddr = addrman.GetAddr(); + BOOST_FOREACH(const CAddress &addr, vAddr) + pfrom->PushAddress(addr); + } + + + else if (strCommand == "mempool") + { + std::vector vtxid; + LOCK2(mempool.cs, pfrom->cs_filter); + mempool.queryHashes(vtxid); + vector vInv; + BOOST_FOREACH(uint256& hash, vtxid) { + CInv inv(MSG_TX, hash); + if ((pfrom->pfilter && pfrom->pfilter->IsRelevantAndUpdate(mempool.lookup(hash), hash)) || + (!pfrom->pfilter)) + vInv.push_back(inv); + if (vInv.size() == MAX_INV_SZ) + break; + } + if (vInv.size() > 0) + pfrom->PushMessage("inv", vInv); + } + + + else if (strCommand == "ping") + { + if (pfrom->nVersion > BIP0031_VERSION) + { + uint64 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 if (strCommand == "filterload") + { + CBloomFilter filter; + vRecv >> filter; + + if (!filter.IsWithinSizeConstraints()) + // There is no excuse for sending a too-large filter + pfrom->Misbehaving(100); + else + { + LOCK(pfrom->cs_filter); + delete pfrom->pfilter; + pfrom->pfilter = new CBloomFilter(filter); + } + pfrom->fRelayTxes = true; + } + + + else if (strCommand == "filteradd") + { + vector vData; + vRecv >> vData; + + // Nodes must NEVER send a data item > 520 bytes (the max size for a script data object, + // and thus, the maximum size any matched object can have) in a filteradd message + if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) + { + pfrom->Misbehaving(100); + } else { + LOCK(pfrom->cs_filter); + if (pfrom->pfilter) + pfrom->pfilter->insert(vData); + else + pfrom->Misbehaving(100); + } + } + + + else if (strCommand == "filterclear") + { + LOCK(pfrom->cs_filter); + delete pfrom->pfilter; + pfrom->pfilter = NULL; + pfrom->fRelayTxes = true; + } + + else if (strCommand == "checkpoint") // ppcoin synchronized 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 + { + // 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; +} + +// requires LOCK(cs_vRecvMsg) +bool ProcessMessages(CNode* pfrom) +{ + //if (fDebug) + // printf("ProcessMessages(%zu messages)\n", pfrom->vRecvMsg.size()); + + // + // Message format + // (4) message start + // (12) command + // (4) size + // (4) checksum + // (x) data + // + bool fOk = true; + + if (!pfrom->vRecvGetData.empty()) + ProcessGetData(pfrom); + + std::deque::iterator it = pfrom->vRecvMsg.begin(); + while (!pfrom->fDisconnect && it != pfrom->vRecvMsg.end()) { + // Don't bother if send buffer is too full to respond anyway + if (pfrom->nSendSize >= SendBufferSize()) + break; + + // get next message + CNetMessage& msg = *it; + + //if (fDebug) + // printf("ProcessMessages(message %u msgsz, %zu bytes, complete:%s)\n", + // msg.hdr.nMessageSize, msg.vRecv.size(), + // msg.complete() ? "Y" : "N"); + + // end, if an incomplete message is found + if (!msg.complete()) + break; + + // at this point, any failure means we can delete the current message + it++; + + // Scan for message start + if (memcmp(msg.hdr.pchMessageStart, pchMessageStart, sizeof(pchMessageStart)) != 0) { + printf("\n\nPROCESSMESSAGE: INVALID MESSAGESTART\n\n"); + fOk = false; + break; + } + + // Read header + CMessageHeader& hdr = msg.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; + + // Checksum + CDataStream& vRecv = msg.vRecv; + uint256 hash = Hash2(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; + } + + // Process message + bool fRet = false; + try + { + { + LOCK(cs_main); + fRet = ProcessMessage(pfrom, strCommand, vRecv); + } + boost::this_thread::interruption_point(); + } + 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 (boost::thread_interrupted) { + throw; + } + catch (std::exception& e) { + PrintExceptionContinue(&e, "ProcessMessages()"); + } catch (...) { + PrintExceptionContinue(NULL, "ProcessMessages()"); + } + + if (!fRet) + printf("ProcessMessage(%s, %u bytes) FAILED\n", strCommand.c_str(), nMessageSize); + } + + // In case the connection got shut down, its receive buffer was wiped + if (!pfrom->fDisconnect) + pfrom->vRecvMsg.erase(pfrom->vRecvMsg.begin(), it); + + return fOk; +} + + +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 > 30 * 60 && pto->vSendMsg.empty()) { + uint64 nonce = 0; + if (pto->nVersion > BIP0031_VERSION) + pto->PushMessage("ping", nonce); + else + pto->PushMessage("ping"); + } + + // Start block sync + if (pto->fStartSync && !fImporting && !fReindex) { + pto->fStartSync = false; + pto->PushGetBlocks(pindexBest, uint256(0)); + } + + // Resend wallet transactions that haven't gotten in a block yet + // Except during reindex, importing and IBD, when old wallet + // transactions become unconfirmed and spams other nodes. + if (!fReindex && !fImporting && !IsInitialBlockDownload()) + { + ResendWalletTransactions(); + } + + // Address refresh broadcast + static int64 nLastRebroadcast; + if (!IsInitialBlockDownload() && (GetTime() - nLastRebroadcast > 24 * 60 * 60)) + { + { + 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 = Hash2(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 nNow = GetTime() * 1000000; + while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow) + { + const CInv& inv = (*pto->mapAskFor.begin()).second; + if (!AlreadyHave(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(); + } + } + pto->mapAskFor.erase(pto->mapAskFor.begin()); + } + if (!vGetData.empty()) + pto->PushMessage("getdata", vGetData); + + } + return true; +} + + + + + + + + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// BitcoinMiner +// + +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]; +} + + +// +// ScanHash scans nonces looking for a hash with at least some zero bits. +// It operates on big endian data. Caller does the byte reversing. +// All input buffers are 16-byte aligned. nNonce is usually preserved +// between calls, but periodically or if nNonce is 0xffff0000 or above, +// the block is rebuilt and nNonce starts over at zero. +// +/* +unsigned int static ScanHash_CryptoPP(char* pmidstate, char* pdata, char* phash1, char* phash, unsigned int& nHashesDone) +{ + unsigned int& nNonce = *(unsigned int*)(pdata + 12); + for (;;) + { + // Crypto++ SHA256 + // Hash pdata using pmidstate as the starting state into + // pre-formatted buffer phash1, then hash phash1 into phash + nNonce++; + SHA256Transform(phash1, pdata, pmidstate); + SHA256Transform(phash, phash1, pSHA256InitState); + + // Return the nonce if the hash has at least some zero bits, + // caller will check if it has enough to reach the target + if (((unsigned short*)phash)[14] == 0) + return nNonce; + + // If nothing found after trying for a while, return -1 + if ((nNonce & 0xffff) == 0) + { + nHashesDone = 0xffff+1; + return (unsigned int) -1; + } + if ((nNonce & 0xfff) == 0) + boost::this_thread::interruption_point(); + } +} +*/ + +// 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().c_str(), dPriority, dFeePerKb); + BOOST_FOREACH(uint256 hash, setDependsOn) + printf(" setDependsOn %s\n", hash.ToString().c_str()); + } +}; + + +uint64 nLastBlockTx = 0; +uint64 nLastBlockSize = 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>(); + } + } +}; + +CBlockTemplate* CreateNewBlock(CReserveKey& reservekey) +{ + // Create new block + auto_ptr pblocktemplate(new CBlockTemplate()); + if(!pblocktemplate.get()) + return NULL; + CBlock *pblock = &pblocktemplate->block; // pointer for convenience + + // Create coinbase tx + CTransaction txNew; + txNew.vin.resize(1); + txNew.vin[0].prevout.SetNull(); + txNew.vout.resize(1); + CPubKey pubkey; + if (!reservekey.GetReservedKey(pubkey)) + return NULL; + txNew.vout[0].scriptPubKey << pubkey << OP_CHECKSIG; + + // Add our coinbase tx as first transaction + pblock->vtx.push_back(txNew); + pblocktemplate->vTxFees.push_back(-1); // updated at end + pblocktemplate->vTxSigOps.push_back(-1); // updated at end + + // 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)); + + // Special compatibility rule before 15 May: limit size to 500,000 bytes: +// if (GetAdjustedTime() < 1368576000) +// nBlockMaxSize = std::min(nBlockMaxSize, (unsigned int)(MAX_BLOCK_SIZE_GEN)); + + // 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); + + // Collect memory pool transactions into the block + int64 nFees = 0; + { + LOCK2(cs_main, mempool.cs); + CBlockIndex* pindexPrev = pindexBest; + CCoinsViewCache view(*pcoinsTip, true); + + // Priority order to process transactions + list vOrphan; // list memory doesn't move + map > mapDependers; + bool fPrintPriority = GetBoolArg("-printpriority"); + + // 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.IsFinal()) + continue; + + COrphan* porphan = NULL; + double dPriority = 0; + int64 nTotalIn = 0; + bool fMissingInputs = false; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + // Read prev transaction + if (!view.HaveCoins(txin.prevout.hash)) + { + // 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; + } + const CCoins &coins = view.GetCoins(txin.prevout.hash); + + int64 nValueIn = coins.vout[txin.prevout.n].nValue; + nTotalIn += nValueIn; + + int nConf = pindexPrev->nHeight - coins.nHeight + 1; + + 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 + uint64 nBlockSize = 1000; + uint64 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; + + // Skip free transactions if we're past the minimum block size: + if (fSortedByFee && (dFeePerKb < CTransaction::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); + } + + if (!tx.HaveInputs(view)) + continue; + + int64 nTxFees = tx.GetValueIn(view)-tx.GetValueOut(); + + nTxSigOps += tx.GetP2SHSigOpCount(view); + if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) + continue; + + CValidationState state; + if (!tx.CheckInputs(state, view, true, SCRIPT_VERIFY_P2SH)) + continue; + + CTxUndo txundo; + uint256 hash = tx.GetHash(); + tx.UpdateCoins(state, view, txundo, pindexPrev->nHeight+1, hash); + + // Added + pblock->vtx.push_back(tx); + pblocktemplate->vTxFees.push_back(nTxFees); + pblocktemplate->vTxSigOps.push_back(nTxSigOps); + nBlockSize += nTxSize; + ++nBlockTx; + nBlockSigOps += nTxSigOps; + nFees += nTxFees; + + if (fPrintPriority) + { + 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 + 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; + printf("CreateNewBlock(): total size %"PRI64u"\n", nBlockSize); + + // Fill in header + pblock->hashPrevBlock = pindexPrev->GetBlockHash(); + pblock->UpdateTime(pindexPrev); + pblock->nBits = GetNextWorkRequired(pindexPrev, pblock); + pblock->nNonce = 0; + + // Calculate nVvalue dependet nBits + pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees, pblock->nBits); + pblocktemplate->vTxFees[0] = -nFees; + + pblock->vtx[0].vin[0].scriptSig = CScript() << OP_0 << OP_0; + pblocktemplate->vTxSigOps[0] = pblock->vtx[0].GetLegacySigOpCount(); + + CBlockIndex indexDummy(*pblock); + indexDummy.pprev = pindexPrev; + indexDummy.nHeight = pindexPrev->nHeight + 1; + CCoinsViewCache viewNew(*pcoinsTip, true); + CValidationState state; + if (!pblock->ConnectBlock(state, &indexDummy, viewNew, true)) + throw std::runtime_error("CreateNewBlock() : ConnectBlock failed"); + } + + return pblocktemplate.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 hash = pblock->GetHash(); + uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + + if (hash > hashTarget) + return false; + + //// debug print + printf("GreenchainMiner:\n"); + printf("proof-of-work found \n hash: %s \ntarget: %s\n", hash.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("GreenchainMiner : 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[pblock->GetHash()] = 0; + } + + // Process this block the same as if we had received it from another node + CValidationState state; + if (!ProcessBlock(state, NULL, pblock)) + return error("GreenchainMiner : ProcessBlock, block not accepted"); + } + + return true; +} + +void static BitcoinMiner(CWallet *pwallet) +{ + printf("GreenchainMiner started\n"); + SetThreadPriority(THREAD_PRIORITY_LOWEST); + RenameThread("Greenchain-miner"); + + // Each thread has its own key and counter + CReserveKey reservekey(pwallet); + unsigned int nExtraNonce = 0; + + try { loop { + // disable in testing + while (vNodes.empty()) + MilliSleep(1000); + printf("Step after sleep\n"); + + // + // Create new block + // + unsigned int nTransactionsUpdatedLast = nTransactionsUpdated; + CBlockIndex* pindexPrev = pindexBest; + + auto_ptr pblocktemplate(CreateNewBlock(reservekey)); + if (!pblocktemplate.get()) + return; + CBlock *pblock = &pblocktemplate->block; + IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); + + printf("Running GreenchainMiner with %"PRIszu" transactions in block (%u bytes)\n", pblock->vtx.size(), + ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION)); + + // + // Pre-build hash buffers + // +/* + char pmidstatebuf[32+16]; char* pmidstate = alignup<16>(pmidstatebuf); + char pdatabuf[128+16]; char* pdata = alignup<16>(pdatabuf); + char phash1buf[64+16]; char* phash1 = alignup<16>(phash1buf); + + FormatHashBuffers(pblock, pmidstate, pdata, phash1); + + unsigned int& nBlockTime = *(unsigned int*)(pdata + 64 + 4); + unsigned int& nBlockBits = *(unsigned int*)(pdata + 64 + 8); + unsigned int& nBlockNonce = *(unsigned int*)(pdata + 64 + 12); + + */ + // + // Search + // +/* + int64 nStart = GetTime(); + uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + uint256 hashbuf[2]; + uint256& hash = *alignup<16>(hashbuf); +*/ + uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + int64 nStart = GetTime(); + uint256 hash; + // unsigned int nHashesDone = 0; + loop + { +// unsigned int nNonceFound; + + hash = pblock->GetHash(); + if (hash <= hashTarget){ + // nHashesDone += pblock->nNonce; + SetThreadPriority(THREAD_PRIORITY_NORMAL); + + printf("proof-of-work found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str()); + pblock->print(); + + CheckWork(pblock, *pwalletMain, reservekey); + SetThreadPriority(THREAD_PRIORITY_LOWEST); + break; + } + ++pblock->nNonce; + + // Meter hashes/sec + static int64 nHashCounter; + if (nHPSTimerStart == 0) + { + nHPSTimerStart = GetTimeMillis(); + nHashCounter = 0; + } + else + // nHashCounter += nHashesDone; + nHashCounter += 1; + if (GetTimeMillis() - nHPSTimerStart > 4000) + { + static CCriticalSection cs; + { + LOCK(cs); + if (GetTimeMillis() - nHPSTimerStart > 4000) + { + dHashesPerSec = 1000.0 * nHashCounter / (GetTimeMillis() - nHPSTimerStart); + nHPSTimerStart = GetTimeMillis(); + nHashCounter = 0; + //static int64 nLogTime; + //if (GetTime() - nLogTime > 30 * 60) + //{ + // nLogTime = GetTime(); + printf("hashmeter %6.0f khash/s\n", dHashesPerSec/1000.0); + //} + } + } + } + + // Check for stop or if block needs to be rebuilt + boost::this_thread::interruption_point(); + // disable in testing + if (vNodes.empty()) + break; + if (++pblock->nNonce >= 0xffff0000) + break; + if (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60) + break; + if (pindexPrev != pindexBest) + break; + + // Update nTime every few seconds + pblock->UpdateTime(pindexPrev); + //nBlockTime = ByteReverse(pblock->nTime); + if (fTestNet) + { + // Changing pblock->nTime can change work required on testnet: + //nBlockBits = ByteReverse(pblock->nBits); + hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + } + } + } } + catch (boost::thread_interrupted) + { + printf("GreenchainMiner terminated\n"); + throw; + } +} + +void GenerateBitcoins(bool fGenerate, CWallet* pwallet) +{ + static boost::thread_group* minerThreads = NULL; + + int nThreads = GetArg("-genproclimit", -1); + if (nThreads < 0) + nThreads = boost::thread::hardware_concurrency(); + + if (minerThreads != NULL) + { + minerThreads->interrupt_all(); + delete minerThreads; + minerThreads = NULL; + } + + if (nThreads == 0 || !fGenerate) + return; + + minerThreads = new boost::thread_group(); + for (int i = 0; i < nThreads; i++) + minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet)); +} + +// Amount compression: +// * If the amount is 0, output 0 +// * first, divide the amount (in base units) by the largest power of 10 possible; call the exponent e (e is max 9) +// * if e<9, the last digit of the resulting number cannot be 0; store it as d, and drop it (divide by 10) +// * call the result n +// * output 1 + 10*(9*n + d - 1) + e +// * if e==9, we only know the resulting number is not zero, so output 1 + 10*(n - 1) + 9 +// (this is decodable, as d is in [1-9] and e is in [0-9]) + +uint64 CTxOutCompressor::CompressAmount(uint64 n) +{ + if (n == 0) + return 0; + int e = 0; + while (((n % 10) == 0) && e < 9) { + n /= 10; + e++; + } + if (e < 9) { + int d = (n % 10); + assert(d >= 1 && d <= 9); + n /= 10; + return 1 + (n*9 + d - 1)*10 + e; + } else { + return 1 + (n - 1)*10 + 9; + } +} + +uint64 CTxOutCompressor::DecompressAmount(uint64 x) +{ + // x = 0 OR x = 1+10*(9*n + d - 1) + e OR x = 1+10*(n - 1) + 9 + if (x == 0) + return 0; + x--; + // x = 10*(9*n + d - 1) + e + int e = x % 10; + x /= 10; + uint64 n = 0; + if (e < 9) { + // x = 9*n + d - 1 + int d = (x % 9) + 1; + x /= 9; + // x = n + n = x*10 + d; + } else { + n = x+1; + } + while (e) { + n *= 10; + e--; + } + return n; +} + + +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 + std::map::iterator it3 = mapOrphanTransactions.begin(); + for (; it3 != mapOrphanTransactions.end(); it3++) + delete (*it3).second; + mapOrphanTransactions.clear(); + } +} instance_of_cmaincleanup; diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..515dc5e --- /dev/null +++ b/src/main.h @@ -0,0 +1,2316 @@ +// 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 "bignum.h" +#include "sync.h" +#include "net.h" +#include "script.h" +#include "hashblock.h" + +#include + +class CWallet; +class CBlock; +class CBlockIndex; +class CKeyItem; +class CReserveKey; + +class CAddress; +class CInv; +class CNode; + +struct CBlockIndexWorkComparator; + +/** The maximum allowed size for a serialized block, in bytes (network rule) */ +static const unsigned int MAX_BLOCK_SIZE = 1000000; +/** The maximum transaction comment size */ +static const unsigned int MAX_TX_COMMENT_LEN = 140; +/** The maximum size for mined blocks */ +static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2; +/** The maximum size for transactions we're willing to relay/mine */ +static const unsigned int MAX_STANDARD_TX_SIZE = MAX_BLOCK_SIZE_GEN/5; +/** The maximum allowed number of signature check operations in a block (network rule) */ +static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50; +/** The maximum number of orphan transactions kept in memory */ +static const unsigned int MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100; +/** The maximum number of entries in an 'inv' protocol message */ +static const unsigned int MAX_INV_SZ = 50000; +/** The maximum size of a blk?????.dat file (since 0.8) */ +static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB +/** The pre-allocation chunk size for blk?????.dat files (since 0.8) */ +static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB +/** The pre-allocation chunk size for rev?????.dat files (since 0.8) */ +static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB +/** Fake height value used in CCoins to signify they are only in the memory pool (since 0.8) */ +static const unsigned int MEMPOOL_HEIGHT = 0x7FFFFFFF; +/** No amount larger than this (in satoshi) is valid */ +static const int64 MAX_MONEY = 1000000000 * COIN; // +inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } +/** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */ +static const int COINBASE_MATURITY = 40; +/** 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; +#ifdef USE_UPNP +static const int fHaveUPnP = true; +#else +static const int fHaveUPnP = false; +#endif + + +extern CScript COINBASE_FLAGS; + + + + + + +extern CCriticalSection cs_main; +extern std::map mapBlockIndex; +extern std::set setBlockIndexValid; +extern uint256 hashGenesisBlock; +extern CBlockIndex* pindexGenesisBlock; +extern int nBestHeight; +extern uint256 nBestChainWork; +extern uint256 nBestInvalidWork; +extern uint256 hashBestChain; +extern CBlockIndex* pindexBest; +extern unsigned int nTransactionsUpdated; +extern uint64 nLastBlockTx; +extern uint64 nLastBlockSize; +extern const std::string strMessageMagic; +extern double dHashesPerSec; +extern int64 nHPSTimerStart; +extern int64 nTimeBestReceived; +extern CCriticalSection cs_setpwalletRegistered; +extern std::set setpwalletRegistered; +extern std::map mapOrphanBlocks; +extern unsigned char pchMessageStart[4]; +extern bool fImporting; +extern bool fReindex; +extern bool fBenchmark; +extern int nScriptCheckThreads; +extern bool fTxIndex; +extern unsigned int nCoinCacheSize; + +// Settings +extern int64 nTransactionFee; + +// Minimum disk space required - used in CheckDiskSpace() +static const uint64 nMinDiskSpace = 52428800; + + +class CReserveKey; +class CCoinsDB; +class CBlockTreeDB; +struct CDiskBlockPos; +class CCoins; +class CTxUndo; +class CCoinsView; +class CCoinsViewCache; +class CScriptCheck; +class CValidationState; + +struct CBlockTemplate; + +/** Register a wallet to receive updates from core */ +void RegisterWallet(CWallet* pwalletIn); +/** Unregister a wallet from core */ +void UnregisterWallet(CWallet* pwalletIn); +/** Push an updated transaction to all registered wallets */ +void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false); +/** Process an incoming block */ +bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp = NULL); +/** Check whether enough disk space is available for an incoming block */ +bool CheckDiskSpace(uint64 nAdditionalBytes = 0); +/** Open a block file (blk?????.dat) */ +FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly = false); +/** Open an undo file (rev?????.dat) */ +FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false); +/** Import blocks from an external file */ +bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp = NULL); +/** Initialize a new block tree database + block data on disk */ +bool InitBlockIndex(); +/** Load the block tree and coins database from disk */ +bool LoadBlockIndex(); +/** Unload database information */ +void UnloadBlockIndex(); +/** Verify consistency of the block and coin databases */ +bool VerifyDB(); +/** Print the loaded block tree */ +void PrintBlockTree(); +/** Find a block by height in the currently-connected chain */ +CBlockIndex* FindBlockByHeight(int nHeight); +/** Process protocol messages received from a given node */ +bool ProcessMessages(CNode* pfrom); +/** Send queued protocol messages to be sent to a give node */ +bool SendMessages(CNode* pto, bool fSendTrickle); +/** Run an instance of the script checking thread */ +void ThreadScriptCheck(); +/** Run the miner threads */ +void GenerateBitcoins(bool fGenerate, CWallet* pwallet); +/** Generate a new block, without valid proof-of-work */ +CBlockTemplate* CreateNewBlock(CReserveKey& reservekey); +/** 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 block */ +bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey); +/** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */ +bool CheckProofOfWork(uint256 hash, unsigned int nBits); +/** Calculate the minimum amount of work a received block needs, without knowing its direct parent */ +unsigned int ComputeMinWork(unsigned int nBase, int64 nTime); +/** Get the number of active peers */ +int GetNumBlocksOfPeers(); +/** Check whether we are doing an initial block download (synchronizing from disk or network) */ +bool IsInitialBlockDownload(); +/** Format a string that describes several potential problems detected by the core */ +std::string GetWarnings(std::string strFor); +/** Retrieve a transaction (from memory pool, or from disk, if possible) */ +bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, bool fAllowSlow = false); +/** Connect/disconnect blocks until pindexNew is the new tip of the active block chain */ +bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew); +/** Find the best known block, and make it the tip of the block chain */ +bool ConnectBestBlock(CValidationState &state); +/** Create a new block index entry for a given block hash */ +CBlockIndex * InsertBlockIndex(uint256 hash); +/** Verify a signature */ +bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); +/** Abort with a message */ +bool AbortNode(const std::string &msg); + + + + + + + + + + + +bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut); + +struct CDiskBlockPos +{ +public: + int nFile; + unsigned int nPos; + + IMPLEMENT_SERIALIZE( + READWRITE(VARINT(nFile)); + READWRITE(VARINT(nPos)); + ) + + CDiskBlockPos() { + SetNull(); + } + + CDiskBlockPos(int nFileIn, unsigned int nPosIn) { + nFile = nFileIn; + nPos = nPosIn; + } + + friend bool operator==(const CDiskBlockPos &a, const CDiskBlockPos &b) { + return (a.nFile == b.nFile && a.nPos == b.nPos); + } + + friend bool operator!=(const CDiskBlockPos &a, const CDiskBlockPos &b) { + return !(a == b); + } + + void SetNull() { nFile = -1; nPos = 0; } + bool IsNull() const { return (nFile == -1); } +}; + +struct CDiskTxPos : public CDiskBlockPos +{ + unsigned int nTxOffset; // after header + + IMPLEMENT_SERIALIZE( + READWRITE(*(CDiskBlockPos*)this); + READWRITE(VARINT(nTxOffset)); + ) + + CDiskTxPos(const CDiskBlockPos &blockIn, unsigned int nTxOffsetIn) : CDiskBlockPos(blockIn.nFile, blockIn.nPos), nTxOffset(nTxOffsetIn) { + } + + CDiskTxPos() { + SetNull(); + } + + void SetNull() { + CDiskBlockPos::SetNull(); + nTxOffset = 0; + } +}; + + +/** An inpoint - a combination of a transaction and an index n into its vin */ +class CInPoint +{ +public: + CTransaction* ptx; + unsigned int n; + + CInPoint() { SetNull(); } + CInPoint(CTransaction* ptxIn, unsigned int nIn) { ptx = ptxIn; n = nIn; } + void SetNull() { ptx = NULL; n = (unsigned int) -1; } + bool IsNull() const { return (ptx == NULL && n == (unsigned int) -1); } +}; + + + +/** An outpoint - a combination of a transaction hash and an index n into its vout */ +class COutPoint +{ +public: + uint256 hash; + unsigned int n; + + COutPoint() { SetNull(); } + COutPoint(uint256 hashIn, unsigned int nIn) { hash = hashIn; n = nIn; } + IMPLEMENT_SERIALIZE( READWRITE(FLATDATA(*this)); ) + void SetNull() { hash = 0; n = (unsigned int) -1; } + bool IsNull() const { return (hash == 0 && n == (unsigned int) -1); } + + friend bool operator<(const COutPoint& a, const COutPoint& b) + { + return (a.hash < b.hash || (a.hash == b.hash && a.n < b.n)); + } + + friend bool operator==(const COutPoint& a, const COutPoint& b) + { + return (a.hash == b.hash && a.n == b.n); + } + + friend bool operator!=(const COutPoint& a, const COutPoint& b) + { + return !(a == b); + } + + std::string ToString() const + { + return strprintf("COutPoint(%s, %u)", hash.ToString().c_str(), n); + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + + + +/** An input of a transaction. It contains the location of the previous + * transaction's output that it claims and a signature that matches the + * output's public key. + */ +class CTxIn +{ +public: + COutPoint prevout; + CScript scriptSig; + unsigned int nSequence; + + CTxIn() + { + nSequence = 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 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 nValue; + CScript scriptPubKey; + + CTxOut() + { + SetNull(); + } + + CTxOut(int64 nValueIn, CScript scriptPubKeyIn) + { + nValue = nValueIn; + scriptPubKey = scriptPubKeyIn; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(nValue); + READWRITE(scriptPubKey); + ) + + void SetNull() + { + nValue = -1; + scriptPubKey.clear(); + } + + bool IsNull() const + { + return (nValue == -1); + } + + 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); + } + + bool IsDust() const; + + std::string ToString() const + { + if (scriptPubKey.size() < 6) + return "CTxOut(error)"; + + return strprintf("CTxOut(nValue=%"PRI64d".%05"PRI64d", scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,30).c_str()); + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + + +enum GetMinFee_mode +{ + GMF_BLOCK, + GMF_RELAY, + GMF_SEND, +}; + +/** 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 int64 nMinTxFee; + static int64 nMinRelayTxFee; + static const int CURRENT_VERSION = 1; + static const int TXCOMMENT_VERSION = 2; + int nVersion; + std::vector vin; + std::vector vout; + unsigned int nLockTime; + std::string strTxComment; + + CTransaction() + { + SetNull(); + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(vin); + READWRITE(vout); + READWRITE(nLockTime); + if(this->nVersion >= TXCOMMENT_VERSION) { + READWRITE(strTxComment); } + ) + + void SetNull() + { + nVersion = CTransaction::CURRENT_VERSION; + vin.clear(); + vout.clear(); + nLockTime = 0; + strTxComment.clear(); + } + + bool IsNull() const + { + return (vin.empty() && vout.empty()); + } + + uint256 GetHash() const + { + return SerializeHash(*this); + } + + bool IsFinal(int nBlockHeight=0, int64 nBlockTime=0) const + { + // Time based nLockTime implemented in 0.1.6 + if (nLockTime == 0) + return true; + if (nBlockHeight == 0) + nBlockHeight = nBestHeight; + if (nBlockTime == 0) + nBlockTime = GetAdjustedTime(); + if ((int64)nLockTime < ((int64)nLockTime < LOCKTIME_THRESHOLD ? (int64)nBlockHeight : nBlockTime)) + return true; + BOOST_FOREACH(const CTxIn& txin, vin) + if (!txin.IsFinal()) + return false; + return true; + } + + bool IsNewerThan(const CTransaction& old) const + { + if (vin.size() != old.vin.size()) + return false; + for (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()); + } + + /** Check for standard transaction types + @return True if all outputs (scriptPubKeys) use only standard transaction forms + */ + bool IsStandard() const; + + /** 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 + */ + bool AreInputsStandard(CCoinsViewCache& 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 + */ + 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 + */ + unsigned int GetP2SHSigOpCount(CCoinsViewCache& mapInputs) const; + + /** Amount of bitcoins spent by this transaction. + @return sum of all outputs (note: does not include fees) + */ + int64 GetValueOut() const + { + int64 nValueOut = 0; + BOOST_FOREACH(const CTxOut& txout, vout) + { + nValueOut += txout.nValue; + if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut)) + throw std::runtime_error("CTransaction::GetValueOut() : value out of range"); + } + return nValueOut; + } + + /** 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) + */ + int64 GetValueIn(CCoinsViewCache& mapInputs) const; + + static bool AllowFree(double dPriority) + { + // Large (in bytes) low-priority (new, small-coin) transactions + // need a fee. + return dPriority > COIN * 1440 / 250; + } + + int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=true, enum GetMinFee_mode mode=GMF_BLOCK) const; + + friend bool operator==(const CTransaction& a, const CTransaction& b) + { + return (a.nVersion == b.nVersion && + a.vin == b.vin && + a.vout == b.vout && + a.nLockTime == b.nLockTime); + } + + friend bool operator!=(const CTransaction& a, const CTransaction& b) + { + return !(a == b); + } + + + std::string ToString() const + { + std::string str; + str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%"PRIszu", vout.size=%"PRIszu", nLockTime=%u, strTxComment=%s)\n", + GetHash().ToString().c_str(), + nVersion, + vin.size(), + vout.size(), + nLockTime, + strTxComment.substr(0,30).c_str()); + 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()); + } + + + // Check whether all prevouts of this transaction are present in the UTXO set represented by view + bool HaveInputs(CCoinsViewCache &view) const; + + // Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts) + // This does not modify the UTXO set. If pvChecks is not NULL, script checks are pushed onto it + // instead of being performed inline. + bool CheckInputs(CValidationState &state, CCoinsViewCache &view, bool fScriptChecks = true, + unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, + std::vector *pvChecks = NULL) const; + + // Apply the effects of this transaction on the UTXO set represented by view + void UpdateCoins(CValidationState &state, CCoinsViewCache &view, CTxUndo &txundo, int nHeight, const uint256 &txhash) const; + + // Context-independent validity checks + bool CheckTransaction(CValidationState &state) const; + + // Try to accept this transaction into the memory pool + bool AcceptToMemoryPool(CValidationState &state, bool fCheckInputs=true, bool fLimitFree = true, bool* pfMissingInputs=NULL); + +protected: + static const CTxOut &GetOutputFor(const CTxIn& input, CCoinsViewCache& mapInputs); +}; + +/** wrapper for CTxOut that provides a more compact serialization */ +class CTxOutCompressor +{ +private: + CTxOut &txout; + +public: + static uint64 CompressAmount(uint64 nAmount); + static uint64 DecompressAmount(uint64 nAmount); + + CTxOutCompressor(CTxOut &txoutIn) : txout(txoutIn) { } + + IMPLEMENT_SERIALIZE(({ + if (!fRead) { + uint64 nVal = CompressAmount(txout.nValue); + READWRITE(VARINT(nVal)); + } else { + uint64 nVal = 0; + READWRITE(VARINT(nVal)); + txout.nValue = DecompressAmount(nVal); + } + CScriptCompressor cscript(REF(txout.scriptPubKey)); + READWRITE(cscript); + });) +}; + +/** Undo information for a CTxIn + * + * Contains the prevout's CTxOut being spent, and if this was the + * last output of the affected transaction, its metadata as well + * (coinbase or not, height, transaction version) + */ +class CTxInUndo +{ +public: + CTxOut txout; // the txout data before being spent + bool fCoinBase; // if the outpoint was the last unspent: whether it belonged to a coinbase + unsigned int nHeight; // if the outpoint was the last unspent: its height + int nVersion; // if the outpoint was the last unspent: its version + + CTxInUndo() : txout(), fCoinBase(false), nHeight(0), nVersion(0) {} + CTxInUndo(const CTxOut &txoutIn, bool fCoinBaseIn = false, unsigned int nHeightIn = 0, int nVersionIn = 0) : txout(txoutIn), fCoinBase(fCoinBaseIn), nHeight(nHeightIn), nVersion(nVersionIn) { } + + unsigned int GetSerializeSize(int nType, int nVersion) const { + return ::GetSerializeSize(VARINT(nHeight*2+(fCoinBase ? 1 : 0)), nType, nVersion) + + (nHeight > 0 ? ::GetSerializeSize(VARINT(this->nVersion), nType, nVersion) : 0) + + ::GetSerializeSize(CTxOutCompressor(REF(txout)), nType, nVersion); + } + + template + void Serialize(Stream &s, int nType, int nVersion) const { + ::Serialize(s, VARINT(nHeight*2+(fCoinBase ? 1 : 0)), nType, nVersion); + if (nHeight > 0) + ::Serialize(s, VARINT(this->nVersion), nType, nVersion); + ::Serialize(s, CTxOutCompressor(REF(txout)), nType, nVersion); + } + + template + void Unserialize(Stream &s, int nType, int nVersion) { + unsigned int nCode = 0; + ::Unserialize(s, VARINT(nCode), nType, nVersion); + nHeight = nCode / 2; + fCoinBase = nCode & 1; + if (nHeight > 0) + ::Unserialize(s, VARINT(this->nVersion), nType, nVersion); + ::Unserialize(s, REF(CTxOutCompressor(REF(txout))), nType, nVersion); + } +}; + +/** Undo information for a CTransaction */ +class CTxUndo +{ +public: + // undo information for all txins + std::vector vprevout; + + IMPLEMENT_SERIALIZE( + READWRITE(vprevout); + ) +}; + +/** Undo information for a CBlock */ +class CBlockUndo +{ +public: + std::vector vtxundo; // for all but the coinbase + + IMPLEMENT_SERIALIZE( + READWRITE(vtxundo); + ) + + bool WriteToDisk(CDiskBlockPos &pos, const uint256 &hashBlock) + { + // Open history file to append + CAutoFile fileout = CAutoFile(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); + if (!fileout) + return error("CBlockUndo::WriteToDisk() : OpenUndoFile failed"); + + // Write index header + unsigned int nSize = fileout.GetSerializeSize(*this); + fileout << FLATDATA(pchMessageStart) << nSize; + + // Write undo data + long fileOutPos = ftell(fileout); + if (fileOutPos < 0) + return error("CBlockUndo::WriteToDisk() : ftell failed"); + pos.nPos = (unsigned int)fileOutPos; + fileout << *this; + + // calculate & write checksum + CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); + hasher << hashBlock; + hasher << *this; + fileout << hasher.GetHash(); + + // Flush stdio buffers and commit to disk before returning + fflush(fileout); + if (!IsInitialBlockDownload()) + FileCommit(fileout); + + return true; + } + + bool ReadFromDisk(const CDiskBlockPos &pos, const uint256 &hashBlock) + { + // Open history file to read + CAutoFile filein = CAutoFile(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION); + if (!filein) + return error("CBlockUndo::ReadFromDisk() : OpenBlockFile failed"); + + // Read block + uint256 hashChecksum; + try { + filein >> *this; + filein >> hashChecksum; + } + catch (std::exception &e) { + return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__); + } + + // Verify checksum + CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); + hasher << hashBlock; + hasher << *this; + if (hashChecksum != hasher.GetHash()) + return error("CBlockUndo::ReadFromDisk() : checksum mismatch"); + + return true; + } +}; + +/** pruned version of CTransaction: only retains metadata and unspent transaction outputs + * + * Serialized format: + * - VARINT(nVersion) + * - VARINT(nCode) + * - unspentness bitvector, for vout[2] and further; least significant byte first + * - the non-spent CTxOuts (via CTxOutCompressor) + * - VARINT(nHeight) + * + * The nCode value consists of: + * - bit 1: IsCoinBase() + * - bit 2: vout[0] is not spent + * - bit 4: vout[1] is not spent + * - The higher bits encode N, the number of non-zero bytes in the following bitvector. + * - In case both bit 2 and bit 4 are unset, they encode N-1, as there must be at + * least one non-spent output). + * + * Example: 0104835800816115944e077fe7c803cfa57f29b36bf87c1d358bb85e + * <><><--------------------------------------------><----> + * | \ | / + * version code vout[1] height + * + * - version = 1 + * - code = 4 (vout[1] is not spent, and 0 non-zero bytes of bitvector follow) + * - unspentness bitvector: as 0 non-zero bytes follow, it has length 0 + * - vout[1]: 835800816115944e077fe7c803cfa57f29b36bf87c1d35 + * * 8358: compact amount representation for 60000000000 (600 BTC) + * * 00: special txout type pay-to-pubkey-hash + * * 816115944e077fe7c803cfa57f29b36bf87c1d35: address uint160 + * - height = 203998 + * + * + * Example: 0109044086ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4eebbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa486af3b + * <><><--><--------------------------------------------------><----------------------------------------------><----> + * / \ \ | | / + * version code unspentness vout[4] vout[16] height + * + * - version = 1 + * - code = 9 (coinbase, neither vout[0] or vout[1] are unspent, + * 2 (1, +1 because both bit 2 and bit 4 are unset) non-zero bitvector bytes follow) + * - unspentness bitvector: bits 2 (0x04) and 14 (0x4000) are set, so vout[2+2] and vout[14+2] are unspent + * - vout[4]: 86ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4ee + * * 86ef97d579: compact amount representation for 234925952 (2.35 BTC) + * * 00: special txout type pay-to-pubkey-hash + * * 61b01caab50f1b8e9c50a5057eb43c2d9563a4ee: address uint160 + * - vout[16]: bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4 + * * bbd123: compact amount representation for 110397 (0.001 BTC) + * * 00: special txout type pay-to-pubkey-hash + * * 8c988f1a4a4de2161e0f50aac7f17e7f9555caa4: address uint160 + * - height = 120891 + */ +class CCoins +{ +public: + // whether transaction is a coinbase + bool fCoinBase; + + // unspent transaction outputs; spent outputs are .IsNull(); spent outputs at the end of the array are dropped + std::vector vout; + + // at which height this transaction was included in the active block chain + int nHeight; + + // version of the CTransaction; accesses to this value should probably check for nHeight as well, + // as new tx version will probably only be introduced at certain heights + int nVersion; + + // construct a CCoins from a CTransaction, at a given height + CCoins(const CTransaction &tx, int nHeightIn) : fCoinBase(tx.IsCoinBase()), vout(tx.vout), nHeight(nHeightIn), nVersion(tx.nVersion) { } + + // empty constructor + CCoins() : fCoinBase(false), vout(0), nHeight(0), nVersion(0) { } + + // remove spent outputs at the end of vout + void Cleanup() { + while (vout.size() > 0 && vout.back().IsNull()) + vout.pop_back(); + if (vout.empty()) + std::vector().swap(vout); + } + + void swap(CCoins &to) { + std::swap(to.fCoinBase, fCoinBase); + to.vout.swap(vout); + std::swap(to.nHeight, nHeight); + std::swap(to.nVersion, nVersion); + } + + // equality test + friend bool operator==(const CCoins &a, const CCoins &b) { + return a.fCoinBase == b.fCoinBase && + a.nHeight == b.nHeight && + a.nVersion == b.nVersion && + a.vout == b.vout; + } + friend bool operator!=(const CCoins &a, const CCoins &b) { + return !(a == b); + } + + // calculate number of bytes for the bitmask, and its number of non-zero bytes + // each bit in the bitmask represents the availability of one output, but the + // availabilities of the first two outputs are encoded separately + void CalcMaskSize(unsigned int &nBytes, unsigned int &nNonzeroBytes) const { + unsigned int nLastUsedByte = 0; + for (unsigned int b = 0; 2+b*8 < vout.size(); b++) { + bool fZero = true; + for (unsigned int i = 0; i < 8 && 2+b*8+i < vout.size(); i++) { + if (!vout[2+b*8+i].IsNull()) { + fZero = false; + continue; + } + } + if (!fZero) { + nLastUsedByte = b + 1; + nNonzeroBytes++; + } + } + nBytes += nLastUsedByte; + } + + bool IsCoinBase() const { + return fCoinBase; + } + + unsigned int GetSerializeSize(int nType, int nVersion) const { + unsigned int nSize = 0; + unsigned int nMaskSize = 0, nMaskCode = 0; + CalcMaskSize(nMaskSize, nMaskCode); + bool fFirst = vout.size() > 0 && !vout[0].IsNull(); + bool fSecond = vout.size() > 1 && !vout[1].IsNull(); + assert(fFirst || fSecond || nMaskCode); + unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0); + // version + nSize += ::GetSerializeSize(VARINT(this->nVersion), nType, nVersion); + // size of header code + nSize += ::GetSerializeSize(VARINT(nCode), nType, nVersion); + // spentness bitmask + nSize += nMaskSize; + // txouts themself + for (unsigned int i = 0; i < vout.size(); i++) + if (!vout[i].IsNull()) + nSize += ::GetSerializeSize(CTxOutCompressor(REF(vout[i])), nType, nVersion); + // height + nSize += ::GetSerializeSize(VARINT(nHeight), nType, nVersion); + return nSize; + } + + template + void Serialize(Stream &s, int nType, int nVersion) const { + unsigned int nMaskSize = 0, nMaskCode = 0; + CalcMaskSize(nMaskSize, nMaskCode); + bool fFirst = vout.size() > 0 && !vout[0].IsNull(); + bool fSecond = vout.size() > 1 && !vout[1].IsNull(); + assert(fFirst || fSecond || nMaskCode); + unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0); + // version + ::Serialize(s, VARINT(this->nVersion), nType, nVersion); + // header code + ::Serialize(s, VARINT(nCode), nType, nVersion); + // spentness bitmask + for (unsigned int b = 0; b + void Unserialize(Stream &s, int nType, int nVersion) { + unsigned int nCode = 0; + // version + ::Unserialize(s, VARINT(this->nVersion), nType, nVersion); + // header code + ::Unserialize(s, VARINT(nCode), nType, nVersion); + fCoinBase = nCode & 1; + std::vector vAvail(2, false); + vAvail[0] = nCode & 2; + vAvail[1] = nCode & 4; + unsigned int nMaskCode = (nCode / 8) + ((nCode & 6) != 0 ? 0 : 1); + // spentness bitmask + while (nMaskCode > 0) { + unsigned char chAvail = 0; + ::Unserialize(s, chAvail, nType, nVersion); + for (unsigned int p = 0; p < 8; p++) { + bool f = (chAvail & (1 << p)) != 0; + vAvail.push_back(f); + } + if (chAvail != 0) + nMaskCode--; + } + // txouts themself + vout.assign(vAvail.size(), CTxOut()); + for (unsigned int i = 0; i < vAvail.size(); i++) { + if (vAvail[i]) + ::Unserialize(s, REF(CTxOutCompressor(vout[i])), nType, nVersion); + } + // coinbase height + ::Unserialize(s, VARINT(nHeight), nType, nVersion); + Cleanup(); + } + + // mark an outpoint spent, and construct undo information + bool Spend(const COutPoint &out, CTxInUndo &undo) { + if (out.n >= vout.size()) + return false; + if (vout[out.n].IsNull()) + return false; + undo = CTxInUndo(vout[out.n]); + vout[out.n].SetNull(); + Cleanup(); + if (vout.size() == 0) { + undo.nHeight = nHeight; + undo.fCoinBase = fCoinBase; + undo.nVersion = this->nVersion; + } + return true; + } + + // mark a vout spent + bool Spend(int nPos) { + CTxInUndo undo; + COutPoint out(0, nPos); + return Spend(out, undo); + } + + // check whether a particular output is still available + bool IsAvailable(unsigned int nPos) const { + return (nPos < vout.size() && !vout[nPos].IsNull()); + } + + // check whether the entire CCoins is spent + // note that only !IsPruned() CCoins can be serialized + bool IsPruned() const { + BOOST_FOREACH(const CTxOut &out, vout) + if (!out.IsNull()) + return false; + return true; + } +}; + +/** 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 CCoins& 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; + int 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(bool fCheckInputs=true, bool fLimitFree=true); +}; + + + + + +/** Data structure that represents a partial merkle tree. + * + * It respresents a subset of the txid's of a known block, in a way that + * allows recovery of the list of txid's and the merkle root, in an + * authenticated way. + * + * The encoding works as follows: we traverse the tree in depth-first order, + * storing a bit for each traversed node, signifying whether the node is the + * parent of at least one matched leaf txid (or a matched txid itself). In + * case we are at the leaf level, or this bit is 0, its merkle node hash is + * stored, and its children are not explorer further. Otherwise, no hash is + * stored, but we recurse into both (or the only) child branch. During + * decoding, the same depth-first traversal is performed, consuming bits and + * hashes as they written during encoding. + * + * The serialization is fixed and provides a hard guarantee about the + * encoded size: + * + * SIZE <= 10 + ceil(32.25*N) + * + * Where N represents the number of leaf nodes of the partial tree. N itself + * is bounded by: + * + * N <= total_transactions + * N <= 1 + matched_transactions*tree_height + * + * The serialization format: + * - uint32 total_transactions (4 bytes) + * - varint number of hashes (1-3 bytes) + * - uint256[] hashes in depth-first order (<= 32*N bytes) + * - varint number of bytes of flag bits (1-3 bytes) + * - byte[] flag bits, packed per 8 in a byte, least significant bit first (<= 2*N-1 bits) + * The size constraints follow from this. + */ +class CPartialMerkleTree +{ +protected: + // the total number of transactions in the block + unsigned int nTransactions; + + // node-is-parent-of-matched-txid bits + std::vector vBits; + + // txids and internal hashes + std::vector vHash; + + // flag set when encountering invalid data + bool fBad; + + // helper function to efficiently calculate the number of nodes at given height in the merkle tree + unsigned int CalcTreeWidth(int height) { + return (nTransactions+(1 << height)-1) >> height; + } + + // calculate the hash of a node in the merkle tree (at leaf level: the txid's themself) + uint256 CalcHash(int height, unsigned int pos, const std::vector &vTxid); + + // recursive function that traverses tree nodes, storing the data as bits and hashes + void TraverseAndBuild(int height, unsigned int pos, const std::vector &vTxid, const std::vector &vMatch); + + // recursive function that traverses tree nodes, consuming the bits and hashes produced by TraverseAndBuild. + // it returns the hash of the respective node. + uint256 TraverseAndExtract(int height, unsigned int pos, unsigned int &nBitsUsed, unsigned int &nHashUsed, std::vector &vMatch); + +public: + + // serialization implementation + IMPLEMENT_SERIALIZE( + READWRITE(nTransactions); + READWRITE(vHash); + std::vector vBytes; + if (fRead) { + READWRITE(vBytes); + CPartialMerkleTree &us = *(const_cast(this)); + us.vBits.resize(vBytes.size() * 8); + for (unsigned int p = 0; p < us.vBits.size(); p++) + us.vBits[p] = (vBytes[p / 8] & (1 << (p % 8))) != 0; + us.fBad = false; + } else { + vBytes.resize((vBits.size()+7)/8); + for (unsigned int p = 0; p < vBits.size(); p++) + vBytes[p / 8] |= vBits[p] << (p % 8); + READWRITE(vBytes); + } + ) + + // Construct a partial merkle tree from a list of transaction id's, and a mask that selects a subset of them + CPartialMerkleTree(const std::vector &vTxid, const std::vector &vMatch); + + CPartialMerkleTree(); + + // extract the matching txid's represented by this partial merkle tree. + // returns the merkle root, or 0 in case of failure + uint256 ExtractMatches(std::vector &vMatch); +}; + + +/** 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. + */ +class CBlockHeader +{ +public: + // header + static const int CURRENT_VERSION=112; + int nVersion; + uint256 hashPrevBlock; + uint256 hashMerkleRoot; + unsigned int nTime; + unsigned int nBits; + unsigned int nNonce; + + CBlockHeader() + { + SetNull(); + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(hashPrevBlock); + READWRITE(hashMerkleRoot); + READWRITE(nTime); + READWRITE(nBits); + READWRITE(nNonce); + ) + + void SetNull() + { + nVersion = CBlockHeader::CURRENT_VERSION; + hashPrevBlock = 0; + hashMerkleRoot = 0; + nTime = 0; + nBits = 0; + nNonce = 0; + } + + bool IsNull() const + { + return (nBits == 0); + } + + uint256 GetHash() const + { + return Hash9(BEGIN(nVersion), END(nNonce)); + } + + int64 GetBlockTime() const + { + return (int64)nTime; + } + + void UpdateTime(const CBlockIndex* pindexPrev); +}; + +class CBlock : public CBlockHeader +{ +public: + // network and disk + std::vector vtx; + + // memory only + mutable std::vector vMerkleTree; + + CBlock() + { + SetNull(); + } + + CBlock(const CBlockHeader &header) + { + SetNull(); + *((CBlockHeader*)this) = header; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(*(CBlockHeader*)this); + READWRITE(vtx); + ) + + void SetNull() + { + CBlockHeader::SetNull(); + vtx.clear(); + vMerkleTree.clear(); + } + + CBlockHeader GetBlockHeader() const + { + CBlockHeader block; + block.nVersion = nVersion; + block.hashPrevBlock = hashPrevBlock; + block.hashMerkleRoot = hashMerkleRoot; + block.nTime = nTime; + block.nBits = nBits; + block.nNonce = nNonce; + return block; + } + + uint256 BuildMerkleTree() const + { + vMerkleTree.clear(); + BOOST_FOREACH(const CTransaction& tx, vtx) + vMerkleTree.push_back(tx.GetHash()); + int j = 0; + for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) + { + for (int i = 0; i < nSize; i += 2) + { + int i2 = std::min(i+1, nSize-1); + vMerkleTree.push_back(Hash(BEGIN(vMerkleTree[j+i]), END(vMerkleTree[j+i]), + BEGIN(vMerkleTree[j+i2]), END(vMerkleTree[j+i2]))); + } + j += nSize; + } + return (vMerkleTree.empty() ? 0 : vMerkleTree.back()); + } + + const uint256 &GetTxHash(unsigned int nIndex) const { + assert(vMerkleTree.size() > 0); // BuildMerkleTree must have been called first + assert(nIndex < vtx.size()); + return vMerkleTree[nIndex]; + } + + std::vector GetMerkleBranch(int nIndex) const + { + if (vMerkleTree.empty()) + BuildMerkleTree(); + std::vector vMerkleBranch; + int j = 0; + for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) + { + int i = std::min(nIndex^1, nSize-1); + vMerkleBranch.push_back(vMerkleTree[j+i]); + nIndex >>= 1; + j += nSize; + } + return vMerkleBranch; + } + + static uint256 CheckMerkleBranch(uint256 hash, const std::vector& vMerkleBranch, int nIndex) + { + if (nIndex == -1) + return 0; + BOOST_FOREACH(const uint256& otherside, vMerkleBranch) + { + if (nIndex & 1) + hash = Hash(BEGIN(otherside), END(otherside), BEGIN(hash), END(hash)); + else + hash = Hash(BEGIN(hash), END(hash), BEGIN(otherside), END(otherside)); + nIndex >>= 1; + } + return hash; + } + + bool WriteToDisk(CDiskBlockPos &pos) + { + // Open history file to append + CAutoFile fileout = CAutoFile(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION); + if (!fileout) + return error("CBlock::WriteToDisk() : OpenBlockFile 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"); + pos.nPos = (unsigned int)fileOutPos; + fileout << *this; + + // Flush stdio buffers and commit to disk before returning + fflush(fileout); + if (!IsInitialBlockDownload()) + FileCommit(fileout); + + return true; + } + + bool ReadFromDisk(const CDiskBlockPos &pos) + { + SetNull(); + + // Open history file to read + CAutoFile filein = CAutoFile(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION); + if (!filein) + return error("CBlock::ReadFromDisk() : OpenBlockFile failed"); + + // Read block + try { + filein >> *this; + } + catch (std::exception &e) { + return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__); + } + + // Check the header + if (!CheckProofOfWork(GetHash(), nBits)) + return error("CBlock::ReadFromDisk() : errors in block header"); + + return true; + } + + + + void print() const + { + printf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%"PRIszu")\n", + GetHash().ToString().c_str(), + nVersion, + hashPrevBlock.ToString().c_str(), + hashMerkleRoot.ToString().c_str(), + nTime, nBits, nNonce, + vtx.size()); + 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().c_str()); + printf("\n"); + } + + + /** Undo the effects of this block (with given index) on the UTXO set represented by coins. + * In case pfClean is provided, operation will try to be tolerant about errors, and *pfClean + * will be true if no problems were found. Otherwise, the return value will be false in case + * of problems. Note that in any case, coins may be modified. */ + bool DisconnectBlock(CValidationState &state, CBlockIndex *pindex, CCoinsViewCache &coins, bool *pfClean = NULL); + + // Apply the effects of this block (with given index) on the UTXO set represented by coins + bool ConnectBlock(CValidationState &state, CBlockIndex *pindex, CCoinsViewCache &coins, bool fJustCheck=false); + + // Read a block from disk + bool ReadFromDisk(const CBlockIndex* pindex); + + // Add this block to the block index, and if necessary, switch the active block chain to this + bool AddToBlockIndex(CValidationState &state, const CDiskBlockPos &pos); + + // Context-independent validity checks + bool CheckBlock(CValidationState &state, bool fCheckPOW=true, bool fCheckMerkleRoot=true) const; + + // Store block on disk + // if dbp is provided, the file is known to already reside on disk + bool AcceptBlock(CValidationState &state, CDiskBlockPos *dbp = NULL); +}; + + + + + +class CBlockFileInfo +{ +public: + unsigned int nBlocks; // number of blocks stored in file + unsigned int nSize; // number of used bytes of block file + unsigned int nUndoSize; // number of used bytes in the undo file + unsigned int nHeightFirst; // lowest height of block in file + unsigned int nHeightLast; // highest height of block in file + uint64 nTimeFirst; // earliest time of block in file + uint64 nTimeLast; // latest time of block in file + + IMPLEMENT_SERIALIZE( + READWRITE(VARINT(nBlocks)); + READWRITE(VARINT(nSize)); + READWRITE(VARINT(nUndoSize)); + READWRITE(VARINT(nHeightFirst)); + READWRITE(VARINT(nHeightLast)); + READWRITE(VARINT(nTimeFirst)); + READWRITE(VARINT(nTimeLast)); + ) + + void SetNull() { + nBlocks = 0; + nSize = 0; + nUndoSize = 0; + nHeightFirst = 0; + nHeightLast = 0; + nTimeFirst = 0; + nTimeLast = 0; + } + + CBlockFileInfo() { + SetNull(); + } + + std::string ToString() const { + return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst).c_str(), DateTimeStrFormat("%Y-%m-%d", nTimeLast).c_str()); + } + + // update statistics (does not update nSize) + void AddBlock(unsigned int nHeightIn, uint64 nTimeIn) { + if (nBlocks==0 || nHeightFirst > nHeightIn) + nHeightFirst = nHeightIn; + if (nBlocks==0 || nTimeFirst > nTimeIn) + nTimeFirst = nTimeIn; + nBlocks++; + if (nHeightIn > nHeightFirst) + nHeightLast = nHeightIn; + if (nTimeIn > nTimeLast) + nTimeLast = nTimeIn; + } +}; + +extern CCriticalSection cs_LastBlockFile; +extern CBlockFileInfo infoLastBlockFile; +extern int nLastBlockFile; + +enum BlockStatus { + BLOCK_VALID_UNKNOWN = 0, + BLOCK_VALID_HEADER = 1, // parsed, version ok, hash satisfies claimed PoW, 1 <= vtx count <= max, timestamp not in future + BLOCK_VALID_TREE = 2, // parent found, difficulty matches, timestamp >= median previous, checkpoint + BLOCK_VALID_TRANSACTIONS = 3, // only first tx is coinbase, 2 <= coinbase input script length <= 100, transactions valid, no duplicate txids, sigops, size, merkle root + BLOCK_VALID_CHAIN = 4, // outputs do not overspend inputs, no double spends, coinbase output ok, immature coinbase spends, BIP30 + BLOCK_VALID_SCRIPTS = 5, // scripts/signatures ok + BLOCK_VALID_MASK = 7, + + BLOCK_HAVE_DATA = 8, // full block available in blk*.dat + BLOCK_HAVE_UNDO = 16, // undo data available in rev*.dat + BLOCK_HAVE_MASK = 24, + + BLOCK_FAILED_VALID = 32, // stage after last reached validness failed + BLOCK_FAILED_CHILD = 64, // descends from failed block + BLOCK_FAILED_MASK = 96 +}; + +/** 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: + // pointer to the hash of the block, if any. memory is owned by this CBlockIndex + const uint256* phashBlock; + + // pointer to the index of the predecessor of this block + CBlockIndex* pprev; + + // (memory only) pointer to the index of the *active* successor of this block + CBlockIndex* pnext; + + // height of the entry in the chain. The genesis block has height 0 + int nHeight; + + // Which # file this block is stored in (blk?????.dat) + int nFile; + + // Byte offset within blk?????.dat where this block's data is stored + unsigned int nDataPos; + + // Byte offset within rev?????.dat where this block's undo data is stored + unsigned int nUndoPos; + + // (memory only) Total amount of work (expected number of hashes) in the chain up to and including this block + uint256 nChainWork; + + // Number of transactions in this block. + // Note: in a potential headers-first mode, this number cannot be relied upon + unsigned int nTx; + + // (memory only) Number of transactions in the chain up to and including this block + unsigned int nChainTx; // change to 64-bit type when necessary; won't happen before 2030 + + // Verification status of this block. See enum BlockStatus + unsigned int nStatus; + + // block header + int nVersion; + uint256 hashMerkleRoot; + unsigned int nTime; + unsigned int nBits; + unsigned int nNonce; + + + CBlockIndex() + { + phashBlock = NULL; + pprev = NULL; + pnext = NULL; + nHeight = 0; + nFile = 0; + nDataPos = 0; + nUndoPos = 0; + nChainWork = 0; + nTx = 0; + nChainTx = 0; + nStatus = 0; + + nVersion = 0; + hashMerkleRoot = 0; + nTime = 0; + nBits = 0; + nNonce = 0; + } + + CBlockIndex(CBlockHeader& block) + { + phashBlock = NULL; + pprev = NULL; + pnext = NULL; + nHeight = 0; + nFile = 0; + nDataPos = 0; + nUndoPos = 0; + nChainWork = 0; + nTx = 0; + nChainTx = 0; + nStatus = 0; + + nVersion = block.nVersion; + hashMerkleRoot = block.hashMerkleRoot; + nTime = block.nTime; + nBits = block.nBits; + nNonce = block.nNonce; + } + + CDiskBlockPos GetBlockPos() const { + CDiskBlockPos ret; + if (nStatus & BLOCK_HAVE_DATA) { + ret.nFile = nFile; + ret.nPos = nDataPos; + } + return ret; + } + + CDiskBlockPos GetUndoPos() const { + CDiskBlockPos ret; + if (nStatus & BLOCK_HAVE_UNDO) { + ret.nFile = nFile; + ret.nPos = nUndoPos; + } + return ret; + } + + CBlockHeader GetBlockHeader() const + { + CBlockHeader block; + block.nVersion = nVersion; + if (pprev) + block.hashPrevBlock = pprev->GetBlockHash(); + block.hashMerkleRoot = hashMerkleRoot; + block.nTime = nTime; + block.nBits = nBits; + block.nNonce = nNonce; + return block; + } + + uint256 GetBlockHash() const + { + return *phashBlock; + } + + int64 GetBlockTime() const + { + return (int64)nTime; + } + + CBigNum GetBlockWork() const + { + CBigNum bnTarget; + bnTarget.SetCompact(nBits); + if (bnTarget <= 0) + return 0; + return (CBigNum(1)<<256) / (bnTarget+1); + } + + bool IsInMainChain() const + { + return (pnext || this == pindexBest); + } + + bool CheckIndex() const + { + return CheckProofOfWork(GetBlockHash(), nBits); + } + + enum { nMedianTimeSpan=12 }; + + int64 GetMedianTimePast() const + { + int64 pmedian[nMedianTimeSpan]; + int64* pbegin = &pmedian[nMedianTimeSpan]; + int64* pend = &pmedian[nMedianTimeSpan]; + + const CBlockIndex* pindex = this; + for (int i = 0; i < nMedianTimeSpan && pindex; i++, pindex = pindex->pprev) + *(--pbegin) = pindex->GetBlockTime(); + + std::sort(pbegin, pend); + return pbegin[(pend - pbegin)*2/3]; + } + + int64 GetMedianTime() const + { + const CBlockIndex* pindex = this; + for (int i = 0; i < nMedianTimeSpan/2; i++) + { + if (!pindex->pnext) + return GetBlockTime(); + pindex = pindex->pnext; + } + return pindex->GetMedianTimePast(); + } + + /** + * 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); + + std::string ToString() const + { + return strprintf("CBlockIndex(pprev=%p, pnext=%p, nHeight=%d, merkle=%s, hashBlock=%s)", + pprev, pnext, nHeight, + hashMerkleRoot.ToString().c_str(), + GetBlockHash().ToString().c_str()); + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + +struct CBlockIndexWorkComparator +{ + bool operator()(CBlockIndex *pa, CBlockIndex *pb) { + if (pa->nChainWork > pb->nChainWork) return false; + if (pa->nChainWork < pb->nChainWork) return true; + + if (pa->GetBlockHash() < pb->GetBlockHash()) return false; + if (pa->GetBlockHash() > pb->GetBlockHash()) return true; + + return false; // identical blocks + } +}; + + + +/** Used to marshal pointers into hashes for db storage. */ +class CDiskBlockIndex : public CBlockIndex +{ +public: + uint256 hashPrev; + + CDiskBlockIndex() { + hashPrev = 0; + } + + explicit CDiskBlockIndex(CBlockIndex* pindex) : CBlockIndex(*pindex) { + hashPrev = (pprev ? pprev->GetBlockHash() : 0); + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(VARINT(nVersion)); + + READWRITE(VARINT(nHeight)); + READWRITE(VARINT(nStatus)); + READWRITE(VARINT(nTx)); + if (nStatus & (BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO)) + READWRITE(VARINT(nFile)); + if (nStatus & BLOCK_HAVE_DATA) + READWRITE(VARINT(nDataPos)); + if (nStatus & BLOCK_HAVE_UNDO) + READWRITE(VARINT(nUndoPos)); + + // block header + READWRITE(this->nVersion); + READWRITE(hashPrev); + READWRITE(hashMerkleRoot); + READWRITE(nTime); + READWRITE(nBits); + READWRITE(nNonce); + ) + + uint256 GetBlockHash() const + { + CBlockHeader block; + block.nVersion = nVersion; + block.hashPrevBlock = hashPrev; + block.hashMerkleRoot = hashMerkleRoot; + block.nTime = nTime; + block.nBits = nBits; + block.nNonce = nNonce; + return block.GetHash(); + } + + + std::string ToString() const + { + std::string str = "CDiskBlockIndex("; + str += CBlockIndex::ToString(); + str += strprintf("\n hashBlock=%s, hashPrev=%s)", + GetBlockHash().ToString().c_str(), + hashPrev.ToString().c_str()); + return str; + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + +/** Capture information about block/transaction validation */ +class CValidationState { +private: + enum mode_state { + MODE_VALID, // everything ok + MODE_INVALID, // network rule violation (DoS value may be set) + MODE_ERROR, // run-time error + } mode; + int nDoS; +public: + CValidationState() : mode(MODE_VALID), nDoS(0) {} + bool DoS(int level, bool ret = false) { + if (mode == MODE_ERROR) + return ret; + nDoS += level; + mode = MODE_INVALID; + return ret; + } + bool Invalid(bool ret = false) { + return DoS(0, ret); + } + bool Error() { + mode = MODE_ERROR; + return false; + } + bool Abort(const std::string &msg) { + AbortNode(msg); + return Error(); + } + bool IsValid() { + return mode == MODE_VALID; + } + bool IsInvalid() { + return mode == MODE_INVALID; + } + bool IsError() { + return mode == MODE_ERROR; + } + bool IsInvalid(int &nDoSOut) { + if (IsInvalid()) { + nDoSOut = nDoS; + return true; + } + return false; + } +}; + + + + + + + +/** 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(hashGenesisBlock); + } + + int GetDistanceBack() + { + // Retrace how far back it was in the sender's branch + int nDistance = 0; + int nStep = 1; + BOOST_FOREACH(const uint256& hash, vHave) + { + std::map::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) + { + CBlockIndex* pindex = (*mi).second; + if (pindex->IsInMainChain()) + return nDistance; + } + nDistance += nStep; + if (nDistance > 10) + nStep *= 2; + } + return nDistance; + } + + CBlockIndex* GetBlockIndex() + { + // Find the first block the caller has in the main chain + BOOST_FOREACH(const uint256& hash, vHave) + { + std::map::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) + { + CBlockIndex* pindex = (*mi).second; + if (pindex->IsInMainChain()) + return pindex; + } + } + return pindexGenesisBlock; + } + + uint256 GetBlockHash() + { + // Find the first block the caller has in the main chain + BOOST_FOREACH(const uint256& hash, vHave) + { + std::map::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) + { + CBlockIndex* pindex = (*mi).second; + if (pindex->IsInMainChain()) + return hash; + } + } + return hashGenesisBlock; + } + + int GetHeight() + { + CBlockIndex* pindex = GetBlockIndex(); + if (!pindex) + return 0; + return pindex->nHeight; + } +}; + + + + + + + + +class CTxMemPool +{ +public: + mutable CCriticalSection cs; + std::map mapTx; + std::map mapNextTx; + + bool accept(CValidationState &state, CTransaction &tx, bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs); + bool addUnchecked(const uint256& hash, CTransaction &tx); + bool remove(const CTransaction &tx, bool fRecursive = false); + bool removeConflicts(const CTransaction &tx); + void clear(); + void queryHashes(std::vector& vtxid); + void pruneSpent(const uint256& hash, CCoins &coins); + + unsigned long 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; + +struct CCoinsStats +{ + int nHeight; + uint256 hashBlock; + uint64 nTransactions; + uint64 nTransactionOutputs; + uint64 nSerializedSize; + uint256 hashSerialized; + int64 nTotalAmount; + + CCoinsStats() : nHeight(0), hashBlock(0), nTransactions(0), nTransactionOutputs(0), nSerializedSize(0), hashSerialized(0), nTotalAmount(0) {} +}; + +/** Abstract view on the open txout dataset. */ +class CCoinsView +{ +public: + // Retrieve the CCoins (unspent transaction outputs) for a given txid + virtual bool GetCoins(const uint256 &txid, CCoins &coins); + + // Modify the CCoins for a given txid + virtual bool SetCoins(const uint256 &txid, const CCoins &coins); + + // Just check whether we have data for a given txid. + // This may (but cannot always) return true for fully spent transactions + virtual bool HaveCoins(const uint256 &txid); + + // Retrieve the block index whose state this CCoinsView currently represents + virtual CBlockIndex *GetBestBlock(); + + // Modify the currently active block index + virtual bool SetBestBlock(CBlockIndex *pindex); + + // Do a bulk modification (multiple SetCoins + one SetBestBlock) + virtual bool BatchWrite(const std::map &mapCoins, CBlockIndex *pindex); + + // Calculate statistics about the unspent transaction output set + virtual bool GetStats(CCoinsStats &stats); + + // As we use CCoinsViews polymorphically, have a virtual destructor + virtual ~CCoinsView() {} +}; + +/** CCoinsView backed by another CCoinsView */ +class CCoinsViewBacked : public CCoinsView +{ +protected: + CCoinsView *base; + +public: + CCoinsViewBacked(CCoinsView &viewIn); + bool GetCoins(const uint256 &txid, CCoins &coins); + bool SetCoins(const uint256 &txid, const CCoins &coins); + bool HaveCoins(const uint256 &txid); + CBlockIndex *GetBestBlock(); + bool SetBestBlock(CBlockIndex *pindex); + void SetBackend(CCoinsView &viewIn); + bool BatchWrite(const std::map &mapCoins, CBlockIndex *pindex); + bool GetStats(CCoinsStats &stats); +}; + +/** CCoinsView that adds a memory cache for transactions to another CCoinsView */ +class CCoinsViewCache : public CCoinsViewBacked +{ +protected: + CBlockIndex *pindexTip; + std::map cacheCoins; + +public: + CCoinsViewCache(CCoinsView &baseIn, bool fDummy = false); + + // Standard CCoinsView methods + bool GetCoins(const uint256 &txid, CCoins &coins); + bool SetCoins(const uint256 &txid, const CCoins &coins); + bool HaveCoins(const uint256 &txid); + CBlockIndex *GetBestBlock(); + bool SetBestBlock(CBlockIndex *pindex); + bool BatchWrite(const std::map &mapCoins, CBlockIndex *pindex); + + // Return a modifiable reference to a CCoins. Check HaveCoins first. + // Many methods explicitly require a CCoinsViewCache because of this method, to reduce + // copying. + CCoins &GetCoins(const uint256 &txid); + + // Push the modifications applied to this cache to its base. + // Failure to call this method before destruction will cause the changes to be forgotten. + bool Flush(); + + // Calculate the size of the cache (in number of transactions) + unsigned int GetCacheSize(); + +private: + std::map::iterator FetchCoins(const uint256 &txid); +}; + +/** CCoinsView that brings transactions from a memorypool into view. + It does not check for spendings by memory pool transactions. */ +class CCoinsViewMemPool : public CCoinsViewBacked +{ +protected: + CTxMemPool &mempool; + +public: + CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn); + bool GetCoins(const uint256 &txid, CCoins &coins); + bool HaveCoins(const uint256 &txid); +}; + +/** Global variable that points to the active CCoinsView (protected by cs_main) */ +extern CCoinsViewCache *pcoinsTip; + +/** Global variable that points to the active block tree (protected by cs_main) */ +extern CBlockTreeDB *pblocktree; + +struct CBlockTemplate +{ + CBlock block; + std::vector vTxFees; + std::vector vTxSigOps; +}; + + + + + + +/** Used to relay blocks as header + vector + * to filtered nodes. + */ +class CMerkleBlock +{ +public: + // Public only for unit testing + CBlockHeader header; + CPartialMerkleTree txn; + +public: + // Public only for unit testing and relay testing + // (not relayed) + std::vector > vMatchedTxn; + + // Create from a CBlock, filtering transactions according to filter + // Note that this will call IsRelevantAndUpdate on the filter for each transaction, + // thus the filter will likely be modified. + CMerkleBlock(const CBlock& block, CBloomFilter& filter); + + IMPLEMENT_SERIALIZE + ( + READWRITE(header); + READWRITE(txn); + ) +}; + +#endif diff --git a/src/makefile.linux-mingw b/src/makefile.linux-mingw new file mode 100644 index 0000000..c3fab81 --- /dev/null +++ b/src/makefile.linux-mingw @@ -0,0 +1,140 @@ +# 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. + +DEPSDIR:=/usr/i586-mingw32msvc + +CC := i586-mingw32msvc-gcc +CXX := i586-mingw32msvc-g++ + +USE_UPNP:=0 +USE_IPV6:=1 + +INCLUDEPATHS= \ + -I"$(CURDIR)" \ + -I"$(CURDIR)"/obj \ + -I"$(DEPSDIR)/boost_1_53_0" \ + -I"$(DEPSDIR)/db-4.8.30.NC/build_unix" \ + -I"$(DEPSDIR)/openssl-1.0.1e/include" \ + -I"$(DEPSDIR)" + +LIBPATHS= \ + -L"$(DEPSDIR)/boost_1_53_0/stage/lib" \ + -L"$(DEPSDIR)/db-4.8.30.NC/build_unix" \ + -L"$(DEPSDIR)/openssl-1.0.1e" + +LIBS= \ + $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a \ + -l boost_system-mt-s \ + -l boost_filesystem-mt-s \ + -l boost_program_options-mt-s \ + -l boost_thread_win32-mt-s \ + -l boost_chrono-mt-s \ + -l db_cxx \ + -l ssl \ + -l crypto + +DEFS=-D_MT -DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE +DEBUGFLAGS=-g +xCXXFLAGS=-O2 -w -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter $(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS) $(CXXFLAGS) +# enable: ASLR, DEP and large address aware +xLDFLAGS=-Wl,--dynamicbase -Wl,--nxcompat -Wl,--large-address-aware $(LDFLAGS) + +TESTDEFS = -DTEST_DATA_DIR=$(abspath test/data) + +ifndef USE_UPNP + override USE_UPNP = - +endif +ifneq (${USE_UPNP}, -) + LIBPATHS += -L"$(DEPSDIR)/miniupnpc" + LIBS += -l miniupnpc -l iphlpapi + DEFS += -DSTATICLIB -DUSE_UPNP=$(USE_UPNP) +endif + +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= \ + leveldb/libleveldb.a \ + obj/alert.o \ + obj/version.o \ + obj/checkpoints.o \ + obj/netbase.o \ + obj/addrman.o \ + obj/crypter.o \ + obj/key.o \ + obj/db.o \ + obj/init.o \ + obj/keystore.o \ + obj/main.o \ + obj/net.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/hash.o \ + obj/bloom.o \ + obj/leveldb.o \ + obj/txdb.o\ + obj/blake.o\ + obj/bmw.o\ + obj/groestl.o\ + obj/jh.o\ + obj/keccak.o\ + obj/skein.o + +all: Greenchaind.exe + +DEFS += -I"$(CURDIR)/leveldb/include" +DEFS += -I"$(CURDIR)/leveldb/helpers" +leveldb/libleveldb.a: + @echo "Building LevelDB ..." && cd leveldb && TARGET_OS=OS_WINDOWS_CROSSCOMPILE $(MAKE) CC=$(CC) CXX=$(CXX) OPT="$(xCXXFLAGS)" libleveldb.a libmemenv.a && i586-mingw32msvc-ranlib libleveldb.a && i586-mingw32msvc-ranlib libmemenv.a && cd .. + +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 $(xCXXFLAGS) -o $@ $< + +obj/%.o: %.c $(HEADERS) + $(CXX) -c $(xCXXFLAGS) -fpermissive -o $@ $< + +Greenchaind.exe: $(OBJS:obj/%=obj/%) + $(CXX) $(xCXXFLAGS) $(xLDFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) + +TESTOBJS := $(patsubst test/%.cpp,obj-test/%.o,$(wildcard test/*.cpp)) + +obj-test/%.o: test/%.cpp $(HEADERS) + $(CXX) -c $(TESTDEFS) $(xCXXFLAGS) -o $@ $< + +test_Greenchain.exe: $(TESTOBJS) $(filter-out obj/init.o,$(OBJS:obj/%=obj/%)) + $(CXX) $(xCXXFLAGS) $(xLDFLAGS) -o $@ $(LIBPATHS) $^ -lboost_unit_test_framework-mt-s $(LIBS) + + +clean: + -rm -f obj/*.o + -rm -f Greenchaind.exe + -rm -f obj-test/*.o + -rm -f test_Greenchain.exe + -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 0000000..f52a2db --- /dev/null +++ b/src/makefile.mingw @@ -0,0 +1,155 @@ +# 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. + +# Makefile for the MinGW g++ compiler/toolchain +# +# Assumes Berkeley DB, Boost, and OpenSSL have all been compiled and installed +# into /usr/local (/usr/local/include, /usr/local/lib). +# +# If dependencies are somewhere else, run 'make DEPSDIR=/path/' +# +# Boost libraries are given wacky names that include the particular version of +# boost you're using; set BOOST_SUFFIX appropriately. +# +# 'make clean' assumes it is running inside a MSYS shell, and uses 'rm' +# to remove files. + +CXX ?= g++ + +USE_UPNP:=1 +USE_IPV6:=1 + +DEPSDIR?=/usr/local +BOOST_SUFFIX?=-mgw48-mt-sd-1_50 + +INCLUDEPATHS?= \ + -I"$(CURDIR)" \ + -I"$(DEPSDIR)/include" \ + -I"/d/boost-1.50.0-mgw/" \ + -I"/d/db-4.8.30.NC-mgw/" \ + -I"/d/openssl-1.0.1g-mgw/" \ + -I"/d/miniupnpc-1.6-mgw/" + +LIBPATHS?= \ + -L"$(CURDIR)/leveldb" \ + -L"/d/boost-1.50.0-mgw/" \ + -L"/d/db-4.8.30.NC-mgw/" \ + -L"/usr/local/" \ + -L"/usr/local/lib" + +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 + +DEFS=-D_MT -DWIN32 -DWIN32_LEAN_AND_MEAN -D_WINDOWS -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -D__NO_SYSTEM_INCLUDES +DEBUGFLAGS=-g +CFLAGS=${ADDITIONALCCFLAGS} -mthreads -O2 -w -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter $(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS) +# enable: ASLR, DEP and large address aware +LDFLAGS=-Wl,--dynamicbase -Wl,--nxcompat -Wl,--large-address-aware + +TESTDEFS = -DTEST_DATA_DIR=$(abspath test/data) + +ifndef USE_UPNP + override USE_UPNP = - +endif +ifneq (${USE_UPNP}, -) + LIBS += -l miniupnpc -l iphlpapi + DEFS += -DSTATICLIB -DUSE_UPNP=$(USE_UPNP) +endif + +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= \ + leveldb/libleveldb.a \ + obj/alert.o \ + obj/version.o \ + obj/checkpoints.o \ + obj/netbase.o \ + obj/addrman.o \ + obj/crypter.o \ + obj/key.o \ + obj/db.o \ + obj/init.o \ + obj/keystore.o \ + obj/main.o \ + obj/net.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/hash.o \ + obj/bloom.o \ + obj/noui.o \ + obj/leveldb.o \ + obj/txdb.o\ + obj/blake.o\ + obj/bmw.o\ + obj/groestl.o\ + obj/jh.o\ + obj/keccak.o\ + obj/skein.o + + +all: Greenchaind.exe + +test check: test_Greenchain.exe FORCE + test_Greenchain.exe + +# +# LevelDB support +# +DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) +DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers) + +leveldb/libleveldb.a: + cd leveldb && $(MAKE) CC=$(CC) CXX=$(CXX) OPT="$(CFLAGS)" TARGET_OS=NATIVE_WINDOWS libleveldb.a libmemenv.a && cd .. + +obj/%.o: %.cpp $(HEADERS) + $(CXX) -c $(CFLAGS) -o $@ $< + +obj/%.o: %.c $(HEADERS) + $(CXX) -c $(xCXXFLAGS) -fpermissive -o $@ $< + +Greenchaind.exe: $(OBJS:obj/%=obj/%) + $(CXX) $(CFLAGS) $(LDFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) + +TESTOBJS := $(patsubst test/%.cpp,obj-test/%.o,$(wildcard test/*.cpp)) + +obj-test/%.o: test/%.cpp $(HEADERS) + $(CXX) -c $(TESTDEFS) $(CFLAGS) -o $@ $< + +test_Greenchain.exe: $(TESTOBJS) $(filter-out obj/init.o,$(OBJS:obj/%=obj/%)) + $(CXX) $(CFLAGS) $(LDFLAGS) -o $@ $(LIBPATHS) $^ -lboost_unit_test_framework$(BOOST_SUFFIX) $(LIBS) + +clean: + rm -f Greenchaind.exe test_Greenchain.exe + rm -f obj/* + rm -f obj-test/* + cd leveldb && $(MAKE) TARGET_OS=NATIVE_WINDOWS clean && cd .. + +FORCE: diff --git a/src/makefile.mingw.bak b/src/makefile.mingw.bak new file mode 100644 index 0000000..f52a2db --- /dev/null +++ b/src/makefile.mingw.bak @@ -0,0 +1,155 @@ +# 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. + +# Makefile for the MinGW g++ compiler/toolchain +# +# Assumes Berkeley DB, Boost, and OpenSSL have all been compiled and installed +# into /usr/local (/usr/local/include, /usr/local/lib). +# +# If dependencies are somewhere else, run 'make DEPSDIR=/path/' +# +# Boost libraries are given wacky names that include the particular version of +# boost you're using; set BOOST_SUFFIX appropriately. +# +# 'make clean' assumes it is running inside a MSYS shell, and uses 'rm' +# to remove files. + +CXX ?= g++ + +USE_UPNP:=1 +USE_IPV6:=1 + +DEPSDIR?=/usr/local +BOOST_SUFFIX?=-mgw48-mt-sd-1_50 + +INCLUDEPATHS?= \ + -I"$(CURDIR)" \ + -I"$(DEPSDIR)/include" \ + -I"/d/boost-1.50.0-mgw/" \ + -I"/d/db-4.8.30.NC-mgw/" \ + -I"/d/openssl-1.0.1g-mgw/" \ + -I"/d/miniupnpc-1.6-mgw/" + +LIBPATHS?= \ + -L"$(CURDIR)/leveldb" \ + -L"/d/boost-1.50.0-mgw/" \ + -L"/d/db-4.8.30.NC-mgw/" \ + -L"/usr/local/" \ + -L"/usr/local/lib" + +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 + +DEFS=-D_MT -DWIN32 -DWIN32_LEAN_AND_MEAN -D_WINDOWS -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -D__NO_SYSTEM_INCLUDES +DEBUGFLAGS=-g +CFLAGS=${ADDITIONALCCFLAGS} -mthreads -O2 -w -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter $(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS) +# enable: ASLR, DEP and large address aware +LDFLAGS=-Wl,--dynamicbase -Wl,--nxcompat -Wl,--large-address-aware + +TESTDEFS = -DTEST_DATA_DIR=$(abspath test/data) + +ifndef USE_UPNP + override USE_UPNP = - +endif +ifneq (${USE_UPNP}, -) + LIBS += -l miniupnpc -l iphlpapi + DEFS += -DSTATICLIB -DUSE_UPNP=$(USE_UPNP) +endif + +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= \ + leveldb/libleveldb.a \ + obj/alert.o \ + obj/version.o \ + obj/checkpoints.o \ + obj/netbase.o \ + obj/addrman.o \ + obj/crypter.o \ + obj/key.o \ + obj/db.o \ + obj/init.o \ + obj/keystore.o \ + obj/main.o \ + obj/net.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/hash.o \ + obj/bloom.o \ + obj/noui.o \ + obj/leveldb.o \ + obj/txdb.o\ + obj/blake.o\ + obj/bmw.o\ + obj/groestl.o\ + obj/jh.o\ + obj/keccak.o\ + obj/skein.o + + +all: Greenchaind.exe + +test check: test_Greenchain.exe FORCE + test_Greenchain.exe + +# +# LevelDB support +# +DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) +DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers) + +leveldb/libleveldb.a: + cd leveldb && $(MAKE) CC=$(CC) CXX=$(CXX) OPT="$(CFLAGS)" TARGET_OS=NATIVE_WINDOWS libleveldb.a libmemenv.a && cd .. + +obj/%.o: %.cpp $(HEADERS) + $(CXX) -c $(CFLAGS) -o $@ $< + +obj/%.o: %.c $(HEADERS) + $(CXX) -c $(xCXXFLAGS) -fpermissive -o $@ $< + +Greenchaind.exe: $(OBJS:obj/%=obj/%) + $(CXX) $(CFLAGS) $(LDFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) + +TESTOBJS := $(patsubst test/%.cpp,obj-test/%.o,$(wildcard test/*.cpp)) + +obj-test/%.o: test/%.cpp $(HEADERS) + $(CXX) -c $(TESTDEFS) $(CFLAGS) -o $@ $< + +test_Greenchain.exe: $(TESTOBJS) $(filter-out obj/init.o,$(OBJS:obj/%=obj/%)) + $(CXX) $(CFLAGS) $(LDFLAGS) -o $@ $(LIBPATHS) $^ -lboost_unit_test_framework$(BOOST_SUFFIX) $(LIBS) + +clean: + rm -f Greenchaind.exe test_Greenchain.exe + rm -f obj/* + rm -f obj-test/* + cd leveldb && $(MAKE) TARGET_OS=NATIVE_WINDOWS clean && cd .. + +FORCE: diff --git a/src/makefile.osx b/src/makefile.osx new file mode 100644 index 0000000..891947a --- /dev/null +++ b/src/makefile.osx @@ -0,0 +1,190 @@ +# -*- 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 MimiCoin +# 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_UPNP:=1 +USE_IPV6:=1 + +LIBS= -dead_strip + +TESTDEFS = -DTEST_DATA_DIR=$(abspath test/data) + +ifdef STATIC +# Build STATIC if you are redistributing the MimiCoind binary +TESTLIBS += \ + $(DEPSDIR)/lib/libboost_unit_test_framework-mt.a +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/libboost_chrono-mt.a \ + $(DEPSDIR)/lib/libssl.a \ + $(DEPSDIR)/lib/libcrypto.a \ + -lz +else +TESTLIBS += \ + -lboost_unit_test_framework-mt +LIBS += \ + -ldb_cxx-4.8 \ + -lboost_system-mt \ + -lboost_filesystem-mt \ + -lboost_program_options-mt \ + -lboost_thread-mt \ + -lboost_chrono-mt \ + -lssl \ + -lcrypto \ + -lz +TESTDEFS += -DBOOST_TEST_DYN_LINK +endif + +DEFS=-DMAC_OSX -DMSG_NOSIGNAL=0 -DBOOST_SPIRIT_THREADSAFE + +ifdef RELEASE +# Compile for maximum compatibility and smallest size. +# This requires that dependencies are compiled +# the same way. +CFLAGS = -mmacosx-version-min=10.5 -arch i386 -O3 +else +DEBUGFLAGS = -g +endif + +# ppc doesn't work because we don't support big-endian +CFLAGS += -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter \ + $(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS) + +OBJS= \ + leveldb/libleveldb.a \ + obj/alert.o \ + obj/version.o \ + obj/checkpoints.o \ + obj/netbase.o \ + obj/addrman.o \ + obj/crypter.o \ + obj/key.o \ + obj/db.o \ + obj/init.o \ + obj/keystore.o \ + obj/main.o \ + obj/net.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/hash.o \ + obj/bloom.o \ + obj/noui.o \ + obj/leveldb.o \ + obj/txdb.o\ + obj/blake.o\ + obj/bmw.o\ + obj/groestl.o\ + obj/jh.o\ + obj/keccak.o\ + obj/skein.o + +ifndef USE_UPNP + override USE_UPNP = - +endif +ifneq (${USE_UPNP}, -) + DEFS += -DUSE_UPNP=$(USE_UPNP) +ifdef STATIC + LIBS += $(DEPSDIR)/lib/libminiupnpc.a +else + LIBS += -lminiupnpc +endif +endif + +ifneq (${USE_IPV6}, -) + DEFS += -DUSE_IPV6=$(USE_IPV6) +endif + +all: MimiCoind + +test check: test_MimiCoin FORCE + ./test_MimiCoin + +# +# LevelDB support +# +LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a +DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) +DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers) +leveldb/libleveldb.a: + @echo "Building LevelDB ..." && cd leveldb && $(MAKE) CC=$(CC) CXX=$(CXX) OPT="$(CFLAGS)" libleveldb.a libmemenv.a && cd .. + +# auto-generated dependencies: +-include obj/*.P +-include obj-test/*.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) + +obj/%.o: %.c + $(CXX) -c $(CFLAGS) -fpermissive -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) + +MimiCoind: $(OBJS:obj/%=obj/%) + $(CXX) $(CFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) + +TESTOBJS := $(patsubst test/%.cpp,obj-test/%.o,$(wildcard test/*.cpp)) + +obj-test/%.o: test/%.cpp + $(CXX) -c $(TESTDEFS) $(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) + +test_MimiCoin: $(TESTOBJS) $(filter-out obj/init.o,$(OBJS:obj/%=obj/%)) + $(CXX) $(CFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) $(TESTLIBS) + +clean: + -rm -f MimiCoind test_MimiCoin + -rm -f obj/*.o + -rm -f obj-test/*.o + -rm -f obj/*.P + -rm -f obj-test/*.P + -rm -f obj/build.h + -cd leveldb && $(MAKE) clean || true + +FORCE: diff --git a/src/makefile.unix b/src/makefile.unix new file mode 100644 index 0000000..222ad4b --- /dev/null +++ b/src/makefile.unix @@ -0,0 +1,218 @@ +# 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. + +# :=0 --> UPnP support turned off by default at runtime +# :=1 --> UPnP support turned on by default at runtime +# :=- --> No UPnP support - miniupnp not required +USE_UPNP:=0 + +# :=1 --> Enable IPv6 support +# :=0 --> Disable IPv6 support +USE_IPV6:=1 + +LINK:=$(CXX) + +DEFS=-DBOOST_SPIRIT_THREADSAFE -D_FILE_OFFSET_BITS=64 + +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)) + +TESTDEFS = -DTEST_DATA_DIR=$(abspath test/data) + +LMODE = dynamic +LMODE2 = dynamic +ifdef STATIC + LMODE = static + ifeq (${STATIC}, all) + LMODE2 = static + endif +else + TESTDEFS += -DBOOST_TEST_DYN_LINK +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 + +TESTLIBS += \ + -Wl,-B$(LMODE) \ + -l boost_unit_test_framework$(BOOST_LIB_SUFFIX) + +ifndef USE_UPNP + override USE_UPNP = - +endif +ifneq (${USE_UPNP}, -) + LIBS += -l miniupnpc + DEFS += -DUSE_UPNP=$(USE_UPNP) +endif + +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 + +# 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=-O2 -pthread -Wall -Wextra -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= \ + leveldb/libleveldb.a \ + obj/alert.o \ + obj/version.o \ + obj/checkpoints.o \ + obj/netbase.o \ + obj/addrman.o \ + obj/crypter.o \ + obj/key.o \ + obj/db.o \ + obj/init.o \ + obj/keystore.o \ + obj/main.o \ + obj/net.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/hash.o \ + obj/bloom.o \ + obj/noui.o \ + obj/leveldb.o \ + obj/txdb.o\ + obj/blake.o\ + obj/bmw.o\ + obj/groestl.o\ + obj/jh.o\ + obj/keccak.o\ + obj/skein.o + + + + + +all: Greenchaind + +test check: test_Greenchain FORCE + ./test_Greenchain + +# +# LevelDB support +# +MAKEOVERRIDES = +LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a +DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) +DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers) +leveldb/libleveldb.a: + @echo "Building LevelDB ..." && cd leveldb && $(MAKE) CC=$(CC) CXX=$(CXX) OPT="$(xCXXFLAGS)" libleveldb.a libmemenv.a && cd .. + +# auto-generated dependencies: +-include obj/*.P +-include obj-test/*.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) + +obj/%.o: %.c + $(CXX) -c $(xCXXFLAGS) -fpermissive -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) + +Greenchaind: $(OBJS:obj/%=obj/%) + $(LINK) $(xCXXFLAGS) -o $@ $^ $(xLDFLAGS) $(LIBS) + +TESTOBJS := $(patsubst test/%.cpp,obj-test/%.o,$(wildcard test/*.cpp)) + +obj-test/%.o: test/%.cpp + $(CXX) -c $(TESTDEFS) $(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) + +test_Greenchain: $(TESTOBJS) $(filter-out obj/init.o,$(OBJS:obj/%=obj/%)) + $(LINK) $(xCXXFLAGS) -o $@ $(LIBPATHS) $^ $(TESTLIBS) $(xLDFLAGS) $(LIBS) + +clean: + -rm -f Greenchaind test_Greenchain + -rm -f obj/*.o + -rm -f obj-test/*.o + -rm -f obj/*.P + -rm -f obj-test/*.P + -rm -f obj/build.h + -cd leveldb && $(MAKE) clean || true + +FORCE: diff --git a/src/mruset.h b/src/mruset.h new file mode 100644 index 0000000..a527351 --- /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 0000000..625bf60 --- /dev/null +++ b/src/net.cpp @@ -0,0 +1,1884 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2017 Greenchain 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 "init.h" +#include "addrman.h" +#include "ui_interface.h" +#include "script.h" + +#ifdef WIN32 +#include +#endif + +#ifdef USE_UPNP +#include +#include +#include +#include +#endif + +// period dump peer.dat in second +const unsigned int dump_addresses_interval = 900; + +using namespace std; +using namespace boost; + +static const int MAX_OUTBOUND_CONNECTIONS = 24; + +bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false); + + +struct LocalServiceInfo { + int nScore; + int nPort; +}; + +// +// Global state variables +// +bool fDiscover = true; +uint64 nLocalServices = 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; +uint64 nLocalHostNonce = 0; +static std::vector vhListenSocket; +CAddrMan addrman; +int nMaxConnections = 125; + +vector vNodes; +CCriticalSection cs_vNodes; +map mapRelay; +deque > vRelayExpiration; +CCriticalSection cs_mapRelay; +limitedmap mapAlreadyAskedFor(MAX_INV_SZ); + +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",0),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 = ""; + loop + { + char c; + int nBytes = recv(hSocket, &c, 1, 0); + if (nBytes > 0) + { + if (c == '\n') + continue; + if (c == '\r') + return true; + strLine += c; + if (strLine.size() >= 9000) + return true; + } + else if (nBytes <= 0) + { + boost::this_thread::interruption_point(); + if (nBytes < 0) + { + int nErr = WSAGetLastError(); + if (nErr == WSAEMSGSIZE) + continue; + if (nErr == WSAEWOULDBLOCK || nErr == WSAEINTR || nErr == WSAEINPROGRESS) + { + MilliSleep(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]; +} + +bool GetMyExternalIP2(const CService& addrConnect, const char* pszGet, const char* pszKeyword, CNetAddr& ipRet) +{ + SOCKET hSocket; + if (!ConnectSocket(addrConnect, hSocket)) + return error("GetMyExternalIP() : connection to %s failed", addrConnect.ToString().c_str()); + + send(hSocket, pszGet, strlen(pszGet), MSG_NOSIGNAL); + + string strLine; + while (RecvLine(hSocket, strLine)) + { + if (strLine.empty()) // HTTP response is separated from headers by blank line + { + loop + { + if (!RecvLine(hSocket, strLine)) + { + closesocket(hSocket); + return false; + } + if (pszKeyword == NULL) + break; + if (strLine.find(pszKeyword) != string::npos) + { + strLine = strLine.substr(strLine.find(pszKeyword) + strlen(pszKeyword)); + break; + } + } + closesocket(hSocket); + if (strLine.find("<") != string::npos) + strLine = strLine.substr(0, strLine.find("<")); + strLine = strLine.substr(strspn(strLine.c_str(), " \t\n\r")); + while (strLine.size() > 0 && isspace(strLine[strLine.size()-1])) + strLine.resize(strLine.size()-1); + CService addr(strLine,0,true); + printf("GetMyExternalIP() received [%s] %s\n", strLine.c_str(), addr.ToString().c_str()); + if (!addr.IsValid() || !addr.IsRoutable()) + return false; + ipRet.SetIP(addr); + return true; + } + } + closesocket(hSocket); + return error("GetMyExternalIP() : connection closed"); +} + +bool GetMyExternalIP(CNetAddr& ipRet) +{ + CService addrConnect; + const char* pszGet; + const char* pszKeyword; + + for (int nLookup = 0; nLookup <= 1; nLookup++) + for (int nHost = 1; nHost <= 2; nHost++) + { + // We should be phasing out our use of sites like these. If we need + // replacements, we should ask for volunteers to put this simple + // php file on their web server that prints the client IP: + // + /* if (nHost == 1) + { + addrConnect = CService("91.198.22.70", 80); // checkip.dyndns.org + + if (nLookup == 1) + { + CService addrIP("checkip.dyndns.org", 80, true); + if (addrIP.IsValid()) + addrConnect = addrIP; + } + + pszGet = "GET / HTTP/1.1\r\n" + "Host: checkip.dyndns.org\r\n" + "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)\r\n" + "Connection: close\r\n" + "\r\n"; + + pszKeyword = "Address:"; + } + else if (nHost == 2) + { + addrConnect = CService("91.198.22.70", 80); // checkip.dyndns.org + + if (nLookup == 1) + { + CService addrIP("checkip.dyndns.org", 80, true); + if (addrIP.IsValid()) + addrConnect = addrIP; + } + + pszGet = "GET /simple/ HTTP/1.1\r\n" + "Host: checkip.dyndns.org\r\n" + "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)\r\n" + "Connection: close\r\n" + "\r\n"; + + pszKeyword = NULL; // Returns just IP address + }*/ + + if (GetMyExternalIP2(addrConnect, pszGet, pszKeyword, ipRet)) + return true; + } + + return false; +} + +void ThreadGetMyExternalIP(void* parg) +{ + // Make this thread recognisable as the external IP detection thread + RenameThread("bitcoin-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); +} + + + + + + + +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) +{ + if (pszDest == NULL) { + if (IsLocal(addrConnect)) + return NULL; + + // Look for an existing connection + CNode* pnode = FindNode((CService)addrConnect); + if (pnode) + { + 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); + 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; + } + + // in case this fails, we'll empty the recv buffer when the CNode is deleted + TRY_LOCK(cs_vRecvMsg, lockRecv); + if (lockRecv) + vRecvMsg.clear(); + + // if this was the sync node, we'll need a new one + if (this == pnodeSync) + pnodeSync = NULL; +} + +void CNode::Cleanup() +{ +} + + +void CNode::PushVersion() +{ + /// when NTP implemented, change to just nTime = GetAdjustedTime() + int64 nTime = (fInbound ? GetAdjustedTime() : GetTime()); + CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0",0))); + CAddress 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 = (*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 >= GetArg("-banscore", 100)) + { + int64 banTime = GetTime()+GetArg("-bantime", 60*60*24); // 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(nStartingHeight); + X(nMisbehavior); + X(nSendBytes); + X(nRecvBytes); + stats.fSyncNode = (this == pnodeSync); +} +#undef X + +// requires LOCK(cs_vRecvMsg) +bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes) +{ + while (nBytes > 0) { + + // get current incomplete message, or create a new one + if (vRecvMsg.empty() || + vRecvMsg.back().complete()) + vRecvMsg.push_back(CNetMessage(SER_NETWORK, nRecvVersion)); + + CNetMessage& msg = vRecvMsg.back(); + + // absorb network data + int handled; + if (!msg.in_data) + handled = msg.readHeader(pch, nBytes); + else + handled = msg.readData(pch, nBytes); + + if (handled < 0) + return false; + + pch += handled; + nBytes -= handled; + } + + return true; +} + +int CNetMessage::readHeader(const char *pch, unsigned int nBytes) +{ + // copy data to temporary parsing buffer + unsigned int nRemaining = 24 - nHdrPos; + unsigned int nCopy = std::min(nRemaining, nBytes); + + memcpy(&hdrbuf[nHdrPos], pch, nCopy); + nHdrPos += nCopy; + + // if header incomplete, exit + if (nHdrPos < 24) + return nCopy; + + // deserialize to CMessageHeader + try { + hdrbuf >> hdr; + } + catch (std::exception &e) { + return -1; + } + + // reject messages larger than MAX_SIZE + if (hdr.nMessageSize > MAX_SIZE) + return -1; + + // switch state to reading message data + in_data = true; + vRecv.resize(hdr.nMessageSize); + + return nCopy; +} + +int CNetMessage::readData(const char *pch, unsigned int nBytes) +{ + unsigned int nRemaining = hdr.nMessageSize - nDataPos; + unsigned int nCopy = std::min(nRemaining, nBytes); + + memcpy(&vRecv[nDataPos], pch, nCopy); + nDataPos += nCopy; + + return nCopy; +} + + + + + + + + + +// requires LOCK(cs_vSend) +void SocketSendData(CNode *pnode) +{ + std::deque::iterator it = pnode->vSendMsg.begin(); + + while (it != pnode->vSendMsg.end()) { + const CSerializeData &data = *it; + assert(data.size() > pnode->nSendOffset); + int nBytes = send(pnode->hSocket, &data[pnode->nSendOffset], data.size() - pnode->nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT); + if (nBytes > 0) { + pnode->nLastSend = GetTime(); + pnode->nSendBytes += nBytes; + pnode->nSendOffset += nBytes; + if (pnode->nSendOffset == data.size()) { + pnode->nSendOffset = 0; + pnode->nSendSize -= data.size(); + it++; + } else { + // could not send full message; stop sending more + break; + } + } 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(); + } + } + // couldn't send anything at all + break; + } + } + + if (it == pnode->vSendMsg.end()) { + assert(pnode->nSendOffset == 0); + assert(pnode->nSendSize == 0); + } + pnode->vSendMsg.erase(pnode->vSendMsg.begin(), it); +} + +static list vNodesDisconnected; + +void ThreadSocketHandler() +{ + unsigned int nPrevNodeCount = 0; + loop + { + // + // Disconnect nodes + // + { + LOCK(cs_vNodes); + // Disconnect unused nodes + vector vNodesCopy = vNodes; + BOOST_FOREACH(CNode* pnode, vNodesCopy) + { + if (pnode->fDisconnect || + (pnode->GetRefCount() <= 0 && pnode->vRecvMsg.empty() && pnode->nSendSize == 0 && pnode->ssSend.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 + 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_vRecvMsg, lockRecv); + if (lockRecv) + { + 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, &fdsetError); + hSocketMax = max(hSocketMax, pnode->hSocket); + have_fds = true; + + // Implement the following logic: + // * If there is data to send, select() for sending data. As this only + // happens when optimistic write failed, we choose to first drain the + // write buffer in this case before receiving more. This avoids + // needlessly queueing received data, if the remote peer is not themselves + // receiving data. This means properly utilizing TCP flow control signalling. + // * Otherwise, if there is no (complete) message in the receive buffer, + // or there is space left in the buffer, select() for receiving data. + // * (if neither of the above applies, there is certainly one message + // in the receiver buffer ready to be processed). + // Together, that means that at least one of the following is always possible, + // so we don't deadlock: + // * We send some data. + // * We wait for data to be received (and disconnect after timeout). + // * We process a message in the buffer (message handler thread). + { + TRY_LOCK(pnode->cs_vSend, lockSend); + if (lockSend && !pnode->vSendMsg.empty()) { + FD_SET(pnode->hSocket, &fdsetSend); + continue; + } + } + { + TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); + if (lockRecv && ( + pnode->vRecvMsg.empty() || !pnode->vRecvMsg.front().complete() || + pnode->GetTotalRecvSize() <= ReceiveFloodSize())) + FD_SET(pnode->hSocket, &fdsetRecv); + } + } + } + + int nSelect = select(have_fds ? hSocketMax + 1 : 0, + &fdsetRecv, &fdsetSend, &fdsetError, &timeout); + boost::this_thread::interruption_point(); + + 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); + MilliSleep(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 >= nMaxConnections - 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) + { + boost::this_thread::interruption_point(); + + // + // Receive + // + if (pnode->hSocket == INVALID_SOCKET) + continue; + if (FD_ISSET(pnode->hSocket, &fdsetRecv) || FD_ISSET(pnode->hSocket, &fdsetError)) + { + TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); + if (lockRecv) + { + { + // typical socket buffer is 8K-64K + char pchBuf[0x10000]; + int nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT); + if (nBytes > 0) + { + if (!pnode->ReceiveMsgBytes(pchBuf, nBytes)) + pnode->CloseSocketDisconnect(); + pnode->nLastRecv = GetTime(); + pnode->nRecvBytes += 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) + SocketSendData(pnode); + } + + // + // Inactivity checking + // + if (pnode->vSendMsg.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(); + } + + MilliSleep(10); + } +} + + + + + + + + + +#ifdef USE_UPNP +void ThreadMapPort() +{ + std::string port = strprintf("%u", GetListenPort()); + const char * multicastif = 0; + const char * minissdpdpath = 0; + struct UPNPDev * devlist = 0; + char lanaddr[64]; + +#ifndef UPNPDISCOVER_SUCCESS + /* miniupnpc 1.5 */ + devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0); +#else + /* miniupnpc 1.6 */ + int error = 0; + devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error); +#endif + + struct UPNPUrls urls; + struct IGDdatas data; + int r; + + r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)); + if (r == 1) + { + if (fDiscover) { + char externalIPAddress[40]; + r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress); + if(r != UPNPCOMMAND_SUCCESS) + printf("UPnP: GetExternalIPAddress() returned %d\n", r); + else + { + if(externalIPAddress[0]) + { + printf("UPnP: ExternalIPAddress = %s\n", externalIPAddress); + AddLocal(CNetAddr(externalIPAddress), LOCAL_UPNP); + } + else + printf("UPnP: GetExternalIPAddress failed.\n"); + } + } + + string strDesc = "Bitcoin " + FormatFullVersion(); + + try { + loop { +#ifndef UPNPDISCOVER_SUCCESS + /* miniupnpc 1.5 */ + r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, + port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0); +#else + /* miniupnpc 1.6 */ + r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, + port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0"); +#endif + + if(r!=UPNPCOMMAND_SUCCESS) + printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", + port.c_str(), port.c_str(), lanaddr, r, strupnperror(r)); + else + printf("UPnP Port Mapping successful.\n");; + + MilliSleep(20*60*1000); // Refresh every 20 minutes + } + } + catch (boost::thread_interrupted) + { + r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0); + printf("UPNP_DeletePortMapping() returned : %d\n", r); + freeUPNPDevlist(devlist); devlist = 0; + FreeUPNPUrls(&urls); + throw; + } + } else { + printf("No valid UPnP IGDs found\n"); + freeUPNPDevlist(devlist); devlist = 0; + if (r != 0) + FreeUPNPUrls(&urls); + } +} + +void MapPort(bool fUseUPnP) +{ + static boost::thread* upnp_thread = NULL; + + if (fUseUPnP) + { + if (upnp_thread) { + upnp_thread->interrupt(); + upnp_thread->join(); + delete upnp_thread; + } + upnp_thread = new boost::thread(boost::bind(&TraceThread >, "upnp", &ThreadMapPort)); + } + else if (upnp_thread) { + upnp_thread->interrupt(); + upnp_thread->join(); + delete upnp_thread; + upnp_thread = NULL; + } +} + +#else +void MapPort(bool) +{ + // Intentionally left blank. +} +#endif + + + + + + + + + +// 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 *strMainNetDNSSeed[][2] = { + {"46.255.168.65", "46.255.168.65"}, + {NULL, NULL} +}; + +static const char *strTestNetDNSSeed[][2] = { + {NULL, NULL} +}; + +void ThreadDNSAddressSeed() +{ + static const char *(*strDNSSeed)[2] = fTestNet ? strTestNetDNSSeed : strMainNetDNSSeed; + + int found = 0; + + printf("Loading addresses from DNS seeds (could take a while)\n"); + + for (unsigned int seed_idx = 0; strDNSSeed[seed_idx][0] != NULL; 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) + { + int nOneDay = 24*3600; + 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); +} + + + + + + + + + + + + +unsigned int pnSeed[] = +{ +}; + +void DumpAddresses() +{ + int64 nStart = GetTimeMillis(); + + CAddrDB adb; + adb.Write(addrman); + + printf("Flushed %d addresses to peers.dat %"PRI64d"ms\n", + addrman.size(), GetTimeMillis() - nStart); +} + +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 ThreadOpenConnections() +{ + // Connect to specific addresses + if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) + { + for (int64 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++) + { + MilliSleep(500); + } + } + MilliSleep(500); + } + } + + // Initiate network connections + int64 nStart = GetTime(); + loop + { + ProcessOneShot(); + + MilliSleep(500); + + CSemaphoreGrant grant(*semOutbound); + boost::this_thread::interruption_point(); + + // Add seed nodes if IRC isn't working + if (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 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")); + } + + // + // 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 nANow = GetAdjustedTime(); + + int nTries = 0; + loop + { + // 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() +{ + { + LOCK(cs_vAddedNodes); + vAddedNodes = mapMultiArgs["-addnode"]; + } + + if (HaveNameProxy()) { + while(true) { + 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()); + MilliSleep(500); + } + MilliSleep(120000); // Retry every 2 minutes + } + } + + for (unsigned int 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); + it--; + break; + } + } + BOOST_FOREACH(vector& vserv, lservAddressesToAdd) + { + CSemaphoreGrant grant(*semOutbound); + OpenNetworkConnection(CAddress(vserv[i % vserv.size()]), &grant); + MilliSleep(500); + } + MilliSleep(120000); // Retry every 2 minutes + } +} + +// 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 + // + boost::this_thread::interruption_point(); + if (!strDest) + if (IsLocal(addrConnect) || + FindNode((CNetAddr)addrConnect) || CNode::IsBanned(addrConnect) || + FindNode(addrConnect.ToStringIPPort().c_str())) + return false; + if (strDest && FindNode(strDest)) + return false; + + CNode* pnode = ConnectNode(addrConnect, strDest); + boost::this_thread::interruption_point(); + + 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 +double static NodeSyncScore(const CNode *pnode) { + return -pnode->nLastRecv; +} + +void static StartSync(const vector &vNodes) { + CNode *pnodeNewSync = NULL; + double dBestScore = 0; + + // fImporting and fReindex are accessed out of cs_main here, but only + // as an optimization - they are checked again in SendMessages. + if (fImporting || fReindex) + return; + + // 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 + double dScore = NodeSyncScore(pnode); + if (pnodeNewSync == NULL || dScore > dBestScore) { + pnodeNewSync = pnode; + dBestScore = dScore; + } + } + } + // if a new sync candidate was found, start sync! + if (pnodeNewSync) { + pnodeNewSync->fStartSync = true; + pnodeSync = pnodeNewSync; + } +} + +void ThreadMessageHandler() +{ + SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL); + while (true) + { + 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) + { + if (pnode->fDisconnect) + continue; + + // Receive messages + { + TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); + if (lockRecv) + if (!ProcessMessages(pnode)) + pnode->CloseSocketDisconnect(); + } + boost::this_thread::interruption_point(); + + // Send messages + { + TRY_LOCK(pnode->cs_vSend, lockSend); + if (lockSend) + SendMessages(pnode, pnode == pnodeTrickle); + } + boost::this_thread::interruption_point(); + } + + { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodesCopy) + pnode->Release(); + } + + MilliSleep(100); + } +} + + + + + + +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; + } + +#ifdef SO_NOSIGPIPE + // Different way of disabling SIGPIPE on BSD + setsockopt(hListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&nOne, sizeof(int)); +#endif + +#ifndef WIN32 + // 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 = 10 /* PROTECTION_LEVEL_UNRESTRICTED */; + int nParameterId = 23 /* IPV6_PROTECTION_LEVEl */; + // this call is allowed to fail + setsockopt(hListenSocket, IPPROTO_IPV6, nParameterId, (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. Bitcoin 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()); + 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()); + 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(boost::thread_group& threadGroup) +{ + if (semOutbound == NULL) { + // initialize semaphore + int nMaxOutbound = min(MAX_OUTBOUND_CONNECTIONS, nMaxConnections); + semOutbound = new CSemaphore(nMaxOutbound); + } + + if (pnodeLocalHost == NULL) + pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), nLocalServices)); + + Discover(); + + // + // Start threads + // + + if (!GetBoolArg("-dnsseed", true)) + printf("DNS seeding disabled\n"); + else + threadGroup.create_thread(boost::bind(&TraceThread >, "dnsseed", &ThreadDNSAddressSeed)); + +#ifdef USE_UPNP + // Map ports with UPnP + MapPort(GetBoolArg("-upnp", USE_UPNP)); +#endif + + // Send and receive from sockets, accept connections + threadGroup.create_thread(boost::bind(&TraceThread, "net", &ThreadSocketHandler)); + + // Initiate outbound connections from -addnode + threadGroup.create_thread(boost::bind(&TraceThread, "addcon", &ThreadOpenAddedConnections)); + + // Initiate outbound connections + threadGroup.create_thread(boost::bind(&TraceThread, "opencon", &ThreadOpenConnections)); + + // Process messages + threadGroup.create_thread(boost::bind(&TraceThread, "msghand", &ThreadMessageHandler)); + + // Dump network addresses + threadGroup.create_thread(boost::bind(&LoopForever, "dumpaddr", &DumpAddresses, 1000 * dump_addresses_interval)); +} + +bool StopNode() +{ + printf("StopNode()\n"); + GenerateBitcoins(false, NULL); + MapPort(false); + nTransactionsUpdated++; + if (semOutbound) + for (int i=0; ipost(); + MilliSleep(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)); + } + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + { + if(!pnode->fRelayTxes) + continue; + LOCK(pnode->cs_filter); + if (pnode->pfilter) + { + if (pnode->pfilter->IsRelevantAndUpdate(tx, hash)) + pnode->PushInventory(inv); + } else + pnode->PushInventory(inv); + } +} diff --git a/src/net.cpp.bak b/src/net.cpp.bak new file mode 100644 index 0000000..d46e844 --- /dev/null +++ b/src/net.cpp.bak @@ -0,0 +1,1885 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2017 Greenchain 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 "init.h" +#include "addrman.h" +#include "ui_interface.h" +#include "script.h" + +#ifdef WIN32 +#include +#endif + +#ifdef USE_UPNP +#include +#include +#include +#include +#endif + +// period dump peer.dat in second +const unsigned int dump_addresses_interval = 900; + +using namespace std; +using namespace boost; + +static const int MAX_OUTBOUND_CONNECTIONS = 24; + +bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false); + + +struct LocalServiceInfo { + int nScore; + int nPort; +}; + +// +// Global state variables +// +bool fDiscover = true; +uint64 nLocalServices = 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; +uint64 nLocalHostNonce = 0; +static std::vector vhListenSocket; +CAddrMan addrman; +int nMaxConnections = 125; + +vector vNodes; +CCriticalSection cs_vNodes; +map mapRelay; +deque > vRelayExpiration; +CCriticalSection cs_mapRelay; +limitedmap mapAlreadyAskedFor(MAX_INV_SZ); + +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",0),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 = ""; + loop + { + char c; + int nBytes = recv(hSocket, &c, 1, 0); + if (nBytes > 0) + { + if (c == '\n') + continue; + if (c == '\r') + return true; + strLine += c; + if (strLine.size() >= 9000) + return true; + } + else if (nBytes <= 0) + { + boost::this_thread::interruption_point(); + if (nBytes < 0) + { + int nErr = WSAGetLastError(); + if (nErr == WSAEMSGSIZE) + continue; + if (nErr == WSAEWOULDBLOCK || nErr == WSAEINTR || nErr == WSAEINPROGRESS) + { + MilliSleep(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]; +} + +bool GetMyExternalIP2(const CService& addrConnect, const char* pszGet, const char* pszKeyword, CNetAddr& ipRet) +{ + SOCKET hSocket; + if (!ConnectSocket(addrConnect, hSocket)) + return error("GetMyExternalIP() : connection to %s failed", addrConnect.ToString().c_str()); + + send(hSocket, pszGet, strlen(pszGet), MSG_NOSIGNAL); + + string strLine; + while (RecvLine(hSocket, strLine)) + { + if (strLine.empty()) // HTTP response is separated from headers by blank line + { + loop + { + if (!RecvLine(hSocket, strLine)) + { + closesocket(hSocket); + return false; + } + if (pszKeyword == NULL) + break; + if (strLine.find(pszKeyword) != string::npos) + { + strLine = strLine.substr(strLine.find(pszKeyword) + strlen(pszKeyword)); + break; + } + } + closesocket(hSocket); + if (strLine.find("<") != string::npos) + strLine = strLine.substr(0, strLine.find("<")); + strLine = strLine.substr(strspn(strLine.c_str(), " \t\n\r")); + while (strLine.size() > 0 && isspace(strLine[strLine.size()-1])) + strLine.resize(strLine.size()-1); + CService addr(strLine,0,true); + printf("GetMyExternalIP() received [%s] %s\n", strLine.c_str(), addr.ToString().c_str()); + if (!addr.IsValid() || !addr.IsRoutable()) + return false; + ipRet.SetIP(addr); + return true; + } + } + closesocket(hSocket); + return error("GetMyExternalIP() : connection closed"); +} + +bool GetMyExternalIP(CNetAddr& ipRet) +{ + CService addrConnect; + const char* pszGet; + const char* pszKeyword; + + for (int nLookup = 0; nLookup <= 1; nLookup++) + for (int nHost = 1; nHost <= 2; nHost++) + { + // We should be phasing out our use of sites like these. If we need + // replacements, we should ask for volunteers to put this simple + // php file on their web server that prints the client IP: + // + /* if (nHost == 1) + { + addrConnect = CService("91.198.22.70", 80); // checkip.dyndns.org + + if (nLookup == 1) + { + CService addrIP("checkip.dyndns.org", 80, true); + if (addrIP.IsValid()) + addrConnect = addrIP; + } + + pszGet = "GET / HTTP/1.1\r\n" + "Host: checkip.dyndns.org\r\n" + "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)\r\n" + "Connection: close\r\n" + "\r\n"; + + pszKeyword = "Address:"; + } + else if (nHost == 2) + { + addrConnect = CService("91.198.22.70", 80); // checkip.dyndns.org + + if (nLookup == 1) + { + CService addrIP("checkip.dyndns.org", 80, true); + if (addrIP.IsValid()) + addrConnect = addrIP; + } + + pszGet = "GET /simple/ HTTP/1.1\r\n" + "Host: checkip.dyndns.org\r\n" + "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)\r\n" + "Connection: close\r\n" + "\r\n"; + + pszKeyword = NULL; // Returns just IP address + }*/ + + if (GetMyExternalIP2(addrConnect, pszGet, pszKeyword, ipRet)) + return true; + } + + return false; +} + +void ThreadGetMyExternalIP(void* parg) +{ + // Make this thread recognisable as the external IP detection thread + RenameThread("bitcoin-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); +} + + + + + + + +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) +{ + if (pszDest == NULL) { + if (IsLocal(addrConnect)) + return NULL; + + // Look for an existing connection + CNode* pnode = FindNode((CService)addrConnect); + if (pnode) + { + 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); + 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; + } + + // in case this fails, we'll empty the recv buffer when the CNode is deleted + TRY_LOCK(cs_vRecvMsg, lockRecv); + if (lockRecv) + vRecvMsg.clear(); + + // if this was the sync node, we'll need a new one + if (this == pnodeSync) + pnodeSync = NULL; +} + +void CNode::Cleanup() +{ +} + + +void CNode::PushVersion() +{ + /// when NTP implemented, change to just nTime = GetAdjustedTime() + int64 nTime = (fInbound ? GetAdjustedTime() : GetTime()); + CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0",0))); + CAddress 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 = (*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 >= GetArg("-banscore", 100)) + { + int64 banTime = GetTime()+GetArg("-bantime", 60*60*24); // 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(nStartingHeight); + X(nMisbehavior); + X(nSendBytes); + X(nRecvBytes); + stats.fSyncNode = (this == pnodeSync); +} +#undef X + +// requires LOCK(cs_vRecvMsg) +bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes) +{ + while (nBytes > 0) { + + // get current incomplete message, or create a new one + if (vRecvMsg.empty() || + vRecvMsg.back().complete()) + vRecvMsg.push_back(CNetMessage(SER_NETWORK, nRecvVersion)); + + CNetMessage& msg = vRecvMsg.back(); + + // absorb network data + int handled; + if (!msg.in_data) + handled = msg.readHeader(pch, nBytes); + else + handled = msg.readData(pch, nBytes); + + if (handled < 0) + return false; + + pch += handled; + nBytes -= handled; + } + + return true; +} + +int CNetMessage::readHeader(const char *pch, unsigned int nBytes) +{ + // copy data to temporary parsing buffer + unsigned int nRemaining = 24 - nHdrPos; + unsigned int nCopy = std::min(nRemaining, nBytes); + + memcpy(&hdrbuf[nHdrPos], pch, nCopy); + nHdrPos += nCopy; + + // if header incomplete, exit + if (nHdrPos < 24) + return nCopy; + + // deserialize to CMessageHeader + try { + hdrbuf >> hdr; + } + catch (std::exception &e) { + return -1; + } + + // reject messages larger than MAX_SIZE + if (hdr.nMessageSize > MAX_SIZE) + return -1; + + // switch state to reading message data + in_data = true; + vRecv.resize(hdr.nMessageSize); + + return nCopy; +} + +int CNetMessage::readData(const char *pch, unsigned int nBytes) +{ + unsigned int nRemaining = hdr.nMessageSize - nDataPos; + unsigned int nCopy = std::min(nRemaining, nBytes); + + memcpy(&vRecv[nDataPos], pch, nCopy); + nDataPos += nCopy; + + return nCopy; +} + + + + + + + + + +// requires LOCK(cs_vSend) +void SocketSendData(CNode *pnode) +{ + std::deque::iterator it = pnode->vSendMsg.begin(); + + while (it != pnode->vSendMsg.end()) { + const CSerializeData &data = *it; + assert(data.size() > pnode->nSendOffset); + int nBytes = send(pnode->hSocket, &data[pnode->nSendOffset], data.size() - pnode->nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT); + if (nBytes > 0) { + pnode->nLastSend = GetTime(); + pnode->nSendBytes += nBytes; + pnode->nSendOffset += nBytes; + if (pnode->nSendOffset == data.size()) { + pnode->nSendOffset = 0; + pnode->nSendSize -= data.size(); + it++; + } else { + // could not send full message; stop sending more + break; + } + } 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(); + } + } + // couldn't send anything at all + break; + } + } + + if (it == pnode->vSendMsg.end()) { + assert(pnode->nSendOffset == 0); + assert(pnode->nSendSize == 0); + } + pnode->vSendMsg.erase(pnode->vSendMsg.begin(), it); +} + +static list vNodesDisconnected; + +void ThreadSocketHandler() +{ + unsigned int nPrevNodeCount = 0; + loop + { + // + // Disconnect nodes + // + { + LOCK(cs_vNodes); + // Disconnect unused nodes + vector vNodesCopy = vNodes; + BOOST_FOREACH(CNode* pnode, vNodesCopy) + { + if (pnode->fDisconnect || + (pnode->GetRefCount() <= 0 && pnode->vRecvMsg.empty() && pnode->nSendSize == 0 && pnode->ssSend.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 + 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_vRecvMsg, lockRecv); + if (lockRecv) + { + 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, &fdsetError); + hSocketMax = max(hSocketMax, pnode->hSocket); + have_fds = true; + + // Implement the following logic: + // * If there is data to send, select() for sending data. As this only + // happens when optimistic write failed, we choose to first drain the + // write buffer in this case before receiving more. This avoids + // needlessly queueing received data, if the remote peer is not themselves + // receiving data. This means properly utilizing TCP flow control signalling. + // * Otherwise, if there is no (complete) message in the receive buffer, + // or there is space left in the buffer, select() for receiving data. + // * (if neither of the above applies, there is certainly one message + // in the receiver buffer ready to be processed). + // Together, that means that at least one of the following is always possible, + // so we don't deadlock: + // * We send some data. + // * We wait for data to be received (and disconnect after timeout). + // * We process a message in the buffer (message handler thread). + { + TRY_LOCK(pnode->cs_vSend, lockSend); + if (lockSend && !pnode->vSendMsg.empty()) { + FD_SET(pnode->hSocket, &fdsetSend); + continue; + } + } + { + TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); + if (lockRecv && ( + pnode->vRecvMsg.empty() || !pnode->vRecvMsg.front().complete() || + pnode->GetTotalRecvSize() <= ReceiveFloodSize())) + FD_SET(pnode->hSocket, &fdsetRecv); + } + } + } + + int nSelect = select(have_fds ? hSocketMax + 1 : 0, + &fdsetRecv, &fdsetSend, &fdsetError, &timeout); + boost::this_thread::interruption_point(); + + 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); + MilliSleep(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 >= nMaxConnections - 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) + { + boost::this_thread::interruption_point(); + + // + // Receive + // + if (pnode->hSocket == INVALID_SOCKET) + continue; + if (FD_ISSET(pnode->hSocket, &fdsetRecv) || FD_ISSET(pnode->hSocket, &fdsetError)) + { + TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); + if (lockRecv) + { + { + // typical socket buffer is 8K-64K + char pchBuf[0x10000]; + int nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT); + if (nBytes > 0) + { + if (!pnode->ReceiveMsgBytes(pchBuf, nBytes)) + pnode->CloseSocketDisconnect(); + pnode->nLastRecv = GetTime(); + pnode->nRecvBytes += 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) + SocketSendData(pnode); + } + + // + // Inactivity checking + // + if (pnode->vSendMsg.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(); + } + + MilliSleep(10); + } +} + + + + + + + + + +#ifdef USE_UPNP +void ThreadMapPort() +{ + std::string port = strprintf("%u", GetListenPort()); + const char * multicastif = 0; + const char * minissdpdpath = 0; + struct UPNPDev * devlist = 0; + char lanaddr[64]; + +#ifndef UPNPDISCOVER_SUCCESS + /* miniupnpc 1.5 */ + devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0); +#else + /* miniupnpc 1.6 */ + int error = 0; + devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error); +#endif + + struct UPNPUrls urls; + struct IGDdatas data; + int r; + + r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)); + if (r == 1) + { + if (fDiscover) { + char externalIPAddress[40]; + r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress); + if(r != UPNPCOMMAND_SUCCESS) + printf("UPnP: GetExternalIPAddress() returned %d\n", r); + else + { + if(externalIPAddress[0]) + { + printf("UPnP: ExternalIPAddress = %s\n", externalIPAddress); + AddLocal(CNetAddr(externalIPAddress), LOCAL_UPNP); + } + else + printf("UPnP: GetExternalIPAddress failed.\n"); + } + } + + string strDesc = "Bitcoin " + FormatFullVersion(); + + try { + loop { +#ifndef UPNPDISCOVER_SUCCESS + /* miniupnpc 1.5 */ + r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, + port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0); +#else + /* miniupnpc 1.6 */ + r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, + port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0"); +#endif + + if(r!=UPNPCOMMAND_SUCCESS) + printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", + port.c_str(), port.c_str(), lanaddr, r, strupnperror(r)); + else + printf("UPnP Port Mapping successful.\n");; + + MilliSleep(20*60*1000); // Refresh every 20 minutes + } + } + catch (boost::thread_interrupted) + { + r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0); + printf("UPNP_DeletePortMapping() returned : %d\n", r); + freeUPNPDevlist(devlist); devlist = 0; + FreeUPNPUrls(&urls); + throw; + } + } else { + printf("No valid UPnP IGDs found\n"); + freeUPNPDevlist(devlist); devlist = 0; + if (r != 0) + FreeUPNPUrls(&urls); + } +} + +void MapPort(bool fUseUPnP) +{ + static boost::thread* upnp_thread = NULL; + + if (fUseUPnP) + { + if (upnp_thread) { + upnp_thread->interrupt(); + upnp_thread->join(); + delete upnp_thread; + } + upnp_thread = new boost::thread(boost::bind(&TraceThread >, "upnp", &ThreadMapPort)); + } + else if (upnp_thread) { + upnp_thread->interrupt(); + upnp_thread->join(); + delete upnp_thread; + upnp_thread = NULL; + } +} + +#else +void MapPort(bool) +{ + // Intentionally left blank. +} +#endif + + + + + + + + + +// 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 *strMainNetDNSSeed[][2] = { + {"greenchain.green-entrepreneurship.cc", "greenchain.green-entrepreneurship.cc"}, + {"gc.green-entrepreneurship.cc", "gc.green-entrepreneurship.cc"}, + {NULL, NULL} +}; + +static const char *strTestNetDNSSeed[][2] = { + {NULL, NULL} +}; + +void ThreadDNSAddressSeed() +{ + static const char *(*strDNSSeed)[2] = fTestNet ? strTestNetDNSSeed : strMainNetDNSSeed; + + int found = 0; + + printf("Loading addresses from DNS seeds (could take a while)\n"); + + for (unsigned int seed_idx = 0; strDNSSeed[seed_idx][0] != NULL; 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) + { + int nOneDay = 24*3600; + 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); +} + + + + + + + + + + + + +unsigned int pnSeed[] = +{ +}; + +void DumpAddresses() +{ + int64 nStart = GetTimeMillis(); + + CAddrDB adb; + adb.Write(addrman); + + printf("Flushed %d addresses to peers.dat %"PRI64d"ms\n", + addrman.size(), GetTimeMillis() - nStart); +} + +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 ThreadOpenConnections() +{ + // Connect to specific addresses + if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) + { + for (int64 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++) + { + MilliSleep(500); + } + } + MilliSleep(500); + } + } + + // Initiate network connections + int64 nStart = GetTime(); + loop + { + ProcessOneShot(); + + MilliSleep(500); + + CSemaphoreGrant grant(*semOutbound); + boost::this_thread::interruption_point(); + + // Add seed nodes if IRC isn't working + if (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 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")); + } + + // + // 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 nANow = GetAdjustedTime(); + + int nTries = 0; + loop + { + // 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() +{ + { + LOCK(cs_vAddedNodes); + vAddedNodes = mapMultiArgs["-addnode"]; + } + + if (HaveNameProxy()) { + while(true) { + 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()); + MilliSleep(500); + } + MilliSleep(120000); // Retry every 2 minutes + } + } + + for (unsigned int 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); + it--; + break; + } + } + BOOST_FOREACH(vector& vserv, lservAddressesToAdd) + { + CSemaphoreGrant grant(*semOutbound); + OpenNetworkConnection(CAddress(vserv[i % vserv.size()]), &grant); + MilliSleep(500); + } + MilliSleep(120000); // Retry every 2 minutes + } +} + +// 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 + // + boost::this_thread::interruption_point(); + if (!strDest) + if (IsLocal(addrConnect) || + FindNode((CNetAddr)addrConnect) || CNode::IsBanned(addrConnect) || + FindNode(addrConnect.ToStringIPPort().c_str())) + return false; + if (strDest && FindNode(strDest)) + return false; + + CNode* pnode = ConnectNode(addrConnect, strDest); + boost::this_thread::interruption_point(); + + 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 +double static NodeSyncScore(const CNode *pnode) { + return -pnode->nLastRecv; +} + +void static StartSync(const vector &vNodes) { + CNode *pnodeNewSync = NULL; + double dBestScore = 0; + + // fImporting and fReindex are accessed out of cs_main here, but only + // as an optimization - they are checked again in SendMessages. + if (fImporting || fReindex) + return; + + // 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 + double dScore = NodeSyncScore(pnode); + if (pnodeNewSync == NULL || dScore > dBestScore) { + pnodeNewSync = pnode; + dBestScore = dScore; + } + } + } + // if a new sync candidate was found, start sync! + if (pnodeNewSync) { + pnodeNewSync->fStartSync = true; + pnodeSync = pnodeNewSync; + } +} + +void ThreadMessageHandler() +{ + SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL); + while (true) + { + 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) + { + if (pnode->fDisconnect) + continue; + + // Receive messages + { + TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); + if (lockRecv) + if (!ProcessMessages(pnode)) + pnode->CloseSocketDisconnect(); + } + boost::this_thread::interruption_point(); + + // Send messages + { + TRY_LOCK(pnode->cs_vSend, lockSend); + if (lockSend) + SendMessages(pnode, pnode == pnodeTrickle); + } + boost::this_thread::interruption_point(); + } + + { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodesCopy) + pnode->Release(); + } + + MilliSleep(100); + } +} + + + + + + +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; + } + +#ifdef SO_NOSIGPIPE + // Different way of disabling SIGPIPE on BSD + setsockopt(hListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&nOne, sizeof(int)); +#endif + +#ifndef WIN32 + // 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 = 10 /* PROTECTION_LEVEL_UNRESTRICTED */; + int nParameterId = 23 /* IPV6_PROTECTION_LEVEl */; + // this call is allowed to fail + setsockopt(hListenSocket, IPPROTO_IPV6, nParameterId, (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. Bitcoin 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()); + 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()); + 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(boost::thread_group& threadGroup) +{ + if (semOutbound == NULL) { + // initialize semaphore + int nMaxOutbound = min(MAX_OUTBOUND_CONNECTIONS, nMaxConnections); + semOutbound = new CSemaphore(nMaxOutbound); + } + + if (pnodeLocalHost == NULL) + pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), nLocalServices)); + + Discover(); + + // + // Start threads + // + + if (!GetBoolArg("-dnsseed", true)) + printf("DNS seeding disabled\n"); + else + threadGroup.create_thread(boost::bind(&TraceThread >, "dnsseed", &ThreadDNSAddressSeed)); + +#ifdef USE_UPNP + // Map ports with UPnP + MapPort(GetBoolArg("-upnp", USE_UPNP)); +#endif + + // Send and receive from sockets, accept connections + threadGroup.create_thread(boost::bind(&TraceThread, "net", &ThreadSocketHandler)); + + // Initiate outbound connections from -addnode + threadGroup.create_thread(boost::bind(&TraceThread, "addcon", &ThreadOpenAddedConnections)); + + // Initiate outbound connections + threadGroup.create_thread(boost::bind(&TraceThread, "opencon", &ThreadOpenConnections)); + + // Process messages + threadGroup.create_thread(boost::bind(&TraceThread, "msghand", &ThreadMessageHandler)); + + // Dump network addresses + threadGroup.create_thread(boost::bind(&LoopForever, "dumpaddr", &DumpAddresses, 1000 * dump_addresses_interval)); +} + +bool StopNode() +{ + printf("StopNode()\n"); + GenerateBitcoins(false, NULL); + MapPort(false); + nTransactionsUpdated++; + if (semOutbound) + for (int i=0; ipost(); + MilliSleep(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)); + } + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + { + if(!pnode->fRelayTxes) + continue; + LOCK(pnode->cs_filter); + if (pnode->pfilter) + { + if (pnode->pfilter->IsRelevantAndUpdate(tx, hash)) + pnode->PushInventory(inv); + } else + pnode->PushInventory(inv); + } +} diff --git a/src/net.h b/src/net.h new file mode 100644 index 0000000..eb844e9 --- /dev/null +++ b/src/net.h @@ -0,0 +1,639 @@ +// 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 +#include +#include + +#ifndef WIN32 +#include +#endif + +#include "mruset.h" +#include "limitedmap.h" +#include "netbase.h" +#include "protocol.h" +#include "addrman.h" +#include "hash.h" +#include "bloom.h" + +class CNode; +class CBlockIndex; +extern int nBestHeight; + + + +inline unsigned int ReceiveFloodSize() { return 1000*GetArg("-maxreceivebuffer", 5*1000); } +inline unsigned int 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); +void MapPort(bool fUseUPnP); +unsigned short GetListenPort(); +bool BindListenPort(const CService &bindAddr, std::string& strError=REF(std::string())); +void StartNode(boost::thread_group& threadGroup); +bool StopNode(); +void SocketSendData(CNode *pnode); + +enum +{ + LOCAL_NONE, // unknown + LOCAL_IF, // address a local interface listens on + LOCAL_BIND, // address explicit bound to + LOCAL_UPNP, // address reported by UPnP + 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); + + +extern bool fDiscover; +extern uint64 nLocalServices; +extern uint64 nLocalHostNonce; +extern CAddrMan addrman; +extern int nMaxConnections; + +extern std::vector vNodes; +extern CCriticalSection cs_vNodes; +extern std::map mapRelay; +extern std::deque > vRelayExpiration; +extern CCriticalSection cs_mapRelay; +extern limitedmap mapAlreadyAskedFor; + +extern std::vector vAddedNodes; +extern CCriticalSection cs_vAddedNodes; + + + + +class CNodeStats +{ +public: + uint64 nServices; + int64 nLastSend; + int64 nLastRecv; + int64 nTimeConnected; + std::string addrName; + int nVersion; + std::string strSubVer; + bool fInbound; + int nStartingHeight; + int nMisbehavior; + uint64 nSendBytes; + uint64 nRecvBytes; + bool fSyncNode; +}; + + + + +class CNetMessage { +public: + bool in_data; // parsing header (false) or data (true) + + CDataStream hdrbuf; // partially received header + CMessageHeader hdr; // complete header + unsigned int nHdrPos; + + CDataStream vRecv; // received message data + unsigned int nDataPos; + + CNetMessage(int nTypeIn, int nVersionIn) : hdrbuf(nTypeIn, nVersionIn), vRecv(nTypeIn, nVersionIn) { + hdrbuf.resize(24); + in_data = false; + nHdrPos = 0; + nDataPos = 0; + } + + bool complete() const + { + if (!in_data) + return false; + return (hdr.nMessageSize == nDataPos); + } + + void SetVersion(int nVersionIn) + { + hdrbuf.SetVersion(nVersionIn); + vRecv.SetVersion(nVersionIn); + } + + int readHeader(const char *pch, unsigned int nBytes); + int readData(const char *pch, unsigned int nBytes); +}; + + + + + +/** Information about a peer */ +class CNode +{ +public: + // socket + uint64 nServices; + SOCKET hSocket; + CDataStream ssSend; + size_t nSendSize; // total size of all vSendMsg entries + size_t nSendOffset; // offset inside the first vSendMsg already sent + uint64 nSendBytes; + std::deque vSendMsg; + CCriticalSection cs_vSend; + + std::deque vRecvGetData; + std::deque vRecvMsg; + CCriticalSection cs_vRecvMsg; + uint64 nRecvBytes; + int nRecvVersion; + + int64 nLastSend; + int64 nLastRecv; + int64 nLastSendEmpty; + int64 nTimeConnected; + CAddress addr; + std::string addrName; + CService addrLocal; + int nVersion; + std::string strSubVer; + bool fOneShot; + bool fClient; + bool fInbound; + bool fNetworkNode; + bool fSuccessfullyConnected; + bool fDisconnect; + // We use fRelayTxes for two purposes - + // a) it allows us to not relay tx invs before receiving the peer's version message + // b) the peer may tell us in their version message that we should not relay tx invs + // until they have initialized their bloom filter. + bool fRelayTxes; + CSemaphoreGrant grantOutbound; + CCriticalSection cs_filter; + CBloomFilter* pfilter; + int nRefCount; +protected: + + // 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: + uint256 hashContinue; + CBlockIndex* pindexLastGetBlocksBegin; + uint256 hashLastGetBlocksEnd; + int 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) : ssSend(SER_NETWORK, MIN_PROTO_VERSION) + { + nServices = 0; + hSocket = hSocketIn; + nRecvVersion = MIN_PROTO_VERSION; + nLastSend = 0; + nLastRecv = 0; + nSendBytes = 0; + nRecvBytes = 0; + nLastSendEmpty = GetTime(); + nTimeConnected = GetTime(); + 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; + nSendSize = 0; + nSendOffset = 0; + hashContinue = 0; + pindexLastGetBlocksBegin = 0; + hashLastGetBlocksEnd = 0; + nStartingHeight = -1; + fStartSync = false; + fGetAddr = false; + nMisbehavior = 0; + hashCheckpointKnown = 0; + fRelayTxes = false; + setInventoryKnown.max_size(SendBufferSize() / 1000); + pfilter = NULL; + + // 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; + } + if (pfilter) + delete pfilter; + } + +private: + CNode(const CNode&); + void operator=(const CNode&); +public: + + + int GetRefCount() + { + assert(nRefCount >= 0); + return nRefCount; + } + + // requires LOCK(cs_vRecvMsg) + unsigned int GetTotalRecvSize() + { + unsigned int total = 0; + BOOST_FOREACH(const CNetMessage &msg, vRecvMsg) + total += msg.vRecv.size() + 24; + return total; + } + + // requires LOCK(cs_vRecvMsg) + bool ReceiveMsgBytes(const char *pch, unsigned int nBytes); + + // requires LOCK(cs_vRecvMsg) + void SetRecvVersion(int nVersionIn) + { + nRecvVersion = nVersionIn; + BOOST_FOREACH(CNetMessage &msg, vRecvMsg) + msg.SetVersion(nVersionIn); + } + + CNode* AddRef() + { + 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 nRequestTime; + limitedmap::const_iterator it = mapAlreadyAskedFor.find(inv); + if (it != mapAlreadyAskedFor.end()) + nRequestTime = it->second; + else + nRequestTime = 0; + if (fDebugNet) + printf("askfor %s %"PRI64d" (%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 nNow = (GetTime() - 1) * 1000000; + static int64 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); + if (it != mapAlreadyAskedFor.end()) + mapAlreadyAskedFor.update(it, nRequestTime); + else + mapAlreadyAskedFor.insert(std::make_pair(inv, nRequestTime)); + mapAskFor.insert(std::make_pair(nRequestTime, inv)); + } + + + + // TODO: Document the postcondition of this function. Is cs_vSend locked? + void BeginMessage(const char* pszCommand) EXCLUSIVE_LOCK_FUNCTION(cs_vSend) + { + ENTER_CRITICAL_SECTION(cs_vSend); + assert(ssSend.size() == 0); + ssSend << CMessageHeader(pszCommand, 0); + if (fDebug) + printf("sending: %s ", pszCommand); + } + + // TODO: Document the precondition of this function. Is cs_vSend locked? + void AbortMessage() UNLOCK_FUNCTION(cs_vSend) + { + ssSend.clear(); + + LEAVE_CRITICAL_SECTION(cs_vSend); + + if (fDebug) + printf("(aborted)\n"); + } + + // TODO: Document the precondition of this function. Is cs_vSend locked? + void EndMessage() UNLOCK_FUNCTION(cs_vSend) + { + if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) + { + printf("dropmessages DROPPING SEND MESSAGE\n"); + AbortMessage(); + return; + } + + if (ssSend.size() == 0) + return; + + // Set the size + unsigned int nSize = ssSend.size() - CMessageHeader::HEADER_SIZE; + memcpy((char*)&ssSend[CMessageHeader::MESSAGE_SIZE_OFFSET], &nSize, sizeof(nSize)); + + // Set the checksum + uint256 hash = Hash2(ssSend.begin() + CMessageHeader::HEADER_SIZE, ssSend.end()); + unsigned int nChecksum = 0; + memcpy(&nChecksum, &hash, sizeof(nChecksum)); + assert(ssSend.size () >= CMessageHeader::CHECKSUM_OFFSET + sizeof(nChecksum)); + memcpy((char*)&ssSend[CMessageHeader::CHECKSUM_OFFSET], &nChecksum, sizeof(nChecksum)); + + if (fDebug) { + printf("(%d bytes)\n", nSize); + } + + std::deque::iterator it = vSendMsg.insert(vSendMsg.end(), CSerializeData()); + ssSend.GetAndClear(*it); + nSendSize += (*it).size(); + + // If write queue empty, attempt "optimistic write" + if (it == vSendMsg.begin()) + SocketSendData(this); + + LEAVE_CRITICAL_SECTION(cs_vSend); + } + + 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); + ssSend << a1; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2) + { + try + { + BeginMessage(pszCommand); + ssSend << a1 << a2; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3) + { + try + { + BeginMessage(pszCommand); + ssSend << 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); + ssSend << 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); + ssSend << 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); + ssSend << 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); + ssSend << 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); + ssSend << 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); + ssSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8 << a9; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + 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); +}; + + + +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 0000000..9eff432 --- /dev/null +++ b/src/netbase.cpp @@ -0,0 +1,1139 @@ +// 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 +#include +#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") return NET_TOR; + return NET_UNROUTABLE; +} + +void SplitHostPort(std::string in, int &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)) + { + if (aiTrav->ai_family == AF_INET) + { + assert(aiTrav->ai_addrlen >= sizeof(sockaddr_in)); + vIP.push_back(CNetAddr(((struct sockaddr_in*)(aiTrav->ai_addr))->sin_addr)); + } + +#ifdef USE_IPV6 + if (aiTrav->ai_family == AF_INET6) + { + assert(aiTrav->ai_addrlen >= sizeof(sockaddr_in6)); + vIP.push_back(CNetAddr(((struct sockaddr_in6*)(aiTrav->ai_addr))->sin6_addr)); + } +#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, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions) +{ + if (pszName[0] == 0) + return false; + int 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, int 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, int 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, int 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) + 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: + return false; + } + + hSocketRet = hSocket; + return true; +} + +bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault, int nTimeout) +{ + string strDest; + int 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: 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}; + +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; + } + 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]; +} + +unsigned int 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()); +} + +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::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()) || IsRFC4843() || IsLocal()); +} + +enum Network CNetAddr::GetNetwork() const +{ + if (!IsRoutable()) + return NET_UNROUTABLE; + + if (IsIPv4()) + return NET_IPV4; + + if (IsTor()) + return NET_TOR; + + return NET_IPV6; +} + +std::string CNetAddr::ToStringIP() const +{ + if (IsTor()) + return EncodeBase32(&ip[6], 10) + ".onion"; + CService serv(*this, 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; + int 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; + } + // for he.net, use /36 groups + else if (GetByte(15) == 0x20 && GetByte(14) == 0x11 && 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 CNetAddr::GetHash() const +{ + uint256 hash = Hash2(&ip[0], &ip[16]); + uint64 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_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_TOR: return REACH_PRIVATE; // either from Tor, or don't care about our address + } + } +} + +void CService::Init() +{ + port = 0; +} + +CService::CService() +{ + Init(); +} + +CService::CService(const CNetAddr& cip, unsigned short portIn) : CNetAddr(cip), port(portIn) +{ +} + +CService::CService(const struct in_addr& ipv4Addr, unsigned short portIn) : CNetAddr(ipv4Addr), port(portIn) +{ +} + +#ifdef USE_IPV6 +CService::CService(const struct in6_addr& ipv6Addr, unsigned short 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, int 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, int 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()) { + 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 0000000..e4ec4ef --- /dev/null +++ b/src/netbase.h @@ -0,0 +1,151 @@ +// 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_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 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) + 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 IsLocal() const; + bool IsRoutable() const; + bool IsValid() const; + bool IsMulticast() const; + enum Network GetNetwork() const; + std::string ToString() const; + std::string ToStringIP() const; + unsigned int GetByte(int n) const; + uint64 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, unsigned short port); + CService(const struct in_addr& ipv4Addr, unsigned short port); + CService(const struct sockaddr_in& addr); + explicit CService(const char *pszIpPort, int portDefault, bool fAllowLookup = false); + explicit CService(const char *pszIpPort, bool fAllowLookup = false); + explicit CService(const std::string& strIpPort, int portDefault, bool fAllowLookup = false); + explicit CService(const std::string& strIpPort, bool fAllowLookup = false); + void Init(); + void SetPort(unsigned short 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, unsigned short 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, int &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, int portDefault = 0, bool fAllowLookup = true); +bool Lookup(const char *pszName, std::vector& vAddr, int portDefault = 0, bool fAllowLookup = true, unsigned int nMaxSolutions = 0); +bool LookupNumeric(const char *pszName, CService& addr, int portDefault = 0); +bool ConnectSocket(const CService &addr, SOCKET& hSocketRet, int nTimeout = nConnectTimeout); +bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault = 0, int nTimeout = nConnectTimeout); + +#endif diff --git a/src/noui.cpp b/src/noui.cpp new file mode 100644 index 0000000..c0e00c4 --- /dev/null +++ b/src/noui.cpp @@ -0,0 +1,51 @@ +// 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 bool noui_ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style) +{ + std::string strCaption; + // Check for usage of predefined caption + switch (style) { + case CClientUIInterface::MSG_ERROR: + strCaption += _("Error"); + break; + case CClientUIInterface::MSG_WARNING: + strCaption += _("Warning"); + break; + case CClientUIInterface::MSG_INFORMATION: + strCaption += _("Information"); + break; + default: + strCaption += caption; // Use supplied caption (can be empty) + } + + printf("%s: %s\n", strCaption.c_str(), message.c_str()); + fprintf(stderr, "%s: %s\n", strCaption.c_str(), message.c_str()); + return false; +} + +static bool noui_ThreadSafeAskFee(int64 /*nFeeRequired*/) +{ + return true; +} + +static void noui_InitMessage(const std::string &message) +{ + printf("init message: %s\n", message.c_str()); +} + +void noui_connect() +{ + // Connect bitcoind signal handlers + uiInterface.ThreadSafeMessageBox.connect(noui_ThreadSafeMessageBox); + uiInterface.ThreadSafeAskFee.connect(noui_ThreadSafeAskFee); + uiInterface.InitMessage.connect(noui_InitMessage); +} diff --git a/src/obj-test/.gitignore b/src/obj-test/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/src/obj-test/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/src/protocol.cpp b/src/protocol.cpp new file mode 100644 index 0000000..88bbe49 --- /dev/null +++ b/src/protocol.cpp @@ -0,0 +1,152 @@ +// 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" +#include "main.h" + +#ifndef WIN32 +# include +#endif + +static const char* ppszTypeName[] = +{ + "ERROR", + "tx", + "block", + "filtered block" +}; + +CMessageHeader::CMessageHeader() +{ + memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)); + memset(pchCommand, 0, sizeof(pchCommand)); + pchCommand[1] = 1; + nMessageSize = -1; + 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 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().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 0000000..eb52d7b --- /dev/null +++ b/src/protocol.h @@ -0,0 +1,147 @@ +// 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 "uint256.h" + +extern bool fTestNet; +static inline unsigned short GetDefaultPort(const bool testnet = fTestNet) +{ + return testnet ? 41743 : 31743; +} + + +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, + HEADER_SIZE=MESSAGE_START_SIZE+COMMAND_SIZE+MESSAGE_SIZE_SIZE+CHECKSUM_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 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 nServices; + + // disk and network only + unsigned int nTime; + + // memory only + int64 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; +}; + +enum +{ + MSG_TX = 1, + MSG_BLOCK, + // Nodes may always request a MSG_FILTERED_BLOCK in a getdata, however, + // MSG_FILTERED_BLOCK should not appear in any invs except as a part of getdata. + MSG_FILTERED_BLOCK, +}; + +#endif // __INCLUDED_PROTOCOL_H__ diff --git a/src/qt/aboutdialog.cpp b/src/qt/aboutdialog.cpp new file mode 100644 index 0000000..87b2d04 --- /dev/null +++ b/src/qt/aboutdialog.cpp @@ -0,0 +1,34 @@ +#include "aboutdialog.h" +#include "ui_aboutdialog.h" + +#include "clientmodel.h" +#include "clientversion.h" + +AboutDialog::AboutDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::AboutDialog) +{ + ui->setupUi(this); + + // Set current copyright year + ui->copyrightLabel->setText( + tr("The source code is available from https://github.com/Greenchain/Greenchain")); +} + +void AboutDialog::setModel(ClientModel *model) +{ + if(model) + { + ui->versionLabel->setText(model->formatFullVersion()); + } +} + +AboutDialog::~AboutDialog() +{ + delete ui; +} + +void AboutDialog::on_buttonBox_accepted() +{ + close(); +} diff --git a/src/qt/aboutdialog.h b/src/qt/aboutdialog.h new file mode 100644 index 0000000..33b1437 --- /dev/null +++ b/src/qt/aboutdialog.h @@ -0,0 +1,29 @@ +#ifndef ABOUTDIALOG_H +#define ABOUTDIALOG_H + +#include + +namespace Ui { + class AboutDialog; +} +class ClientModel; + +/** "About" dialog box */ +class AboutDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AboutDialog(QWidget *parent = 0); + ~AboutDialog(); + + void setModel(ClientModel *model); + +private: + Ui::AboutDialog *ui; + +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 0000000..feb441b --- /dev/null +++ b/src/qt/addressbookpage.cpp @@ -0,0 +1,390 @@ +#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" + +#ifdef USE_QRCODE +#include "qrcodedialog.h" +#endif + +#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->newAddress->setIcon(QIcon()); + ui->copyAddress->setIcon(QIcon()); + ui->deleteAddress->setIcon(QIcon()); + ui->verifyMessage->setIcon(QIcon()); + ui->signMessage->setIcon(QIcon()); + ui->exportButton->setIcon(QIcon()); +#endif + +#ifndef USE_QRCODE + ui->showQRCode->setVisible(false); +#endif + + switch(mode) + { + case ForSending: + connect(ui->tableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(accept())); + ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers); + ui->tableView->setFocus(); + ui->exportButton->hide(); + break; + case ForEditing: + ui->buttonBox->setVisible(false); + break; + } + switch(tab) + { + case SendingTab: + ui->labelExplanation->setText(tr("These are your Greenchain addresses for sending payments. Always check the amount and the receiving address before sending coins.")); + ui->deleteAddress->setVisible(true); + ui->signMessage->setVisible(false); + break; + case ReceivingTab: + ui->labelExplanation->setText(tr("These are your Greenchain 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.")); + ui->deleteAddress->setVisible(false); + ui->signMessage->setVisible(true); + break; + } + + // Context menu actions + QAction *copyAddressAction = new QAction(ui->copyAddress->text(), this); + QAction *copyLabelAction = new QAction(tr("Copy &Label"), this); + QAction *editAction = new QAction(tr("&Edit"), this); + QAction *sendCoinsAction = new QAction(tr("Send &Coins"), 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->deleteAddress->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(); + if(tab == SendingTab) + contextMenu->addAction(sendCoinsAction); +#ifdef USE_QRCODE + contextMenu->addAction(showQRCodeAction); +#endif + 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_copyAddress_clicked())); + connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(onCopyLabelAction())); + connect(editAction, SIGNAL(triggered()), this, SLOT(onEditAction())); + connect(deleteAction, SIGNAL(triggered()), this, SLOT(on_deleteAddress_clicked())); + connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(onSendCoinsAction())); + 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()->setResizeMode(AddressTableModel::Label, QHeaderView::Stretch); + ui->tableView->horizontalHeader()->setResizeMode(AddressTableModel::Address, QHeaderView::ResizeToContents); + + 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_copyAddress_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); + + foreach (QModelIndex index, indexes) + { + QString address = index.data().toString(); + emit signMessage(address); + } +} + +void AddressBookPage::on_verifyMessage_clicked() +{ + QTableView *table = ui->tableView; + QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); + + foreach (QModelIndex index, indexes) + { + QString address = index.data().toString(); + emit verifyMessage(address); + } +} + +void AddressBookPage::onSendCoinsAction() +{ + QTableView *table = ui->tableView; + QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); + + foreach (QModelIndex index, indexes) + { + QString address = index.data().toString(); + emit sendCoins(address); + } +} + +void AddressBookPage::on_newAddress_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_deleteAddress_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->deleteAddress->setEnabled(true); + ui->deleteAddress->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->deleteAddress->setEnabled(false); + ui->deleteAddress->setVisible(false); + deleteAction->setEnabled(false); + ui->signMessage->setEnabled(true); + ui->signMessage->setVisible(true); + ui->verifyMessage->setEnabled(false); + ui->verifyMessage->setVisible(false); + break; + } + ui->copyAddress->setEnabled(true); + ui->showQRCode->setEnabled(true); + } + else + { + ui->deleteAddress->setEnabled(false); + ui->showQRCode->setEnabled(false); + ui->copyAddress->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::on_exportButton_clicked() +{ + // 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() +{ +#ifdef USE_QRCODE + QTableView *table = ui->tableView; + QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); + + foreach (QModelIndex index, indexes) + { + QString address = index.data().toString(); + QString label = index.sibling(index.row(), 0).data(Qt::EditRole).toString(); + + QRCodeDialog *dialog = new QRCodeDialog(address, label, tab == ReceivingTab, this); + dialog->setModel(optionsModel); + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->show(); + } +#endif +} + +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 0000000..34465aa --- /dev/null +++ b/src/qt/addressbookpage.h @@ -0,0 +1,94 @@ +#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); + +private: + Ui::AddressBookPage *ui; + AddressTableModel *model; + OptionsModel *optionsModel; + Mode mode; + Tabs tab; + QString returnValue; + QSortFilterProxyModel *proxyModel; + QMenu *contextMenu; + QAction *deleteAction; // to be able to explicitly disable it + QString newAddressToSelect; + +private slots: + /** Delete currently selected address entry */ + void on_deleteAddress_clicked(); + /** Create a new address for receiving coins and / or add a new address book entry */ + void on_newAddress_clicked(); + /** Copy address of currently selected address entry to clipboard */ + void on_copyAddress_clicked(); + /** Open the sign message tab in the Sign/Verify Message dialog with currently selected address */ + void on_signMessage_clicked(); + /** Open the verify message tab in the Sign/Verify Message dialog with currently selected address */ + void on_verifyMessage_clicked(); + /** Open send coins dialog for currently selected address (no button) */ + void onSendCoinsAction(); + /** Generate a QR Code from the currently selected address */ + void on_showQRCode_clicked(); + /** Copy label of currently selected address entry to clipboard (no button) */ + void onCopyLabelAction(); + /** Edit currently selected address entry (no button) */ + void onEditAction(); + /** Export button clicked */ + void on_exportButton_clicked(); + + /** Set button states based on selected tab and selection */ + void selectionChanged(); + /** Spawn contextual menu (right mouse menu) for address book entry */ + void contextualMenu(const QPoint &point); + /** 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); + void sendCoins(QString addr); +}; + +#endif // ADDRESSBOOKPAGE_H diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp new file mode 100644 index 0000000..1801444 --- /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 + +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 0000000..48baff5 --- /dev/null +++ b/src/qt/addresstablemodel.h @@ -0,0 +1,93 @@ +#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 0000000..62bc931 --- /dev/null +++ b/src/qt/askpassphrasedialog.cpp @@ -0,0 +1,248 @@ +#include "askpassphrasedialog.h" +#include "ui_askpassphrasedialog.h" + +#include "guiconstants.h" +#include "walletmodel.h" + +#include +#include +#include + +AskPassphraseDialog::AskPassphraseDialog(Mode mode, QWidget *parent) : + QDialog(parent), + 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 + 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 Greenchain!") + "

" + 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("Greenchain 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 Decrypt: + if(!model->setWalletEncrypted(false, oldpass)) + { + QMessageBox::critical(this, tr("Wallet decryption failed"), + tr("The passphrase entered for the wallet decryption was incorrect.")); + } + else + { + QDialog::accept(); // Success + } + break; + case ChangePass: + if(newpass1 == newpass2) + { + if(model->changePassphrase(oldpass, newpass1)) + { + QMessageBox::information(this, tr("Wallet encrypted"), + tr("Wallet passphrase was 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 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 >= 'a' && *psz <= 'z') || (!fShift && *psz >= 'A' && *psz <= 'Z')) { + 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 0000000..9df002d --- /dev/null +++ b/src/qt/askpassphrasedialog.h @@ -0,0 +1,44 @@ +#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 */ + 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 *object, QEvent *event); +}; + +#endif // ASKPASSPHRASEDIALOG_H diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp new file mode 100644 index 0000000..29c2e15 --- /dev/null +++ b/src/qt/bitcoin.cpp @@ -0,0 +1,297 @@ +/* + * W.J. van der Laan 2011-2012 + */ + +#include + +#include "bitcoingui.h" +#include "clientmodel.h" +#include "walletmodel.h" +#include "optionsmodel.h" +#include "guiutil.h" +#include "guiconstants.h" +#include "init.h" +#include "util.h" +#include "ui_interface.h" +#include "paymentserver.h" +#include "splashscreen.h" + +#include +#include +#include +#include +#include +#include + +#ifdef Q_OS_MAC +#include "macdockiconhandler.h" +#endif + +#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 + +// Declare meta types used for QMetaObject::invokeMethod +Q_DECLARE_METATYPE(bool*) + +// Need a global reference for the notifications to find the GUI +static BitcoinGUI *guiref; +static SplashScreen *splashref; + +static bool ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style) +{ + // Message from network thread + if(guiref) + { + bool modal = (style & CClientUIInterface::MODAL); + bool ret = false; + // In case of modal message, use blocking connection to wait for user to click a button + QMetaObject::invokeMethod(guiref, "message", + modal ? GUIUtil::blockingGUIThreadConnection() : Qt::QueuedConnection, + Q_ARG(QString, QString::fromStdString(caption)), + Q_ARG(QString, QString::fromStdString(message)), + Q_ARG(unsigned int, style), + Q_ARG(bool*, &ret)); + return ret; + } + else + { + printf("%s: %s\n", caption.c_str(), message.c_str()); + fprintf(stderr, "%s: %s\n", caption.c_str(), message.c_str()); + return false; + } +} + +static bool ThreadSafeAskFee(int64 nFeeRequired) +{ + if(!guiref) + return false; + if(nFeeRequired < CTransaction::nMinTxFee || 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 InitMessage(const std::string &message) +{ + if(splashref) + { + splashref->showMessage(QString::fromStdString(message), Qt::AlignBottom|Qt::AlignHCenter, QColor(127,131,147)); + qApp->processEvents(); + } + printf("init message: %s\n", message.c_str()); +} + +/* + Translate string to current locale using Qt. + */ +static std::string Translate(const char* psz) +{ + return QCoreApplication::translate("Greenchain-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. Greenchain 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[]) +{ + // Command-line options take precedence: + ParseParameters(argc, argv); + + // Internal string conversion is all UTF-8 + QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8")); + QTextCodec::setCodecForCStrings(QTextCodec::codecForTr()); + + Q_INIT_RESOURCE(bitcoin); + QApplication app(argc, argv); + + // Register meta types used for QMetaObject::invokeMethod + qRegisterMetaType< bool* >(); + + // Do this early as we don't want to bother initializing if we are just calling IPC + // ... but do it after creating app, so QCoreApplication::arguments is initialized: + if (PaymentServer::ipcSendCommandLine()) + exit(0); + PaymentServer* paymentServer = new PaymentServer(&app); + + // Install global event filter that makes sure that long tooltips can be word-wrapped + app.installEventFilter(new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app)); + + // ... then Greenchain.conf: + if (!boost::filesystem::is_directory(GetDataDir(false))) + { + // This message can not be translated, as translation is not initialized yet + // (which not yet possible because lang=XX can be overridden in Greenchain.conf in the data directory) + QMessageBox::critical(0, "Greenchain", + QString("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(mapArgs["-datadir"]))); + return 1; + } + ReadConfigFile(mapArgs, mapMultiArgs); + + // Application identification (must be set before OptionsModel is initialized, + // as it is used to locate QSettings) + QApplication::setOrganizationName("Greenchain"); + QApplication::setOrganizationDomain("Greenchain.org"); + if(GetBoolArg("-testnet")) // Separate UI settings for testnet + QApplication::setApplicationName("Greenchain-Qt-testnet"); + else + QApplication::setApplicationName("Greenchain-Qt"); + + // ... then GUI settings: + OptionsModel optionsModel; + + // Get desired locale (e.g. "de_DE") from command line or use system locale + QString lang_territory = QString::fromStdString(GetArg("-lang", QLocale::system().name().toStdString())); + QString lang = lang_territory; + // Convert to "de" only by truncating "_DE" + lang.truncate(lang_territory.lastIndexOf('_')); + + QTranslator qtTranslatorBase, qtTranslator, translatorBase, translator; + // 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))) + app.installTranslator(&qtTranslatorBase); + + // Load e.g. qt_de_DE.qm + if (qtTranslator.load("qt_" + lang_territory, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) + app.installTranslator(&qtTranslator); + + // Load e.g. bitcoin_de.qm (shortcut "de" needs to be defined in bitcoin.qrc) + if (translatorBase.load(lang, ":/translations/")) + app.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/")) + app.installTranslator(&translator); + + // Subscribe to global signals from core + uiInterface.ThreadSafeMessageBox.connect(ThreadSafeMessageBox); + uiInterface.ThreadSafeAskFee.connect(ThreadSafeAskFee); + uiInterface.InitMessage.connect(InitMessage); + 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; + } + +#ifdef Q_OS_MAC + // on mac, also change the icon now because it would look strange to have a testnet splash (green) and a std app icon (orange) + if(GetBoolArg("-testnet")) { + MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin_testnet")); + } +#endif + + SplashScreen splash(QPixmap(), 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); + + boost::thread_group threadGroup; + + BitcoinGUI window; + guiref = &window; + + QTimer* pollShutdownTimer = new QTimer(guiref); + QObject::connect(pollShutdownTimer, SIGNAL(timeout()), guiref, SLOT(detectShutdown())); + pollShutdownTimer->start(200); + + if(AppInit2(threadGroup)) + { + { + // Put this in a block, so that the Model objects are cleaned up before + // calling Shutdown(). + + optionsModel.Upgrade(); // Must be done after AppInit2 + + if (splashref) + splash.finish(&window); + + ClientModel clientModel(&optionsModel); + WalletModel walletModel(pwalletMain, &optionsModel); + + window.setClientModel(&clientModel); + window.addWallet("~Default", &walletModel); + window.setCurrentWallet("~Default"); + + // If -min option passed, start window minimized. + if(GetBoolArg("-min")) + { + window.showMinimized(); + } + else + { + window.show(); + } + + // Now that initialization/startup is done, process any command-line + // Greenchain: URIs + QObject::connect(paymentServer, SIGNAL(receivedURI(QString)), &window, SLOT(handleURI(QString))); + QTimer::singleShot(100, paymentServer, SLOT(uiReady())); + + app.exec(); + + window.hide(); + window.setClientModel(0); + window.removeAllWallets(); + guiref = 0; + } + // Shutdown the core and its threads, but don't exit Greenchain-Qt here + threadGroup.interrupt_all(); + threadGroup.join_all(); + Shutdown(); + } + else + { + threadGroup.interrupt_all(); + threadGroup.join_all(); + Shutdown(); + 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 0000000..1597ded --- /dev/null +++ b/src/qt/bitcoin.qrc @@ -0,0 +1,55 @@ + + + res/icons/bitcoin.png + res/icons/address-book.png + res/icons/quit.png + res/icons/send.png + res/icons/toolbar.png + res/icons/connect0_16.png + res/icons/connect1_16.png + res/icons/connect2_16.png + res/icons/connect3_16.png + res/icons/connect4_16.png + res/icons/transaction0.png + res/icons/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/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/coin.png + + + res/images/about.png + res/images/splash.png + res/images/wallet_bgcoin.png + + + res/movies/update_spinner.mng + + + locale/bitcoin_en.qm + locale/bitcoin_zh_CN.qm + + diff --git a/src/qt/bitcoinaddressvalidator.cpp b/src/qt/bitcoinaddressvalidator.cpp new file mode 100644 index 0000000..5136ea0 --- /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 0000000..2b6a593 --- /dev/null +++ b/src/qt/bitcoinaddressvalidator.h @@ -0,0 +1,21 @@ +#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; +}; + +#endif // BITCOINADDRESSVALIDATOR_H diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp new file mode 100644 index 0000000..d5e1652 --- /dev/null +++ b/src/qt/bitcoinamountfield.cpp @@ -0,0 +1,169 @@ +#include "bitcoinamountfield.h" + +#include "qvaluecombobox.h" +#include "bitcoinunits.h" +#include "guiconstants.h" + +#include +#include +#include +#include +#include // for qPow() + +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()); + QApplication::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(currentUnit == BitcoinUnits::cgc) + amount->setSingleStep(0.01); + else + amount->setSingleStep(0.001); + + 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 0000000..dacbb39 --- /dev/null +++ b/src/qt/bitcoinamountfield.h @@ -0,0 +1,61 @@ +#ifndef BITCOINAMOUNTFIELD_H +#define BITCOINAMOUNTFIELD_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 // BITCOINAMOUNTFIELD_H diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp new file mode 100644 index 0000000..dbe3a6d --- /dev/null +++ b/src/qt/bitcoingui.cpp @@ -0,0 +1,818 @@ +/* + * Qt4 bitcoin GUI. + * + * W.J. van der Laan 2011-2012 + * The Bitcoin Developers 2011-2012 + */ + +#include + +#include "bitcoingui.h" + +#include "transactiontablemodel.h" +#include "optionsdialog.h" +#include "aboutdialog.h" +#include "clientmodel.h" +#include "walletmodel.h" +#include "walletframe.h" +#include "optionsmodel.h" +#include "transactiondescdialog.h" +#include "bitcoinunits.h" +#include "guiconstants.h" +#include "notificator.h" +#include "guiutil.h" +#include "rpcconsole.h" +#include "ui_interface.h" +#include "wallet.h" +#include "init.h" + +#ifdef Q_OS_MAC +#include "macdockiconhandler.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +const QString BitcoinGUI::DEFAULT_WALLET = "~Default"; + +BitcoinGUI::BitcoinGUI(QWidget *parent) : + QMainWindow(parent), + clientModel(0), + encryptWalletAction(0), + changePassphraseAction(0), + aboutQtAction(0), + trayIcon(0), + notificator(0), + rpcConsole(0), + prevBlocks(0) +{ + restoreWindowGeometry(); + setWindowTitle(tr("Greenchain") + " - " + tr("Wallet")); +#ifndef Q_OS_MAC + QApplication::setWindowIcon(QIcon(":icons/bitcoin")); + setWindowIcon(QIcon(":icons/bitcoin")); +#else + setUnifiedTitleAndToolBarOnMac(true); + QApplication::setAttribute(Qt::AA_DontShowIconsInMenus); +#endif + // Create wallet frame and make it the central widget + walletFrame = new WalletFrame(this); + setCentralWidget(walletFrame); + + // Accept D&D of URIs + setAcceptDrops(true); + + // Create actions for the toolbar, menu bar and tray/dock icon + // Needs walletFrame to be initialized + createActions(); + + // Create application menu bar + createMenuBar(); + + // Create the toolbars + createToolBars(); + + // Create system tray icon and notification + createTrayIcon(); + + // Create status bar + statusBar(); + + // Status bar notification icons + QFrame *frameBlocks = new QFrame(); + frameBlocks->setContentsMargins(0,0,0,0); + frameBlocks->setMinimumWidth(56); + frameBlocks->setMaximumWidth(56); + QHBoxLayout *frameBlocksLayout = new QHBoxLayout(frameBlocks); + frameBlocksLayout->setContentsMargins(3,0,3,0); + frameBlocksLayout->setSpacing(3); + labelEncryptionIcon = new QLabel(); + labelConnectionsIcon = new QLabel(); + labelBlocksIcon = new QLabel(); + frameBlocksLayout->addStretch(); + frameBlocksLayout->addWidget(labelEncryptionIcon); + 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 = QApplication::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); + + rpcConsole = new RPCConsole(this); + connect(openRPCConsoleAction, SIGNAL(triggered()), rpcConsole, SLOT(show())); + + // Install event filter to be able to catch status tip events (QEvent::StatusTip) + this->installEventFilter(this); +} + +BitcoinGUI::~BitcoinGUI() +{ + saveWindowGeometry(); + if(trayIcon) // Hide tray icon, as deleting will let it linger until quit (on Ubuntu) + trayIcon->hide(); +#ifdef Q_OS_MAC + delete appMenuBar; + MacDockIconHandler::instance()->setMainWindow(NULL); +#endif +} + +void BitcoinGUI::createActions() +{ + QActionGroup *tabGroup = new QActionGroup(this); + + overviewAction = new QAction(QIcon(":/icons/overview"), tr("&Overview"), this); + overviewAction->setStatusTip(tr("Show general overview of wallet")); + overviewAction->setToolTip(overviewAction->statusTip()); + overviewAction->setCheckable(true); + overviewAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_1)); + tabGroup->addAction(overviewAction); + + sendCoinsAction = new QAction(QIcon(":/icons/send"), tr("&Send"), this); + sendCoinsAction->setStatusTip(tr("Send coins to a Greenchain address")); + sendCoinsAction->setToolTip(sendCoinsAction->statusTip()); + sendCoinsAction->setCheckable(true); + sendCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_2)); + tabGroup->addAction(sendCoinsAction); + + receiveCoinsAction = new QAction(QIcon(":/icons/receiving_addresses"), tr("&Receive"), this); + receiveCoinsAction->setStatusTip(tr("Show the list of addresses for receiving payments")); + receiveCoinsAction->setToolTip(receiveCoinsAction->statusTip()); + receiveCoinsAction->setCheckable(true); + receiveCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_3)); + tabGroup->addAction(receiveCoinsAction); + + historyAction = new QAction(QIcon(":/icons/history"), tr("&Transactions"), this); + historyAction->setStatusTip(tr("Browse transaction history")); + historyAction->setToolTip(historyAction->statusTip()); + historyAction->setCheckable(true); + historyAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_4)); + tabGroup->addAction(historyAction); + + addressBookAction = new QAction(QIcon(":/icons/address-book"), tr("&Addresses"), this); + addressBookAction->setStatusTip(tr("Edit the list of stored addresses and labels")); + addressBookAction->setToolTip(addressBookAction->statusTip()); + addressBookAction->setCheckable(true); + addressBookAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_5)); + tabGroup->addAction(addressBookAction); + + 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(addressBookAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); + connect(addressBookAction, SIGNAL(triggered()), this, SLOT(gotoAddressBookPage())); + + 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/coin"), tr("&About Greenchain"), this); + aboutAction->setStatusTip(tr("Show information about Greenchain")); + aboutAction->setMenuRole(QAction::AboutRole); + aboutQtAction = new QAction(QIcon(":/trolltech/qmessagebox/images/qtlogo-64.png"), tr("About &Qt"), this); + 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 Greenchain")); + optionsAction->setMenuRole(QAction::PreferencesRole); + toggleHideAction = new QAction(QIcon(":/icons/bitcoin"), tr("&Show / Hide"), this); + toggleHideAction->setStatusTip(tr("Show or hide the main Window")); + + encryptWalletAction = new QAction(QIcon(":/icons/lock_closed"), tr("&Encrypt Wallet..."), this); + encryptWalletAction->setStatusTip(tr("Encrypt the private keys that belong to your wallet")); + encryptWalletAction->setCheckable(true); + backupWalletAction = new QAction(QIcon(":/icons/filesave"), tr("&Backup Wallet..."), this); + backupWalletAction->setStatusTip(tr("Backup wallet to another location")); + 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 Greenchain 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 Greenchain addresses")); + + 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)), walletFrame, SLOT(encryptWallet(bool))); + connect(backupWalletAction, SIGNAL(triggered()), walletFrame, SLOT(backupWallet())); + connect(changePassphraseAction, SIGNAL(triggered()), walletFrame, SLOT(changePassphrase())); + connect(signMessageAction, SIGNAL(triggered()), this, SLOT(gotoSignMessageTab())); + connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(gotoVerifyMessageTab())); +} + +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->addAction(signMessageAction); + file->addAction(verifyMessageAction); + file->addSeparator(); + file->addAction(quitAction); + + QMenu *settings = appMenuBar->addMenu(tr("&Settings")); + settings->addAction(encryptWalletAction); + settings->addAction(changePassphraseAction); + settings->addSeparator(); + 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(addressBookAction); +} + +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 + QApplication::setWindowIcon(QIcon(":icons/bitcoin_testnet")); + setWindowIcon(QIcon(":icons/bitcoin_testnet")); +#else + MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin_testnet")); +#endif + if(trayIcon) + { + // Just attach " [testnet]" to the existing tooltip + trayIcon->setToolTip(trayIcon->toolTip() + QString(" ") + tr("[testnet]")); + trayIcon->setIcon(QIcon(":/icons/toolbar_testnet")); + } + + toggleHideAction->setIcon(QIcon(":/icons/toolbar_testnet")); + aboutAction->setIcon(QIcon(":/icons/toolbar_testnet")); + } + + // Create system tray menu (or setup the dock menu) that late to prevent users from calling actions, + // while the client has not yet fully loaded + createTrayIconMenu(); + + // 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))); + + // Receive and report messages from network/worker thread + connect(clientModel, SIGNAL(message(QString,QString,unsigned int)), this, SLOT(message(QString,QString,unsigned int))); + + rpcConsole->setClientModel(clientModel); + walletFrame->setClientModel(clientModel); + } +} + +bool BitcoinGUI::addWallet(const QString& name, WalletModel *walletModel) +{ + return walletFrame->addWallet(name, walletModel); +} + +bool BitcoinGUI::setCurrentWallet(const QString& name) +{ + return walletFrame->setCurrentWallet(name); +} + +void BitcoinGUI::removeAllWallets() +{ + walletFrame->removeAllWallets(); +} + +void BitcoinGUI::createTrayIcon() +{ +#ifndef Q_OS_MAC + trayIcon = new QSystemTrayIcon(this); + + trayIcon->setToolTip(tr("Greenchain client")); + trayIcon->setIcon(QIcon(":/icons/toolbar")); + trayIcon->show(); +#endif + + notificator = new Notificator(QApplication::applicationName(), trayIcon); +} + +void BitcoinGUI::createTrayIconMenu() +{ + QMenu *trayIconMenu; +#ifndef Q_OS_MAC + // return if trayIcon is unset (only on non-Mac OSes) + if (!trayIcon) + return; + + trayIconMenu = new QMenu(this); + trayIcon->setContextMenu(trayIconMenu); + + connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), + this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason))); +#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(receiveCoinsAction); + trayIconMenu->addSeparator(); + trayIconMenu->addAction(signMessageAction); + trayIconMenu->addAction(verifyMessageAction); + trayIconMenu->addSeparator(); + trayIconMenu->addAction(optionsAction); + trayIconMenu->addAction(openRPCConsoleAction); +#ifndef Q_OS_MAC // This is built-in on Mac + trayIconMenu->addSeparator(); + trayIconMenu->addAction(quitAction); +#endif +} + +#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::saveWindowGeometry() +{ + QSettings settings; + settings.setValue("nWindowPos", pos()); + settings.setValue("nWindowSize", size()); +} + +void BitcoinGUI::restoreWindowGeometry() +{ + QSettings settings; + QPoint pos = settings.value("nWindowPos").toPoint(); + QSize size = settings.value("nWindowSize", QSize(850, 550)).toSize(); + if (!pos.x() && !pos.y()) + { + QRect screen = QApplication::desktop()->screenGeometry(); + pos.setX((screen.width()-size.width())/2); + pos.setY((screen.height()-size.height())/2); + } + resize(size); + move(pos); +} + +void BitcoinGUI::optionsClicked() +{ + if(!clientModel || !clientModel->getOptionsModel()) + return; + OptionsDialog dlg; + dlg.setModel(clientModel->getOptionsModel()); + dlg.exec(); +} + +void BitcoinGUI::aboutClicked() +{ + AboutDialog dlg; + dlg.setModel(clientModel); + dlg.exec(); +} + +void BitcoinGUI::gotoOverviewPage() +{ + if (walletFrame) walletFrame->gotoOverviewPage(); +} + +void BitcoinGUI::gotoHistoryPage() +{ + if (walletFrame) walletFrame->gotoHistoryPage(); +} + +void BitcoinGUI::gotoAddressBookPage() +{ + if (walletFrame) walletFrame->gotoAddressBookPage(); +} + +void BitcoinGUI::gotoReceiveCoinsPage() +{ + if (walletFrame) walletFrame->gotoReceiveCoinsPage(); +} + +void BitcoinGUI::gotoSendCoinsPage(QString addr) +{ + if (walletFrame) walletFrame->gotoSendCoinsPage(addr); +} + +void BitcoinGUI::gotoSignMessageTab(QString addr) +{ + if (walletFrame) walletFrame->gotoSignMessageTab(addr); +} + +void BitcoinGUI::gotoVerifyMessageTab(QString addr) +{ + if (walletFrame) walletFrame->gotoVerifyMessageTab(addr); +} + +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 Greenchain network", "", count)); +} + +void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks) +{ + // Prevent orphan statusbar messages (e.g. hover Quit in main menu, wait until chain-sync starts -> garbelled text) + statusBar()->clearMessage(); + + // Acquire current block source + enum BlockSource blockSource = clientModel->getBlockSource(); + switch (blockSource) { + case BLOCK_SOURCE_NETWORK: + progressBarLabel->setText(tr("Synchronizing with network...")); + break; + case BLOCK_SOURCE_DISK: + progressBarLabel->setText(tr("Importing blocks from disk...")); + break; + case BLOCK_SOURCE_REINDEX: + progressBarLabel->setText(tr("Reindexing blocks on disk...")); + break; + case BLOCK_SOURCE_NONE: + // Case: not Importing, not Reindexing and no network connection + progressBarLabel->setText(tr("No block source available...")); + break; + } + + QString tooltip; + + QDateTime lastBlockDate = clientModel->getLastBlockDate(); + QDateTime currentDate = QDateTime::currentDateTime(); + int secs = lastBlockDate.secsTo(currentDate); + + if(count < nTotalBlocks) + { + tooltip = tr("Processed %1 of %2 (estimated) blocks of transaction history.").arg(count).arg(nTotalBlocks); + } + else + { + tooltip = tr("Processed %1 blocks of transaction history.").arg(count); + } + + // 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)); + + walletFrame->showOutOfSyncWarning(false); + + progressBarLabel->setVisible(false); + progressBar->setVisible(false); + } + else + { + // Represent time from last generated block in human readable text + QString timeBehindText; + if(secs < 48*60*60) + { + timeBehindText = tr("%n hour(s)","",secs/(60*60)); + } + else if(secs < 14*24*60*60) + { + timeBehindText = tr("%n day(s)","",secs/(24*60*60)); + } + else + { + timeBehindText = tr("%n week(s)","",secs/(7*24*60*60)); + } + + progressBarLabel->setVisible(true); + progressBar->setFormat(tr("%1 behind").arg(timeBehindText)); + progressBar->setMaximum(1000000000); + progressBar->setValue(clientModel->getVerificationProgress() * 1000000000.0 + 0.5); + progressBar->setVisible(true); + + tooltip = tr("Catching up...") + QString("
") + tooltip; + labelBlocksIcon->setMovie(syncIconMovie); + if(count != prevBlocks) + syncIconMovie->jumpToNextFrame(); + prevBlocks = count; + + walletFrame->showOutOfSyncWarning(true); + + tooltip += QString("
"); + tooltip += tr("Last received block was generated %1 ago.").arg(timeBehindText); + tooltip += QString("
"); + tooltip += tr("Transactions after this will not yet be visible."); + } + + // Don't word-wrap this (fixed-width) tooltip + tooltip = QString("") + tooltip + QString(""); + + labelBlocksIcon->setToolTip(tooltip); + progressBarLabel->setToolTip(tooltip); + progressBar->setToolTip(tooltip); +} + +void BitcoinGUI::message(const QString &title, const QString &message, unsigned int style, bool *ret) +{ + QString strTitle = tr("Greenchain"); // default title + // Default to information icon + int nMBoxIcon = QMessageBox::Information; + int nNotifyIcon = Notificator::Information; + + // Override title based on style + QString msgType; + switch (style) { + case CClientUIInterface::MSG_ERROR: + msgType = tr("Error"); + break; + case CClientUIInterface::MSG_WARNING: + msgType = tr("Warning"); + break; + case CClientUIInterface::MSG_INFORMATION: + msgType = tr("Information"); + break; + default: + msgType = title; // Use supplied title + } + if (!msgType.isEmpty()) + strTitle += " - " + msgType; + + // 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; + if (!(buttons = (QMessageBox::StandardButton)(style & CClientUIInterface::BTN_MASK))) + buttons = QMessageBox::Ok; + + QMessageBox mBox((QMessageBox::Icon)nMBoxIcon, strTitle, message, buttons); + int r = mBox.exec(); + if (ret != NULL) + *ret = r == QMessageBox::Ok; + } + 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()->getMinimizeToTray() && + !clientModel->getOptionsModel()->getMinimizeOnClose()) + { + QApplication::quit(); + } +#endif + } + 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::gc, 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 QString& date, int unit, qint64 amount, const QString& type, const QString& address) +{ + // On new transaction, make an info balloon + message((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(unit, amount, true)) + .arg(type) + .arg(address), CClientUIInterface::MSG_INFORMATION); +} + +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 (walletFrame->handleURI(uri.toString())) + nValidUrisFound++; + } + + // if valid URIs were found + if (nValidUrisFound) + walletFrame->gotoSendCoinsPage(); + else + message(tr("URI handling"), tr("URI can not be parsed! This can be caused by an invalid Greenchain address or malformed URI parameters."), + CClientUIInterface::ICON_WARNING); + } + + event->acceptProposedAction(); +} + +bool BitcoinGUI::eventFilter(QObject *object, QEvent *event) +{ + // Catch status tip events + if (event->type() == QEvent::StatusTip) + { + // Prevent adding text from setStatusTip(), if we currently use the status bar for displaying other stuff + if (progressBarLabel->isVisible() || progressBar->isVisible()) + return true; + } + return QMainWindow::eventFilter(object, event); +} + +void BitcoinGUI::handleURI(QString strURI) +{ + // URI has to be valid + if (!walletFrame->handleURI(strURI)) + message(tr("URI handling"), tr("URI can not be parsed! This can be caused by an invalid Greenchain address or malformed URI parameters."), + CClientUIInterface::ICON_WARNING); +} + +void BitcoinGUI::setEncryptionStatus(int status) +{ + switch(status) + { + case WalletModel::Unencrypted: + labelEncryptionIcon->hide(); + encryptWalletAction->setChecked(false); + changePassphraseAction->setEnabled(false); + encryptWalletAction->setEnabled(true); + break; + case WalletModel::Unlocked: + labelEncryptionIcon->show(); + labelEncryptionIcon->setPixmap(QIcon(":/icons/lock_open").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelEncryptionIcon->setToolTip(tr("Wallet is encrypted and currently unlocked")); + encryptWalletAction->setChecked(true); + changePassphraseAction->setEnabled(true); + encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported + break; + case WalletModel::Locked: + labelEncryptionIcon->show(); + labelEncryptionIcon->setPixmap(QIcon(":/icons/lock_closed").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelEncryptionIcon->setToolTip(tr("Wallet is encrypted and currently locked")); + encryptWalletAction->setChecked(true); + changePassphraseAction->setEnabled(true); + encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported + break; + } +} + +void BitcoinGUI::showNormalIfMinimized(bool fToggleHidden) +{ + // activateWindow() (sometimes) helps with keyboard focus on Windows + if (isHidden()) + { + show(); + activateWindow(); + } + else if (isMinimized()) + { + showNormal(); + activateWindow(); + } + else if (GUIUtil::isObscured(this)) + { + raise(); + activateWindow(); + } + else if(fToggleHidden) + hide(); +} + +void BitcoinGUI::toggleHidden() +{ + showNormalIfMinimized(true); +} + +void BitcoinGUI::detectShutdown() +{ + if (ShutdownRequested()) + QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection); +} diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h new file mode 100644 index 0000000..f361a62 --- /dev/null +++ b/src/qt/bitcoingui.h @@ -0,0 +1,198 @@ +#ifndef BITCOINGUI_H +#define BITCOINGUI_H + +#include +#include +#include + +class TransactionTableModel; +class WalletFrame; +class WalletView; +class ClientModel; +class WalletModel; +class WalletStack; +class TransactionView; +class OverviewPage; +class AddressBookPage; +class SendCoinsDialog; +class SignVerifyMessageDialog; +class Notificator; +class RPCConsole; + +class CWallet; + +QT_BEGIN_NAMESPACE +class QLabel; +class QModelIndex; +class QProgressBar; +class QStackedWidget; +class QUrl; +class QListWidget; +class QPushButton; +class QAction; +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: + static const QString DEFAULT_WALLET; + + 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. + */ + + bool addWallet(const QString& name, WalletModel *walletModel); + bool setCurrentWallet(const QString& name); + + void removeAllWallets(); + + /** Used by WalletView to allow access to needed QActions */ + // Todo: Use Qt signals for these + QAction * getOverviewAction() { return overviewAction; } + QAction * getHistoryAction() { return historyAction; } + QAction * getAddressBookAction() { return addressBookAction; } + QAction * getReceiveCoinsAction() { return receiveCoinsAction; } + QAction * getSendCoinsAction() { return sendCoinsAction; } + +protected: + void changeEvent(QEvent *e); + void closeEvent(QCloseEvent *event); + void dragEnterEvent(QDragEnterEvent *event); + void dropEvent(QDropEvent *event); + bool eventFilter(QObject *object, QEvent *event); + +private: + ClientModel *clientModel; + WalletFrame *walletFrame; + + QLabel *labelEncryptionIcon; + QLabel *labelConnectionsIcon; + QLabel *labelBlocksIcon; + QLabel *progressBarLabel; + QProgressBar *progressBar; + + QMenuBar *appMenuBar; + QAction *overviewAction; + QAction *historyAction; + QAction *quitAction; + QAction *sendCoinsAction; + QAction *addressBookAction; + QAction *signMessageAction; + QAction *verifyMessageAction; + QAction *aboutAction; + QAction *receiveCoinsAction; + QAction *optionsAction; + QAction *toggleHideAction; + QAction *encryptWalletAction; + QAction *backupWalletAction; + QAction *changePassphraseAction; + QAction *aboutQtAction; + QAction *openRPCConsoleAction; + + QSystemTrayIcon *trayIcon; + Notificator *notificator; + TransactionView *transactionView; + RPCConsole *rpcConsole; + + QMovie *syncIconMovie; + /** Keep track of previous number of blocks, to detect progress */ + int prevBlocks; + + /** Create the main UI actions. */ + void createActions(); + /** Create the menu bar and sub-menus. */ + void createMenuBar(); + /** Create the toolbars */ + void createToolBars(); + /** Create system tray icon and notification */ + void createTrayIcon(); + /** Create system tray menu (or setup the dock menu) */ + void createTrayIconMenu(); + /** Save window size and position */ + void saveWindowGeometry(); + /** Restore window size and position */ + void restoreWindowGeometry(); + +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 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 event from the core network or transaction handling code. + @param[in] title the message box / notification title + @param[in] message the displayed text + @param[in] style modality and style definitions (icon and used buttons - buttons only for message boxes) + @see CClientUIInterface::MessageBoxFlags + @param[in] ret pointer to a bool that will be modified to whether Ok was clicked (modal only) + */ + void message(const QString &title, const QString &message, unsigned int style, bool *ret = NULL); + /** 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); + + /** Show incoming transaction notification for new transactions. */ + void incomingTransaction(const QString& date, int unit, qint64 amount, const QString& type, const QString& address); + +private slots: + /** Switch to overview (home) page */ + void gotoOverviewPage(); + /** Switch to history (transactions) page */ + void gotoHistoryPage(); + /** Switch to address book page */ + void gotoAddressBookPage(); + /** Switch to receive coins page */ + void gotoReceiveCoinsPage(); + /** Switch to send coins page */ + void gotoSendCoinsPage(QString addr = ""); + + /** 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 configuration dialog */ + void optionsClicked(); + /** Show about dialog */ + void aboutClicked(); +#ifndef Q_OS_MAC + /** Handle tray icon clicked */ + void trayIconActivated(QSystemTrayIcon::ActivationReason reason); +#endif + + /** 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(); + + /** called by a timer to check if fRequestShutdown has been set **/ + void detectShutdown(); +}; + +#endif // BITCOINGUI_H diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp new file mode 100644 index 0000000..abc14fb --- /dev/null +++ b/src/qt/bitcoinstrings.cpp @@ -0,0 +1,210 @@ +#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("Greenchain-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=Greenchainrpc\n" +"rpcpassword=%s\n" +"(you do not need to remember this password)\n" +"The username and password MUST NOT be the same.\n" +"If the file does not exist, create it with owner-readable-only file " +"permissions.\n" +"It is also recommended to set alertnotify so you are notified of problems;\n" +"for example: alertnotify=echo %%s | mail -s \"Greenchain Alert\" admin@foo.com\n"), +QT_TRANSLATE_NOOP("Greenchain-core", "" +"Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:" +"@STRENGTH)"), +QT_TRANSLATE_NOOP("Greenchain-core", "" +"An error occurred while setting up the RPC port %u for listening on IPv4: %s"), +QT_TRANSLATE_NOOP("Greenchain-core", "" +"An error occurred while setting up the RPC port %u for listening on IPv6, " +"falling back to IPv4: %s"), +QT_TRANSLATE_NOOP("Greenchain-core", "" +"Bind to given address and always listen on it. Use [host]:port notation for " +"IPv6"), +QT_TRANSLATE_NOOP("Greenchain-core", "" +"Cannot obtain a lock on data directory %s. Greenchain is probably already " +"running."), +QT_TRANSLATE_NOOP("Greenchain-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("Greenchain-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("Greenchain-core", "" +"Execute command when a relevant alert is received (%s in cmd is replaced by " +"message)"), +QT_TRANSLATE_NOOP("Greenchain-core", "" +"Execute command when a wallet transaction changes (%s in cmd is replaced by " +"TxID)"), +QT_TRANSLATE_NOOP("Greenchain-core", "" +"Execute command when the best block changes (%s in cmd is replaced by block " +"hash)"), +QT_TRANSLATE_NOOP("Greenchain-core", "" +"Listen for JSON-RPC connections on (default: 31742 or testnet: 41742)"), +QT_TRANSLATE_NOOP("Greenchain-core", "" +"Number of seconds to keep misbehaving peers from reconnecting (default: " +"86400)"), +QT_TRANSLATE_NOOP("Greenchain-core", "" +"Set maximum size of high-priority/low-fee transactions in bytes (default: " +"27000)"), +QT_TRANSLATE_NOOP("Greenchain-core", "" +"Set the number of script verification threads (up to 16, 0 = auto, <0 = " +"leave that many cores free, default: 0)"), +QT_TRANSLATE_NOOP("Greenchain-core", "" +"This is a pre-release test build - use at your own risk - do not use for " +"mining or merchant applications"), +QT_TRANSLATE_NOOP("Greenchain-core", "" +"Unable to bind to %s on this computer. Greenchain is probably already running."), +QT_TRANSLATE_NOOP("Greenchain-core", "" +"Warning: -paytxfee is set very high! This is the transaction fee you will " +"pay if you send a transaction."), +QT_TRANSLATE_NOOP("Greenchain-core", "" +"Warning: Displayed transactions may not be correct! You may need to upgrade, " +"or other nodes may need to upgrade."), +QT_TRANSLATE_NOOP("Greenchain-core", "" +"Warning: Please check that your computer's date and time are correct! If " +"your clock is wrong Greenchain will not work properly."), +QT_TRANSLATE_NOOP("Greenchain-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("Greenchain-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("Greenchain-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("Greenchain-core", "Accept command line and JSON-RPC commands"), +QT_TRANSLATE_NOOP("Greenchain-core", "Accept connections from outside (default: 1 if no -proxy or -connect)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Add a node to connect to and attempt to keep the connection open"), +QT_TRANSLATE_NOOP("Greenchain-core", "Allow DNS lookups for -addnode, -seednode and -connect"), +QT_TRANSLATE_NOOP("Greenchain-core", "Allow JSON-RPC connections from specified IP address"), +QT_TRANSLATE_NOOP("Greenchain-core", "Attempt to recover private keys from a corrupt wallet.dat"), +QT_TRANSLATE_NOOP("Greenchain-core", "Greenchain version"), +QT_TRANSLATE_NOOP("Greenchain-core", "Block creation options:"), +QT_TRANSLATE_NOOP("Greenchain-core", "Cannot downgrade wallet"), +QT_TRANSLATE_NOOP("Greenchain-core", "Cannot resolve -bind address: '%s'"), +QT_TRANSLATE_NOOP("Greenchain-core", "Cannot resolve -externalip address: '%s'"), +QT_TRANSLATE_NOOP("Greenchain-core", "Cannot write default address"), +QT_TRANSLATE_NOOP("Greenchain-core", "Connect only to the specified node(s)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Connect through socks proxy"), +QT_TRANSLATE_NOOP("Greenchain-core", "Connect to a node to retrieve peer addresses, and disconnect"), +QT_TRANSLATE_NOOP("Greenchain-core", "Corrupted block database detected"), +QT_TRANSLATE_NOOP("Greenchain-core", "Discover own IP address (default: 1 when listening and no -externalip)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Do you want to rebuild the block database now?"), +QT_TRANSLATE_NOOP("Greenchain-core", "Done loading"), +QT_TRANSLATE_NOOP("Greenchain-core", "Error initializing block database"), +QT_TRANSLATE_NOOP("Greenchain-core", "Error initializing wallet database environment %s!"), +QT_TRANSLATE_NOOP("Greenchain-core", "Error loading block database"), +QT_TRANSLATE_NOOP("Greenchain-core", "Error loading wallet.dat"), +QT_TRANSLATE_NOOP("Greenchain-core", "Error loading wallet.dat: Wallet corrupted"), +QT_TRANSLATE_NOOP("Greenchain-core", "Error loading wallet.dat: Wallet requires newer version of Greenchain"), +QT_TRANSLATE_NOOP("Greenchain-core", "Error opening block database"), +QT_TRANSLATE_NOOP("Greenchain-core", "Error"), +QT_TRANSLATE_NOOP("Greenchain-core", "Error: Disk space is low!"), +QT_TRANSLATE_NOOP("Greenchain-core", "Error: Wallet locked, unable to create transaction!"), +QT_TRANSLATE_NOOP("Greenchain-core", "Error: system error: "), +QT_TRANSLATE_NOOP("Greenchain-core", "Failed to listen on any port. Use -listen=0 if you want this."), +QT_TRANSLATE_NOOP("Greenchain-core", "Failed to read block info"), +QT_TRANSLATE_NOOP("Greenchain-core", "Failed to read block"), +QT_TRANSLATE_NOOP("Greenchain-core", "Failed to sync block index"), +QT_TRANSLATE_NOOP("Greenchain-core", "Failed to write block index"), +QT_TRANSLATE_NOOP("Greenchain-core", "Failed to write block info"), +QT_TRANSLATE_NOOP("Greenchain-core", "Failed to write block"), +QT_TRANSLATE_NOOP("Greenchain-core", "Failed to write file info"), +QT_TRANSLATE_NOOP("Greenchain-core", "Failed to write to coin database"), +QT_TRANSLATE_NOOP("Greenchain-core", "Failed to write transaction index"), +QT_TRANSLATE_NOOP("Greenchain-core", "Failed to write undo data"), +QT_TRANSLATE_NOOP("Greenchain-core", "Fee per KB to add to transactions you send"), +QT_TRANSLATE_NOOP("Greenchain-core", "Find peers using DNS lookup (default: 1 unless -connect)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Generate coins (default: 0)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Get help for a command"), +QT_TRANSLATE_NOOP("Greenchain-core", "How many blocks to check at startup (default: 288, 0 = all)"), +QT_TRANSLATE_NOOP("Greenchain-core", "How thorough the block verification is (0-4, default: 3)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Imports blocks from external blk000??.dat file"), +QT_TRANSLATE_NOOP("Greenchain-core", "Information"), +QT_TRANSLATE_NOOP("Greenchain-core", "Insufficient funds"), +QT_TRANSLATE_NOOP("Greenchain-core", "Invalid -proxy address: '%s'"), +QT_TRANSLATE_NOOP("Greenchain-core", "Invalid -tor address: '%s'"), +QT_TRANSLATE_NOOP("Greenchain-core", "Invalid amount for -minrelaytxfee=: '%s'"), +QT_TRANSLATE_NOOP("Greenchain-core", "Invalid amount for -mintxfee=: '%s'"), +QT_TRANSLATE_NOOP("Greenchain-core", "Invalid amount for -paytxfee=: '%s'"), +QT_TRANSLATE_NOOP("Greenchain-core", "Invalid amount"), +QT_TRANSLATE_NOOP("Greenchain-core", "List commands"), +QT_TRANSLATE_NOOP("Greenchain-core", "Listen for connections on (default: 31743 or testnet: 41743)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Loading addresses..."), +QT_TRANSLATE_NOOP("Greenchain-core", "Loading block index..."), +QT_TRANSLATE_NOOP("Greenchain-core", "Loading wallet..."), +QT_TRANSLATE_NOOP("Greenchain-core", "Maintain a full transaction index (default: 0)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Maintain at most connections to peers (default: 125)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Maximum per-connection receive buffer, *1000 bytes (default: 5000)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Maximum per-connection send buffer, *1000 bytes (default: 1000)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Not enough file descriptors available."), +QT_TRANSLATE_NOOP("Greenchain-core", "Only accept block chain matching built-in checkpoints (default: 1)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Only connect to nodes in network (IPv4, IPv6 or Tor)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Options:"), +QT_TRANSLATE_NOOP("Greenchain-core", "Output extra debugging information. Implies all other -debug* options"), +QT_TRANSLATE_NOOP("Greenchain-core", "Output extra network debugging information"), +QT_TRANSLATE_NOOP("Greenchain-core", "Password for JSON-RPC connections"), +QT_TRANSLATE_NOOP("Greenchain-core", "Prepend debug output with timestamp"), +QT_TRANSLATE_NOOP("Greenchain-core", "Rebuild block chain index from current blk000??.dat files"), +QT_TRANSLATE_NOOP("Greenchain-core", "Rescan the block chain for missing wallet transactions"), +QT_TRANSLATE_NOOP("Greenchain-core", "Rescanning..."), +QT_TRANSLATE_NOOP("Greenchain-core", "Run in the background as a daemon and accept commands"), +QT_TRANSLATE_NOOP("Greenchain-core", "SSL options: (see the Greenchain Wiki for SSL setup instructions)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Select the version of socks proxy to use (4-5, default: 5)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Send command to -server or Greenchaind"), +QT_TRANSLATE_NOOP("Greenchain-core", "Send commands to node running on (default: 127.0.0.1)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Send trace/debug info to console instead of debug.log file"), +QT_TRANSLATE_NOOP("Greenchain-core", "Send trace/debug info to debugger"), +QT_TRANSLATE_NOOP("Greenchain-core", "Server certificate file (default: server.cert)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Server private key (default: server.pem)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Set database cache size in megabytes (default: 25)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Set key pool size to (default: 100)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Set maximum block size in bytes (default: 250000)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Set minimum block size in bytes (default: 0)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Set the number of threads to service RPC calls (default: 4)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Shrink debug.log file on client startup (default: 1 when no -debug)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Signing transaction failed"), +QT_TRANSLATE_NOOP("Greenchain-core", "Specify configuration file (default: Greenchain.conf)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Specify connection timeout in milliseconds (default: 5000)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Specify data directory"), +QT_TRANSLATE_NOOP("Greenchain-core", "Specify pid file (default: Greenchaind.pid)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Specify your own public address"), +QT_TRANSLATE_NOOP("Greenchain-core", "System error: "), +QT_TRANSLATE_NOOP("Greenchain-core", "This help message"), +QT_TRANSLATE_NOOP("Greenchain-core", "Threshold for disconnecting misbehaving peers (default: 100)"), +QT_TRANSLATE_NOOP("Greenchain-core", "To use the %s option"), +QT_TRANSLATE_NOOP("Greenchain-core", "Transaction amount too small"), +QT_TRANSLATE_NOOP("Greenchain-core", "Transaction amounts must be positive"), +QT_TRANSLATE_NOOP("Greenchain-core", "Transaction too large"), +QT_TRANSLATE_NOOP("Greenchain-core", "Unable to bind to %s on this computer (bind returned error %d, %s)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Unknown -socks proxy version requested: %i"), +QT_TRANSLATE_NOOP("Greenchain-core", "Unknown network specified in -onlynet: '%s'"), +QT_TRANSLATE_NOOP("Greenchain-core", "Upgrade wallet to latest format"), +QT_TRANSLATE_NOOP("Greenchain-core", "Usage:"), +QT_TRANSLATE_NOOP("Greenchain-core", "Use OpenSSL (https) for JSON-RPC connections"), +QT_TRANSLATE_NOOP("Greenchain-core", "Use UPnP to map the listening port (default: 0)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Use UPnP to map the listening port (default: 1 when listening)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Use proxy to reach tor hidden services (default: same as -proxy)"), +QT_TRANSLATE_NOOP("Greenchain-core", "Use the test network"), +QT_TRANSLATE_NOOP("Greenchain-core", "Username for JSON-RPC connections"), +QT_TRANSLATE_NOOP("Greenchain-core", "Verifying blocks..."), +QT_TRANSLATE_NOOP("Greenchain-core", "Verifying wallet..."), +QT_TRANSLATE_NOOP("Greenchain-core", "Wallet needed to be rewritten: restart Greenchain to complete"), +QT_TRANSLATE_NOOP("Greenchain-core", "Warning"), +QT_TRANSLATE_NOOP("Greenchain-core", "Warning: This version is obsolete, upgrade required!"), +QT_TRANSLATE_NOOP("Greenchain-core", "You need to rebuild the databases using -reindex to change -txindex"), +QT_TRANSLATE_NOOP("Greenchain-core", "wallet.dat corrupt, salvage failed"), +}; diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp new file mode 100644 index 0000000..065ca4a --- /dev/null +++ b/src/qt/bitcoinunits.cpp @@ -0,0 +1,181 @@ +#include "bitcoinunits.h" + +#include + +BitcoinUnits::BitcoinUnits(QObject *parent): + QAbstractListModel(parent), + unitlist(availableUnits()) +{ +} + +QList BitcoinUnits::availableUnits() +{ + QList unitlist; + unitlist.append(gc); + unitlist.append(cgc); + unitlist.append(mgc); + return unitlist; +} + +bool BitcoinUnits::valid(int unit) +{ + switch(unit) + { + case gc: + case cgc: + case mgc: + return true; + default: + return false; + } +} + +QString BitcoinUnits::name(int unit) +{ + switch(unit) + { + case gc: return QString("gc"); + case cgc: return QString("cgc"); + case mgc: return QString("mgc"); + default: return QString("???"); + } +} + +QString BitcoinUnits::description(int unit) +{ + switch(unit) + { + case gc: return QString("Greenchains"); + case cgc: return QString("Centum-Greenchains (1 / 100)"); + case mgc: return QString("Milli-Greenchains (1 / 1,000)"); + default: return QString("???"); + } +} + +qint64 BitcoinUnits::factor(int unit) +{ + switch(unit) + { + case gc: return 100000; + case cgc: return 1000; + case mgc: return 100; + default: return 100000; + } +} + +int BitcoinUnits::amountDigits(int unit) +{ + switch(unit) + { + case gc: return 17; // trillions (# digits, without commas) + case cgc: return 19; // *100 + case mgc: return 20; // *1,000 + default: return 0; + } +} + +int BitcoinUnits::decimals(int unit) +{ + switch(unit) + { + case gc: return 5; + case cgc: return 3; + case mgc: return 2; + default: return 0; + } +} + +QString BitcoinUnits::format(int unit, qint64 n, bool fPlus) +{ + // Note: not using straight sprintf here because we do NOT want + // localized number formatting. + if(!valid(unit)) + return QString(); // Refuse to format invalid unit + qint64 coin = factor(unit); + int num_decimals = decimals(unit); + qint64 n_abs = (n > 0 ? n : -n); + 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>=2 && (remainder_str.at(i) == '0'); --i) + ++nTrim; + remainder_str.chop(nTrim); + + if (n < 0) + quotient_str.insert(0, '-'); + else if (fPlus && n > 0) + quotient_str.insert(0, '+'); + return quotient_str + QString(".") + remainder_str; +} + +QString BitcoinUnits::formatWithUnit(int unit, qint64 amount, bool plussign) +{ + return format(unit, amount, plussign) + 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(); +} diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h new file mode 100644 index 0000000..723278f --- /dev/null +++ b/src/qt/bitcoinunits.h @@ -0,0 +1,69 @@ +#ifndef BITCOINUNITS_H +#define BITCOINUNITS_H + +#include +#include + +/** Bitcoin unit definitions. Encapsulates parsing and formatting + and serves as list model for drop-down selection boxes. +*/ +class BitcoinUnits: public QAbstractListModel +{ + Q_OBJECT + +public: + explicit BitcoinUnits(QObject *parent); + + /** Bitcoin units. + @note Source: https://en.bitcoin.it/wiki/Units . Please add only sensible ones + */ + enum Unit + { + gc, + cgc, + mgc + }; + + //! @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); + //! Format as string (with unit) + static QString formatWithUnit(int unit, qint64 amount, bool plussign=false); + //! Parse string to coin amount + static bool parse(int unit, const QString &value, qint64 *val_out); + ///@} + + //! @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 0000000..e8d99a8 --- /dev/null +++ b/src/qt/clientmodel.cpp @@ -0,0 +1,209 @@ +#include "clientmodel.h" + +#include "guiconstants.h" +#include "optionsmodel.h" +#include "addresstablemodel.h" +#include "transactiontablemodel.h" + +#include "alert.h" +#include "main.h" +#include "checkpoints.h" +#include "ui_interface.h" + +#include +#include + +static const int64 nClientStartupTime = GetTime(); + +ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) : + QObject(parent), optionsModel(optionsModel), + cachedNumBlocks(0), cachedNumBlocksOfPeers(0), + cachedReindexing(0), cachedImporting(0), + numBlocksAtStartup(-1), pollTimer(0) +{ + pollTimer = new QTimer(this); + pollTimer->setInterval(MODEL_UPDATE_DELAY); + pollTimer->start(); + connect(pollTimer, SIGNAL(timeout()), this, SLOT(updateTimer())); + + subscribeToCoreSignals(); +} + +ClientModel::~ClientModel() +{ + unsubscribeFromCoreSignals(); +} + +int ClientModel::getNumConnections() const +{ + return vNodes.size(); +} + +int ClientModel::getNumBlocks() const +{ + return nBestHeight; +} + +int ClientModel::getNumBlocksAtStartup() +{ + if (numBlocksAtStartup == -1) numBlocksAtStartup = getNumBlocks(); + return numBlocksAtStartup; +} + +QDateTime ClientModel::getLastBlockDate() const +{ + if (pindexBest) + return QDateTime::fromTime_t(pindexBest->GetBlockTime()); + else if(!isTestNet()) + return QDateTime::fromTime_t(1231006505); // Genesis block's time + else + return QDateTime::fromTime_t(1296688602); // Genesis block's time (testnet) +} + +double ClientModel::getVerificationProgress() const +{ + return Checkpoints::GuessVerificationProgress(pindexBest); +} + +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(); + + // check for changed number of blocks we have, number of blocks peers claim to have, reindexing state and importing state + if (cachedNumBlocks != newNumBlocks || cachedNumBlocksOfPeers != newNumBlocksOfPeers || + cachedReindexing != fReindex || cachedImporting != fImporting) + { + cachedNumBlocks = newNumBlocks; + cachedNumBlocksOfPeers = newNumBlocksOfPeers; + cachedReindexing = fReindex; + cachedImporting = fImporting; + + // ensure we return the maximum of newNumBlocksOfPeers and newNumBlocks to not create weird displays in the GUI + emit numBlocksChanged(newNumBlocks, std::max(newNumBlocksOfPeers, newNumBlocks)); + } +} + +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 message(tr("Network Alert"), QString::fromStdString(alert.strStatusBar), CClientUIInterface::ICON_ERROR); + } + } + + emit alertsChanged(getStatusBarWarnings()); +} + +bool ClientModel::isTestNet() const +{ + return fTestNet; +} + +bool ClientModel::inInitialBlockDownload() const +{ + return IsInitialBlockDownload(); +} + +enum BlockSource ClientModel::getBlockSource() const +{ + if (fReindex) + return BLOCK_SOURCE_REINDEX; + else if (fImporting) + return BLOCK_SOURCE_DISK; + else if (getNumConnections() > 0) + return BLOCK_SOURCE_NETWORK; + + return BLOCK_SOURCE_NONE; +} + +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); +} + +bool ClientModel::isReleaseVersion() const +{ + return CLIENT_VERSION_IS_RELEASE; +} + +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 0000000..f9d491a --- /dev/null +++ b/src/qt/clientmodel.h @@ -0,0 +1,87 @@ +#ifndef CLIENTMODEL_H +#define CLIENTMODEL_H + +#include + +class OptionsModel; +class AddressTableModel; +class TransactionTableModel; +class CWallet; + +QT_BEGIN_NAMESPACE +class QDateTime; +class QTimer; +QT_END_NAMESPACE + +enum BlockSource { + BLOCK_SOURCE_NONE, + BLOCK_SOURCE_REINDEX, + BLOCK_SOURCE_DISK, + BLOCK_SOURCE_NETWORK +}; + +/** Model for Bitcoin network client. */ +class ClientModel : public QObject +{ + Q_OBJECT + +public: + explicit ClientModel(OptionsModel *optionsModel, QObject *parent = 0); + ~ClientModel(); + + OptionsModel *getOptionsModel(); + + int getNumConnections() const; + int getNumBlocks() const; + int getNumBlocksAtStartup(); + + double getVerificationProgress() 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 true if core is importing blocks + enum BlockSource getBlockSource() 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; + bool isReleaseVersion() const; + QString clientName() const; + QString formatClientStartupTime() const; + +private: + OptionsModel *optionsModel; + + int cachedNumBlocks; + int cachedNumBlocksOfPeers; + bool cachedReindexing; + bool cachedImporting; + + int numBlocksAtStartup; + + QTimer *pollTimer; + + void subscribeToCoreSignals(); + void unsubscribeFromCoreSignals(); + +signals: + void numConnectionsChanged(int count); + void numBlocksChanged(int count, int countOfPeers); + void alertsChanged(const QString &warnings); + + //! Asynchronous message notification + void message(const QString &title, const QString &message, unsigned int style); + +public slots: + void updateTimer(); + void updateNumConnections(int numConnections); + void updateAlert(const QString &hash, int status); +}; + +#endif // CLIENTMODEL_H diff --git a/src/qt/csvmodelwriter.cpp b/src/qt/csvmodelwriter.cpp new file mode 100644 index 0000000..8a50bba --- /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 0000000..c4504ee --- /dev/null +++ b/src/qt/csvmodelwriter.h @@ -0,0 +1,42 @@ +#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; +}; + +#endif // CSVMODELWRITER_H diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp new file mode 100644 index 0000000..c2fba57 --- /dev/null +++ b/src/qt/editaddressdialog.cpp @@ -0,0 +1,137 @@ +#include "editaddressdialog.h" +#include "ui_editaddressdialog.h" + +#include "addresstablemodel.h" +#include "guiutil.h" + +#include +#include + +EditAddressDialog::EditAddressDialog(Mode mode, QWidget *parent) : + QDialog(parent), + ui(new Ui::EditAddressDialog), mapper(0), mode(mode), model(0) +{ + ui->setupUi(this); + + GUIUtil::setupAddressWidget(ui->addressEdit, this); + + switch(mode) + { + case NewReceivingAddress: + setWindowTitle(tr("New receiving address")); + ui->addressEdit->setEnabled(false); + break; + case NewSendingAddress: + setWindowTitle(tr("New sending address")); + break; + case EditReceivingAddress: + setWindowTitle(tr("Edit receiving address")); + ui->addressEdit->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 Greenchain 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 0000000..44e5023 --- /dev/null +++ b/src/qt/editaddressdialog.h @@ -0,0 +1,52 @@ +#ifndef EDITADDRESSDIALOG_H +#define EDITADDRESSDIALOG_H + +#include + +namespace Ui { + class EditAddressDialog; +} +class AddressTableModel; + +QT_BEGIN_NAMESPACE +class QDataWidgetMapper; +QT_END_NAMESPACE + +/** 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/Noname1.txt b/src/qt/forms/Noname1.txt new file mode 100644 index 0000000..bd80362 --- /dev/null +++ b/src/qt/forms/Noname1.txt @@ -0,0 +1,19 @@ + + + + + + + :/images/wallet_bgcoin + + + false + + + Qt::AlignCenter + + + -2 + + + \ No newline at end of file diff --git a/src/qt/forms/aboutdialog.ui b/src/qt/forms/aboutdialog.ui new file mode 100644 index 0000000..2f23ef3 --- /dev/null +++ b/src/qt/forms/aboutdialog.ui @@ -0,0 +1,187 @@ + + + AboutDialog + + + + 0 + 0 + 620 + 319 + + + + About Greenchain + + + + + + + 0 + 0 + + + + :/images/about + + + + + + + 50 + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + IBeamCursor + + + <html><head/><body><p><span style=" font-weight:600;">Greenchain</span> version</p></body></html> + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + IBeamCursor + + + 0.3.666-beta + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + IBeamCursor + + + Copyright &copy; 2017 The Greenchain Developers + + + Qt::RichText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + IBeamCursor + + + + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + + + + + buttonBox + accepted() + AboutDialog + accept() + + + 360 + 308 + + + 157 + 274 + + + + + buttonBox + rejected() + AboutDialog + reject() + + + 428 + 308 + + + 286 + 274 + + + + + diff --git a/src/qt/forms/addressbookpage.ui b/src/qt/forms/addressbookpage.ui new file mode 100644 index 0000000..991f425 --- /dev/null +++ b/src/qt/forms/addressbookpage.ui @@ -0,0 +1,186 @@ + + + AddressBookPage + + + + 0 + 0 + 994 + 380 + + + + Address Book + + + + + + 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 + + + + + + + <html><head/><body><p>Sign a message to prove you own a Greenchain address</p></body></html> + + + Sign &Message + + + + :/icons/edit:/icons/edit + + + + + + + <html><head/><body><p>Verify a message to ensure it was signed with a specified Greenchain address</p></body></html> + + + &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 + + + + + + + + Export the data in the current tab to a file + + + &Export + + + + :/icons/export:/icons/export + + + + + + + + 0 + 0 + + + + QDialogButtonBox::Ok + + + + + + + + + + + + diff --git a/src/qt/forms/askpassphrasedialog.ui b/src/qt/forms/askpassphrasedialog.ui new file mode 100644 index 0000000..2516904 --- /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/editaddressdialog.ui b/src/qt/forms/editaddressdialog.ui new file mode 100644 index 0000000..b4a4c1b --- /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/optionsdialog.ui b/src/qt/forms/optionsdialog.ui new file mode 100644 index 0000000..a458c04 --- /dev/null +++ b/src/qt/forms/optionsdialog.ui @@ -0,0 +1,480 @@ + + + OptionsDialog + + + + 0 + 0 + 540 + 380 + + + + Options + + + true + + + + + + QTabWidget::North + + + 3 + + + + &Main + + + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + Qt::PlainText + + + true + + + + + + + + + Pay transaction &fee + + + Qt::PlainText + + + transactionFee + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Automatically start Greenchain after logging in to the system. + + + &Start Greenchain on system login + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Reset all client options to default. + + + &Reset Options + + + false + + + + + + + + + + &Network + + + + + + <html><head/><body><p>Automatically open the Greenchain client port on the router. This only works when your router supports UPnP and it is enabled.</p></body></html> + + + Map port using &UPnP + + + + + + + Connect to the Greenchain 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 + + + + + + + + + + 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 Quit in the menu. + + + M&inimize on close + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + &Display + + + + + + + + User Interface &language: + + + Qt::PlainText + + + lang + + + + + + + <html><head/><body><p>The user interface language can be set here. This setting will take effect after restarting Greenchain.</p></body></html> + + + + + + + + + + + &Unit to show amounts in: + + + Qt::PlainText + + + unit + + + + + + + Choose the default subdivision unit to show in the interface and when sending coins. + + + + + + + + + <html><head/><body><p>Whether to show Greenchain addresses in the transaction list or not.</p></body></html> + + + &Display addresses in transaction list + + + + + + + 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 0000000..6ef0c2e --- /dev/null +++ b/src/qt/forms/overviewpage.ui @@ -0,0 +1,365 @@ + + + OverviewPage + + + + 0 + 0 + 573 + 342 + + + + Form + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + 75 + true + + + + Wallet + + + + + + + <html><head/><body><p>The displayed information may be out of date. Your wallet automatically synchronizes with the Greenchain network after a connection is established, but this process has not completed yet.</p></body></html> + + + QLabel { color: red; } + + + (out of sync) + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 12 + + + + + Balance: + + + + + + + + 75 + true + + + + IBeamCursor + + + Your current balance + + + Qt::LeftToRight + + + 0 gc + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 80 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Unconfirmed: + + + + + + + + 75 + true + + + + IBeamCursor + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + + + Qt::LeftToRight + + + 0 gc + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 80 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Immature: + + + + + + + + 75 + true + + + + Mined balance that has not yet matured + + + Qt::LeftToRight + + + 0 gc + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 0 + + + 80 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + :/images/wallet_bgcoin + + + false + + + Qt::AlignCenter + + + 0 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + <b>Recent transactions</b> + + + + + + + <html><head/><body><p>The displayed information may be out of date. Your wallet automatically synchronizes with the Greenchain network after a connection is established, but this process has not completed yet.</p></body></html> + + + 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 + + + + + + + + + + + + + 0 + 0 + 16 + 20 + + + + false + + + background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop:0 #F0D0A0, stop:1 #F8D488); color:#000000; + + + true + + + 3 + + + + + + + + diff --git a/src/qt/forms/qrcodedialog.ui b/src/qt/forms/qrcodedialog.ui new file mode 100644 index 0000000..52e9db3 --- /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 0000000..c5cabd2 --- /dev/null +++ b/src/qt/forms/rpcconsole.ui @@ -0,0 +1,456 @@ + + + RPCConsole + + + + 0 + 0 + 740 + 491 + + + + Greenchain - Debug window + + + + + + 1 + + + + &Information + + + + 12 + + + + + + 75 + true + + + + Greenchain Core + + + + + + + Client name + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Client version + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Using OpenSSL version + + + 10 + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Build date + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Startup time + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + 75 + true + + + + Network + + + + + + + Number of connections + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + On testnet + + + + + + + false + + + + + + + + + + + 75 + true + + + + Block chain + + + + + + + Current number of blocks + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Estimated total blocks + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Last block time + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Qt::Vertical + + + + 20 + 20 + + + + + + + + + 75 + true + + + + Debug log file + + + + + + + <html><head/><body><p>Open the Greenchain debug log file from the current data directory. This can take a few seconds for large log files.</p></body></html> + + + &Open + + + false + + + + + + + + 75 + true + + + + Command-line options + + + + + + + <html><head/><body><p>Show the Greenchain-Qt help message to get a list with possible Greenchain command-line options.</p></body></html> + + + &Show + + + false + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + &Console + + + + 3 + + + + + + 0 + 100 + + + + true + + + false + + + 2 + + + + + + + 3 + + + + + > + + + + + + + + + + + 24 + 24 + + + + Clear console + + + + + + + :/icons/remove:/icons/remove + + + Ctrl+L + + + false + + + + + + + + + + + + + + + + diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui new file mode 100644 index 0000000..9524a99 --- /dev/null +++ b/src/qt/forms/sendcoinsdialog.ui @@ -0,0 +1,175 @@ + + + SendCoinsDialog + + + + 0 + 0 + 686 + 217 + + + + Send Coins + + + + + + true + + + + + 0 + 0 + 666 + 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 gc + + + 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 0000000..82e889d --- /dev/null +++ b/src/qt/forms/sendcoinsentry.ui @@ -0,0 +1,159 @@ + + + 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 + + + + + + + + + + &Label: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + addAsLabel + + + + + + + 0 + + + + + <html><head/><body><p>The address to send the payment to (e.g. GarkEpjqT9AN6qpugjQBic31sAh14ngyWD)</p></body></html> + + + 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 + + + + + + + + + Enter a label for this address to add it to your address book + + + + + + + + BitcoinAmountField + QSpinBox +
bitcoinamountfield.h
+ 1 +
+ + QValidatedLineEdit + QLineEdit +
qvalidatedlineedit.h
+
+
+ + + + +
diff --git a/src/qt/forms/signverifymessagedialog.ui b/src/qt/forms/signverifymessagedialog.ui new file mode 100644 index 0000000..8104711 --- /dev/null +++ b/src/qt/forms/signverifymessagedialog.ui @@ -0,0 +1,396 @@ + + + SignVerifyMessageDialog + + + + 0 + 0 + 700 + 380 + + + + Signatures - Sign / Verify a Message + + + true + + + + + + 0 + + + + &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 + + + + + <html><head/><body><p>The address to sign the message with (e.g. GarkEpjqT9AN6qpugjQBic31sAh14ngyWD)</p></body></html> + + + 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 + + + + + + + Signature + + + Qt::PlainText + + + + + + + 0 + + + + + + true + + + + true + + + + + + + Copy the current signature to the system clipboard + + + + + + + :/icons/editcopy:/icons/editcopy + + + false + + + + + + + + + + + <html><head/><body><p>Sign the message to prove you own this Greenchain address</p></body></html> + + + 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 + + + + + <html><head/><body><p>The address the message was signed with (e.g. GarkEpjqT9AN6qpugjQBic31sAh14ngyWD)</p></body></html> + + + 34 + + + + + + + Choose an address from the address book + + + + + + + :/icons/address-book:/icons/address-book + + + Alt+A + + + false + + + + + + + + + + + + + + + + + <html><head/><body><p>Verify the message to ensure it was signed with the specified Greenchain address</p></body></html> + + + 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 0000000..b38dffc --- /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 + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + TransactionDescDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h new file mode 100644 index 0000000..9241783 --- /dev/null +++ b/src/qt/guiconstants.h @@ -0,0 +1,34 @@ +#ifndef GUICONSTANTS_H +#define GUICONSTANTS_H + +/* Milliseconds between model updates */ +static const int MODEL_UPDATE_DELAY = 250; + +/* 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 + +#endif // GUICONSTANTS_H diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp new file mode 100644 index 0000000..9b1f1e9 --- /dev/null +++ b/src/qt/guiutil.cpp @@ -0,0 +1,461 @@ +#include + +#include "guiutil.h" + +#include "bitcoinaddressvalidator.h" +#include "walletmodel.h" +#include "bitcoinunits.h" + +#include "util.h" +#include "init.h" + +#include +#include +#include +#include +#include +#include // For Qt::escape +#include +#include +#include +#include +#include + +#include +#include + +#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 + +namespace GUIUtil { + +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) +{ + // return if URI is not valid or is no bitcoin URI + if(!uri.isValid() || uri.scheme() != QString("Greenchain")) + return false; + + SendCoinsRecipient rv; + rv.address = uri.path(); + rv.amount = 0; + QList > items = uri.queryItems(); + 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::gc, 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 Greenchain:// to Greenchain: + // + // Cannot handle this later, because Greenchain:// will cause Qt to see the part after // as host, + // which will lower-case it (and thus invalidate the address). + if(uri.startsWith("Greenchain://")) + { + uri.replace(0, 10, "Greenchain:"); + } + QUrl uriInstance(uri); + return parseBitcoinURI(uriInstance, out); +} + +QString HtmlEscape(const QString& str, bool fMultiLine) +{ + QString escaped = Qt::escape(str); + 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 (global clipboard) + QApplication::clipboard()->setText(selection.at(0).data(role).toString(), QClipboard::Clipboard); + // Copy first item (global mouse selection for e.g. X11 - NOP on Windows) + QApplication::clipboard()->setText(selection.at(0).data(role).toString(), QClipboard::Selection); + } +} + +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 + { + myDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); + } + 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() != qApp->thread()) + { + return Qt::BlockingQueuedConnection; + } + else + { + return Qt::DirectConnection; + } +} + +bool checkPoint(const QPoint &p, const QWidget *w) +{ + QWidget *atW = QApplication::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()))); +} + +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) / "Greenchain.lnk"; +} + +bool GetStartOnSystemStartup() +{ + // check for Greenchain.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() / "Greenchain.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 Greenchain.desktop file to the autostart directory: + optionFile << "[Desktop Entry]\n"; + optionFile << "Type=Application\n"; + optionFile << "Name=Greenchain\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("Greenchain-Qt") + " " + tr("version") + " " + + QString::fromStdString(FormatFullVersion()) + "\n\n" + + tr("Usage:") + "\n" + + " Greenchain-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("Greenchain-Qt")); + 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); +} + +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 +} + +} // namespace GUIUtil diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h new file mode 100644 index 0000000..a79e49f --- /dev/null +++ b/src/qt/guiutil.h @@ -0,0 +1,121 @@ +#ifndef GUIUTIL_H +#define GUIUTIL_H + +#include +#include +#include + +class SendCoinsRecipient; + +QT_BEGIN_NAMESPACE +class QFont; +class QLineEdit; +class QWidget; +class QDateTime; +class QUrl; +class QAbstractItemView; +QT_END_NAMESPACE + +/** Utility functions used by the Bitcoin Qt UI. + */ +namespace GUIUtil +{ + // 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 "Greenchain:" 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(); + + /** 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; + }; + +} // namespace GUIUtil + +#endif // GUIUTIL_H diff --git a/src/qt/locale/bitcoin_en.qm b/src/qt/locale/bitcoin_en.qm new file mode 100644 index 0000000000000000000000000000000000000000..79e05472f4f64cd66f9598a4719ff8eafa6fd72d GIT binary patch literal 79898 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 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 0000000..9b87816 --- /dev/null +++ b/src/qt/locale/bitcoin_en.ts @@ -0,0 +1,3040 @@ + + + +UTF-8 + + AboutDialog + + + About Greenchain + About Greenchain + + + + <html><head/><body><p><span style=" font-weight:600;">Greenchain</span> version</p></body></html> + + + + <b>Greenchain</b> version + <b>Greenchain</b> version + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + + + + Copyright + Copyright + + + + The Greenchain Developers + + + + The Greenchain developers + The Greenchain developers + + + + AddressBookPage + + + Address Book + Address Book + + + + Double-click to edit address or label + Double-click to edit address or label + + + + Create a new address + Create a new address + + + + Copy the currently selected address to the system clipboard + Copy the currently selected address to the system clipboard + + + + <html><head/><body><p>Sign a message to prove you own a Greenchain address</p></body></html> + + + + + &New Address + &New Address + + + + These are your Greenchain 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 Greenchain 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. + + + + &Copy Address + &Copy Address + + + + Show &QR Code + Show &QR Code + + + Sign a message to prove you own a Greenchain address + Sign a message to prove you own a Greenchain address + + + + Sign &Message + Sign &Message + + + + <html><head/><body><p>Verify a message to ensure it was signed with a specified Greenchain address</p></body></html> + + + + + Delete the currently selected address from the list + Delete the currently selected address from the list + + + + Export the data in the current tab to a file + Export the data in the current tab to a file + + + + &Export + &Export + + + Verify a message to ensure it was signed with a specified Greenchain address + Verify a message to ensure it was signed with a specified Greenchain address + + + + &Verify Message + &Verify Message + + + + &Delete + &Delete + + + + These are your Greenchain addresses for sending payments. Always check the amount and the receiving address before sending coins. + These are your Greenchain addresses for sending payments. Always check the amount and the receiving address before sending coins. + + + + Copy &Label + Copy &Label + + + + &Edit + &Edit + + + + Send &Coins + Send &Coins + + + + 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 QUARKCOINS</b>! + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR QUARKCOINS</b>! + + + + Are you sure you wish to encrypt your wallet? + Are you sure you wish to encrypt your wallet? + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR Greenchain</b>! + + + + + Greenchain 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. + + + + + Warning: The Caps Lock key is on! + Warning: The Caps Lock key is on! + + + + + Wallet encrypted + Wallet encrypted + + + Greenchain will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your Greenchains from being stolen by malware infecting your computer. + Greenchain will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your Greenchains from being stolen by malware infecting your computer. + + + + + + + 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 passphrase was successfully changed. + Wallet passphrase was successfully changed. + + + + BitcoinGUI + + + Sign &message... + Sign &message... + + + + Synchronizing with network... + Synchronizing with network... + + + + &Overview + &Overview + + + + Show general overview of wallet + Show general overview of wallet + + + + &Transactions + &Transactions + + + + Browse transaction history + Browse transaction history + + + + Edit the list of stored addresses and labels + Edit the list of stored addresses and labels + + + + Show the list of addresses for receiving payments + Show the list of addresses for receiving payments + + + + E&xit + E&xit + + + + Quit application + Quit application + + + + Show information about Greenchain + Show information about Greenchain + + + + About &Qt + About &Qt + + + + Show information about Qt + Show information about Qt + + + + &Options... + &Options... + + + + &Encrypt Wallet... + &Encrypt Wallet... + + + + &Backup Wallet... + &Backup Wallet... + + + + &Change Passphrase... + &Change Passphrase... + + + + Importing blocks from disk... + Importing blocks from disk... + + + + Reindexing blocks on disk... + Reindexing blocks on disk... + + + + Send coins to a Greenchain address + Send coins to a Greenchain address + + + + Modify configuration options for Greenchain + Modify configuration options for Greenchain + + + + Backup wallet to another location + Backup wallet to another location + + + + Change the passphrase used for wallet encryption + Change the passphrase used for wallet encryption + + + + &Debug window + &Debug window + + + + Open debugging and diagnostic console + Open debugging and diagnostic console + + + + &Verify message... + &Verify message... + + + + + Greenchain + Greenchain + + + + Wallet + Wallet + + + + &Send + &Send + + + + &Receive + &Receive + + + + &Addresses + &Addresses + + + + &About Greenchain + &About Greenchain + + + + &Show / Hide + &Show / Hide + + + + Show or hide the main Window + Show or hide the main Window + + + + Encrypt the private keys that belong to your wallet + Encrypt the private keys that belong to your wallet + + + + Sign messages with your Greenchain addresses to prove you own them + Sign messages with your Greenchain addresses to prove you own them + + + + Verify messages to ensure they were signed with specified Greenchain addresses + Verify messages to ensure they were signed with specified Greenchain addresses + + + + &File + &File + + + + &Settings + &Settings + + + + &Help + &Help + + + + Tabs toolbar + Tabs toolbar + + + + + [testnet] + [testnet] + + + + Greenchain client + Greenchain client + + + + %n active connection(s) to Greenchain network + + %n active connection to Greenchain network + %n active connections to Greenchain network + + + + + No block source available... + No block source available... + + + + Processed %1 of %2 (estimated) blocks of transaction history. + Processed %1 of %2 (estimated) blocks of transaction history. + + + + Processed %1 blocks of transaction history. + Processed %1 blocks of transaction history. + + + + %n hour(s) + + %n hour + %n hours + + + + + %n day(s) + + %n day + %n days + + + + + %n week(s) + + %n week + %n weeks + + + + + %1 behind + %1 behind + + + + Last received block was generated %1 ago. + Last received block was generated %1 ago. + + + + Transactions after this will not yet be visible. + Transactions after this will not yet be visible. + + + + 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? + + + + Up to date + Up to date + + + + Catching up... + Catching up... + + + + 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 Greenchain address or malformed URI parameters. + URI can not be parsed! This can be caused by an invalid Greenchain 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> + + + + A fatal error occurred. Greenchain can no longer continue safely and will quit. + A fatal error occurred. Greenchain can no longer continue safely and will quit. + + + + ClientModel + + + Network Alert + Network Alert + + + + 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 already in the address book. + The entered address "%1" is already in the address book. + + + + The entered address "%1" is not a valid Greenchain address. + The entered address "%1" is not a valid Greenchain address. + + + + Could not unlock wallet. + Could not unlock wallet. + + + + New key generation failed. + New key generation failed. + + + + GUIUtil::HelpMessageBox + + + + Greenchain-Qt + Greenchain-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) + + + + OptionsDialog + + + Options + Options + + + + &Main + &Main + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + Pay transaction &fee + Pay transaction &fee + + + + Automatically start Greenchain after logging in to the system. + Automatically start Greenchain after logging in to the system. + + + + &Start Greenchain on system login + &Start Greenchain on system login + + + + Reset all client options to default. + Reset all client options to default. + + + + &Reset Options + &Reset Options + + + + &Network + &Network + + + Automatically open the Greenchain client port on the router. This only works when your router supports UPnP and it is enabled. + Automatically open the Greenchain client port on the router. This only works when your router supports UPnP and it is enabled. + + + + <html><head/><body><p>Automatically open the Greenchain client port on the router. This only works when your router supports UPnP and it is enabled.</p></body></html> + + + + + Map port using &UPnP + Map port using &UPnP + + + + Connect to the Greenchain network through a SOCKS proxy (e.g. when connecting through Tor). + Connect to the Greenchain 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) + + + + &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 Quit 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 Quit in the menu. + + + + M&inimize on close + M&inimize on close + + + + &Display + &Display + + + + User Interface &language: + User Interface &language: + + + + <html><head/><body><p>The user interface language can be set here. This setting will take effect after restarting Greenchain.</p></body></html> + + + + + <html><head/><body><p>Whether to show Greenchain addresses in the transaction list or not.</p></body></html> + + + + The user interface language can be set here. This setting will take effect after restarting Greenchain. + The user interface language can be set here. This setting will take effect after restarting Greenchain. + + + + &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 Greenchain addresses in the transaction list or not. + Whether to show Greenchain addresses in the transaction list or not. + + + + &Display addresses in transaction list + &Display addresses in transaction list + + + + &OK + &OK + + + + &Cancel + &Cancel + + + + &Apply + &Apply + + + + default + default + + + + Confirm options reset + Confirm options reset + + + + Some settings may require a client restart to take effect. + Some settings may require a client restart to take effect. + + + + Do you want to proceed? + Do you want to proceed? + + + + + Warning + Warning + + + + + This setting will take effect after restarting Greenchain. + This setting will take effect after restarting Greenchain. + + + + The supplied proxy address is invalid. + The supplied proxy address is invalid. + + + + OverviewPage + + + Form + Form + + + The displayed information may be out of date. Your wallet automatically synchronizes with the Greenchain 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 Greenchain network after a connection is established, but this process has not completed yet. + + + + Balance: + Balance: + + + + Unconfirmed: + Unconfirmed: + + + + Wallet + Wallet + + + + + <html><head/><body><p>The displayed information may be out of date. Your wallet automatically synchronizes with the Greenchain network after a connection is established, but this process has not completed yet.</p></body></html> + + + + + Immature: + Immature: + + + + Mined balance that has not yet matured + Mined balance that has not yet matured + + + + <b>Recent transactions</b> + <b>Recent transactions</b> + + + + Your current balance + Your current balance + + + + 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 + + + + + out of sync + out of sync + + + + PaymentServer + + + Cannot start Greenchain: click-to-pay handler + Cannot start Greenchain: click-to-pay handler + + + + QRCodeDialog + + + QR Code Dialog + QR Code Dialog + + + + Request Payment + Request Payment + + + + Amount: + Amount: + + + + Label: + Label: + + + + Message: + Message: + + + + &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 + + + Client name + Client name + + + + + + + + + + + + + N/A + N/A + + + + Client version + Client version + + + + &Information + &Information + + + + Using OpenSSL version + Using OpenSSL version + + + + Startup time + Startup time + + + + Network + Network + + + + Number of connections + Number of connections + + + + On testnet + On testnet + + + + Block chain + Block chain + + + + Current number of blocks + Current number of blocks + + + + Estimated total blocks + Estimated total blocks + + + + Last block time + Last block time + + + + <html><head/><body><p>Open the Greenchain debug log file from the current data directory. This can take a few seconds for large log files.</p></body></html> + + + + + &Open + &Open + + + + Command-line options + Command-line options + + + + <html><head/><body><p>Show the Greenchain-Qt help message to get a list with possible Greenchain command-line options.</p></body></html> + + + + Show the Greenchain-Qt help message to get a list with possible Greenchain command-line options. + Show the Greenchain-Qt help message to get a list with possible Greenchain command-line options. + + + + &Show + &Show + + + + &Console + &Console + + + + Build date + Build date + + + + Greenchain - Debug window + Greenchain - Debug window + + + + Greenchain Core + Greenchain Core + + + + Debug log file + Debug log file + + + Open the Greenchain debug log file from the current data directory. This can take a few seconds for large log files. + Open the Greenchain debug log file from the current data directory. This can take a few seconds for large log files. + + + + Clear console + Clear console + + + + Welcome to the Greenchain RPC console. + Welcome to the Greenchain 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. + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Send Coins + + + + 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 V + 123.456 V + + + + Confirm the send action + Confirm the send action + + + + S&end + S&end + + + + <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: + + + The address to send the payment to (e.g. GarkEpjqT9AN6qpugjQBic31sAh14ngyWD) + The address to send the payment to (e.g. GarkEpjqT9AN6qpugjQBic31sAh14ngyWD) + + + + + 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: + + + + <html><head/><body><p>The address to send the payment to (e.g. GarkEpjqT9AN6qpugjQBic31sAh14ngyWD)</p></body></html> + + + + + 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 Greenchain address (e.g. GarkEpjqT9AN6qpugjQBic31sAh14ngyWD) + Enter a Greenchain address (e.g. GarkEpjqT9AN6qpugjQBic31sAh14ngyWD) + + + + 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. GarkEpjqT9AN6qpugjQBic31sAh14ngyWD) + The address to sign the message with (e.g. GarkEpjqT9AN6qpugjQBic31sAh14ngyWD) + + + + <html><head/><body><p>The address to sign the message with (e.g. GarkEpjqT9AN6qpugjQBic31sAh14ngyWD)</p></body></html> + + + + + + 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 + + + + Signature + Signature + + + + Copy the current signature to the system clipboard + Copy the current signature to the system clipboard + + + + <html><head/><body><p>Sign the message to prove you own this Greenchain address</p></body></html> + + + + + <html><head/><body><p>The address the message was signed with (e.g. GarkEpjqT9AN6qpugjQBic31sAh14ngyWD)</p></body></html> + + + + + <html><head/><body><p>Verify the message to ensure it was signed with the specified Greenchain address</p></body></html> + + + + Sign the message to prove you own this Greenchain address + Sign the message to prove you own this Greenchain address + + + + Sign &Message + Sign &Message + + + + 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. GarkEpjqT9AN6qpugjQBic31sAh14ngyWD) + The address the message was signed with (e.g. GarkEpjqT9AN6qpugjQBic31sAh14ngyWD) + + + Verify the message to ensure it was signed with the specified Greenchain address + Verify the message to ensure it was signed with the specified Greenchain address + + + + Verify &Message + Verify &Message + + + + Reset all verify message fields + Reset all verify message fields + + + + + Enter a Greenchain address (e.g. GarkEpjqT9AN6qpugjQBic31sAh14ngyWD) + Enter a Greenchain address (e.g. GarkEpjqT9AN6qpugjQBic31sAh14ngyWD) + + + + Click "Sign Message" to generate signature + Click "Sign Message" to generate signature + + + + Enter Greenchain signature + Enter Greenchain 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. + + + + SplashScreen + + The Greenchain developers + The Greenchain developers + + + [testnet] + [testnet] + + + + TransactionDesc + + + Open until %1 + Open until %1 + + + + %1/offline + %1/offline + + + + %1/unconfirmed + %1/unconfirmed + + + + %1 confirmations + %1 confirmations + + + + Status + Status + + + + , broadcast through %n node(s) + + , broadcast through %n node + , broadcast through %n nodes + + + + + Date + Date + + + + Source + Source + + + + Generated + Generated + + + + + From + From + + + + + + To + To + + + + + own address + own address + + + + label + label + + + + + + + + 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 60 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 60 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 + + + + , has not been successfully broadcast yet + , has not been successfully broadcast yet + + + + Open for %n more block(s) + + Open for %n more block + Open for %n more blocks + + + + + unknown + unknown + + + + 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 + + + + Amount + Amount + + + + Open for %n more block(s) + + Open for %n more block + Open for %n more 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 + + + + 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 + + + + Greenchain-core + + + Greenchain version + Greenchain version + + + + Usage: + Usage: + + + + Send command to -server or Greenchaind + Send command to -server or Greenchaind + + + + List commands + List commands + + + + Get help for a command + Get help for a command + + + + Options: + Options: + + + + Specify configuration file (default: Greenchain.conf) + Specify configuration file (default: Greenchain.conf) + + + + Specify pid file (default: Greenchaind.pid) + Specify pid file (default: Greenchaind.pid) + + + + Specify data directory + Specify data directory + + + + Set database cache size in megabytes (default: 25) + Set database cache size in megabytes (default: 25) + + + Listen for connections on <port> (default: 8333 or testnet: 18333) + Listen for connections on <port> (default: 8333 or testnet: 18333) + + + + Maintain at most <n> connections to peers (default: 125) + Maintain at most <n> connections to peers (default: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + Connect to a node to retrieve peer addresses, and disconnect + + + + Specify your own public address + Specify your own public address + + + + Threshold for disconnecting misbehaving peers (default: 100) + Threshold for disconnecting misbehaving peers (default: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + + + + 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 + + + Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332) + Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332) + + + + Accept command line and JSON-RPC commands + Accept command line and JSON-RPC commands + + + + Run in the background as a daemon and accept commands + Run in the background as a daemon and accept commands + + + + Use the test network + Use the test network + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + Accept connections from outside (default: 1 if no -proxy or -connect) + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=Greenchainrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "Greenchain Alert" admin@foo.com + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=Greenchainrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "Greenchain Alert" admin@foo.com + + + + + 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 + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + + + + Cannot obtain a lock on data directory %s. Greenchain is probably already running. + Cannot obtain a lock on data directory %s. Greenchain is probably already running. + + + + 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! + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + Execute command when a relevant alert is received (%s in cmd is replaced by message) + + + + 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) + + + + Listen for JSON-RPC connections on <port> (default: 31742 or testnet: 41742) + Listen for JSON-RPC connections on <port> (default: 31742 or testnet: 41742) + + + + 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) + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + + 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: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong Greenchain will not work properly. + Warning: Please check that your computer's date and time are correct! If your clock is wrong Greenchain 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. + + + + Attempt to recover private keys from a corrupt wallet.dat + Attempt to recover private keys from a corrupt wallet.dat + + + + Block creation options: + Block creation options: + + + + Connect only to the specified node(s) + Connect only to the specified node(s) + + + + Corrupted block database detected + Corrupted block database detected + + + + Discover own IP address (default: 1 when listening and no -externalip) + Discover own IP address (default: 1 when listening and no -externalip) + + + + Do you want to rebuild the block database now? + Do you want to rebuild the block database now? + + + + Error initializing block database + Error initializing block database + + + + Error initializing wallet database environment %s! + Error initializing wallet database environment %s! + + + + Error loading block database + Error loading block database + + + + Error opening block database + Error opening block database + + + + Error: Disk space is low! + Error: Disk space is low! + + + + Error: Wallet locked, unable to create transaction! + Error: Wallet locked, unable to create transaction! + + + + Error: system error: + Error: system error: + + + + 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. + + + + Failed to read block info + Failed to read block info + + + + Failed to read block + Failed to read block + + + + Failed to sync block index + Failed to sync block index + + + + Failed to write block index + Failed to write block index + + + + Failed to write block info + Failed to write block info + + + + Failed to write block + Failed to write block + + + + Failed to write file info + Failed to write file info + + + + Failed to write to coin database + Failed to write to coin database + + + + Failed to write transaction index + Failed to write transaction index + + + + Failed to write undo data + Failed to write undo data + + + + Find peers using DNS lookup (default: 1 unless -connect) + Find peers using DNS lookup (default: 1 unless -connect) + + + + Generate coins (default: 0) + Generate coins (default: 0) + + + + How many blocks to check at startup (default: 288, 0 = all) + How many blocks to check at startup (default: 288, 0 = all) + + + + How thorough the block verification is (0-4, default: 3) + How thorough the block verification is (0-4, default: 3) + + + + Listen for connections on <port> (default: 31743 or testnet: 41743) + Listen for connections on <port> (default: 31743 or testnet: 41743) + + + + Not enough file descriptors available. + Not enough file descriptors available. + + + + Rebuild block chain index from current blk000??.dat files + Rebuild block chain index from current blk000??.dat files + + + + Set the number of threads to service RPC calls (default: 4) + Set the number of threads to service RPC calls (default: 4) + + + + Verifying blocks... + Verifying blocks... + + + + Verifying wallet... + Verifying wallet... + + + + Imports blocks from external blk000??.dat file + Imports blocks from external blk000??.dat file + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + Information + Information + + + + Invalid -tor address: '%s' + Invalid -tor address: '%s' + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + Invalid amount for -mintxfee=<amount>: '%s' + Invalid amount for -mintxfee=<amount>: '%s' + + + + Maintain a full transaction index (default: 0) + Maintain a full transaction index (default: 0) + + + + 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) + + + + Only accept block chain matching built-in checkpoints (default: 1) + Only accept block chain matching built-in checkpoints (default: 1) + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + + + + 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 + + + + Prepend debug output with timestamp + Prepend debug output with timestamp + + + + SSL options: (see the Greenchain Wiki for SSL setup instructions) + SSL options: (see the Greenchain 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 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 + + + + 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) + + + + Signing transaction failed + Signing transaction failed + + + + Specify connection timeout in milliseconds (default: 5000) + Specify connection timeout in milliseconds (default: 5000) + + + + System error: + System error: + + + + Transaction amount too small + Transaction amount too small + + + + Transaction amounts must be positive + Transaction amounts must be positive + + + + Transaction too large + Transaction too large + + + + Use UPnP to map the listening port (default: 0) + Use UPnP to map the listening port (default: 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + Use UPnP to map the listening port (default: 1 when listening) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + Use proxy to reach tor hidden services (default: same as -proxy) + + + + Username for JSON-RPC connections + Username for JSON-RPC connections + + + + Warning + Warning + + + + Warning: This version is obsolete, upgrade required! + Warning: This version is obsolete, upgrade required! + + + + You need to rebuild the databases using -reindex to change -txindex + You need to rebuild the databases using -reindex to change -txindex + + + + wallet.dat corrupt, salvage failed + wallet.dat corrupt, salvage failed + + + + Password for JSON-RPC connections + Password for JSON-RPC connections + + + + Allow JSON-RPC connections from specified IP address + Allow JSON-RPC connections from specified IP address + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Send commands to node running on <ip> (default: 127.0.0.1) + + + + 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) + + + + Upgrade wallet to latest format + Upgrade wallet to latest format + + + + Set key pool size to <n> (default: 100) + Set key pool size to <n> (default: 100) + + + + Rescan the block chain for missing wallet transactions + Rescan the block chain for missing wallet transactions + + + + Use OpenSSL (https) for JSON-RPC connections + Use OpenSSL (https) for JSON-RPC connections + + + + Server certificate file (default: server.cert) + Server certificate file (default: server.cert) + + + + Server private key (default: server.pem) + Server private key (default: server.pem) + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + This help message + + + + 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) + + + + Connect through socks proxy + Connect through socks proxy + + + + Allow DNS lookups for -addnode, -seednode and -connect + Allow DNS lookups for -addnode, -seednode and -connect + + + + Loading addresses... + Loading addresses... + + + + Error loading wallet.dat: Wallet corrupted + Error loading wallet.dat: Wallet corrupted + + + + Error loading wallet.dat: Wallet requires newer version of Greenchain + Error loading wallet.dat: Wallet requires newer version of Greenchain + + + + Wallet needed to be rewritten: restart Greenchain to complete + Wallet needed to be rewritten: restart Greenchain to complete + + + + Error loading wallet.dat + Error loading wallet.dat + + + + Invalid -proxy address: '%s' + Invalid -proxy address: '%s' + + + + Unknown network specified in -onlynet: '%s' + Unknown network specified in -onlynet: '%s' + + + + Unknown -socks proxy version requested: %i + Unknown -socks proxy version requested: %i + + + + Cannot resolve -bind address: '%s' + Cannot resolve -bind address: '%s' + + + + Cannot resolve -externalip address: '%s' + Cannot resolve -externalip address: '%s' + + + + Invalid amount for -paytxfee=<amount>: '%s' + Invalid amount for -paytxfee=<amount>: '%s' + + + + Invalid amount + Invalid amount + + + + Insufficient funds + Insufficient funds + + + + Loading block index... + Loading block index... + + + + 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 + + + + Unable to bind to %s on this computer. Greenchain is probably already running. + Unable to bind to %s on this computer. Greenchain is probably already running. + + + + Fee per KB to add to transactions you send + Fee per KB to add to transactions you send + + + + Loading wallet... + Loading wallet... + + + + Cannot downgrade wallet + Cannot downgrade wallet + + + + Cannot write default address + Cannot write default address + + + + Rescanning... + Rescanning... + + + + Done loading + Done loading + + + + To use the %s option + To use the %s option + + + + Error + Error + + + + 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. + + + + WalletModel + + + Send Coins + Send Coins + + + + WalletView + + + &Export + &Export + + + + Export the data in the current tab to a file + Export the data in the current tab to a file + + + + 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. + + + + Backup Successful + Backup Successful + + + + The wallet data was successfully saved to the new location. + The wallet data was successfully saved to the new location. + + + diff --git a/src/qt/locale/bitcoin_zh_CN.qm b/src/qt/locale/bitcoin_zh_CN.qm new file mode 100644 index 0000000000000000000000000000000000000000..8a2a738fe1eccfdf2fd738f642131e5c036b0c9d GIT binary patch literal 62020 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