aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE.md38
-rw-r--r--.gitignore6
-rw-r--r--.travis.yml3
-rw-r--r--CONTRIBUTING.md21
-rw-r--r--INSTALL5
-rw-r--r--INSTALL.md5
-rw-r--r--Makefile.am11
-rw-r--r--README.md5
-rwxr-xr-xautogen.sh4
-rw-r--r--build-aux/m4/ax_boost_base.m44
-rw-r--r--build-aux/m4/bitcoin_find_bdb48.m44
-rw-r--r--build-aux/m4/bitcoin_qt.m44
-rw-r--r--build-aux/m4/bitcoin_subdir_to_include.m44
-rw-r--r--build-aux/m4/l_atomic.m48
-rw-r--r--configure.ac49
-rw-r--r--contrib/README.md10
-rw-r--r--contrib/debian/bitcoin-qt.desktop5
-rw-r--r--contrib/debian/bitcoin-qt.manpages1
-rw-r--r--contrib/debian/bitcoin-tx.manpages1
-rw-r--r--contrib/debian/bitcoind.manpages5
-rw-r--r--contrib/debian/copyright7
-rw-r--r--contrib/debian/manpages/bitcoin-cli.121
-rw-r--r--contrib/debian/manpages/bitcoin-qt.113
-rw-r--r--contrib/debian/manpages/bitcoin.conf.519
-rw-r--r--contrib/debian/manpages/bitcoind.130
-rwxr-xr-xcontrib/debian/rules3
-rw-r--r--contrib/devtools/README.md61
-rwxr-xr-xcontrib/devtools/copyright_header.py610
-rwxr-xr-xcontrib/devtools/fix-copyright-headers.py67
-rwxr-xr-xcontrib/devtools/git-subtree-check.sh3
-rwxr-xr-xcontrib/devtools/github-merge.py2
-rwxr-xr-xcontrib/devtools/optimize-pngs.py3
-rwxr-xr-xcontrib/devtools/security-check.py51
-rwxr-xr-xcontrib/devtools/test-security-check.py3
-rwxr-xr-xcontrib/gitian-build.sh4
-rw-r--r--contrib/gitian-descriptors/gitian-win.yml2
-rw-r--r--contrib/gitian-keys/README.md16
-rwxr-xr-xcontrib/macdeploy/detached-sig-apply.sh4
-rwxr-xr-xcontrib/macdeploy/detached-sig-create.sh4
-rwxr-xr-xcontrib/macdeploy/extract-osx-sdk.sh4
-rw-r--r--contrib/qos/tc.sh4
-rwxr-xr-xcontrib/qt_translations.py22
-rw-r--r--contrib/rpm/bitcoin.spec10
-rwxr-xr-xcontrib/seeds/makeseeds.py3
-rw-r--r--contrib/spendfrom/README.md35
-rw-r--r--contrib/spendfrom/setup.py9
-rwxr-xr-xcontrib/spendfrom/spendfrom.py267
-rw-r--r--contrib/testgen/base58.py3
-rwxr-xr-xcontrib/testgen/gen_base58_test_vectors.py3
-rwxr-xr-xcontrib/tidy_datadir.sh3
-rwxr-xr-xcontrib/verify-commits/gpg.sh4
-rwxr-xr-xcontrib/verify-commits/pre-push-hook.sh4
-rwxr-xr-xcontrib/verify-commits/verify-commits.sh4
-rwxr-xr-xcontrib/verifybinaries/verify.sh3
-rwxr-xr-xcontrib/zmq/zmq_sub.py3
-rw-r--r--depends/hosts/darwin.mk2
-rw-r--r--depends/packages/boost.mk6
-rw-r--r--depends/packages/expat.mk4
-rw-r--r--depends/packages/fontconfig.mk4
-rw-r--r--depends/packages/libevent.mk5
-rw-r--r--depends/packages/native_ccache.mk6
-rw-r--r--depends/packages/qt.mk3
-rw-r--r--depends/patches/libevent/libevent-2-fixes.patch18
-rw-r--r--depends/patches/qt/configure-xcoderun.patch25
-rw-r--r--doc/Doxyfile2748
-rw-r--r--doc/README.md12
-rw-r--r--doc/bips.md5
-rw-r--r--doc/build-osx.md4
-rw-r--r--doc/build-unix.md45
-rw-r--r--doc/build-windows.md85
-rw-r--r--doc/developer-notes.md57
-rw-r--r--doc/files.md1
-rw-r--r--doc/fuzzing.md66
-rw-r--r--doc/gitian-building.md12
-rw-r--r--doc/multiwallet-qt.md48
-rw-r--r--doc/reduce-traffic.md3
-rw-r--r--doc/release-notes.md15
-rw-r--r--doc/release-notes/release-notes-0.13.0.md17
-rw-r--r--doc/release-notes/release-notes-0.13.1.md410
-rw-r--r--doc/release-process.md12
-rw-r--r--doc/shared-libraries.md5
-rw-r--r--doc/tor.md19
-rw-r--r--doc/translation_process.md2
-rw-r--r--doc/travis-ci.md (renamed from doc/travis-ci.txt)9
-rw-r--r--doc/unit-tests.md18
-rwxr-xr-xqa/pull-tester/rpc-tests.py81
-rw-r--r--qa/pull-tester/run-bitcoind-for-test.sh.in36
-rwxr-xr-xqa/rpc-tests/bip68-112-113-p2p.py1
-rwxr-xr-xqa/rpc-tests/bip9-softforks.py10
-rwxr-xr-xqa/rpc-tests/create_cache.py10
-rwxr-xr-xqa/rpc-tests/forknotify.py6
-rwxr-xr-xqa/rpc-tests/fundrawtransaction.py20
-rwxr-xr-xqa/rpc-tests/import-rescan.py155
-rwxr-xr-xqa/rpc-tests/importmulti.py360
-rwxr-xr-xqa/rpc-tests/maxuploadtarget.py41
-rwxr-xr-xqa/rpc-tests/mempool_limit.py6
-rwxr-xr-xqa/rpc-tests/mempool_packages.py4
-rwxr-xr-xqa/rpc-tests/mempool_reorg.py2
-rwxr-xr-xqa/rpc-tests/multi_rpc.py4
-rwxr-xr-xqa/rpc-tests/nulldummy.py148
-rwxr-xr-xqa/rpc-tests/p2p-compactblocks.py771
-rwxr-xr-xqa/rpc-tests/p2p-fullblocktest.py26
-rwxr-xr-xqa/rpc-tests/p2p-mempool.py2
-rwxr-xr-xqa/rpc-tests/p2p-segwit.py336
-rwxr-xr-xqa/rpc-tests/p2p-versionbits-warning.py50
-rwxr-xr-xqa/rpc-tests/preciousblock.py119
-rwxr-xr-xqa/rpc-tests/prioritise_transaction.py8
-rwxr-xr-xqa/rpc-tests/proxy_test.py16
-rwxr-xr-xqa/rpc-tests/pruning.py55
-rwxr-xr-xqa/rpc-tests/rawtransactions.py39
-rwxr-xr-xqa/rpc-tests/reindex.py16
-rwxr-xr-xqa/rpc-tests/replace-by-fee.py2
-rwxr-xr-xqa/rpc-tests/rpcbind_test.py6
-rwxr-xr-xqa/rpc-tests/segwit.py379
-rwxr-xr-xqa/rpc-tests/sendheaders.py79
-rwxr-xr-xqa/rpc-tests/smartfees.py12
-rw-r--r--qa/rpc-tests/test_framework/address.py74
-rw-r--r--qa/rpc-tests/test_framework/authproxy.py24
-rw-r--r--qa/rpc-tests/test_framework/blockstore.py9
-rwxr-xr-xqa/rpc-tests/test_framework/comptool.py19
-rw-r--r--qa/rpc-tests/test_framework/coverage.py4
-rw-r--r--qa/rpc-tests/test_framework/key.py25
-rwxr-xr-xqa/rpc-tests/test_framework/mininode.py73
-rw-r--r--qa/rpc-tests/test_framework/netutil.py2
-rwxr-xr-xqa/rpc-tests/test_framework/test_framework.py22
-rw-r--r--qa/rpc-tests/test_framework/util.py93
-rwxr-xr-xqa/rpc-tests/wallet-dump.py8
-rwxr-xr-xqa/rpc-tests/wallet.py35
-rwxr-xr-xqa/rpc-tests/walletbackup.py7
-rwxr-xr-xshare/genbuild.sh4
-rw-r--r--share/qt/Info.plist.in2
-rwxr-xr-xshare/qt/extract_strings_qt.py3
-rw-r--r--share/qt/protobuf.pri35
-rwxr-xr-xshare/rpcuser/rpcuser.py2
-rw-r--r--src/Makefile.am21
-rw-r--r--src/Makefile.bench.include34
-rw-r--r--src/Makefile.leveldb.include5
-rw-r--r--src/Makefile.qt.include27
-rw-r--r--src/Makefile.qttest.include13
-rw-r--r--src/Makefile.test.include50
-rw-r--r--src/addrdb.cpp2
-rw-r--r--src/addrdb.h3
-rw-r--r--src/addrman.cpp13
-rw-r--r--src/addrman.h67
-rw-r--r--src/amount.h2
-rw-r--r--src/arith_uint256.cpp2
-rw-r--r--src/arith_uint256.h2
-rw-r--r--src/base58.cpp10
-rw-r--r--src/bench/Examples.cpp2
-rw-r--r--src/bench/base58.cpp4
-rw-r--r--src/bench/bench.cpp29
-rw-r--r--src/bench/bench.h10
-rw-r--r--src/bench/bench_bitcoin.cpp2
-rw-r--r--src/bench/ccoins_caching.cpp87
-rw-r--r--src/bench/checkblock.cpp56
-rw-r--r--src/bench/coin_selection.cpp62
-rw-r--r--src/bench/crypto_hash.cpp10
-rw-r--r--src/bench/data/block413567.rawbin0 -> 999887 bytes
-rw-r--r--src/bench/lockedpool.cpp47
-rw-r--r--src/bench/mempool_eviction.cpp115
-rw-r--r--src/bench/perf.cpp53
-rw-r--r--src/bench/perf.h37
-rw-r--r--src/bench/verify_script.cpp103
-rw-r--r--src/bitcoin-cli.cpp106
-rw-r--r--src/bitcoin-tx.cpp222
-rw-r--r--src/bitcoind.cpp55
-rw-r--r--src/blockencodings.cpp26
-rw-r--r--src/blockencodings.h26
-rw-r--r--src/bloom.cpp10
-rw-r--r--src/bloom.h2
-rw-r--r--src/chain.cpp7
-rw-r--r--src/chain.h14
-rw-r--r--src/chainparams.cpp32
-rw-r--r--src/chainparams.h3
-rw-r--r--src/checkpoints.cpp12
-rw-r--r--src/checkpoints.h3
-rw-r--r--src/coins.cpp2
-rw-r--r--src/coins.h48
-rw-r--r--src/compat.h1
-rw-r--r--src/compat/byteswap.h21
-rw-r--r--src/compat/endian.h2
-rw-r--r--src/compressor.h14
-rw-r--r--src/consensus/merkle.cpp6
-rw-r--r--src/consensus/params.h3
-rw-r--r--src/core_io.h23
-rw-r--r--src/core_memusage.h4
-rw-r--r--src/core_read.cpp2
-rw-r--r--src/core_write.cpp15
-rw-r--r--src/crypto/ctaes/ctaes.c8
-rw-r--r--src/crypto/ctaes/test.c2
-rw-r--r--src/cuckoocache.h457
-rw-r--r--src/dbwrapper.h26
-rw-r--r--src/hash.h12
-rw-r--r--src/httprpc.cpp16
-rw-r--r--src/init.cpp224
-rw-r--r--src/init.h25
-rw-r--r--src/key.cpp40
-rw-r--r--src/key.h31
-rw-r--r--src/leveldb/.travis.yml13
-rw-r--r--src/leveldb/Makefile467
-rw-r--r--src/leveldb/README51
-rw-r--r--src/leveldb/README.md39
-rwxr-xr-xsrc/leveldb/build_detect_platform2
-rw-r--r--src/leveldb/db/corruption_test.cc2
-rw-r--r--src/leveldb/db/db_bench.cc24
-rw-r--r--src/leveldb/db/db_impl.cc194
-rw-r--r--src/leveldb/db/db_impl.h8
-rw-r--r--src/leveldb/db/db_test.cc32
-rw-r--r--src/leveldb/db/fault_injection_test.cc554
-rw-r--r--src/leveldb/db/leveldbutil.cc (renamed from src/leveldb/db/leveldb_main.cc)0
-rw-r--r--src/leveldb/db/log_reader.cc22
-rw-r--r--src/leveldb/db/log_reader.h5
-rw-r--r--src/leveldb/db/log_test.cc101
-rw-r--r--src/leveldb/db/log_writer.cc17
-rw-r--r--src/leveldb/db/log_writer.h6
-rw-r--r--src/leveldb/db/memtable.h5
-rw-r--r--src/leveldb/db/recovery_test.cc324
-rw-r--r--src/leveldb/db/skiplist.h8
-rw-r--r--src/leveldb/db/skiplist_test.cc2
-rw-r--r--src/leveldb/db/snapshot.h1
-rw-r--r--src/leveldb/db/version_set.cc40
-rw-r--r--src/leveldb/db/version_set.h4
-rw-r--r--src/leveldb/db/write_batch_internal.h1
-rw-r--r--src/leveldb/doc/index.html2
-rw-r--r--src/leveldb/helpers/memenv/memenv.cc13
-rw-r--r--src/leveldb/helpers/memenv/memenv_test.cc13
-rw-r--r--src/leveldb/include/leveldb/cache.h11
-rw-r--r--src/leveldb/include/leveldb/db.h4
-rw-r--r--src/leveldb/include/leveldb/env.h18
-rw-r--r--src/leveldb/include/leveldb/iterator.h2
-rw-r--r--src/leveldb/include/leveldb/options.h6
-rw-r--r--src/leveldb/include/leveldb/status.h6
-rw-r--r--src/leveldb/port/atomic_pointer.h19
-rw-r--r--src/leveldb/port/port_posix.cc1
-rw-r--r--src/leveldb/table/filter_block.cc4
-rw-r--r--src/leveldb/table/format.cc3
-rw-r--r--src/leveldb/table/iterator_wrapper.h3
-rw-r--r--src/leveldb/table/table.cc2
-rw-r--r--src/leveldb/table/table_test.cc20
-rw-r--r--src/leveldb/util/arena.cc6
-rw-r--r--src/leveldb/util/arena.h10
-rw-r--r--src/leveldb/util/bloom.cc2
-rw-r--r--src/leveldb/util/bloom_test.cc3
-rw-r--r--src/leveldb/util/cache.cc136
-rw-r--r--src/leveldb/util/cache_test.cc42
-rw-r--r--src/leveldb/util/env.cc4
-rw-r--r--src/leveldb/util/env_posix.cc13
-rw-r--r--src/leveldb/util/env_win.cc37
-rw-r--r--src/leveldb/util/options.cc2
-rw-r--r--src/leveldb/util/testutil.h10
-rw-r--r--src/memusage.h2
-rw-r--r--src/merkleblock.cpp6
-rw-r--r--src/merkleblock.h4
-rw-r--r--src/miner.cpp25
-rw-r--r--src/miner.h2
-rw-r--r--src/net.cpp311
-rw-r--r--src/net.h283
-rw-r--r--src/net_processing.cpp3077
-rw-r--r--src/net_processing.h51
-rw-r--r--src/netaddress.h6
-rw-r--r--src/netbase.cpp58
-rw-r--r--src/netmessagemaker.h36
-rw-r--r--src/policy/fees.cpp179
-rw-r--r--src/policy/fees.h120
-rw-r--r--src/policy/policy.cpp58
-rw-r--r--src/policy/policy.h19
-rw-r--r--src/policy/rbf.cpp2
-rw-r--r--src/policy/rbf.h2
-rw-r--r--src/prevector.h8
-rw-r--r--src/primitives/block.cpp2
-rw-r--r--src/primitives/block.h11
-rw-r--r--src/primitives/transaction.cpp33
-rw-r--r--src/primitives/transaction.h188
-rw-r--r--src/protocol.cpp10
-rw-r--r--src/protocol.h64
-rw-r--r--src/pubkey.cpp6
-rw-r--r--src/pubkey.h21
-rw-r--r--src/qt/addressbookpage.cpp18
-rw-r--r--src/qt/addresstablemodel.cpp12
-rw-r--r--src/qt/askpassphrasedialog.cpp8
-rw-r--r--src/qt/bantablemodel.cpp7
-rw-r--r--src/qt/bantablemodel.h3
-rw-r--r--src/qt/bitcoin.cpp54
-rw-r--r--src/qt/bitcoin.qrc1
-rw-r--r--src/qt/bitcoingui.cpp168
-rw-r--r--src/qt/bitcoingui.h14
-rw-r--r--src/qt/clientmodel.cpp52
-rw-r--r--src/qt/clientmodel.h9
-rw-r--r--src/qt/coincontroldialog.cpp75
-rw-r--r--src/qt/coincontroldialog.h38
-rw-r--r--src/qt/csvmodelwriter.cpp8
-rw-r--r--src/qt/editaddressdialog.cpp18
-rw-r--r--src/qt/forms/debugwindow.ui27
-rw-r--r--src/qt/forms/modaloverlay.ui376
-rw-r--r--src/qt/forms/overviewpage.ui9
-rw-r--r--src/qt/forms/sendcoinsdialog.ui26
-rw-r--r--src/qt/guiutil.cpp73
-rw-r--r--src/qt/guiutil.h38
-rw-r--r--src/qt/intro.cpp4
-rw-r--r--src/qt/modaloverlay.cpp170
-rw-r--r--src/qt/modaloverlay.h47
-rw-r--r--src/qt/networkstyle.cpp6
-rw-r--r--src/qt/notificator.cpp12
-rw-r--r--src/qt/optionsdialog.cpp14
-rw-r--r--src/qt/optionsmodel.cpp2
-rw-r--r--src/qt/optionsmodel.h2
-rw-r--r--src/qt/overviewpage.cpp20
-rw-r--r--src/qt/overviewpage.h5
-rw-r--r--src/qt/paymentrequest.proto2
-rw-r--r--src/qt/paymentrequestplus.cpp20
-rw-r--r--src/qt/paymentrequestplus.h2
-rw-r--r--src/qt/paymentserver.cpp51
-rw-r--r--src/qt/paymentserver.h5
-rw-r--r--src/qt/peertablemodel.cpp16
-rw-r--r--src/qt/peertablemodel.h5
-rw-r--r--src/qt/platformstyle.cpp10
-rw-r--r--src/qt/qvalidatedlineedit.cpp8
-rw-r--r--src/qt/qvaluecombobox.cpp4
-rw-r--r--src/qt/receivecoinsdialog.cpp70
-rw-r--r--src/qt/receivecoinsdialog.h2
-rw-r--r--src/qt/receiverequestdialog.cpp14
-rw-r--r--src/qt/recentrequeststablemodel.cpp2
-rw-r--r--src/qt/recentrequeststablemodel.h3
-rw-r--r--src/qt/res/icons/network_disabled.pngbin0 -> 2438 bytes
-rwxr-xr-xsrc/qt/res/movies/makespinner.sh4
-rw-r--r--src/qt/res/src/connect-0.svg4
-rw-r--r--src/qt/res/src/connect-1.svg4
-rw-r--r--src/qt/res/src/connect-2.svg4
-rw-r--r--src/qt/res/src/connect-3.svg4
-rw-r--r--src/qt/res/src/connect-4.svg4
-rw-r--r--src/qt/res/src/network_disabled.svg49
-rw-r--r--src/qt/rpcconsole.cpp425
-rw-r--r--src/qt/rpcconsole.h13
-rw-r--r--src/qt/sendcoinsdialog.cpp84
-rw-r--r--src/qt/sendcoinsdialog.h2
-rw-r--r--src/qt/sendcoinsentry.cpp12
-rw-r--r--src/qt/signverifymessagedialog.cpp12
-rw-r--r--src/qt/splashscreen.cpp13
-rw-r--r--src/qt/splashscreen.h5
-rw-r--r--src/qt/test/compattests.cpp23
-rw-r--r--src/qt/test/compattests.h19
-rw-r--r--src/qt/test/rpcnestedtests.cpp132
-rw-r--r--src/qt/test/rpcnestedtests.h25
-rw-r--r--src/qt/test/test_main.cpp20
-rw-r--r--src/qt/transactiondesc.cpp31
-rw-r--r--src/qt/transactionfilterproxy.cpp8
-rw-r--r--src/qt/transactionrecord.cpp20
-rw-r--r--src/qt/transactionrecord.h12
-rw-r--r--src/qt/transactiontablemodel.cpp20
-rw-r--r--src/qt/transactionview.cpp22
-rw-r--r--src/qt/utilitydialog.cpp8
-rw-r--r--src/qt/utilitydialog.h2
-rw-r--r--src/qt/walletframe.cpp14
-rw-r--r--src/qt/walletframe.h13
-rw-r--r--src/qt/walletmodel.cpp52
-rw-r--r--src/qt/walletmodel.h20
-rw-r--r--src/qt/walletmodeltransaction.cpp8
-rw-r--r--src/qt/walletview.cpp50
-rw-r--r--src/qt/walletview.h5
-rw-r--r--src/random.cpp18
-rw-r--r--src/random.h35
-rw-r--r--src/rest.cpp19
-rw-r--r--src/reverselock.h6
-rw-r--r--src/rpc/blockchain.cpp250
-rw-r--r--src/rpc/client.cpp3
-rw-r--r--src/rpc/mining.cpp211
-rw-r--r--src/rpc/misc.cpp93
-rw-r--r--src/rpc/net.cpp105
-rw-r--r--src/rpc/protocol.cpp11
-rw-r--r--src/rpc/protocol.h2
-rw-r--r--src/rpc/rawtransaction.cpp154
-rw-r--r--src/rpc/server.cpp39
-rw-r--r--src/rpc/server.h19
-rw-r--r--src/scheduler.cpp7
-rw-r--r--src/script/bitcoinconsensus.cpp21
-rw-r--r--src/script/bitcoinconsensus.h7
-rw-r--r--src/script/interpreter.cpp92
-rw-r--r--src/script/interpreter.h14
-rw-r--r--src/script/ismine.cpp62
-rw-r--r--src/script/ismine.h11
-rw-r--r--src/script/script_error.cpp6
-rw-r--r--src/script/script_error.h5
-rw-r--r--src/script/sigcache.cpp77
-rw-r--r--src/script/sigcache.h9
-rw-r--r--src/script/sign.cpp6
-rw-r--r--src/secp256k1/.gitignore1
-rw-r--r--src/secp256k1/.travis.yml9
-rw-r--r--src/secp256k1/Makefile.am18
-rw-r--r--src/secp256k1/build-aux/m4/bitcoin_secp.m44
-rw-r--r--src/secp256k1/configure.ac30
-rw-r--r--src/secp256k1/include/secp256k1.h14
-rw-r--r--src/secp256k1/include/secp256k1_schnorr.h173
-rw-r--r--src/secp256k1/src/ecdsa_impl.h18
-rw-r--r--src/secp256k1/src/ecmult_const_impl.h2
-rw-r--r--src/secp256k1/src/ecmult_gen_impl.h2
-rw-r--r--src/secp256k1/src/ecmult_impl.h23
-rw-r--r--src/secp256k1/src/field.h7
-rw-r--r--src/secp256k1/src/field_impl.h2
-rw-r--r--src/secp256k1/src/group.h4
-rw-r--r--src/secp256k1/src/group_impl.h78
-rw-r--r--src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java34
-rw-r--r--src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java21
-rw-r--r--src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c34
-rw-r--r--src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h8
-rwxr-xr-x[-rw-r--r--]src/secp256k1/src/modules/recovery/main_impl.h4
-rw-r--r--src/secp256k1/src/modules/schnorr/Makefile.am.include10
-rw-r--r--src/secp256k1/src/modules/schnorr/main_impl.h164
-rw-r--r--src/secp256k1/src/modules/schnorr/schnorr.h20
-rw-r--r--src/secp256k1/src/modules/schnorr/schnorr_impl.h207
-rw-r--r--src/secp256k1/src/modules/schnorr/tests_impl.h175
-rw-r--r--src/secp256k1/src/scalar.h4
-rw-r--r--src/secp256k1/src/scalar_4x64_impl.h26
-rw-r--r--src/secp256k1/src/scalar_impl.h39
-rw-r--r--src/secp256k1/src/scalar_low.h15
-rw-r--r--src/secp256k1/src/scalar_low_impl.h114
-rwxr-xr-x[-rw-r--r--]src/secp256k1/src/secp256k1.c4
-rw-r--r--src/secp256k1/src/tests.c20
-rw-r--r--src/secp256k1/src/tests_exhaustive.c329
-rw-r--r--src/serialize.h559
-rw-r--r--src/streams.h185
-rw-r--r--src/support/allocators/secure.h12
-rw-r--r--src/support/lockedpool.cpp385
-rw-r--r--src/support/lockedpool.h231
-rw-r--r--src/support/pagelocker.cpp70
-rw-r--r--src/support/pagelocker.h177
-rw-r--r--src/test/Checkpoints_tests.cpp27
-rw-r--r--src/test/DoS_tests.cpp25
-rw-r--r--src/test/README.md47
-rw-r--r--src/test/addrman_tests.cpp7
-rw-r--r--src/test/allocator_tests.cpp290
-rw-r--r--src/test/base58_tests.cpp2
-rw-r--r--src/test/bctest.py145
-rwxr-xr-xsrc/test/bitcoin-util-test.py39
-rw-r--r--src/test/blockencodings_tests.cpp73
-rw-r--r--src/test/bloom_tests.cpp32
-rw-r--r--src/test/bswap_tests.cpp26
-rw-r--r--src/test/coins_tests.cpp4
-rw-r--r--src/test/crypto_tests.cpp22
-rw-r--r--src/test/cuckoocache_tests.cpp394
-rw-r--r--src/test/data/bitcoin-util-test.json172
-rw-r--r--src/test/data/blanktx.json11
-rw-r--r--src/test/data/blanktxv1.hex (renamed from src/test/data/blanktx.hex)0
-rw-r--r--src/test/data/blanktxv2.hex1
-rw-r--r--src/test/data/blanktxv2.json11
-rw-r--r--src/test/data/script_tests.json482
-rw-r--r--src/test/data/tt-delin1-out.json217
-rw-r--r--src/test/data/tt-delout1-out.json213
-rw-r--r--src/test/data/tt-locktime317000-out.json226
-rw-r--r--src/test/data/tx_invalid.json26
-rw-r--r--src/test/data/tx_valid.json43
-rw-r--r--src/test/data/txcreate1.hex2
-rw-r--r--src/test/data/txcreate1.json64
-rw-r--r--src/test/data/txcreate2.hex2
-rw-r--r--src/test/data/txcreate2.json20
-rw-r--r--src/test/data/txcreatedata1.hex2
-rw-r--r--src/test/data/txcreatedata1.json42
-rw-r--r--src/test/data/txcreatedata2.hex2
-rw-r--r--src/test/data/txcreatedata2.json42
-rw-r--r--src/test/data/txcreatedata_seq0.hex2
-rw-r--r--src/test/data/txcreatedata_seq0.json33
-rw-r--r--src/test/data/txcreatedata_seq1.json42
-rw-r--r--src/test/data/txcreatesignv1.hex (renamed from src/test/data/txcreatesign.hex)0
-rw-r--r--src/test/data/txcreatesignv1.json33
-rw-r--r--src/test/data/txcreatesignv2.hex1
-rw-r--r--src/test/dbwrapper_tests.cpp2
-rw-r--r--src/test/hash_tests.cpp6
-rw-r--r--src/test/limitedmap_tests.cpp2
-rw-r--r--src/test/main_tests.cpp3
-rw-r--r--src/test/mempool_tests.cpp72
-rw-r--r--src/test/merkle_tests.cpp10
-rw-r--r--src/test/miner_tests.cpp59
-rw-r--r--src/test/net_tests.cpp18
-rw-r--r--src/test/pmt_tests.cpp6
-rw-r--r--src/test/policyestimator_tests.cpp107
-rw-r--r--src/test/prevector_tests.cpp14
-rw-r--r--src/test/rpc_tests.cpp29
-rw-r--r--src/test/scheduler_tests.cpp4
-rw-r--r--src/test/script_P2SH_tests.cpp2
-rw-r--r--src/test/script_tests.cpp129
-rw-r--r--src/test/serialize_tests.cpp71
-rw-r--r--src/test/sighash_tests.cpp10
-rw-r--r--src/test/sigopcount_tests.cpp2
-rw-r--r--src/test/skiplist_tests.cpp2
-rw-r--r--src/test/streams_tests.cpp58
-rw-r--r--src/test/test_bitcoin.cpp25
-rw-r--r--src/test/test_bitcoin.h4
-rw-r--r--src/test/test_bitcoin_fuzzy.cpp256
-rw-r--r--src/test/test_random.h23
-rw-r--r--src/test/transaction_tests.cpp90
-rw-r--r--src/test/txvalidationcache_tests.cpp3
-rw-r--r--src/test/uint256_tests.cpp46
-rw-r--r--src/test/univalue_tests.cpp2
-rw-r--r--src/test/util_tests.cpp2
-rw-r--r--src/test/versionbits_tests.cpp108
-rw-r--r--src/timedata.cpp5
-rw-r--r--src/torcontrol.cpp56
-rw-r--r--src/txdb.h2
-rw-r--r--src/txmempool.cpp80
-rw-r--r--src/txmempool.h49
-rw-r--r--src/ui_interface.cpp5
-rw-r--r--src/ui_interface.h5
-rw-r--r--src/uint256.h9
-rw-r--r--src/undo.h26
-rw-r--r--src/univalue/.travis.yml2
-rw-r--r--src/univalue/include/univalue.h32
-rw-r--r--src/univalue/lib/univalue.cpp31
-rw-r--r--src/util.cpp32
-rw-r--r--src/util.h9
-rw-r--r--src/utiltime.cpp3
-rw-r--r--src/validation.cpp (renamed from src/main.cpp)3327
-rw-r--r--src/validation.h (renamed from src/main.h)99
-rw-r--r--src/validationinterface.cpp8
-rw-r--r--src/validationinterface.h8
-rw-r--r--src/version.h7
-rw-r--r--src/versionbits.cpp35
-rw-r--r--src/versionbits.h4
-rw-r--r--src/wallet/coincontrol.h (renamed from src/coincontrol.h)9
-rw-r--r--src/wallet/crypter.cpp14
-rw-r--r--src/wallet/crypter.h21
-rw-r--r--src/wallet/rpcdump.cpp532
-rw-r--r--src/wallet/rpcwallet.cpp558
-rw-r--r--src/wallet/test/accounting_tests.cpp28
-rw-r--r--src/wallet/test/crypto_tests.cpp46
-rw-r--r--src/wallet/test/wallet_tests.cpp78
-rw-r--r--src/wallet/wallet.cpp465
-rw-r--r--src/wallet/wallet.h97
-rw-r--r--src/wallet/walletdb.cpp80
-rw-r--r--src/wallet/walletdb.h9
-rw-r--r--src/warnings.cpp89
-rw-r--r--src/warnings.h21
-rw-r--r--src/zmq/zmqnotificationinterface.cpp9
-rw-r--r--src/zmq/zmqnotificationinterface.h2
-rw-r--r--src/zmq/zmqpublishnotifier.cpp8
533 files changed, 23425 insertions, 11319 deletions
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index a16cedd7fd..28c1814998 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -1,23 +1,37 @@
<!--- Remove sections that do not apply -->
+
+This issue tracker is only for technical issues related to bitcoin-core.
+
+General bitcoin questions and/or support requests and are best directed to the [Bitcoin StackExchange](https://bitcoin.stackexchange.com).
+
+For reporting security issues, please read instructions at [https://bitcoincore.org/en/contact/](https://bitcoincore.org/en/contact/).
+
### Describe the issue
-### Is the issue reproducible?
-#### List steps to reproduce below:
-1.
-2.
-3.
+### Can you reliably reproduce the issue?
+#### If so, please list the steps to reproduce below:
+1.
+2.
+3.
-### Expected behavior
+### Expected behaviour
Tell us what should happen
-### Actual behavior
+### Actual behaviour
Tell us what happens instead
-### Any information in the debug.log file related to this issue?
-
-### Screenshots (if available)
+### Screenshots.
+If the issue is related to the GUI, screenshots can be added to this issue via drag & drop.
-### What binary version was used (official or self compiled)
+### What version of bitcoin-core are you using?
+List the version number/commit ID, and if it is an official binary, self compiled or a distribution package such as PPA.
-### Machine specs: CPU, RAM, Disk space & OS (Windows, OS X, Linux)
+### Machine specs:
+- OS:
+- CPU:
+- RAM:
+- Disk size:
+- Disk Type (HD/SDD):
+### Any extra information that might be useful in the debugging process.
+This is normally the contents of a `debug.log` or `config.log` file. Raw text or a link to a pastebin type site are preferred.
diff --git a/.gitignore b/.gitignore
index 892b2a3e08..8ace3c7123 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@ src/bitcoind
src/bitcoin-cli
src/bitcoin-tx
src/test/test_bitcoin
+src/test/test_bitcoin_fuzzy
src/qt/test/test_bitcoin-qt
# autoreconf
@@ -55,7 +56,6 @@ src/qt/test/moc*.cpp
*.o
*.o-*
*.patch
-.bitcoin
*.a
*.pb.cc
*.pb.h
@@ -101,12 +101,8 @@ coverage_percent.txt
linux-coverage-build
linux-build
win32-build
-qa/pull-tester/run-bitcoind-for-test.sh
qa/pull-tester/tests_config.py
-qa/pull-tester/test.*/*
-qa/tmp
qa/cache/*
-share/BitcoindComparisonTool.jar
!src/leveldb*/Makefile
diff --git a/.travis.yml b/.travis.yml
index 0b38fd45fe..6570e6571b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -34,7 +34,7 @@ env:
# No wallet
- HOST=x86_64-unknown-linux-gnu PACKAGES="python3" DEP_OPTS="NO_WALLET=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports"
# Cross-Mac
- - HOST=x86_64-apple-darwin11 PACKAGES="cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev" BITCOIN_CONFIG="--enable-reduce-exports" OSX_SDK=10.11 GOAL="deploy"
+ - HOST=x86_64-apple-darwin11 PACKAGES="cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev" BITCOIN_CONFIG="--enable-gui --enable-reduce-exports" OSX_SDK=10.11 GOAL="deploy"
before_install:
- export PATH=$(echo $PATH | tr ':' "\n" | sed '/\/opt\/python/d' | tr "\n" ":" | sed "s|::|:|g")
@@ -60,6 +60,7 @@ script:
- mkdir build && cd build
- ../configure $BITCOIN_CONFIG_ALL $BITCOIN_CONFIG || ( cat config.log && false)
- make $MAKEJOBS $GOAL || ( echo "Build failure. Verbose build follows." && make $GOAL V=1 ; false )
+ - make distdir
- export LD_LIBRARY_PATH=$TRAVIS_BUILD_DIR/depends/$HOST/lib
- if [ "$RUN_TESTS" = "true" ]; then make $MAKEJOBS check VERBOSE=1; fi
- if [ "$RUN_TESTS" = "true" ]; then qa/pull-tester/rpc-tests.py --coverage; fi
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 62ebc7917a..06fcd8dd81 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -87,6 +87,15 @@ before it will be merged. The basic squashing workflow is shown below.
# save and quit
git push -f # (force push to GitHub)
+If you have problems with squashing (or other workflows with `git`), you can
+alternatively enable "Allow edits from maintainers" in the right GitHub
+sidebar and ask for help in the pull request.
+
+Please refrain from creating several pull requests for the same change.
+Use the pull request that is already open (or was created earlier) to amend
+changes. This preserves the discussion and review that happened earlier for
+the respective change set.
+
The length of time required for peer review is unpredictable and will vary from
pull request to pull request.
@@ -169,7 +178,9 @@ language is used within pull-request comments:
- ACK means "I have tested the code and I agree it should be merged";
- NACK means "I disagree this should be merged", and must be accompanied by
- sound technical justification. NACKs without accompanying reasoning may be disregarded;
+ sound technical justification (or in certain cases of copyright/patent/licensing
+ issues, legal justification). NACKs without accompanying reasoning may be
+ disregarded;
- utACK means "I have not tested the code, but I have reviewed it and it looks
OK, I agree it can be merged";
- Concept ACK means "I agree in the general principle of this pull request";
@@ -198,3 +209,11 @@ Release Policy
--------------
The project leader is the release manager for each Bitcoin Core release.
+
+Copyright
+---------
+
+By contributing to this repository, you agree to license your work under the
+MIT license unless specified otherwise in `contrib/debian/copyright` or at
+the top of the file itself. Any work contributed where you are not the original
+author must contain its license header with the original author(s) and source.
diff --git a/INSTALL b/INSTALL
deleted file mode 100644
index 07ee48427c..0000000000
--- a/INSTALL
+++ /dev/null
@@ -1,5 +0,0 @@
-Building Bitcoin
-
-See doc/build-*.md for instructions on building bitcoind,
-the intended-for-services, no-graphical-interface, reference
-implementation of Bitcoin. \ No newline at end of file
diff --git a/INSTALL.md b/INSTALL.md
new file mode 100644
index 0000000000..520a47d960
--- /dev/null
+++ b/INSTALL.md
@@ -0,0 +1,5 @@
+Building Bitcoin
+================
+
+See doc/build-*.md for instructions on building the various
+elements of the Bitcoin Core reference implementation of Bitcoin.
diff --git a/Makefile.am b/Makefile.am
index 2e061c3773..76eba51906 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,3 +1,7 @@
+# Copyright (c) 2013-2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
ACLOCAL_AMFLAGS = -I build-aux/m4
SUBDIRS = src
if ENABLE_MAN
@@ -35,6 +39,11 @@ OSX_PLIST=$(top_builddir)/share/qt/Info.plist #not installed
OSX_QT_TRANSLATIONS = da,de,es,hu,ru,uk,zh_CN,zh_TW
DIST_DOCS = $(wildcard doc/*.md) $(wildcard doc/release-notes/*.md)
+DIST_CONTRIB = $(top_srcdir)/contrib/bitcoin-cli.bash-completion \
+ $(top_srcdir)/contrib/bitcoin-tx.bash-completion \
+ $(top_srcdir)/contrib/bitcoind.bash-completion \
+ $(top_srcdir)/contrib/init \
+ $(top_srcdir)/contrib/rpm
BIN_CHECKS=$(top_srcdir)/contrib/devtools/symbol-check.py \
$(top_srcdir)/contrib/devtools/security-check.py
@@ -207,7 +216,7 @@ endif
dist_noinst_SCRIPTS = autogen.sh
-EXTRA_DIST = $(top_srcdir)/share/genbuild.sh qa/pull-tester/rpc-tests.py qa/rpc-tests $(DIST_DOCS) $(WINDOWS_PACKAGING) $(OSX_PACKAGING) $(BIN_CHECKS)
+EXTRA_DIST = $(top_srcdir)/share/genbuild.sh qa/pull-tester/rpc-tests.py qa/rpc-tests $(DIST_CONTRIB) $(DIST_DOCS) $(WINDOWS_PACKAGING) $(OSX_PACKAGING) $(BIN_CHECKS)
CLEANFILES = $(OSX_DMG) $(BITCOIN_WIN_INSTALLER)
diff --git a/README.md b/README.md
index 3c41649c1b..38a90dde49 100644
--- a/README.md
+++ b/README.md
@@ -49,9 +49,10 @@ lots of money.
### Automated Testing
-Developers are strongly encouraged to write [unit tests](/doc/unit-tests.md) for new code, and to
+Developers are strongly encouraged to write [unit tests](src/test/README.md) for new code, and to
submit new unit tests for old code. Unit tests can be compiled and run
-(assuming they weren't disabled in configure) with: `make check`
+(assuming they weren't disabled in configure) with: `make check`. Further details on running
+and extending unit tests can be found in [/src/test/README.md](/src/test/README.md).
There are also [regression and integration tests](/qa) of the RPC interface, written
in Python, that are run automatically on the build server.
diff --git a/autogen.sh b/autogen.sh
index 46e36ff5b2..27417daf76 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -1,4 +1,8 @@
#!/bin/sh
+# Copyright (c) 2013-2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
set -e
srcdir="$(dirname $0)"
cd "$srcdir"
diff --git a/build-aux/m4/ax_boost_base.m4 b/build-aux/m4/ax_boost_base.m4
index 45d948933d..650c94fa64 100644
--- a/build-aux/m4/ax_boost_base.m4
+++ b/build-aux/m4/ax_boost_base.m4
@@ -33,7 +33,7 @@
# and this notice are preserved. This file is offered as-is, without any
# warranty.
-#serial 26
+#serial 27
AC_DEFUN([AX_BOOST_BASE],
[
@@ -96,7 +96,7 @@ if test "x$want_boost" = "xyes"; then
libsubdirs="lib64 libx32 lib lib64"
;;
ppc64|s390x|sparc64|aarch64|ppc64le)
- libsubdirs="lib64 lib lib64 ppc64le"
+ libsubdirs="lib64 lib lib64"
;;
esac
diff --git a/build-aux/m4/bitcoin_find_bdb48.m4 b/build-aux/m4/bitcoin_find_bdb48.m4
index 2aa493a6af..0c3d49c2bc 100644
--- a/build-aux/m4/bitcoin_find_bdb48.m4
+++ b/build-aux/m4/bitcoin_find_bdb48.m4
@@ -1,3 +1,7 @@
+dnl Copyright (c) 2013-2015 The Bitcoin Core developers
+dnl Distributed under the MIT software license, see the accompanying
+dnl file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
AC_DEFUN([BITCOIN_FIND_BDB48],[
AC_MSG_CHECKING([for Berkeley DB C++ headers])
BDB_CPPFLAGS=
diff --git a/build-aux/m4/bitcoin_qt.m4 b/build-aux/m4/bitcoin_qt.m4
index d26136cbe9..509283a0b9 100644
--- a/build-aux/m4/bitcoin_qt.m4
+++ b/build-aux/m4/bitcoin_qt.m4
@@ -1,3 +1,7 @@
+dnl Copyright (c) 2013-2016 The Bitcoin Core developers
+dnl Distributed under the MIT software license, see the accompanying
+dnl file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
dnl Helper for cases where a qt dependency is not met.
dnl Output: If qt version is auto, set bitcoin_enable_qt to false. Else, exit.
AC_DEFUN([BITCOIN_QT_FAIL],[
diff --git a/build-aux/m4/bitcoin_subdir_to_include.m4 b/build-aux/m4/bitcoin_subdir_to_include.m4
index 66f106c7d4..7841042ac8 100644
--- a/build-aux/m4/bitcoin_subdir_to_include.m4
+++ b/build-aux/m4/bitcoin_subdir_to_include.m4
@@ -1,3 +1,7 @@
+dnl Copyright (c) 2013-2014 The Bitcoin Core developers
+dnl Distributed under the MIT software license, see the accompanying
+dnl file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
dnl BITCOIN_SUBDIR_TO_INCLUDE([CPPFLAGS-VARIABLE-NAME],[SUBDIRECTORY-NAME],[HEADER-FILE])
dnl SUBDIRECTORY-NAME must end with a path separator
AC_DEFUN([BITCOIN_SUBDIR_TO_INCLUDE],[
diff --git a/build-aux/m4/l_atomic.m4 b/build-aux/m4/l_atomic.m4
index 906724b640..75c43f9a92 100644
--- a/build-aux/m4/l_atomic.m4
+++ b/build-aux/m4/l_atomic.m4
@@ -1,3 +1,9 @@
+dnl Copyright (c) 2015 Tim Kosse <tim.kosse@filezilla-project.org>
+dnl Copying and distribution of this file, with or without modification, are
+dnl permitted in any medium without royalty provided the copyright notice
+dnl and this notice are preserved. This file is offered as-is, without any
+dnl warranty.
+
# Some versions of gcc/libstdc++ require linking with -latomic if
# using the C++ atomic library.
#
@@ -32,7 +38,7 @@ AC_DEFUN([CHECK_ATOMIC], [
AC_MSG_RESULT([yes])
],[
AC_MSG_RESULT([no])
- AC_MSG_FAILURE([cannot figure our how to use std::atomic])
+ AC_MSG_FAILURE([cannot figure out how to use std::atomic])
])
])
diff --git a/configure.ac b/configure.ac
index 25b828f18c..ced258e02e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -9,7 +9,7 @@ define(_COPYRIGHT_YEAR, 2016)
define(_COPYRIGHT_HOLDERS,[The %s developers])
define(_COPYRIGHT_HOLDERS_SUBSTITUTION,[[Bitcoin Core]])
AC_INIT([Bitcoin Core],[_CLIENT_VERSION_MAJOR._CLIENT_VERSION_MINOR._CLIENT_VERSION_REVISION],[https://github.com/bitcoin/bitcoin/issues],[bitcoin],[https://bitcoincore.org/])
-AC_CONFIG_SRCDIR([src/main.cpp])
+AC_CONFIG_SRCDIR([src/validation.cpp])
AC_CONFIG_HEADERS([src/config/bitcoin-config.h])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([build-aux/m4])
@@ -45,7 +45,6 @@ else
CXXFLAGS_overridden=no
fi
AC_PROG_CXX
-m4_ifdef([AC_PROG_OBJCXX],[AC_PROG_OBJCXX])
dnl By default, libtool for mingw refuses to link static libs into a dll for
dnl fear of mixing pic/non-pic objects, and import/export complications. Since
@@ -60,6 +59,15 @@ AX_CXX_COMPILE_STDCXX([11], [noext], [mandatory])
dnl Check if -latomic is required for <std::atomic>
CHECK_ATOMIC
+dnl Unless the user specified OBJCXX, force it to be the same as CXX. This ensures
+dnl that we get the same -std flags for both.
+m4_ifdef([AC_PROG_OBJCXX],[
+if test "x${OBJCXX+set}" = "x"; then
+ OBJCXX="${CXX}"
+fi
+AC_PROG_OBJCXX
+])
+
dnl Libtool init checks.
LT_INIT([pic-only])
@@ -482,6 +490,7 @@ if test x$use_hardening != xno; then
AX_CHECK_LINK_FLAG([[-Wl,--dynamicbase]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,--dynamicbase"])
AX_CHECK_LINK_FLAG([[-Wl,--nxcompat]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,--nxcompat"])
+ AX_CHECK_LINK_FLAG([[-Wl,--high-entropy-va]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,--high-entropy-va"])
AX_CHECK_LINK_FLAG([[-Wl,-z,relro]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,-z,relro"])
AX_CHECK_LINK_FLAG([[-Wl,-z,now]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,-z,now"])
@@ -503,11 +512,12 @@ if test x$TARGET_OS = xdarwin; then
fi
AC_CHECK_HEADERS([endian.h sys/endian.h byteswap.h stdio.h stdlib.h unistd.h strings.h sys/types.h sys/stat.h sys/select.h sys/prctl.h])
-AC_SEARCH_LIBS([getaddrinfo_a], [anl], [AC_DEFINE(HAVE_GETADDRINFO_A, 1, [Define this symbol if you have getaddrinfo_a])])
-AC_SEARCH_LIBS([inet_pton], [nsl resolv], [AC_DEFINE(HAVE_INET_PTON, 1, [Define this symbol if you have inet_pton])])
AC_CHECK_DECLS([strnlen])
+# Check for daemon(3), unrelated to --with-daemon (although used by it)
+AC_CHECK_DECLS([daemon])
+
AC_CHECK_DECLS([le16toh, le32toh, le64toh, htole16, htole32, htole64, be16toh, be32toh, be64toh, htobe16, htobe32, htobe64],,,
[#if HAVE_ENDIAN_H
#include <endian.h>
@@ -585,8 +595,11 @@ fi
if test x$use_boost = xyes; then
+dnl Minimum required Boost version
+define(MINIMUM_REQUIRED_BOOST, 1.47.0)
+
dnl Check for boost libs
-AX_BOOST_BASE
+AX_BOOST_BASE([MINIMUM_REQUIRED_BOOST])
AX_BOOST_SYSTEM
AX_BOOST_FILESYSTEM
AX_BOOST_PROGRAM_OPTIONS
@@ -1064,7 +1077,6 @@ AC_SUBST(ZMQ_LIBS)
AC_SUBST(PROTOBUF_LIBS)
AC_SUBST(QR_LIBS)
AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile share/setup.nsi share/qt/Info.plist src/test/buildenv.py])
-AC_CONFIG_FILES([qa/pull-tester/run-bitcoind-for-test.sh],[chmod +x qa/pull-tester/run-bitcoind-for-test.sh])
AC_CONFIG_FILES([qa/pull-tester/tests_config.py],[chmod +x qa/pull-tester/tests_config.py])
AC_CONFIG_FILES([contrib/devtools/split-debug.sh],[chmod +x contrib/devtools/split-debug.sh])
AC_CONFIG_LINKS([qa/pull-tester/rpc-tests.py:qa/pull-tester/rpc-tests.py])
@@ -1119,3 +1131,28 @@ case ${OS} in
mv qa/pull-tester/tests_config-2.py qa/pull-tester/tests_config.py
;;
esac
+
+echo
+echo "Options used to compile and link:"
+echo " with wallet = $enable_wallet"
+echo " with gui / qt = $bitcoin_enable_qt"
+if test x$bitcoin_enable_qt != xno; then
+ echo " qt version = $bitcoin_qt_got_major_vers"
+ echo " with qr = $use_qr"
+fi
+echo " with zmq = $use_zmq"
+echo " with test = $use_tests"
+echo " with bench = $use_bench"
+echo " with upnp = $use_upnp"
+echo " debug enabled = $enable_debug"
+echo
+echo " target os = $TARGET_OS"
+echo " build os = $BUILD_OS"
+echo
+echo " CC = $CC"
+echo " CFLAGS = $CFLAGS"
+echo " CPPFLAGS = $CPPFLAGS"
+echo " CXX = $CXX"
+echo " CXXFLAGS = $CXXFLAGS"
+echo " LDFLAGS = $LDFLAGS"
+echo
diff --git a/contrib/README.md b/contrib/README.md
index 3e3c83da5f..4ea9700f59 100644
--- a/contrib/README.md
+++ b/contrib/README.md
@@ -1,11 +1,3 @@
-Wallet Tools
----------------------
-
-### [SpendFrom](/contrib/spendfrom) ###
-
-Use the raw transactions API to send coins received on a particular
-address (or addresses).
-
Repository Tools
---------------------
@@ -46,7 +38,7 @@ Scripts and notes for Mac builds.
RPM spec file for building bitcoin-core on RPM based distributions
### [Gitian-build](/contrib/gitian-build.sh) ###
-Script for running full gitian builds.
+Script for running full Gitian builds.
Test and Verify Tools
---------------------
diff --git a/contrib/debian/bitcoin-qt.desktop b/contrib/debian/bitcoin-qt.desktop
index 61e1aca6ad..593d7584ab 100644
--- a/contrib/debian/bitcoin-qt.desktop
+++ b/contrib/debian/bitcoin-qt.desktop
@@ -1,7 +1,8 @@
[Desktop Entry]
Encoding=UTF-8
-Name=Bitcoin
-Comment=Bitcoin P2P Cryptocurrency
+Name=Bitcoin Core
+Comment=Connect to the Bitcoin P2P Network
+Comment[de]=Verbinde mit dem Bitcoin peer-to-peer Netzwerk
Comment[fr]=Bitcoin, monnaie virtuelle cryptographique pair à pair
Comment[tr]=Bitcoin, eÅŸten eÅŸe kriptografik sanal para birimi
Exec=bitcoin-qt %u
diff --git a/contrib/debian/bitcoin-qt.manpages b/contrib/debian/bitcoin-qt.manpages
new file mode 100644
index 0000000000..9a3cc31c09
--- /dev/null
+++ b/contrib/debian/bitcoin-qt.manpages
@@ -0,0 +1 @@
+doc/man/bitcoin-qt.1
diff --git a/contrib/debian/bitcoin-tx.manpages b/contrib/debian/bitcoin-tx.manpages
new file mode 100644
index 0000000000..861d49d070
--- /dev/null
+++ b/contrib/debian/bitcoin-tx.manpages
@@ -0,0 +1 @@
+doc/man/bitcoin-tx.1
diff --git a/contrib/debian/bitcoind.manpages b/contrib/debian/bitcoind.manpages
index 6d3e683855..bab644ece1 100644
--- a/contrib/debian/bitcoind.manpages
+++ b/contrib/debian/bitcoind.manpages
@@ -1,3 +1,2 @@
-debian/manpages/bitcoind.1
-debian/manpages/bitcoin.conf.5
-debian/manpages/bitcoin-cli.1
+doc/man/bitcoind.1
+doc/man/bitcoin-cli.1
diff --git a/contrib/debian/copyright b/contrib/debian/copyright
index cc4606ca88..0fa06f1aa9 100644
--- a/contrib/debian/copyright
+++ b/contrib/debian/copyright
@@ -15,10 +15,6 @@ Copyright: 2010-2011, Jonas Smedegaard <dr@jones.dk>
2011, Matt Corallo <matt@bluematt.me>
License: GPL-2+
-Files: debian/manpages/*
-Copyright: Micah Anderson <micah@debian.org>
-License: GPL-3+
-
Files: src/qt/res/icons/add.png
src/qt/res/icons/address-book.png
src/qt/res/icons/chevron.png
@@ -51,7 +47,10 @@ Comment: Site: https://github.com/stephenhutchings/typicons.font
Files: src/qt/res/icons/connect*.png
src/qt/res/src/connect-*.svg
+ src/qt/res/icons/network_disabled.png
+ src/qt/res/src/network_disabled.svg
Copyright: Marco Falke
+ Luke Dashjr
License: Expat
Comment: Inspired by Stephan Hutchings Typicons
diff --git a/contrib/debian/manpages/bitcoin-cli.1 b/contrib/debian/manpages/bitcoin-cli.1
deleted file mode 100644
index 16c338dd3e..0000000000
--- a/contrib/debian/manpages/bitcoin-cli.1
+++ /dev/null
@@ -1,21 +0,0 @@
-.TH BITCOIN-CLI "1" "February 2016" "bitcoin-cli 0.12"
-.SH NAME
-bitcoin-cli \- a remote procedure call client for Bitcoin Core.
-.SH SYNOPSIS
-bitcoin-cli [options] <command> [params] \- Send command to Bitcoin Core.
-.TP
-bitcoin-cli [options] help \- Asks Bitcoin Core for a list of supported commands.
-.SH DESCRIPTION
-This manual page documents the bitcoin-cli program. bitcoin-cli is an RPC client used to send commands to Bitcoin Core.
-
-.SH OPTIONS
-.TP
-\fB\-?\fR
-Show possible options.
-
-.SH "SEE ALSO"
-\fBbitcoind\fP, \fBbitcoin.conf\fP
-.SH AUTHOR
-This manual page was written by Ciemon Dunville <ciemon@gmail.com>. Permission is granted to copy, distribute and/or modify this document under the terms of the MIT License.
-
-The complete text of the MIT License can be found on the web at \fIhttp://opensource.org/licenses/MIT\fP.
diff --git a/contrib/debian/manpages/bitcoin-qt.1 b/contrib/debian/manpages/bitcoin-qt.1
deleted file mode 100644
index 685a282080..0000000000
--- a/contrib/debian/manpages/bitcoin-qt.1
+++ /dev/null
@@ -1,13 +0,0 @@
-.TH BITCOIN-QT "1" "February 2016" "bitcoin-qt 0.12"
-.SH NAME
-bitcoin-qt \- peer-to-peer network based digital currency
-.SH DESCRIPTION
-.SS "Usage:"
-.IP
-bitcoin\-qt [command\-line options]
-.SH OPTIONS
-.TP
-\-?
-List options.
-.SH "SEE ALSO"
-bitcoind(1)
diff --git a/contrib/debian/manpages/bitcoin.conf.5 b/contrib/debian/manpages/bitcoin.conf.5
deleted file mode 100644
index 839dc26c1a..0000000000
--- a/contrib/debian/manpages/bitcoin.conf.5
+++ /dev/null
@@ -1,19 +0,0 @@
-.TH BITCOIN.CONF "5" "February 2016" "bitcoin.conf 0.12"
-.SH NAME
-bitcoin.conf \- bitcoin configuration file
-.SH SYNOPSIS
-All command-line options (except for '\-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. Please refer to bitcoind(1) for a up to date list of valid options.
-.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 "SEE ALSO"
-bitcoind(1)
-.SH AUTHOR
-This manual page was written by Micah Anderson <micah@debian.org> 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
deleted file mode 100644
index 5c3e52f441..0000000000
--- a/contrib/debian/manpages/bitcoind.1
+++ /dev/null
@@ -1,30 +0,0 @@
-.TH BITCOIND "1" "February 2016" "bitcoind 0.12"
-.SH NAME
-bitcoind \- peer-to-peer network based digital currency
-.SH SYNOPSIS
-bitcoin [options] <command> [params]
-.TP
-bitcoin [options] help <command> \- Get help for a command
-.SH DESCRIPTION
-This manual page documents the bitcoind program. Bitcoin is an experimental new digital currency that enables instant payments to anyone, anywhere in the world. Bitcoin uses peer-to-peer technology to operate with no central authority: managing transactions and issuing money are carried out collectively by the network. Bitcoin Core is the name of open source software which enables the use of this currency.
-
-.SH OPTIONS
-.TP
-\-?
-List of possible options.
-.SH COMMANDS
-.TP
-\fBhelp\fR
-List commands.
-
-.TP
-\fBhelp 'command'\fR
-Get help for a command.
-
-.SH "SEE ALSO"
-bitcoin.conf(5)
-.SH AUTHOR
-This manual page was written by Micah Anderson <micah@debian.org> 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/rules b/contrib/debian/rules
index 52b357cf01..3896d2caa3 100755
--- a/contrib/debian/rules
+++ b/contrib/debian/rules
@@ -5,9 +5,6 @@
#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 $@
diff --git a/contrib/devtools/README.md b/contrib/devtools/README.md
index 60fe69e7e3..6c0047833f 100644
--- a/contrib/devtools/README.md
+++ b/contrib/devtools/README.md
@@ -24,21 +24,64 @@ the script should be called from the git root folder as follows.
```
git diff -U0 HEAD~1.. | ./contrib/devtools/clang-format-diff.py -p1 -i -v
```
+copyright\_header.py
+====================
-fix-copyright-headers.py
-========================
+Provides utilities for managing copyright headers of `The Bitcoin Core
+developers` in repository source files. It has three subcommands:
-Every year newly updated files need to have its copyright headers updated to reflect the current year.
-If you run this script from the root folder it will automatically update the year on the copyright header for all
-source files if these have a git commit from the current year.
+```
+$ ./copyright_header.py report <base_directory> [verbose]
+$ ./copyright_header.py update <base_directory>
+$ ./copyright_header.py insert <file>
+```
+Running these subcommands without arguments displays a usage string.
-For example a file changed in 2015 (with 2015 being the current year):
+copyright\_header.py report \<base\_directory\> [verbose]
+---------------------------------------------------------
-```// Copyright (c) 2009-2013 The Bitcoin Core developers```
+Produces a report of all copyright header notices found inside the source files
+of a repository. Useful to quickly visualize the state of the headers.
+Specifying `verbose` will list the full filenames of files of each category.
-would be changed to:
+copyright\_header.py update \<base\_directory\> [verbose]
+---------------------------------------------------------
+Updates all the copyright headers of `The Bitcoin Core developers` which were
+changed in a year more recent than is listed. For example:
+```
+// Copyright (c) <firstYear>-<lastYear> The Bitcoin Core developers
+```
+will be updated to:
+```
+// Copyright (c) <firstYear>-<lastModifiedYear> The Bitcoin Core developers
+```
+where `<lastModifiedYear>` is obtained from the `git log` history.
-```// Copyright (c) 2009-2015 The Bitcoin Core developers```
+This subcommand also handles copyright headers that have only a single year. In
+those cases:
+```
+// Copyright (c) <year> The Bitcoin Core developers
+```
+will be updated to:
+```
+// Copyright (c) <year>-<lastModifiedYear> The Bitcoin Core developers
+```
+where the update is appropriate.
+
+copyright\_header.py insert \<file\>
+------------------------------------
+Inserts a copyright header for `The Bitcoin Core developers` at the top of the
+file in either Python or C++ style as determined by the file extension. If the
+file is a Python file and it has `#!` starting the first line, the header is
+inserted in the line below it.
+
+The copyright dates will be set to be `<year_introduced>-<current_year>` where
+`<year_introduced>` is according to the `git log` history. If
+`<year_introduced>` is equal to `<current_year>`, it will be set as a single
+year rather than two hyphenated years.
+
+If the file already has a copyright for `The Bitcoin Core developers`, the
+script will exit.
gen-manpages.sh
===============
diff --git a/contrib/devtools/copyright_header.py b/contrib/devtools/copyright_header.py
new file mode 100755
index 0000000000..9f35c378bf
--- /dev/null
+++ b/contrib/devtools/copyright_header.py
@@ -0,0 +1,610 @@
+#!/usr/bin/env python3
+# Copyright (c) 2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+import re
+import fnmatch
+import sys
+import subprocess
+import datetime
+import os
+
+################################################################################
+# file filtering
+################################################################################
+
+EXCLUDE = [
+ # libsecp256k1:
+ 'src/secp256k1/include/secp256k1.h',
+ 'src/secp256k1/include/secp256k1_ecdh.h',
+ 'src/secp256k1/include/secp256k1_recovery.h',
+ 'src/secp256k1/include/secp256k1_schnorr.h',
+ 'src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c',
+ 'src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h',
+ 'src/secp256k1/src/java/org_bitcoin_Secp256k1Context.c',
+ 'src/secp256k1/src/java/org_bitcoin_Secp256k1Context.h',
+ # auto generated:
+ 'src/univalue/lib/univalue_escapes.h',
+ 'src/qt/bitcoinstrings.cpp',
+ 'src/chainparamsseeds.h',
+ # other external copyrights:
+ 'src/tinyformat.h',
+ 'src/leveldb/util/env_win.cc',
+ 'src/crypto/ctaes/bench.c',
+ 'qa/rpc-tests/test_framework/bignum.py',
+ # python init:
+ '*__init__.py',
+]
+EXCLUDE_COMPILED = re.compile('|'.join([fnmatch.translate(m) for m in EXCLUDE]))
+
+INCLUDE = ['*.h', '*.cpp', '*.cc', '*.c', '*.py']
+INCLUDE_COMPILED = re.compile('|'.join([fnmatch.translate(m) for m in INCLUDE]))
+
+def applies_to_file(filename):
+ return ((EXCLUDE_COMPILED.match(filename) is None) and
+ (INCLUDE_COMPILED.match(filename) is not None))
+
+################################################################################
+# obtain list of files in repo according to INCLUDE and EXCLUDE
+################################################################################
+
+GIT_LS_CMD = 'git ls-files'
+
+def call_git_ls():
+ out = subprocess.check_output(GIT_LS_CMD.split(' '))
+ return [f for f in out.decode("utf-8").split('\n') if f != '']
+
+def get_filenames_to_examine():
+ filenames = call_git_ls()
+ return sorted([filename for filename in filenames if
+ applies_to_file(filename)])
+
+################################################################################
+# define and compile regexes for the patterns we are looking for
+################################################################################
+
+
+COPYRIGHT_WITH_C = 'Copyright \(c\)'
+COPYRIGHT_WITHOUT_C = 'Copyright'
+ANY_COPYRIGHT_STYLE = '(%s|%s)' % (COPYRIGHT_WITH_C, COPYRIGHT_WITHOUT_C)
+
+YEAR = "20[0-9][0-9]"
+YEAR_RANGE = '(%s)(-%s)?' % (YEAR, YEAR)
+YEAR_LIST = '(%s)(, %s)+' % (YEAR, YEAR)
+ANY_YEAR_STYLE = '(%s|%s)' % (YEAR_RANGE, YEAR_LIST)
+ANY_COPYRIGHT_STYLE_OR_YEAR_STYLE = ("%s %s" % (ANY_COPYRIGHT_STYLE,
+ ANY_YEAR_STYLE))
+
+ANY_COPYRIGHT_COMPILED = re.compile(ANY_COPYRIGHT_STYLE_OR_YEAR_STYLE)
+
+def compile_copyright_regex(copyright_style, year_style, name):
+ return re.compile('%s %s %s' % (copyright_style, year_style, name))
+
+EXPECTED_HOLDER_NAMES = [
+ "Satoshi Nakamoto\n",
+ "The Bitcoin Core developers\n",
+ "The Bitcoin Core developers \n",
+ "Bitcoin Core Developers\n",
+ "the Bitcoin Core developers\n",
+ "The Bitcoin developers\n",
+ "The LevelDB Authors\. All rights reserved\.\n",
+ "BitPay Inc\.\n",
+ "BitPay, Inc\.\n",
+ "University of Illinois at Urbana-Champaign\.\n",
+ "MarcoFalke\n",
+ "Pieter Wuille\n",
+ "Pieter Wuille +\*\n",
+ "Pieter Wuille, Gregory Maxwell +\*\n",
+ "Pieter Wuille, Andrew Poelstra +\*\n",
+ "Andrew Poelstra +\*\n",
+ "Wladimir J. van der Laan\n",
+ "Jeff Garzik\n",
+ "Diederik Huys, Pieter Wuille +\*\n",
+ "Thomas Daede, Cory Fields +\*\n",
+ "Jan-Klaas Kollhof\n",
+ "Sam Rushing\n",
+ "ArtForz -- public domain half-a-node\n",
+]
+
+DOMINANT_STYLE_COMPILED = {}
+YEAR_LIST_STYLE_COMPILED = {}
+WITHOUT_C_STYLE_COMPILED = {}
+
+for holder_name in EXPECTED_HOLDER_NAMES:
+ DOMINANT_STYLE_COMPILED[holder_name] = (
+ compile_copyright_regex(COPYRIGHT_WITH_C, YEAR_RANGE, holder_name))
+ YEAR_LIST_STYLE_COMPILED[holder_name] = (
+ compile_copyright_regex(COPYRIGHT_WITH_C, YEAR_LIST, holder_name))
+ WITHOUT_C_STYLE_COMPILED[holder_name] = (
+ compile_copyright_regex(COPYRIGHT_WITHOUT_C, ANY_YEAR_STYLE,
+ holder_name))
+
+################################################################################
+# search file contents for copyright message of particular category
+################################################################################
+
+def get_count_of_copyrights_of_any_style_any_holder(contents):
+ return len(ANY_COPYRIGHT_COMPILED.findall(contents))
+
+def file_has_dominant_style_copyright_for_holder(contents, holder_name):
+ match = DOMINANT_STYLE_COMPILED[holder_name].search(contents)
+ return match is not None
+
+def file_has_year_list_style_copyright_for_holder(contents, holder_name):
+ match = YEAR_LIST_STYLE_COMPILED[holder_name].search(contents)
+ return match is not None
+
+def file_has_without_c_style_copyright_for_holder(contents, holder_name):
+ match = WITHOUT_C_STYLE_COMPILED[holder_name].search(contents)
+ return match is not None
+
+################################################################################
+# get file info
+################################################################################
+
+def read_file(filename):
+ return open(os.path.abspath(filename), 'r').read()
+
+def gather_file_info(filename):
+ info = {}
+ info['filename'] = filename
+ c = read_file(filename)
+ info['contents'] = c
+
+ info['all_copyrights'] = get_count_of_copyrights_of_any_style_any_holder(c)
+
+ info['classified_copyrights'] = 0
+ info['dominant_style'] = {}
+ info['year_list_style'] = {}
+ info['without_c_style'] = {}
+ for holder_name in EXPECTED_HOLDER_NAMES:
+ has_dominant_style = (
+ file_has_dominant_style_copyright_for_holder(c, holder_name))
+ has_year_list_style = (
+ file_has_year_list_style_copyright_for_holder(c, holder_name))
+ has_without_c_style = (
+ file_has_without_c_style_copyright_for_holder(c, holder_name))
+ info['dominant_style'][holder_name] = has_dominant_style
+ info['year_list_style'][holder_name] = has_year_list_style
+ info['without_c_style'][holder_name] = has_without_c_style
+ if has_dominant_style or has_year_list_style or has_without_c_style:
+ info['classified_copyrights'] = info['classified_copyrights'] + 1
+ return info
+
+################################################################################
+# report execution
+################################################################################
+
+SEPARATOR = '-'.join(['' for _ in range(80)])
+
+def print_filenames(filenames, verbose):
+ if not verbose:
+ return
+ for filename in filenames:
+ print("\t%s" % filename)
+
+def print_report(file_infos, verbose):
+ print(SEPARATOR)
+ examined = [i['filename'] for i in file_infos]
+ print("%d files examined according to INCLUDE and EXCLUDE fnmatch rules" %
+ len(examined))
+ print_filenames(examined, verbose)
+
+ print(SEPARATOR)
+ print('')
+ zero_copyrights = [i['filename'] for i in file_infos if
+ i['all_copyrights'] == 0]
+ print("%4d with zero copyrights" % len(zero_copyrights))
+ print_filenames(zero_copyrights, verbose)
+ one_copyright = [i['filename'] for i in file_infos if
+ i['all_copyrights'] == 1]
+ print("%4d with one copyright" % len(one_copyright))
+ print_filenames(one_copyright, verbose)
+ two_copyrights = [i['filename'] for i in file_infos if
+ i['all_copyrights'] == 2]
+ print("%4d with two copyrights" % len(two_copyrights))
+ print_filenames(two_copyrights, verbose)
+ three_copyrights = [i['filename'] for i in file_infos if
+ i['all_copyrights'] == 3]
+ print("%4d with three copyrights" % len(three_copyrights))
+ print_filenames(three_copyrights, verbose)
+ four_or_more_copyrights = [i['filename'] for i in file_infos if
+ i['all_copyrights'] >= 4]
+ print("%4d with four or more copyrights" % len(four_or_more_copyrights))
+ print_filenames(four_or_more_copyrights, verbose)
+ print('')
+ print(SEPARATOR)
+ print('Copyrights with dominant style:\ne.g. "Copyright (c)" and '
+ '"<year>" or "<startYear>-<endYear>":\n')
+ for holder_name in EXPECTED_HOLDER_NAMES:
+ dominant_style = [i['filename'] for i in file_infos if
+ i['dominant_style'][holder_name]]
+ if len(dominant_style) > 0:
+ print("%4d with '%s'" % (len(dominant_style),
+ holder_name.replace('\n', '\\n')))
+ print_filenames(dominant_style, verbose)
+ print('')
+ print(SEPARATOR)
+ print('Copyrights with year list style:\ne.g. "Copyright (c)" and '
+ '"<year1>, <year2>, ...":\n')
+ for holder_name in EXPECTED_HOLDER_NAMES:
+ year_list_style = [i['filename'] for i in file_infos if
+ i['year_list_style'][holder_name]]
+ if len(year_list_style) > 0:
+ print("%4d with '%s'" % (len(year_list_style),
+ holder_name.replace('\n', '\\n')))
+ print_filenames(year_list_style, verbose)
+ print('')
+ print(SEPARATOR)
+ print('Copyrights with no "(c)" style:\ne.g. "Copyright" and "<year>" or '
+ '"<startYear>-<endYear>":\n')
+ for holder_name in EXPECTED_HOLDER_NAMES:
+ without_c_style = [i['filename'] for i in file_infos if
+ i['without_c_style'][holder_name]]
+ if len(without_c_style) > 0:
+ print("%4d with '%s'" % (len(without_c_style),
+ holder_name.replace('\n', '\\n')))
+ print_filenames(without_c_style, verbose)
+
+ print('')
+ print(SEPARATOR)
+
+ unclassified_copyrights = [i['filename'] for i in file_infos if
+ i['classified_copyrights'] < i['all_copyrights']]
+ print("%d with unexpected copyright holder names" %
+ len(unclassified_copyrights))
+ print_filenames(unclassified_copyrights, verbose)
+ print(SEPARATOR)
+
+def exec_report(base_directory, verbose):
+ original_cwd = os.getcwd()
+ os.chdir(base_directory)
+ filenames = get_filenames_to_examine()
+ file_infos = [gather_file_info(f) for f in filenames]
+ print_report(file_infos, verbose)
+ os.chdir(original_cwd)
+
+################################################################################
+# report cmd
+################################################################################
+
+REPORT_USAGE = """
+Produces a report of all copyright header notices found inside the source files
+of a repository.
+
+Usage:
+ $ ./copyright_header.py report <base_directory> [verbose]
+
+Arguments:
+ <base_directory> - The base directory of a bitcoin source code repository.
+ [verbose] - Includes a list of every file of each subcategory in the report.
+"""
+
+def report_cmd(argv):
+ if len(argv) == 2:
+ sys.exit(REPORT_USAGE)
+
+ base_directory = argv[2]
+ if not os.path.exists(base_directory):
+ sys.exit("*** bad <base_directory>: %s" % base_directory)
+
+ if len(argv) == 3:
+ verbose = False
+ elif argv[3] == 'verbose':
+ verbose = True
+ else:
+ sys.exit("*** unknown argument: %s" % argv[2])
+
+ exec_report(base_directory, verbose)
+
+################################################################################
+# query git for year of last change
+################################################################################
+
+GIT_LOG_CMD = "git log --pretty=format:%%ai %s"
+
+def call_git_log(filename):
+ out = subprocess.check_output((GIT_LOG_CMD % filename).split(' '))
+ return out.decode("utf-8").split('\n')
+
+def get_git_change_years(filename):
+ git_log_lines = call_git_log(filename)
+ if len(git_log_lines) == 0:
+ return [datetime.date.today().year]
+ # timestamp is in ISO 8601 format. e.g. "2016-09-05 14:25:32 -0600"
+ return [line.split(' ')[0].split('-')[0] for line in git_log_lines]
+
+def get_most_recent_git_change_year(filename):
+ return max(get_git_change_years(filename))
+
+################################################################################
+# read and write to file
+################################################################################
+
+def read_file_lines(filename):
+ f = open(os.path.abspath(filename), 'r')
+ file_lines = f.readlines()
+ f.close()
+ return file_lines
+
+def write_file_lines(filename, file_lines):
+ f = open(os.path.abspath(filename), 'w')
+ f.write(''.join(file_lines))
+ f.close()
+
+################################################################################
+# update header years execution
+################################################################################
+
+COPYRIGHT = 'Copyright \(c\)'
+YEAR = "20[0-9][0-9]"
+YEAR_RANGE = '(%s)(-%s)?' % (YEAR, YEAR)
+HOLDER = 'The Bitcoin Core developers'
+UPDATEABLE_LINE_COMPILED = re.compile(' '.join([COPYRIGHT, YEAR_RANGE, HOLDER]))
+
+def get_updatable_copyright_line(file_lines):
+ index = 0
+ for line in file_lines:
+ if UPDATEABLE_LINE_COMPILED.search(line) is not None:
+ return index, line
+ index = index + 1
+ return None, None
+
+def parse_year_range(year_range):
+ year_split = year_range.split('-')
+ start_year = year_split[0]
+ if len(year_split) == 1:
+ return start_year, start_year
+ return start_year, year_split[1]
+
+def year_range_to_str(start_year, end_year):
+ if start_year == end_year:
+ return start_year
+ return "%s-%s" % (start_year, end_year)
+
+def create_updated_copyright_line(line, last_git_change_year):
+ copyright_splitter = 'Copyright (c) '
+ copyright_split = line.split(copyright_splitter)
+ # Preserve characters on line that are ahead of the start of the copyright
+ # notice - they are part of the comment block and vary from file-to-file.
+ before_copyright = copyright_split[0]
+ after_copyright = copyright_split[1]
+
+ space_split = after_copyright.split(' ')
+ year_range = space_split[0]
+ start_year, end_year = parse_year_range(year_range)
+ if end_year == last_git_change_year:
+ return line
+ return (before_copyright + copyright_splitter +
+ year_range_to_str(start_year, last_git_change_year) + ' ' +
+ ' '.join(space_split[1:]))
+
+def update_updatable_copyright(filename):
+ file_lines = read_file_lines(filename)
+ index, line = get_updatable_copyright_line(file_lines)
+ if not line:
+ print_file_action_message(filename, "No updatable copyright.")
+ return
+ last_git_change_year = get_most_recent_git_change_year(filename)
+ new_line = create_updated_copyright_line(line, last_git_change_year)
+ if line == new_line:
+ print_file_action_message(filename, "Copyright up-to-date.")
+ return
+ file_lines[index] = new_line
+ write_file_lines(filename, file_lines)
+ print_file_action_message(filename,
+ "Copyright updated! -> %s" % last_git_change_year)
+
+def exec_update_header_year(base_directory):
+ original_cwd = os.getcwd()
+ os.chdir(base_directory)
+ for filename in get_filenames_to_examine():
+ update_updatable_copyright(filename)
+ os.chdir(original_cwd)
+
+################################################################################
+# update cmd
+################################################################################
+
+UPDATE_USAGE = """
+Updates all the copyright headers of "The Bitcoin Core developers" which were
+changed in a year more recent than is listed. For example:
+
+// Copyright (c) <firstYear>-<lastYear> The Bitcoin Core developers
+
+will be updated to:
+
+// Copyright (c) <firstYear>-<lastModifiedYear> The Bitcoin Core developers
+
+where <lastModifiedYear> is obtained from the 'git log' history.
+
+This subcommand also handles copyright headers that have only a single year. In those cases:
+
+// Copyright (c) <year> The Bitcoin Core developers
+
+will be updated to:
+
+// Copyright (c) <year>-<lastModifiedYear> The Bitcoin Core developers
+
+where the update is appropriate.
+
+Usage:
+ $ ./copyright_header.py update <base_directory>
+
+Arguments:
+ <base_directory> - The base directory of a bitcoin source code repository.
+"""
+
+def print_file_action_message(filename, action):
+ print("%-52s %s" % (filename, action))
+
+def update_cmd(argv):
+ if len(argv) != 3:
+ sys.exit(UPDATE_USAGE)
+
+ base_directory = argv[2]
+ if not os.path.exists(base_directory):
+ sys.exit("*** bad base_directory: %s" % base_directory)
+ exec_update_header_year(base_directory)
+
+################################################################################
+# inserted copyright header format
+################################################################################
+
+def get_header_lines(header, start_year, end_year):
+ lines = header.split('\n')[1:-1]
+ lines[0] = lines[0] % year_range_to_str(start_year, end_year)
+ return [line + '\n' for line in lines]
+
+CPP_HEADER = '''
+// Copyright (c) %s The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+'''
+
+def get_cpp_header_lines_to_insert(start_year, end_year):
+ return reversed(get_header_lines(CPP_HEADER, start_year, end_year))
+
+PYTHON_HEADER = '''
+# Copyright (c) %s The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+'''
+
+def get_python_header_lines_to_insert(start_year, end_year):
+ return reversed(get_header_lines(PYTHON_HEADER, start_year, end_year))
+
+################################################################################
+# query git for year of last change
+################################################################################
+
+def get_git_change_year_range(filename):
+ years = get_git_change_years(filename)
+ return min(years), max(years)
+
+################################################################################
+# check for existing core copyright
+################################################################################
+
+def file_already_has_core_copyright(file_lines):
+ index, _ = get_updatable_copyright_line(file_lines)
+ return index != None
+
+################################################################################
+# insert header execution
+################################################################################
+
+def file_has_hashbang(file_lines):
+ if len(file_lines) < 1:
+ return False
+ if len(file_lines[0]) <= 2:
+ return False
+ return file_lines[0][:2] == '#!'
+
+def insert_python_header(filename, file_lines, start_year, end_year):
+ if file_has_hashbang(file_lines):
+ insert_idx = 1
+ else:
+ insert_idx = 0
+ header_lines = get_python_header_lines_to_insert(start_year, end_year)
+ for line in header_lines:
+ file_lines.insert(insert_idx, line)
+ write_file_lines(filename, file_lines)
+
+def insert_cpp_header(filename, file_lines, start_year, end_year):
+ header_lines = get_cpp_header_lines_to_insert(start_year, end_year)
+ for line in header_lines:
+ file_lines.insert(0, line)
+ write_file_lines(filename, file_lines)
+
+def exec_insert_header(filename, style):
+ file_lines = read_file_lines(filename)
+ if file_already_has_core_copyright(file_lines):
+ sys.exit('*** %s already has a copyright by The Bitcoin Core developers'
+ % (filename))
+ start_year, end_year = get_git_change_year_range(filename)
+ if style == 'python':
+ insert_python_header(filename, file_lines, start_year, end_year)
+ else:
+ insert_cpp_header(filename, file_lines, start_year, end_year)
+
+################################################################################
+# insert cmd
+################################################################################
+
+INSERT_USAGE = """
+Inserts a copyright header for "The Bitcoin Core developers" at the top of the
+file in either Python or C++ style as determined by the file extension. If the
+file is a Python file and it has a '#!' starting the first line, the header is
+inserted in the line below it.
+
+The copyright dates will be set to be:
+
+"<year_introduced>-<current_year>"
+
+where <year_introduced> is according to the 'git log' history. If
+<year_introduced> is equal to <current_year>, the date will be set to be:
+
+"<current_year>"
+
+If the file already has a copyright for "The Bitcoin Core developers", the
+script will exit.
+
+Usage:
+ $ ./copyright_header.py insert <file>
+
+Arguments:
+ <file> - A source file in the bitcoin repository.
+"""
+
+def insert_cmd(argv):
+ if len(argv) != 3:
+ sys.exit(INSERT_USAGE)
+
+ filename = argv[2]
+ if not os.path.isfile(filename):
+ sys.exit("*** bad filename: %s" % filename)
+ _, extension = os.path.splitext(filename)
+ if extension not in ['.h', '.cpp', '.cc', '.c', '.py']:
+ sys.exit("*** cannot insert for file extension %s" % extension)
+
+ if extension == '.py':
+ style = 'python'
+ else:
+ style = 'cpp'
+ exec_insert_header(filename, style)
+
+################################################################################
+# UI
+################################################################################
+
+USAGE = """
+copyright_header.py - utilities for managing copyright headers of 'The Bitcoin
+Core developers' in repository source files.
+
+Usage:
+ $ ./copyright_header <subcommand>
+
+Subcommands:
+ report
+ update
+ insert
+
+To see subcommand usage, run them without arguments.
+"""
+
+SUBCOMMANDS = ['report', 'update', 'insert']
+
+if __name__ == "__main__":
+ if len(sys.argv) == 1:
+ sys.exit(USAGE)
+ subcommand = sys.argv[1]
+ if subcommand not in SUBCOMMANDS:
+ sys.exit(USAGE)
+ if subcommand == 'report':
+ report_cmd(sys.argv)
+ elif subcommand == 'update':
+ update_cmd(sys.argv)
+ elif subcommand == 'insert':
+ insert_cmd(sys.argv)
diff --git a/contrib/devtools/fix-copyright-headers.py b/contrib/devtools/fix-copyright-headers.py
deleted file mode 100755
index 54836bd83f..0000000000
--- a/contrib/devtools/fix-copyright-headers.py
+++ /dev/null
@@ -1,67 +0,0 @@
-#!/usr/bin/env python3
-"""
-Run this script to update all the copyright headers of files
-that were changed this year.
-
-For example:
-
-// Copyright (c) 2009-2012 The Bitcoin Core developers
-
-it will change it to
-
-// Copyright (c) 2009-2015 The Bitcoin Core developers
-"""
-import subprocess
-import time
-import re
-
-CMD_GIT_LIST_FILES = ['git', 'ls-files']
-CMD_GIT_DATE = ['git', 'log', '--format=%ad', '--date=short', '-1']
-CMD_PERL_REGEX = ['perl', '-pi', '-e']
-REGEX_TEMPLATE = 's/(20\\d\\d)(?:-20\\d\\d)? The Bitcoin/$1-%s The Bitcoin/'
-
-FOLDERS = ["qa/", "src/"]
-EXTENSIONS = [".cpp",".h", ".py"]
-
-
-def get_git_date(file_path):
- d = subprocess.run(CMD_GIT_DATE + [file_path],
- stdout=subprocess.PIPE,
- check=True,
- universal_newlines=True).stdout
- # yyyy-mm-dd
- return d.split('-')[0]
-
-
-def skip_file(file_path):
- for ext in EXTENSIONS:
- if file_path.endswith(ext):
- return False
- else:
- return True
-
-if __name__ == "__main__":
- year = str(time.gmtime()[0])
- regex_current = re.compile("%s The Bitcoin" % year)
- n = 1
- for folder in FOLDERS:
- for file_path in subprocess.run(
- CMD_GIT_LIST_FILES + [folder],
- stdout=subprocess.PIPE,
- check=True,
- universal_newlines=True
- ).stdout.split("\n"):
- if skip_file(file_path):
- # print(file_path, "(skip)")
- continue
- git_date = get_git_date(file_path)
- if not year == git_date:
- # print(file_path, year, "(skip)")
- continue
- if regex_current.search(open(file_path, "r").read()) is not None:
- # already up to date
- # print(file_path, year, "(skip)")
- continue
- print(n, file_path, "(update to %s)" % year)
- subprocess.run(CMD_PERL_REGEX + [REGEX_TEMPLATE % year, file_path], check=True)
- n = n + 1
diff --git a/contrib/devtools/git-subtree-check.sh b/contrib/devtools/git-subtree-check.sh
index 1cb82fe682..2384d66cad 100755
--- a/contrib/devtools/git-subtree-check.sh
+++ b/contrib/devtools/git-subtree-check.sh
@@ -1,4 +1,7 @@
#!/bin/sh
+# Copyright (c) 2015 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
DIR="$1"
COMMIT="$2"
diff --git a/contrib/devtools/github-merge.py b/contrib/devtools/github-merge.py
index f82362fe41..aae966a8f6 100755
--- a/contrib/devtools/github-merge.py
+++ b/contrib/devtools/github-merge.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016 Bitcoin Core Developers
+# Copyright (c) 2016 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/contrib/devtools/optimize-pngs.py b/contrib/devtools/optimize-pngs.py
index 799e0cc7d0..b7b8dc0082 100755
--- a/contrib/devtools/optimize-pngs.py
+++ b/contrib/devtools/optimize-pngs.py
@@ -1,4 +1,7 @@
#!/usr/bin/env python
+# Copyright (c) 2014-2015 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
'''
Run this script every time you change one of the png files. Using pngcrush, it will optimize the png files, remove various color profiles, remove ancillary chunks (alla) and text chunks (text).
#pngcrush -brute -ow -rem gAMA -rem cHRM -rem iCCP -rem sRGB -rem alla -rem text
diff --git a/contrib/devtools/security-check.py b/contrib/devtools/security-check.py
index c61d652641..c90541e271 100755
--- a/contrib/devtools/security-check.py
+++ b/contrib/devtools/security-check.py
@@ -1,4 +1,7 @@
#!/usr/bin/env python
+# Copyright (c) 2015-2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
'''
Perform basic ELF security checks on a series of executables.
Exit status will be 0 if successful, and the program will be silent.
@@ -12,6 +15,7 @@ import os
READELF_CMD = os.getenv('READELF', '/usr/bin/readelf')
OBJDUMP_CMD = os.getenv('OBJDUMP', '/usr/bin/objdump')
+NONFATAL = {'HIGH_ENTROPY_VA'} # checks which are non-fatal for now but only generate a warning
def check_ELF_PIE(executable):
'''
@@ -114,26 +118,50 @@ def check_ELF_Canary(executable):
def get_PE_dll_characteristics(executable):
'''
- Get PE DllCharacteristics bits
+ Get PE DllCharacteristics bits.
+ Returns a tuple (arch,bits) where arch is 'i386:x86-64' or 'i386'
+ and bits is the DllCharacteristics value.
'''
p = subprocess.Popen([OBJDUMP_CMD, '-x', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
(stdout, stderr) = p.communicate()
if p.returncode:
raise IOError('Error opening file')
+ arch = ''
+ bits = 0
for line in stdout.split('\n'):
tokens = line.split()
+ if len(tokens)>=2 and tokens[0] == 'architecture:':
+ arch = tokens[1].rstrip(',')
if len(tokens)>=2 and tokens[0] == 'DllCharacteristics':
- return int(tokens[1],16)
- return 0
+ bits = int(tokens[1],16)
+ return (arch,bits)
+IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA = 0x0020
+IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE = 0x0040
+IMAGE_DLL_CHARACTERISTICS_NX_COMPAT = 0x0100
-def check_PE_PIE(executable):
+def check_PE_DYNAMIC_BASE(executable):
'''PIE: DllCharacteristics bit 0x40 signifies dynamicbase (ASLR)'''
- return bool(get_PE_dll_characteristics(executable) & 0x40)
+ (arch,bits) = get_PE_dll_characteristics(executable)
+ reqbits = IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE
+ return (bits & reqbits) == reqbits
+
+# On 64 bit, must support high-entropy 64-bit address space layout randomization in addition to DYNAMIC_BASE
+# to have secure ASLR.
+def check_PE_HIGH_ENTROPY_VA(executable):
+ '''PIE: DllCharacteristics bit 0x20 signifies high-entropy ASLR'''
+ (arch,bits) = get_PE_dll_characteristics(executable)
+ if arch == 'i386:x86-64':
+ reqbits = IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA
+ else: # Unnecessary on 32-bit
+ assert(arch == 'i386')
+ reqbits = 0
+ return (bits & reqbits) == reqbits
def check_PE_NX(executable):
'''NX: DllCharacteristics bit 0x100 signifies nxcompat (DEP)'''
- return bool(get_PE_dll_characteristics(executable) & 0x100)
+ (arch,bits) = get_PE_dll_characteristics(executable)
+ return (bits & IMAGE_DLL_CHARACTERISTICS_NX_COMPAT) == IMAGE_DLL_CHARACTERISTICS_NX_COMPAT
CHECKS = {
'ELF': [
@@ -143,7 +171,8 @@ CHECKS = {
('Canary', check_ELF_Canary)
],
'PE': [
- ('PIE', check_PE_PIE),
+ ('DYNAMIC_BASE', check_PE_DYNAMIC_BASE),
+ ('HIGH_ENTROPY_VA', check_PE_HIGH_ENTROPY_VA),
('NX', check_PE_NX)
]
}
@@ -168,12 +197,18 @@ if __name__ == '__main__':
continue
failed = []
+ warning = []
for (name, func) in CHECKS[etype]:
if not func(filename):
- failed.append(name)
+ if name in NONFATAL:
+ warning.append(name)
+ else:
+ failed.append(name)
if failed:
print('%s: failed %s' % (filename, ' '.join(failed)))
retval = 1
+ if warning:
+ print('%s: warning %s' % (filename, ' '.join(warning)))
except IOError:
print('%s: cannot open' % filename)
retval = 1
diff --git a/contrib/devtools/test-security-check.py b/contrib/devtools/test-security-check.py
index 324b7bcd85..c0f120392e 100755
--- a/contrib/devtools/test-security-check.py
+++ b/contrib/devtools/test-security-check.py
@@ -1,4 +1,7 @@
#!/usr/bin/env python2
+# Copyright (c) 2015-2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
'''
Test script for security-check.py
'''
diff --git a/contrib/gitian-build.sh b/contrib/gitian-build.sh
index ee47d138f3..53c24e3a87 100755
--- a/contrib/gitian-build.sh
+++ b/contrib/gitian-build.sh
@@ -1,3 +1,7 @@
+# Copyright (c) 2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
# What to do
sign=false
verify=false
diff --git a/contrib/gitian-descriptors/gitian-win.yml b/contrib/gitian-descriptors/gitian-win.yml
index 32b57b3160..fe01b5b957 100644
--- a/contrib/gitian-descriptors/gitian-win.yml
+++ b/contrib/gitian-descriptors/gitian-win.yml
@@ -27,7 +27,7 @@ remotes:
files: []
script: |
WRAP_DIR=$HOME/wrapped
- HOSTS="x86_64-w64-mingw32 i686-w64-mingw32"
+ HOSTS="i686-w64-mingw32 x86_64-w64-mingw32"
CONFIGFLAGS="--enable-reduce-exports --disable-bench --disable-gui-tests"
FAKETIME_HOST_PROGS="g++ ar ranlib nm windres strip objcopy"
FAKETIME_PROGS="date makensis zip"
diff --git a/contrib/gitian-keys/README.md b/contrib/gitian-keys/README.md
new file mode 100644
index 0000000000..439910330d
--- /dev/null
+++ b/contrib/gitian-keys/README.md
@@ -0,0 +1,16 @@
+PGP keys
+========
+
+This folder contains the public keys of developers and active contributors.
+
+The keys are mainly used to sign git commits or the build results of gitian
+builds.
+
+You can import the keys into gpg as follows. Also, make sure to fetch the
+latest version from the key server to see if any key was revoked in the
+meantime.
+
+```sh
+gpg --import ./*.pgp
+gpg --refresh-keys
+```
diff --git a/contrib/macdeploy/detached-sig-apply.sh b/contrib/macdeploy/detached-sig-apply.sh
index 781fe315ed..91674a92e6 100755
--- a/contrib/macdeploy/detached-sig-apply.sh
+++ b/contrib/macdeploy/detached-sig-apply.sh
@@ -1,4 +1,8 @@
#!/bin/sh
+# Copyright (c) 2014-2015 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
set -e
UNSIGNED="$1"
diff --git a/contrib/macdeploy/detached-sig-create.sh b/contrib/macdeploy/detached-sig-create.sh
index 89a2da32f7..5022ea88bc 100755
--- a/contrib/macdeploy/detached-sig-create.sh
+++ b/contrib/macdeploy/detached-sig-create.sh
@@ -1,4 +1,8 @@
#!/bin/sh
+# Copyright (c) 2014-2015 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
set -e
ROOTDIR=dist
diff --git a/contrib/macdeploy/extract-osx-sdk.sh b/contrib/macdeploy/extract-osx-sdk.sh
index 46d2d825d4..ff9fbd58df 100755
--- a/contrib/macdeploy/extract-osx-sdk.sh
+++ b/contrib/macdeploy/extract-osx-sdk.sh
@@ -1,4 +1,8 @@
#!/bin/bash
+# Copyright (c) 2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
set -e
INPUTFILE="Xcode_7.3.1.dmg"
diff --git a/contrib/qos/tc.sh b/contrib/qos/tc.sh
index f620604212..aaf5e1fa11 100644
--- a/contrib/qos/tc.sh
+++ b/contrib/qos/tc.sh
@@ -1,3 +1,7 @@
+# Copyright (c) 2013 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
#network interface on which to limit traffic
IF="eth0"
#limit of the network interface in question
diff --git a/contrib/qt_translations.py b/contrib/qt_translations.py
deleted file mode 100755
index fd8a8b7129..0000000000
--- a/contrib/qt_translations.py
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/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/rpm/bitcoin.spec b/contrib/rpm/bitcoin.spec
index 38ae038180..516f42334e 100644
--- a/contrib/rpm/bitcoin.spec
+++ b/contrib/rpm/bitcoin.spec
@@ -27,10 +27,9 @@ Source1: http://download.oracle.com/berkeley-db/db-%{bdbv}.NC.tar.gz
Source10: https://raw.githubusercontent.com/bitcoin/bitcoin/v%{version}/contrib/debian/examples/bitcoin.conf
#man pages
-Source20: https://raw.githubusercontent.com/bitcoin/bitcoin/v%{version}/contrib/debian/manpages/bitcoind.1
-Source21: https://raw.githubusercontent.com/bitcoin/bitcoin/v%{version}/contrib/debian/manpages/bitcoin-cli.1
-Source22: https://raw.githubusercontent.com/bitcoin/bitcoin/v%{version}/contrib/debian/manpages/bitcoin-qt.1
-Source23: https://raw.githubusercontent.com/bitcoin/bitcoin/v%{version}/contrib/debian/manpages/bitcoin.conf.5
+Source20: https://raw.githubusercontent.com/bitcoin/bitcoin/v%{version}/doc/man/bitcoind.1
+Source21: https://raw.githubusercontent.com/bitcoin/bitcoin/v%{version}/doc/man/bitcoin-cli.1
+Source22: https://raw.githubusercontent.com/bitcoin/bitcoin/v%{version}/doc/man/bitcoin-qt.1
#selinux
Source30: https://raw.githubusercontent.com/bitcoin/bitcoin/v%{version}/contrib/rpm/bitcoin.te
@@ -306,7 +305,6 @@ install -p %{SOURCE21} %{buildroot}%{_mandir}/man1/bitcoin-cli.1
%if %{_buildqt}
install -p %{SOURCE22} %{buildroot}%{_mandir}/man1/bitcoin-qt.1
%endif
-install -D -p %{SOURCE23} %{buildroot}%{_mandir}/man5/bitcoin.conf.5
# nuke these, we do extensive testing of binaries in %%check before packaging
rm -f %{buildroot}%{_bindir}/test_*
@@ -415,7 +413,6 @@ rm -rf %{buildroot}
%config(noreplace) %attr(0600,root,root) %{_sysconfdir}/sysconfig/bitcoin
%attr(0644,root,root) %{_datadir}/selinux/*/*.pp
%attr(0644,root,root) %{_mandir}/man1/bitcoind.1*
-%attr(0644,root,root) %{_mandir}/man5/bitcoin.conf.5*
%files utils
%defattr(-,root,root,-)
@@ -425,7 +422,6 @@ rm -rf %{buildroot}
%attr(0755,root,root) %{_bindir}/bitcoin-tx
%attr(0755,root,root) %{_bindir}/bench_bitcoin
%attr(0644,root,root) %{_mandir}/man1/bitcoin-cli.1*
-%attr(0644,root,root) %{_mandir}/man5/bitcoin.conf.5*
diff --git a/contrib/seeds/makeseeds.py b/contrib/seeds/makeseeds.py
index 4072405ef5..041f224f40 100755
--- a/contrib/seeds/makeseeds.py
+++ b/contrib/seeds/makeseeds.py
@@ -1,4 +1,7 @@
#!/usr/bin/env python
+# Copyright (c) 2013-2015 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
#
# Generate seeds.txt from Pieter's DNS seeder
#
diff --git a/contrib/spendfrom/README.md b/contrib/spendfrom/README.md
deleted file mode 100644
index c0a9c9ccf9..0000000000
--- a/contrib/spendfrom/README.md
+++ /dev/null
@@ -1,35 +0,0 @@
-### SpendFrom ###
-
-Use the raw transactions API to send coins received on a particular
-address (or addresses).
-
-### Usage: ###
-Depends on [jsonrpc](http://json-rpc.org/).
-
- 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`.
-
-### Notes ###
-
-- You may explicitly 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 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
deleted file mode 100644
index 01b9768a5b..0000000000
--- a/contrib/spendfrom/setup.py
+++ /dev/null
@@ -1,9 +0,0 @@
-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
deleted file mode 100755
index 72ee0425eb..0000000000
--- a/contrib/spendfrom/spendfrom.py
+++ /dev/null
@@ -1,267 +0,0 @@
-#!/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/testgen/base58.py b/contrib/testgen/base58.py
index b716495145..72b288b2dc 100644
--- a/contrib/testgen/base58.py
+++ b/contrib/testgen/base58.py
@@ -1,3 +1,6 @@
+# Copyright (c) 2012 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
'''
Bitcoin base58 encoding and decoding.
diff --git a/contrib/testgen/gen_base58_test_vectors.py b/contrib/testgen/gen_base58_test_vectors.py
index 1813436953..8518774db3 100755
--- a/contrib/testgen/gen_base58_test_vectors.py
+++ b/contrib/testgen/gen_base58_test_vectors.py
@@ -1,4 +1,7 @@
#!/usr/bin/env python
+# Copyright (c) 2012 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
'''
Generate valid and invalid base58 address and private key test vectors.
diff --git a/contrib/tidy_datadir.sh b/contrib/tidy_datadir.sh
index 5d6d826444..8960f8811d 100755
--- a/contrib/tidy_datadir.sh
+++ b/contrib/tidy_datadir.sh
@@ -1,4 +1,7 @@
#!/bin/bash
+# Copyright (c) 2013 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
if [ -d "$1" ]; then
cd "$1"
diff --git a/contrib/verify-commits/gpg.sh b/contrib/verify-commits/gpg.sh
index 375d711725..09ff237544 100755
--- a/contrib/verify-commits/gpg.sh
+++ b/contrib/verify-commits/gpg.sh
@@ -1,4 +1,8 @@
#!/bin/sh
+# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
INPUT=$(cat /dev/stdin)
VALID=false
REVSIG=false
diff --git a/contrib/verify-commits/pre-push-hook.sh b/contrib/verify-commits/pre-push-hook.sh
index c57222818a..c21febb9e9 100755
--- a/contrib/verify-commits/pre-push-hook.sh
+++ b/contrib/verify-commits/pre-push-hook.sh
@@ -1,4 +1,8 @@
#!/bin/bash
+# Copyright (c) 2014-2015 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
if ! [[ "$2" =~ ^(git@)?(www.)?github.com(:|/)bitcoin/bitcoin(.git)?$ ]]; then
exit 0
fi
diff --git a/contrib/verify-commits/verify-commits.sh b/contrib/verify-commits/verify-commits.sh
index 5219331e2e..cfe4f11a0b 100755
--- a/contrib/verify-commits/verify-commits.sh
+++ b/contrib/verify-commits/verify-commits.sh
@@ -1,4 +1,8 @@
#!/bin/sh
+# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
# Not technically POSIX-compliant due to use of "local", but almost every
# shell anyone uses today supports it, so its probably fine
diff --git a/contrib/verifybinaries/verify.sh b/contrib/verifybinaries/verify.sh
index e22b911743..e20770c96a 100755
--- a/contrib/verifybinaries/verify.sh
+++ b/contrib/verifybinaries/verify.sh
@@ -1,4 +1,7 @@
#!/bin/bash
+# Copyright (c) 2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
### This script attempts to download the signature file SHA256SUMS.asc from bitcoin.org
### It first checks if the signature passes, and then downloads the files specified in
diff --git a/contrib/zmq/zmq_sub.py b/contrib/zmq/zmq_sub.py
index 6268123dd8..3dea5e3c14 100755
--- a/contrib/zmq/zmq_sub.py
+++ b/contrib/zmq/zmq_sub.py
@@ -1,4 +1,7 @@
#!/usr/bin/env python2
+# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
import array
import binascii
diff --git a/depends/hosts/darwin.mk b/depends/hosts/darwin.mk
index 985649619f..4e58bec74e 100644
--- a/depends/hosts/darwin.mk
+++ b/depends/hosts/darwin.mk
@@ -1,4 +1,4 @@
-OSX_MIN_VERSION=10.7
+OSX_MIN_VERSION=10.8
OSX_SDK_VERSION=10.11
OSX_SDK=$(SDK_PATH)/MacOSX$(OSX_SDK_VERSION).sdk
LD64_VERSION=253.9
diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk
index ef1307c241..4173c0d168 100644
--- a/depends/packages/boost.mk
+++ b/depends/packages/boost.mk
@@ -1,8 +1,8 @@
package=boost
-$(package)_version=1_59_0
-$(package)_download_path=http://sourceforge.net/projects/boost/files/boost/1.59.0
+$(package)_version=1_61_0
+$(package)_download_path=https://sourceforge.net/projects/boost/files/boost/1.61.0
$(package)_file_name=$(package)_$($(package)_version).tar.bz2
-$(package)_sha256_hash=727a932322d94287b62abb1bd2d41723eec4356a7728909e38adb65ca25241ca
+$(package)_sha256_hash=a547bd06c2fd9a71ba1d169d9cf0339da7ebf4753849a8f7d6fdb8feee99b640
define $(package)_set_vars
$(package)_config_opts_release=variant=release
diff --git a/depends/packages/expat.mk b/depends/packages/expat.mk
index bd29275638..81a660e83a 100644
--- a/depends/packages/expat.mk
+++ b/depends/packages/expat.mk
@@ -1,8 +1,8 @@
package=expat
-$(package)_version=2.1.1
+$(package)_version=2.2.0
$(package)_download_path=https://downloads.sourceforge.net/project/expat/expat/$($(package)_version)
$(package)_file_name=$(package)-$($(package)_version).tar.bz2
-$(package)_sha256_hash=aff584e5a2f759dcfc6d48671e9529f6afe1e30b0cd6a4cec200cbe3f793de67
+$(package)_sha256_hash=d9e50ff2d19b3538bd2127902a89987474e1a4db8e43a66a4d1a712ab9a504ff
define $(package)_set_vars
$(package)_config_opts=--disable-static
diff --git a/depends/packages/fontconfig.mk b/depends/packages/fontconfig.mk
index 2cf553ed96..fb97e0b9ec 100644
--- a/depends/packages/fontconfig.mk
+++ b/depends/packages/fontconfig.mk
@@ -1,8 +1,8 @@
package=fontconfig
-$(package)_version=2.11.1
+$(package)_version=2.12.1
$(package)_download_path=http://www.freedesktop.org/software/fontconfig/release/
$(package)_file_name=$(package)-$($(package)_version).tar.bz2
-$(package)_sha256_hash=dc62447533bca844463a3c3fd4083b57c90f18a70506e7a9f4936b5a1e516a99
+$(package)_sha256_hash=b449a3e10c47e1d1c7a6ec6e2016cca73d3bd68fbbd4f0ae5cc6b573f7d6c7f3
$(package)_dependencies=freetype expat
define $(package)_set_vars
diff --git a/depends/packages/libevent.mk b/depends/packages/libevent.mk
index 2e9be1e98c..4b02b2eff9 100644
--- a/depends/packages/libevent.mk
+++ b/depends/packages/libevent.mk
@@ -3,10 +3,11 @@ $(package)_version=2.0.22
$(package)_download_path=https://github.com/libevent/libevent/releases/download/release-2.0.22-stable
$(package)_file_name=$(package)-$($(package)_version)-stable.tar.gz
$(package)_sha256_hash=71c2c49f0adadacfdbe6332a372c38cf9c8b7895bb73dabeaa53cdcc1d4e1fa3
-$(package)_patches=reuseaddr.patch
+$(package)_patches=reuseaddr.patch libevent-2-fixes.patch
define $(package)_preprocess_cmds
- patch -p1 < $($(package)_patch_dir)/reuseaddr.patch
+ patch -p1 < $($(package)_patch_dir)/reuseaddr.patch && \
+ patch -p1 < $($(package)_patch_dir)/libevent-2-fixes.patch
endef
define $(package)_set_vars
diff --git a/depends/packages/native_ccache.mk b/depends/packages/native_ccache.mk
index 9216e17598..a3a58604e5 100644
--- a/depends/packages/native_ccache.mk
+++ b/depends/packages/native_ccache.mk
@@ -1,8 +1,8 @@
package=native_ccache
-$(package)_version=3.2.5
-$(package)_download_path=http://samba.org/ftp/ccache
+$(package)_version=3.3.1
+$(package)_download_path=https://samba.org/ftp/ccache
$(package)_file_name=ccache-$($(package)_version).tar.bz2
-$(package)_sha256_hash=7a553809e90faf9de3a23ee9c5b5f786cfd4836bf502744bedb824a24bee1097
+$(package)_sha256_hash=cb6e4bafbb19ba0a2ec43386b123a5f92a20e1e3384c071d5d13e0cb3c84bf73
define $(package)_set_vars
$(package)_config_opts=
diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk
index d41d0b9ea5..bdde15aa17 100644
--- a/depends/packages/qt.mk
+++ b/depends/packages/qt.mk
@@ -8,7 +8,7 @@ $(package)_dependencies=openssl
$(package)_linux_dependencies=freetype fontconfig libxcb libX11 xproto libXext
$(package)_build_subdir=qtbase
$(package)_qt_libs=corelib network widgets gui plugins testlib
-$(package)_patches=mac-qmake.conf mingw-uuidof.patch pidlist_absolute.patch fix-xcb-include-order.patch fix_qt_pkgconfig.patch
+$(package)_patches=mac-qmake.conf configure-xcoderun.patch mingw-uuidof.patch pidlist_absolute.patch fix-xcb-include-order.patch fix_qt_pkgconfig.patch
$(package)_qttranslations_file_name=qttranslations-$($(package)_suffix)
$(package)_qttranslations_sha256_hash=dcc1534d247babca1840cb6d0a000671801a341ea352d0535474f86adadaf028
@@ -134,6 +134,7 @@ define $(package)_preprocess_cmds
cp -f qtbase/mkspecs/macx-clang/Info.plist.app qtbase/mkspecs/macx-clang-linux/ &&\
cp -f qtbase/mkspecs/macx-clang/qplatformdefs.h qtbase/mkspecs/macx-clang-linux/ &&\
cp -f $($(package)_patch_dir)/mac-qmake.conf qtbase/mkspecs/macx-clang-linux/qmake.conf && \
+ patch -p1 < $($(package)_patch_dir)/configure-xcoderun.patch && \
patch -p1 < $($(package)_patch_dir)/mingw-uuidof.patch && \
patch -p1 < $($(package)_patch_dir)/pidlist_absolute.patch && \
patch -p1 < $($(package)_patch_dir)/fix-xcb-include-order.patch && \
diff --git a/depends/patches/libevent/libevent-2-fixes.patch b/depends/patches/libevent/libevent-2-fixes.patch
new file mode 100644
index 0000000000..79fec8a488
--- /dev/null
+++ b/depends/patches/libevent/libevent-2-fixes.patch
@@ -0,0 +1,18 @@
+--- a/util-internal.h 2013-11-01 12:18:57.000000000 -0600
++++ b/util-internal.h 2015-07-20 20:19:43.199560900 -0500
+@@ -299,8 +299,13 @@ HANDLE evutil_load_windows_system_librar
+
+ #if defined(__STDC__) && defined(__STDC_VERSION__)
+ #if (__STDC_VERSION__ >= 199901L)
+-#define EV_SIZE_FMT "%zu"
+-#define EV_SSIZE_FMT "%zd"
++ #if defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__)
++ #define EV_SIZE_FMT "%Iu"
++ #define EV_SSIZE_FMT "%Id"
++ #else
++ #define EV_SIZE_FMT "%zu"
++ #define EV_SSIZE_FMT "%zd"
++ #endif
+ #define EV_SIZE_ARG(x) (x)
+ #define EV_SSIZE_ARG(x) (x)
+ #endif
diff --git a/depends/patches/qt/configure-xcoderun.patch b/depends/patches/qt/configure-xcoderun.patch
new file mode 100644
index 0000000000..08286d8420
--- /dev/null
+++ b/depends/patches/qt/configure-xcoderun.patch
@@ -0,0 +1,25 @@
+--- old/qtbase/configure
++++ new/qtbase/configure
+@@ -543,7 +543,7 @@ if [ "$BUILD_ON_MAC" = "yes" ]; then
+ exit 2
+ fi
+
+- if ! /usr/bin/xcrun -find xcrun >/dev/null 2>&1; then
++ if ! /usr/bin/xcrun -find xcodebuild >/dev/null 2>&1; then
+ echo >&2
+ echo " Xcode not set up properly. You may need to confirm the license" >&2
+ echo " agreement by running /usr/bin/xcodebuild without arguments." >&2
+diff --git a/mkspecs/features/mac/default_pre.prf b/mkspecs/features/mac/default_pre.prf
+index 0cc8cd6..5df99d1 100644
+--- old/qtbase/mkspecs/features/mac/default_pre.prf
++++ new/qtbase/mkspecs/features/mac/default_pre.prf
+@@ -12,7 +12,7 @@ isEmpty(QMAKE_XCODE_DEVELOPER_PATH) {
+ error("Xcode is not installed in $${QMAKE_XCODE_DEVELOPER_PATH}. Please use xcode-select to choose Xcode installation path.")
+
+ # Make sure Xcode is set up properly
+- isEmpty($$list($$system("/usr/bin/xcrun -find xcrun 2>/dev/null"))): \
++ isEmpty($$list($$system("/usr/bin/xcrun -find xcodebuild 2>/dev/null"))): \
+ error("Xcode not set up properly. You may need to confirm the license agreement by running /usr/bin/xcodebuild.")
+ }
+
+--
diff --git a/doc/Doxyfile b/doc/Doxyfile
index 22850db835..ef55acdbc3 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -1,106 +1,122 @@
-# Doxyfile 1.7.4
-
-# !!! Invoke doxygen from project root using:
-# doxygen doc/Doxyfile
+# Doxyfile 1.8.12
# This file describes the settings to be used by the documentation system
-# doxygen (www.doxygen.org) for a project
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
#
-# All text after a hash (#) is considered a comment and will be ignored
+# All text after a single 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 (" ")
+# 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.
+# 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.
+# The default value is: UTF-8.
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.
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
-PROJECT_NAME = Bitcoin
+PROJECT_NAME = "Bitcoin Core"
-# 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.
+# 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.13.99
-# 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.
+# 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.
+# With the PROJECT_LOGO tag one can specify a logo or an 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.
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. 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.
+# 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 causes
+# performance problems for the file system.
+# The default value is: NO.
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.
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES = 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.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
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.
+# If the BRIEF_MEMBER_DESC tag is set to YES, 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.
+# The default value is: YES.
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
+# If the REPEAT_BRIEF tag is set to YES, 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.
+# The default value is: YES.
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"
+# 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 and the.
ABBREVIATE_BRIEF = "The $name class" \
"The $name widget" \
@@ -114,531 +130,690 @@ ABBREVIATE_BRIEF = "The $name class" \
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
+# 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.
+# The default value is: NO.
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
+# 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.
+# The default value is: NO.
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.
+# If the FULL_PATH_NAMES tag is set to YES, 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
+# The default value is: YES.
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.
+# 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.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
-STRIP_FROM_PATH =
+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.
+# 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 list of include paths that are normally passed to the compiler
+# using the -I flag.
-STRIP_FROM_INC_PATH =
+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.
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
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.)
+# 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-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
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.)
+# 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 Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
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.
+# 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 behavior. 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 behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
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.
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
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.
+# 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.
+# The default value is: NO.
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.
+# 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.
+# Minimum value: 1, maximum value: 16, default value: 4.
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.
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
-ALIASES =
+TCL_SUBST =
-# 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.
+# 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.
+# The default value is: NO.
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.
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
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.
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
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.
+# 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.
+# The default value is: NO.
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
+# 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,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), VHDL. 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: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
+# to that level are automatically included in the table of contents, even if
+# they do not have an id attribute.
+# Note: This feature currently applies only to Markdown headings.
+# Minimum value: 0, maximum value: 99, default value: 0.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+TOC_INCLUDE_HEADINGS = 0
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# 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);
+# versus func(std::string) {}). This also make the inheritance and collaboration
# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
BUILTIN_STL_SUPPORT = NO
-# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
# enable parsing support.
+# The default value is: NO.
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.
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) 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.
+# The default value is: NO.
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.
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to 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.
+# The default value is: YES.
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
+# 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.
+# The default value is: NO.
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.
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES 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.
+# The default value is: YES.
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).
+# 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).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
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
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT tag 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.
+# The default value is: NO.
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
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_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
+# 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 respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
EXTRACT_ALL = YES
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
-# will be included in the documentation.
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
EXTRACT_PRIVATE = YES
-# If the EXTRACT_STATIC tag is set to YES all static members of a file
-# will be included in the documentation.
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
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.
+# 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. Does not have any effect
+# for Java sources.
+# The default value is: YES.
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.
+# This flag is only useful for Objective-C code. If 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, only methods in the interface are
+# included.
+# The default value is: NO.
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.
+# 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 namespace
+# are hidden.
+# The default value is: NO.
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.
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO 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.
+# The default value is: NO.
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.
+# 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, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
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.
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO, these declarations will be
+# included in the documentation.
+# The default value is: NO.
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.
+# 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, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
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.
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
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
+# 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.
+# The default value is: system dependent.
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.
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
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.
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
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.
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# 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.
+# The default value is: NO.
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.
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
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.
+# If the SORT_MEMBER_DOCS tag is set to YES 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.
+# The default value is: YES.
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.
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
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.
+# 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 constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: 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.
+# 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 group names will
+# appear in their defined order.
+# The default value is: NO.
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.
+# 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 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.
+# The default value is: NO.
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.
+# 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.
+# The default value is: NO.
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.
+# 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.
+# The default value is: YES.
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.
+# 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.
+# The default value is: YES.
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.
+# 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.
+# The default value is: YES.
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.
+# 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.
+# The default value is: YES.
GENERATE_DEPRECATEDLIST= YES
-# The ENABLED_SECTIONS tag can be used to enable conditional
-# documentation sections, marked by \if sectionname ... \endif.
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
-ENABLED_SECTIONS =
+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.
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have 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 value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
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
+# 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.
+# The default value is: YES.
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.
+# 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 value 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.
+# 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 value 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 <command> <input-file>, where <command> is the value of
-# the FILE_VERSION_FILTER tag, and <input-file> 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.
+# 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 command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+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. To 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.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
-FILE_VERSION_FILTER =
+LAYOUT_FILE =
-# 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.
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
-LAYOUT_FILE =
+CITE_BIB_FILES =
#---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
+# 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.
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
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.
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
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.
+# If the WARN_IF_UNDOCUMENTED tag 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.
+# The default value is: YES.
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.
+# If the WARN_IF_DOC_ERROR tag 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.
+# The default value is: YES.
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.
+# This 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, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation.
+# The default value is: NO.
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)
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered.
+# The default value is: NO.
+
+WARN_AS_ERROR = 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)
+# The default value is: $file:$line: $text.
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.
+# 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 standard
+# error (stderr).
-WARN_LOGFILE =
+WARN_LOGFILE =
#---------------------------------------------------------------------------
-# configuration options related to the input files
+# 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.
+# The INPUT tag is 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. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
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.
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
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
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
+# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
+# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf.
FILE_PATTERNS = *.c \
*.cc \
@@ -673,875 +848,1300 @@ FILE_PATTERNS = *.c \
*.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.
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
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
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# 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.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
-EXCLUDE = src/leveldb src/json src/test /src/qt/test
+EXCLUDE = src/leveldb \
+ src/json \
+ src/test \
+ src/qt/test
-# 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
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
# from the input.
+# The default value is: NO.
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/*
+# 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 =
+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,
+# 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
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
-EXCLUDE_SYMBOLS = boost google
+EXCLUDE_SYMBOLS = boost \
+ google
-# 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).
+# 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 =
+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.
+# 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.
+# 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.
+# The default value is: NO.
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).
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
-IMAGE_PATH =
+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 <filter> <input-file>, where <filter>
-# is the value of the INPUT_FILTER tag, and <input-file> 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.
+# 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:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> 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.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+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 information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
-INPUT_FILTER =
+FILTER_PATTERNS =
-# 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.
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
-FILTER_PATTERNS =
+FILTER_SOURCE_FILES = NO
-# 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).
+# 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 tag requires that the tag FILTER_SOURCE_FILES is set to YES.
-FILTER_SOURCE_FILES = NO
+FILTER_SOURCE_PATTERNS =
-# 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.
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
-FILTER_SOURCE_PATTERNS =
+USE_MDFILE_AS_MAINPAGE =
#---------------------------------------------------------------------------
-# configuration options related to source browsing
+# 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.
+# 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 that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
SOURCE_BROWSER = YES
-# Setting the INLINE_SOURCES tag to YES will include the body
-# of functions and classes directly in the documentation.
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
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.
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
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.
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
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.
+# 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.
+# The default value is: NO.
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.
+# If the REFERENCES_LINK_SOURCE tag is set to YES 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.
+# The default value is: YES.
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.
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = 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.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
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.
+# If the VERBATIM_HEADERS tag is set the YES 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.
+# See also: Section \class.
+# The default value is: YES.
VERBATIM_HEADERS = YES
#---------------------------------------------------------------------------
-# configuration options related to the alphabetical class index
+# 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.
+# 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.
+# The default value is: YES.
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])
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
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.
+# 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 a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
-IGNORE_PREFIX =
+IGNORE_PREFIX =
#---------------------------------------------------------------------------
-# configuration options related to the HTML output
+# Configuration options related to the HTML output
#---------------------------------------------------------------------------
-# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
-# generate HTML output.
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
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.
+# 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.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
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.
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
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.
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+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 left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_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.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet 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.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
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.
+# 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.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
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.
+# 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.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
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.
+# 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 YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
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).
+# 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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
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
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# 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 (see: http://developer.apple.com/tools/xcode/), 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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
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.
+# This tag determines the name of the docset 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.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
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.
+# 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.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
-DOCSET_BUNDLE_ID = org.doxygen.Project
+DOCSET_BUNDLE_ID = org.bitcoin.Bitcoin-Core
-# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
-# the documentation publisher. This should be a reverse domain-name style
+# The DOCSET_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.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
-DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+DOCSET_PUBLISHER_ID = org.bitcoin.Bitcoin-Core
-# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
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.
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
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
+# 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.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-CHM_FILE =
+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.
+# 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.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-HHC_LOCATION =
+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).
+# 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).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
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.
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-CHM_INDEX_ENCODING =
+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.
+# 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. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
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.
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
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.
+# 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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
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.
+# 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.
+# This tag requires that the tag GENERATE_QHP is set to YES.
-QCH_FILE =
+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
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
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
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
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
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
-QHP_CUST_FILTER_NAME =
+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
-# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
-# Qt Help Project / Custom Filters</a>.
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
-QHP_CUST_FILTER_ATTRS =
+QHP_CUST_FILTER_ATTRS =
-# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
-# project's
-# filter section matches.
-# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
-# Qt Help Project / Filter Attributes</a>.
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
-QHP_SECT_FILTER_ATTRS =
+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.
+# 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.
+# This tag requires that the tag GENERATE_QHP is set to YES.
-QHG_LOCATION =
+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.
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they 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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
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.
+# 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. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
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.
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
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.
+# 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. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
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.
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values 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.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
-USE_INLINE_TREES = NO
+ENUM_VALUES_PER_LINE = 4
-# 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.
+# 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.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
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.
+# If 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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
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.
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. 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.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
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.
+# 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 directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
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.
+# 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 pre-rendered 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 may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
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.
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# 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
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
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.
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# 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. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
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.
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
SERVER_BASED_SEARCH = NO
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
#---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
+# Configuration options related to the LaTeX output
#---------------------------------------------------------------------------
-# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
-# generate Latex output.
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
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.
+# 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.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
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.
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# 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.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
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.
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
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.
+# 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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
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.
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
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.
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+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. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+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. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
-EXTRA_PACKAGES =
+LATEX_FOOTER =
-# 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!
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
-LATEX_HEADER =
+LATEX_EXTRA_STYLESHEET =
-# 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!
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
-LATEX_FOOTER =
+LATEX_EXTRA_FILES =
-# 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.
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). 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.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
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
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
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.
+# 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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
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.
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
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.
+# If the LATEX_SOURCE_CODE tag 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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_SOURCE_CODE = NO
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE = plain
+
+# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
+# 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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_TIMESTAMP = NO
+
#---------------------------------------------------------------------------
-# configuration options related to the RTF output
+# 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.
+# 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 too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
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.
+# 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.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
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.
+# 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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
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.
+# 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 some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
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.
+# 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.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
-RTF_STYLESHEET_FILE =
+RTF_STYLESHEET_FILE =
-# Set optional variables used in the generation of an rtf document.
-# Syntax is similar to doxygen's config file.
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
-RTF_EXTENSIONS_FILE =
+RTF_EXTENSIONS_FILE =
+
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE = NO
#---------------------------------------------------------------------------
-# configuration options related to the man page output
+# Configuration options related to the man page output
#---------------------------------------------------------------------------
-# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
-# generate man pages
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
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.
+# 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. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
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)
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
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.
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR =
+
+# 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 value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_LINKS = NO
#---------------------------------------------------------------------------
-# configuration options related to the XML output
+# 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.
+# 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.
+# The default value is: NO.
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.
+# 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.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
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.
+# 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.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
-XML_SCHEMA =
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
-# 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.
+GENERATE_DOCBOOK = NO
-XML_DTD =
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
-# 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.
+DOCBOOK_OUTPUT = docbook
-XML_PROGRAMLISTING = YES
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
#---------------------------------------------------------------------------
-# configuration options for the AutoGen Definitions output
+# 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.
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://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.
+# The default value is: NO.
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
-# configuration options related to the Perl module output
+# 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.
+# 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.
+# The default value is: NO.
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.
+# 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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
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.
+# 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.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
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.
+# 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.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
-PERLMOD_MAKEVAR_PREFIX =
+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.
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
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.
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set 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.
+# 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.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
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.
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
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.
+# 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.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
-INCLUDE_PATH =
+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.
+# 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.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-INCLUDE_FILE_PATTERNS =
+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.
+# 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 e.g.
+# 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.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-PREDEFINED =
+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.
+# 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.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-EXPAND_AS_DEFINED =
+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.
+# If the SKIP_FUNCTION_MACROS tag is set to YES 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. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
-# Configuration::additions related to external references
+# Configuration options 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.
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. 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. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: 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 =
+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.
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
-GENERATE_TAGFILE =
+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.
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
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.
+# 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.
+# The default value is: YES.
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').
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
PERL_PATH = /usr/bin/perl
@@ -1549,204 +2149,312 @@ 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.
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML 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.
+# The default value is: YES.
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
+# 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 =
+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.
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_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.
+# The default value is: YES.
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)
+# 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 (see:
+# http://www.graphviz.org/), 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 value is: NO.
HAVE_DOT = YES
-# 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.
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 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.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
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.
+# When you want a differently looking font in the dot files that doxygen
+# generates 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.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
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.
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
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.
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
-DOT_FONTPATH =
+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.
+# If the CLASS_GRAPH tag is 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 CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
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.
+# If the COLLABORATION_GRAPH tag is 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.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
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
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
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
+# 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.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
UML_LOOK = NO
-# If set to YES, the inheritance and collaboration graphs will show the
-# relations between templates and their instances.
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
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.
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES 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.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
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.
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES 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.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
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.
+# If the CALL_GRAPH tag is 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. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
CALL_GRAPH = YES
-# 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.
+# If the CALLER_GRAPH tag is 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. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
CALLER_GRAPH = YES
-# 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.
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
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.
+# If the DIRECTORY_GRAPH tag is 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.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
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.
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_IMAGE_FORMAT = svg
-# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = NO
+
+# The DOT_PATH tag 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.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_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).
+# This tag requires that the tag HAVE_DOT is set to YES.
-# 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 =
-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).
-# 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 =
-MSCFILE_DIRS =
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
-# 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.
+DIAFILE_DIRS =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH =
+
+# 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.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
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
+# 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.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
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).
+# 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).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
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.
+# Set the DOT_MULTI_TARGETS tag to YES to 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.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
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.
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
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.
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_CLEANUP = YES
diff --git a/doc/README.md b/doc/README.md
index 09a507c9ce..36684e5401 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -3,7 +3,9 @@ Bitcoin Core 0.13.99
Setup
---------------------
-[Bitcoin Core](http://bitcoin.org/en/download) is the original Bitcoin client and it builds the backbone of the network. However, it downloads and stores the entire history of Bitcoin transactions (which is currently several GBs); 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.
+Bitcoin Core is the original Bitcoin client and it builds the backbone of the network. However, it downloads and stores the entire history of Bitcoin transactions (which is currently several GBs); 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.
+
+To download Bitcoin Core, visit [bitcoincore.org](https://bitcoincore.org/en/releases/).
Running
---------------------
@@ -46,13 +48,12 @@ Development
The Bitcoin repo's [root README](/README.md) contains relevant information on the development process and automated testing.
- [Developer Notes](developer-notes.md)
-- [Multiwallet Qt Development](multiwallet-qt.md)
- [Release Notes](release-notes.md)
- [Release Process](release-process.md)
- [Source Code Documentation (External Link)](https://dev.visucore.com/bitcoin/doxygen/)
- [Translation Process](translation_process.md)
- [Translation Strings Policy](translation_strings_policy.md)
-- [Unit Tests](unit-tests.md)
+- [Travis CI](travis-ci.md)
- [Unauthenticated REST Interface](REST-interface.md)
- [Shared Libraries](shared-libraries.md)
- [BIPS](bips.md)
@@ -67,11 +68,14 @@ The Bitcoin repo's [root README](/README.md) contains relevant information on th
### Miscellaneous
- [Assets Attribution](assets-attribution.md)
- [Files](files.md)
+- [Fuzz-testing](fuzzing.md)
+- [Reduce Traffic](reduce-traffic.md)
- [Tor Support](tor.md)
- [Init Scripts (systemd/upstart/openrc)](init.md)
+- [ZMQ](zmq.md)
License
---------------------
-Distributed under the [MIT software license](http://www.opensource.org/licenses/mit-license.php).
+Distributed under the [MIT software license](/COPYING).
This product includes software developed by the OpenSSL Project for use in the [OpenSSL Toolkit](https://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.
diff --git a/doc/bips.md b/doc/bips.md
index 039d5114fd..4f41610089 100644
--- a/doc/bips.md
+++ b/doc/bips.md
@@ -26,4 +26,9 @@ BIPs that are implemented by Bitcoin Core (up-to-date up to **v0.13.0**):
* [`BIP 125`](https://github.com/bitcoin/bips/blob/master/bip-0125.mediawiki): Opt-in full replace-by-fee signaling honoured in mempool and mining as of **v0.12.0** ([PR 6871](https://github.com/bitcoin/bitcoin/pull/6871)).
* [`BIP 130`](https://github.com/bitcoin/bips/blob/master/bip-0130.mediawiki): direct headers announcement is negotiated with peer versions `>=70012` as of **v0.12.0** ([PR 6494](https://github.com/bitcoin/bitcoin/pull/6494)).
* [`BIP 133`](https://github.com/bitcoin/bips/blob/master/bip-0133.mediawiki): feefilter messages are respected and sent for peer versions `>=70013` as of **v0.13.0** ([PR 7542](https://github.com/bitcoin/bitcoin/pull/7542)).
+* [`BIP 141`](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki): Segregated Witness (Consensus Layer) as of **v0.13.0** ([PR 8149](https://github.com/bitcoin/bitcoin/pull/8149)), and defined for mainnet as of **v0.13.1** ([PR 8937](https://github.com/bitcoin/bitcoin/pull/8937)).
+* [`BIP 143`](https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki): Transaction Signature Verification for Version 0 Witness Program as of **v0.13.0** ([PR 8149](https://github.com/bitcoin/bitcoin/pull/8149)) and defined for mainnet as of **v0.13.1** ([PR 8937](https://github.com/bitcoin/bitcoin/pull/8937)).
+* [`BIP 144`](https://github.com/bitcoin/bips/blob/master/bip-0144.mediawiki): Segregated Witness as of **0.13.0** ([PR 8149](https://github.com/bitcoin/bitcoin/pull/8149)).
+* [`BIP 145`](https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki): getblocktemplate updates for Segregated Witness as of **v0.13.0** ([PR 8149](https://github.com/bitcoin/bitcoin/pull/8149)).
+* [`BIP 147`](https://github.com/bitcoin/bips/blob/master/bip-0147.mediawiki): NULLDUMMY softfork as of **v0.13.1** ([PR 8636](https://github.com/bitcoin/bitcoin/pull/8636) and [PR 8937](https://github.com/bitcoin/bitcoin/pull/8937)).
* [`BIP 152`](https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki): Compact block transfer and related optimizations are used as of **v0.13.0** ([PR 8068](https://github.com/bitcoin/bitcoin/pull/8068)).
diff --git a/doc/build-osx.md b/doc/build-osx.md
index c9eb4225ab..63a7ee28ca 100644
--- a/doc/build-osx.md
+++ b/doc/build-osx.md
@@ -16,7 +16,7 @@ Then install [Homebrew](http://brew.sh).
Dependencies
----------------------
- brew install automake berkeley-db4 libtool boost --c++11 miniupnpc openssl pkg-config protobuf --c++11 qt5 libevent
+ brew install automake berkeley-db4 libtool boost --c++11 miniupnpc openssl pkg-config homebrew/versions/protobuf260 --c++11 qt5 libevent
NOTE: Building with Qt4 is still supported, however, could result in a broken UI. Building with Qt5 is recommended.
@@ -90,6 +90,6 @@ Uncheck everything except Qt Creator during the installation process.
Notes
-----
-* Tested on OS X 10.7 through 10.11 on 64-bit Intel processors only.
+* Tested on OS X 10.8 through 10.12 on 64-bit Intel processors only.
* Building with downloaded Qt binaries is not officially supported. See the notes in [#7714](https://github.com/bitcoin/bitcoin/issues/7714)
diff --git a/doc/build-unix.md b/doc/build-unix.md
index 62e3e793e9..5b0a38457e 100644
--- a/doc/build-unix.md
+++ b/doc/build-unix.md
@@ -79,9 +79,12 @@ install necessary parts of boost:
sudo apt-get install libboost-all-dev
-BerkeleyDB is required for the wallet. db4.8 packages are available [here](https://launchpad.net/~bitcoin/+archive/bitcoin).
+BerkeleyDB is required for the wallet.
+
+**For Ubuntu only:** db4.8 packages are available [here](https://launchpad.net/~bitcoin/+archive/bitcoin).
You can add the repository and install using the following commands:
+ sudo apt-get install software-properties-common
sudo add-apt-repository ppa:bitcoin/bitcoin
sudo apt-get update
sudo apt-get install libdb4.8-dev libdb4.8++-dev
@@ -93,13 +96,13 @@ pass `--with-incompatible-bdb` to configure.
See the section "Disable-wallet mode" to build Bitcoin Core without wallet.
-Optional:
+Optional (see --with-miniupnpc and --enable-upnp-default):
- sudo apt-get install libminiupnpc-dev (see --with-miniupnpc and --enable-upnp-default)
+ sudo apt-get install libminiupnpc-dev
-ZMQ dependencies:
+ZMQ dependencies (provides ZMQ API 4.x):
- sudo apt-get install libzmq3-dev (provides ZMQ API 4.x)
+ sudo apt-get install libzmq3-dev
Dependencies for the GUI: Ubuntu & Debian
-----------------------------------------
@@ -308,3 +311,35 @@ To build executables for ARM:
For further documentation on the depends system see [README.md](../depends/README.md) in the depends directory.
+
+Building on FreeBSD
+--------------------
+
+(Updated as of FreeBSD 10.3)
+
+Clang is installed by default as `cc` compiler, this makes it easier to get
+started than on [OpenBSD](build-openbsd.md). Installing dependencies:
+
+ pkg install autoconf automake libtool pkgconf
+ pkg install boost-libs openssl libevent2
+
+(`libressl` instead of `openssl` will also work)
+
+For the wallet (optional):
+
+ pkg install db5
+
+This will give a warning "configure: WARNING: Found Berkeley DB other
+than 4.8; wallets opened by this build will not be portable!", but as FreeBSD never
+had a binary release, this may not matter. If backwards compatibility
+with 4.8-built Bitcoin Core is needed follow the steps under "Berkeley DB" above.
+
+Then build using:
+
+ ./autogen.sh
+ ./configure --with-incompatible-bdb CPPFLAGS=-I/usr/local/include/db5 LDFLAGS=-L/usr/local/lib/db5
+ make
+
+*Note on debugging*: The version of `gdb` installed by default is [ancient and considered harmful](https://wiki.freebsd.org/GdbRetirement).
+It is not suitable for debugging a multi-threaded C++ program, not even for getting backtraces. Please install the package `gdb` and
+use the versioned gdb command e.g. `gdb7111`.
diff --git a/doc/build-windows.md b/doc/build-windows.md
index 129774491b..b6a047b9c3 100644
--- a/doc/build-windows.md
+++ b/doc/build-windows.md
@@ -1,13 +1,44 @@
WINDOWS BUILD NOTES
====================
-Some notes on how to build Bitcoin Core for Windows.
+Below are some notes on how to build Bitcoin Core for Windows.
Most developers use cross-compilation from Ubuntu to build executables for
Windows. This is also used to build the release binaries.
-Building on Windows itself is possible (for example using msys / mingw-w64),
-but no one documented the steps to do this. If you are doing this, please contribute them.
+While there are potentially a number of ways to build on Windows (for example using msys / mingw-w64),
+using the Windows Subsystem For Linux is the most straightforward. If you are building with
+another method, please contribute the instructions here for others who are running versions
+of Windows that are not compatible with the Windows Subsystem for Linux.
+
+Compiling with Windows Subsystem For Linux
+-------------------------------------------
+
+With Windows 10, Microsoft has released a new feature named the [Windows
+Subsystem for Linux](https://msdn.microsoft.com/commandline/wsl/about). This
+feature allows you to run a bash shell directly on Windows in an Ubuntu based
+environment. Within this environment you can cross compile for Windows without
+the need for a separate Linux VM or Server.
+
+This feature is not supported in versions of Windows prior to Windows 10 or on Windows Server SKUs.
+
+To get the bash shell, you must first activate the feature in Windows.
+
+1. Turn on Developer Mode
+ * Open Settings -> Update and Security -> For developers
+ * Select the Developer Mode radio button
+ * Restart if necessary
+2. Enable the Windows Subsystem for Linux feature
+ * From Start, search for "Turn Windows features on or off" (type 'turn')
+ * Select Windows Subsystem for Linux (beta)
+ * Click OK
+ * Restart if necessary
+3. Complete Installation
+ * Open a cmd prompt and type "bash"
+ * Accept the license
+ * Create a new UNIX user account (this is a separate account from your Windows account)
+
+After the bash shell is active, you can follow the instructions below for Windows 64-bit Cross-compilation.
Cross-compilation
-------------------
@@ -16,27 +47,55 @@ These steps can be performed on, for example, an Ubuntu VM. The depends system
will also work on other Linux distributions, however the commands for
installing the toolchain will be different.
-Make sure you install the build requirements mentioned in
-[build-unix.md](/doc/build-unix.md).
-Then, install the toolchains and curl:
+First, install the general dependencies:
- sudo apt-get install g++-mingw-w64-i686 mingw-w64-i686-dev g++-mingw-w64-x86-64 mingw-w64-x86-64-dev curl
+ sudo apt-get install build-essential libtool autotools-dev automake pkg-config bsdmainutils curl
-To build executables for Windows 32-bit:
+A host toolchain (`build-essential`) is necessary because some dependency
+packages (such as `protobuf`) need to build host utilities that are used in the
+build process.
+
+## Building for 64-bit Windows
+
+To build executables for Windows 64-bit, install the following dependencies:
+
+ sudo apt-get install g++-mingw-w64-x86-64 mingw-w64-x86-64-dev
+
+Then build using:
cd depends
- make HOST=i686-w64-mingw32 -j4
+ make HOST=x86_64-w64-mingw32 -j4
cd ..
- ./configure --prefix=`pwd`/depends/i686-w64-mingw32
+ ./autogen.sh # not required when building from tarball
+ CONFIG_SITE=$PWD/depends/x86_64-w64-mingw32/share/config.site ./configure --prefix=/
make
-To build executables for Windows 64-bit:
+## Building for 32-bit Windows
+
+To build executables for Windows 32-bit, install the following dependencies:
+
+ sudo apt-get install g++-mingw-w64-i686 mingw-w64-i686-dev
+
+Then build using:
cd depends
- make HOST=x86_64-w64-mingw32 -j4
+ make HOST=i686-w64-mingw32 -j4
cd ..
- ./configure --prefix=`pwd`/depends/x86_64-w64-mingw32
+ ./autogen.sh # not required when building from tarball
+ CONFIG_SITE=$PWD/depends/i686-w64-mingw32/share/config.site ./configure --prefix=/
make
+## Depends system
+
For further documentation on the depends system see [README.md](../depends/README.md) in the depends directory.
+Installation
+-------------
+
+After building using the Windows subsystem it can be useful to copy the compiled
+executables to a directory on the windows drive in the same directory structure
+as they appear in the release `.zip` archive. This can be done in the following
+way. This will install to `c:\workspace\bitcoin`, for example:
+
+ make install DESTDIR=/mnt/c/workspace/bitcoin
+
diff --git a/doc/developer-notes.md b/doc/developer-notes.md
index 70c0690ba3..ba03579e86 100644
--- a/doc/developer-notes.md
+++ b/doc/developer-notes.md
@@ -331,6 +331,32 @@ Strings and formatting
- *Rationale*: Bitcoin Core uses tinyformat, which is type safe. Leave them out to avoid confusion
+Variable names
+--------------
+
+The shadowing warning (`-Wshadow`) is enabled by default. It prevents issues rising
+from using a different variable with the same name.
+
+Please name variables so that their names do not shadow variables defined in the source code.
+
+E.g. in member initializers, prepend `_` to the argument name shadowing the
+member name:
+
+```c++
+class AddressBookPage
+{
+ Mode mode;
+}
+
+AddressBookPage::AddressBookPage(Mode _mode) :
+ mode(_mode)
+...
+```
+
+When using nested cycles, do not name the inner cycle variable the same as in
+upper cycle etc.
+
+
Threads and synchronization
----------------------------
@@ -382,6 +408,37 @@ GUI
should not interact with the user. That's where View classes come in. The converse also
holds: try to not directly access core data structures from Views.
+Subtrees
+----------
+
+Several parts of the repository are subtrees of software maintained elsewhere.
+
+Some of these are maintained by active developers of Bitcoin Core, in which case changes should probably go
+directly upstream without being PRed directly against the project. They will be merged back in the next
+subtree merge.
+
+Others are external projects without a tight relationship with our project. Changes to these should also
+be sent upstream but bugfixes may also be prudent to PR against Bitcoin Core so that they can be integrated
+quickly. Cosmetic changes should be purely taken upstream.
+
+There is a tool in contrib/devtools/git-subtree-check.sh to check a subtree directory for consistency with
+its upstream repository.
+
+Current subtrees include:
+
+- src/leveldb
+ - Upstream at https://github.com/google/leveldb ; Maintained by Google, but open important PRs to Core to avoid delay
+
+- src/libsecp256k1
+ - Upstream at https://github.com/bitcoin-core/secp256k1/ ; actively maintaned by Core contributors.
+
+- src/crypto/ctaes
+ - Upstream at https://github.com/bitcoin-core/ctaes ; actively maintained by Core contributors.
+
+- src/univalue
+ - Upstream at https://github.com/jgarzik/univalue ; report important PRs to Core to avoid delay.
+
+
Git and github tips
---------------------
diff --git a/doc/files.md b/doc/files.md
index f7eca57dcb..928977143b 100644
--- a/doc/files.md
+++ b/doc/files.md
@@ -10,6 +10,7 @@
* db.log: wallet database log file
* debug.log: contains debug information and general logging generated by bitcoind or bitcoin-qt
* fee_estimates.dat: stores statistics used to estimate minimum transaction fees and priorities required for confirmation; since 0.10.0
+* mempool.dat: dump of the mempool's transactions; since 0.14.0.
* peers.dat: peer IP address database (custom format); since 0.7.0
* wallet.dat: personal wallet (BDB) with keys and transactions
* .cookie: session RPC authentication cookie (written at start when cookie authentication is used, deleted on shutdown): since 0.12.0
diff --git a/doc/fuzzing.md b/doc/fuzzing.md
new file mode 100644
index 0000000000..bf3ad17861
--- /dev/null
+++ b/doc/fuzzing.md
@@ -0,0 +1,66 @@
+Fuzz-testing Bitcoin Core
+==========================
+
+A special test harness `test_bitcoin_fuzzy` is provided to provide an easy
+entry point for fuzzers and the like. In this document we'll describe how to
+use it with AFL.
+
+Building AFL
+-------------
+
+It is recommended to always use the latest version of afl:
+```
+wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz
+tar -zxvf afl-latest.tgz
+cd afl-<version>
+make
+export AFLPATH=$PWD
+```
+
+Instrumentation
+----------------
+
+To build Bitcoin Core using AFL instrumentation (this assumes that the
+`AFLPATH` was set as above):
+```
+./configure --disable-ccache --disable-shared --enable-tests CC=${AFLPATH}/afl-gcc CXX=${AFLPATH}/afl-g++
+export AFL_HARDEN=1
+cd src/
+make test/test_bitcoin_fuzzy
+```
+We disable ccache because we don't want to pollute the ccache with instrumented
+objects, and similarly don't want to use non-instrumented cached objects linked
+in.
+
+Preparing fuzzing
+------------------
+
+AFL needs an input directory with examples, and an output directory where it
+will place examples that it found. These can be anywhere in the file system,
+we'll define environment variables to make it easy to reference them.
+
+```
+mkdir inputs
+AFLIN=$PWD/inputs
+mkdir outputs
+AFLOUT=$PWD/outputs
+```
+
+Example inputs are available from:
+
+- https://download.visucore.com/bitcoin/bitcoin_fuzzy_in.tar.xz
+- http://strateman.ninja/fuzzing.tar.xz
+
+Extract these (or other starting inputs) into the `inputs` directory before starting fuzzing.
+
+Fuzzing
+--------
+
+To start the actual fuzzing use:
+```
+$AFLPATH/afl-fuzz -i ${AFLIN} -o ${AFLOUT} -m52 -- test/test_bitcoin_fuzzy
+```
+
+You may have to change a few kernel parameters to test optimally - `afl-fuzz`
+will print an error and suggestion if so.
+
diff --git a/doc/gitian-building.md b/doc/gitian-building.md
index 938f92ff12..442d2d605f 100644
--- a/doc/gitian-building.md
+++ b/doc/gitian-building.md
@@ -95,14 +95,14 @@ After creating the VM, we need to configure it.
- Click `Ok` twice to save.
-Get the [Debian 8.x net installer](http://cdimage.debian.org/debian-cd/8.5.0/amd64/iso-cd/debian-8.5.0-amd64-netinst.iso) (a more recent minor version should also work, see also [Debian Network installation](https://www.debian.org/CD/netinst/)).
-This DVD image can be validated using a SHA256 hashing tool, for example on
+Get the [Debian 8.x net installer](http://cdimage.debian.org/mirror/cdimage/archive/8.5.0/amd64/iso-cd/debian-8.5.0-amd64-netinst.iso) (a more recent minor version should also work, see also [Debian Network installation](https://www.debian.org/CD/netinst/)).
+This DVD image can be [validated](https://www.debian.org/CD/verify) using a SHA256 hashing tool, for example on
Unixy OSes by entering the following in a terminal:
echo "ad4e8c27c561ad8248d5ebc1d36eb172f884057bfeb2c22ead823f59fa8c3dff debian-8.5.0-amd64-netinst.iso" | sha256sum -c
# (must return OK)
-Then start the VM. On the first launch you will be asked for a CD or DVD image. Choose the downloaded iso.
+Then start the VM. On the first launch you will be asked for a CD or DVD image. Choose the downloaded ISO.
![](gitian-building/select_startup_disk.png)
@@ -336,8 +336,10 @@ There will be a lot of warnings printed during the build of the image. These can
Getting and building the inputs
--------------------------------
-Follow the instructions in [doc/release-process.md](release-process.md#fetch-and-build-inputs-first-time-or-when-dependency-versions-change)
-in the bitcoin repository under 'Fetch and build inputs' to install sources which require
+At this point you have two options, you can either use the automated script (found in [contrib/gitian-build.sh](/contrib/gitian-build.sh)) or you could manually do everything by following this guide. If you're using the automated script, then run it with the "--setup" command. Afterwards, run it with the "--build" command (example: "contrib/gitian-building.sh -b signer 0.13.0"). Otherwise ignore this.
+
+Follow the instructions in [doc/release-process.md](release-process.md#fetch-and-create-inputs-first-time-or-when-dependency-versions-change)
+in the bitcoin repository under 'Fetch and create inputs' to install sources which require
manual intervention. Also optionally follow the next step: 'Seed the Gitian sources cache
and offline git repositories' which will fetch the remaining files required for building
offline.
diff --git a/doc/multiwallet-qt.md b/doc/multiwallet-qt.md
deleted file mode 100644
index 3caab81807..0000000000
--- a/doc/multiwallet-qt.md
+++ /dev/null
@@ -1,48 +0,0 @@
-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.
-
-Two new classes have been implemented in two 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, two 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 *WalletFrame* inheriting from QFrame has been written as a container for embedding all wallet-related
-controls into BitcoinGUI. At present it contains the WalletView instances for the wallets and does little more than passing on messages
-from BitcoinGUI to the currently selected WalletView. 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/reduce-traffic.md b/doc/reduce-traffic.md
index 2d86588eb2..697099beab 100644
--- a/doc/reduce-traffic.md
+++ b/doc/reduce-traffic.md
@@ -19,8 +19,7 @@ This is *not* a hard limit; only a threshold to minimize the outbound
traffic. When the limit is about to be reached, the uploaded data is cut by no
longer serving historic blocks (blocks older than one week).
Keep in mind that new nodes require other nodes that are willing to serve
-historic blocks. **The recommended minimum is 144 blocks per day (max. 144MB
-per day)**
+historic blocks.
Whitelisted peers will never be disconnected, although their traffic counts for
calculating the target.
diff --git a/doc/release-notes.md b/doc/release-notes.md
index 58994a6839..fe7f69d1f8 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -48,6 +48,19 @@ Low-level RPC changes
an optional third arg, which was always ignored. Make sure to never pass more
than two arguments.
+Removal of Priority Estimation
+------------------------------
+
+- Estimation of "priority" needed for a transaction to be included within a target
+ number of blocks has been removed. The rpc calls are deprecated and will either
+ return -1 or 1e24 appropriately. The format for `fee_estimates.dat` has also
+ changed to no longer save these priority estimates. It will automatically be
+ converted to the new format which is not readable by prior versions of the
+ software.
+
+- The concept of "priority" transactions is planned to be removed in the next
+ major version. To prepare for this, the default for the rate limit of priority
+ transactions (`-limitfreerelay`) has been set to `0` kB/minute.
0.14.0 Change log
=================
@@ -65,6 +78,8 @@ contain invalid parameters.
The first boolean argument to `getaddednodeinfo` has been removed. This is an incompatible change.
+Call "getmininginfo" loses the "testnet" field in favor of the more generic "chain" (which has been present for years).
+
### Configuration and command-line options
### Block and transaction handling
diff --git a/doc/release-notes/release-notes-0.13.0.md b/doc/release-notes/release-notes-0.13.0.md
index 5dd3f5a651..4c3cb97df3 100644
--- a/doc/release-notes/release-notes-0.13.0.md
+++ b/doc/release-notes/release-notes-0.13.0.md
@@ -95,14 +95,19 @@ executables.
The following extra files can be found in the download directory or torrent:
-- `bitcoin-${VERSION}-arm-linux-gnueabihf.tar.gz`: Linux binaries for the most
- common 32-bit ARM architecture.
-- `bitcoin-${VERSION}-aarch64-linux-gnu.tar.gz`: Linux binaries for the most
- common 64-bit ARM architecture.
+- `bitcoin-${VERSION}-arm-linux-gnueabihf.tar.gz`: Linux binaries targeting
+ the 32-bit ARMv7-A architecture.
+- `bitcoin-${VERSION}-aarch64-linux-gnu.tar.gz`: Linux binaries targeting
+ the 64-bit ARMv8-A architecture.
ARM builds are still experimental. If you have problems on a certain device or
Linux distribution combination please report them on the bug tracker, it may be
-possible to resolve them.
+possible to resolve them. Note that the device you use must be (backward)
+compatible with the architecture targeted by the binary that you use.
+For example, a Raspberry Pi 2 Model B or Raspberry Pi 3 Model B (in its 32-bit
+execution state) device, can run the 32-bit ARMv7-A targeted binary. However,
+no model of Raspberry Pi 1 device can run either binary because they are all
+ARMv6 architecture devices that are not compatible with ARMv7-A or ARMv8-A.
Note that Android is not considered ARM Linux in this context. The executables
are not expected to work out of the box on Android.
@@ -643,7 +648,7 @@ git merge commit are mentioned.
- #8041 `5b736dd` Fix bip9-softforks blockstore issue (MarcoFalke)
- #7994 `1f01443` Add op csv tests to script_tests.json (Christewart)
- #8038 `e2bf830` Various minor fixes (MarcoFalke)
-- #8072 `1b87e5b` Travis: 'make check' in parallel and verbose (MarcoFalke)
+- #8072 `1b87e5b` Travis: 'make check' in parallel and verbose (theuni)
- #8056 `8844ef1` Remove hardcoded "4 nodes" from test_framework (MarcoFalke)
- #8047 `37f9a1f` Test_framework: Set wait-timeout for bitcoind procs (MarcoFalke)
- #8095 `6700cc9` Test framework: only cleanup on successful test runs (sdaftuar)
diff --git a/doc/release-notes/release-notes-0.13.1.md b/doc/release-notes/release-notes-0.13.1.md
new file mode 100644
index 0000000000..75c2d61be8
--- /dev/null
+++ b/doc/release-notes/release-notes-0.13.1.md
@@ -0,0 +1,410 @@
+Bitcoin Core version 0.13.1 is now available from:
+
+ <https://bitcoin.org/bin/bitcoin-core-0.13.1/>
+
+This is a new minor version release, including activation parameters for the
+segwit softfork, various bugfixes and performance improvements, as well as
+updated translations.
+
+Please report bugs using the issue tracker at github:
+
+ <https://github.com/bitcoin/bitcoin/issues>
+
+To receive security and update notifications, please subscribe to:
+
+ <https://bitcoincore.org/en/list/announcements/join/>
+
+Compatibility
+==============
+
+Microsoft ended support for Windows XP on [April 8th, 2014](https://www.microsoft.com/en-us/WindowsForBusiness/end-of-xp-support),
+an OS initially released in 2001. This means that not even critical security
+updates will be released anymore. Without security updates, using a bitcoin
+wallet on a XP machine is irresponsible at least.
+
+In addition to that, with 0.12.x there have been varied reports of Bitcoin Core
+randomly crashing on Windows XP. It is [not clear](https://github.com/bitcoin/bitcoin/issues/7681#issuecomment-217439891)
+what the source of these crashes is, but it is likely that upstream
+libraries such as Qt are no longer being tested on XP.
+
+We do not have time nor resources to provide support for an OS that is
+end-of-life. From 0.13.0 on, Windows XP is no longer supported. Users are
+suggested to upgrade to a newer version of Windows, or install an alternative OS
+that is supported.
+
+No attempt is made to prevent installing or running the software on Windows XP,
+you can still do so at your own risk, but do not expect it to work: do not
+report issues about Windows XP to the issue tracker.
+
+From 0.13.1 onwards OS X 10.7 is no longer supported. 0.13.0 was intended to work on 10.7+,
+but severe issues with the libc++ version on 10.7.x keep it from running reliably.
+0.13.1 now requires 10.8+, and will communicate that to 10.7 users, rather than crashing unexpectedly.
+
+Notable changes
+===============
+
+Segregated witness soft fork
+----------------------------
+
+Segregated witness (segwit) is a soft fork that, if activated, will
+allow transaction-producing software to separate (segregate) transaction
+signatures (witnesses) from the part of the data in a transaction that is
+covered by the txid. This provides several immediate benefits:
+
+- **Elimination of unwanted transaction malleability:** Segregating the witness
+ allows both existing and upgraded software to calculate the transaction
+ identifier (txid) of transactions without referencing the witness, which can
+ sometimes be changed by third-parties (such as miners) or by co-signers in a
+ multisig spend. This solves all known cases of unwanted transaction
+ malleability, which is a problem that makes programming Bitcoin wallet
+ software more difficult and which seriously complicates the design of smart
+ contracts for Bitcoin.
+
+- **Capacity increase:** Segwit transactions contain new fields that are not
+ part of the data currently used to calculate the size of a block, which
+ allows a block containing segwit transactions to hold more data than allowed
+ by the current maximum block size. Estimates based on the transactions
+ currently found in blocks indicate that if all wallets switch to using
+ segwit, the network will be able to support about 70% more transactions. The
+ network will also be able to support more of the advanced-style payments
+ (such as multisig) than it can support now because of the different weighting
+ given to different parts of a transaction after segwit activates (see the
+ following section for details).
+
+- **Weighting data based on how it affects node performance:** Some parts of
+ each Bitcoin block need to be stored by nodes in order to validate future
+ blocks; other parts of a block can be immediately forgotten (pruned) or used
+ only for helping other nodes sync their copy of the block chain. One large
+ part of the immediately prunable data are transaction signatures (witnesses),
+ and segwit makes it possible to give a different "weight" to segregated
+ witnesses to correspond with the lower demands they place on node resources.
+ Specifically, each byte of a segregated witness is given a weight of 1, each
+ other byte in a block is given a weight of 4, and the maximum allowed weight
+ of a block is 4 million. Weighting the data this way better aligns the most
+ profitable strategy for creating blocks with the long-term costs of block
+ validation.
+
+- **Signature covers value:** A simple improvement in the way signatures are
+ generated in segwit simplifies the design of secure signature generators
+ (such as hardware wallets), reduces the amount of data the signature
+ generator needs to download, and allows the signature generator to operate
+ more quickly. This is made possible by having the generator sign the amount
+ of bitcoins they think they are spending, and by having full nodes refuse to
+ accept those signatures unless the amount of bitcoins being spent is exactly
+ the same as was signed. For non-segwit transactions, wallets instead had to
+ download the complete previous transactions being spent for every payment
+ they made, which could be a slow operation on hardware wallets and in other
+ situations where bandwidth or computation speed was constrained.
+
+- **Linear scaling of sighash operations:** In 2015 a block was produced that
+ required about 25 seconds to validate on modern hardware because of the way
+ transaction signature hashes are performed. Other similar blocks, or blocks
+ that could take even longer to validate, can still be produced today. The
+ problem that caused this can't be fixed in a soft fork without unwanted
+ side-effects, but transactions that opt-in to using segwit will now use a
+ different signature method that doesn't suffer from this problem and doesn't
+ have any unwanted side-effects.
+
+- **Increased security for multisig:** Bitcoin addresses (both P2PKH addresses
+ that start with a '1' and P2SH addresses that start with a '3') use a hash
+ function known as RIPEMD-160. For P2PKH addresses, this provides about 160
+ bits of security---which is beyond what cryptographers believe can be broken
+ today. But because P2SH is more flexible, only about 80 bits of security is
+ provided per address. Although 80 bits is very strong security, it is within
+ the realm of possibility that it can be broken by a powerful adversary.
+ Segwit allows advanced transactions to use the SHA256 hash function instead,
+ which provides about 128 bits of security (that is 281 trillion times as
+ much security as 80 bits and is equivalent to the maximum bits of security
+ believed to be provided by Bitcoin's choice of parameters for its Elliptic
+ Curve Digital Security Algorithm [ECDSA].)
+
+- **More efficient almost-full-node security** Satoshi Nakamoto's original
+ Bitcoin paper describes a method for allowing newly-started full nodes to
+ skip downloading and validating some data from historic blocks that are
+ protected by large amounts of proof of work. Unfortunately, Nakamoto's
+ method can't guarantee that a newly-started node using this method will
+ produce an accurate copy of Bitcoin's current ledger (called the UTXO set),
+ making the node vulnerable to falling out of consensus with other nodes.
+ Although the problems with Nakamoto's method can't be fixed in a soft fork,
+ Segwit accomplishes something similar to his original proposal: it makes it
+ possible for a node to optionally skip downloading some blockchain data
+ (specifically, the segregated witnesses) while still ensuring that the node
+ can build an accurate copy of the UTXO set for the block chain with the most
+ proof of work. Segwit enables this capability at the consensus layer, but
+ note that Bitcoin Core does not provide an option to use this capability as
+ of this 0.13.1 release.
+
+- **Script versioning:** Segwit makes it easy for future soft forks to allow
+ Bitcoin users to individually opt-in to almost any change in the Bitcoin
+ Script language when those users receive new transactions. Features
+ currently being researched by Bitcoin Core contributors that may use this
+ capability include support for Schnorr signatures, which can improve the
+ privacy and efficiency of multisig transactions (or transactions with
+ multiple inputs), and Merklized Abstract Syntax Trees (MAST), which can
+ improve the privacy and efficiency of scripts with two or more conditions.
+ Other Bitcoin community members are studying several other improvements
+ that can be made using script versioning.
+
+Activation for the segwit soft fork is being managed using BIP9
+versionbits. Segwit's version bit is bit 1, and nodes will begin
+tracking which blocks signal support for segwit at the beginning of the
+first retarget period after segwit's start date of 15 November 2016. If
+95% of blocks within a 2,016-block retarget period (about two weeks)
+signal support for segwit, the soft fork will be locked in. After
+another 2,016 blocks, segwit will activate.
+
+For more information about segwit, please see the [segwit FAQ][], the
+[segwit wallet developers guide][] or BIPs [141][BIP141], [143][BIP143],
+[144][BIP144], and [145][BIP145]. If you're a miner or mining pool
+operator, please see the [versionbits FAQ][] for information about
+signaling support for a soft fork.
+
+[Segwit FAQ]: https://bitcoincore.org/en/2016/01/26/segwit-benefits/
+[segwit wallet developers guide]: https://bitcoincore.org/en/segwit_wallet_dev/
+[BIP141]: https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki
+[BIP143]: https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki
+[BIP144]: https://github.com/bitcoin/bips/blob/master/bip-0144.mediawiki
+[BIP145]: https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki
+[versionbits FAQ]: https://bitcoincore.org/en/2016/06/08/version-bits-miners-faq/
+
+
+Null dummy soft fork
+-------------------
+
+Combined with the segwit soft fork is an additional change that turns a
+long-existing network relay policy into a consensus rule. The
+`OP_CHECKMULTISIG` and `OP_CHECKMULTISIGVERIFY` opcodes consume an extra
+stack element ("dummy element") after signature validation. The dummy
+element is not inspected in any manner, and could be replaced by any
+value without invalidating the script.
+
+Because any value can be used for this dummy element, it's possible for
+a third-party to insert data into other people's transactions, changing
+the transaction's txid (called transaction malleability) and possibly
+causing other problems.
+
+Since Bitcoin Core 0.10.0, nodes have defaulted to only relaying and
+mining transactions whose dummy element was a null value (0x00, also
+called OP_0). The null dummy soft fork turns this relay rule into a
+consensus rule both for non-segwit transactions and segwit transactions,
+so that this method of mutating transactions is permanently eliminated
+from the network.
+
+Signaling for the null dummy soft fork is done by signaling support
+for segwit, and the null dummy soft fork will activate at the same time
+as segwit.
+
+For more information, please see [BIP147][].
+
+[BIP147]: https://github.com/bitcoin/bips/blob/master/bip-0147.mediawiki
+
+Low-level RPC changes
+---------------------
+
+- `importprunedfunds` only accepts two required arguments. Some versions accept
+ an optional third arg, which was always ignored. Make sure to never pass more
+ than two arguments.
+
+
+Linux ARM builds
+----------------
+
+With the 0.13.0 release, pre-built Linux ARM binaries were added to the set of
+uploaded executables. Additional detail on the ARM architecture targeted by each
+is provided below.
+
+The following extra files can be found in the download directory or torrent:
+
+- `bitcoin-${VERSION}-arm-linux-gnueabihf.tar.gz`: Linux binaries targeting
+ the 32-bit ARMv7-A architecture.
+- `bitcoin-${VERSION}-aarch64-linux-gnu.tar.gz`: Linux binaries targeting
+ the 64-bit ARMv8-A architecture.
+
+ARM builds are still experimental. If you have problems on a certain device or
+Linux distribution combination please report them on the bug tracker, it may be
+possible to resolve them. Note that the device you use must be (backward)
+compatible with the architecture targeted by the binary that you use.
+For example, a Raspberry Pi 2 Model B or Raspberry Pi 3 Model B (in its 32-bit
+execution state) device, can run the 32-bit ARMv7-A targeted binary. However,
+no model of Raspberry Pi 1 device can run either binary because they are all
+ARMv6 architecture devices that are not compatible with ARMv7-A or ARMv8-A.
+
+Note that Android is not considered ARM Linux in this context. The executables
+are not expected to work out of the box on Android.
+
+
+0.13.1 Change log
+=================
+
+Detailed release notes follow. This overview includes changes that affect
+behavior, not code moves, refactors and string updates. For convenience in locating
+the code changes and accompanying discussion, both the pull request and
+git merge commit are mentioned.
+
+### Consensus
+- #8636 `9dfa0c8` Implement NULLDUMMY softfork (BIP147) (jl2012)
+- #8848 `7a34a46` Add NULLDUMMY verify flag in bitcoinconsensus.h (jl2012)
+- #8937 `8b66659` Define start and end time for segwit deployment (sipa)
+
+### RPC and other APIs
+- #8581 `526d2b0` Drop misleading option in importprunedfunds (MarcoFalke)
+- #8699 `a5ec248` Remove createwitnessaddress RPC command (jl2012)
+- #8780 `794b007` Deprecate getinfo (MarcoFalke)
+- #8832 `83ad563` Throw JSONRPCError when utxo set can not be read (MarcoFalke)
+- #8884 `b987348` getblockchaininfo help: pruneheight is the lowest, not highest, block (luke-jr)
+- #8858 `3f508ed` rpc: Generate auth cookie in hex instead of base64 (laanwj)
+- #8951 `7c2bf4b` RPC/Mining: getblocktemplate: Update and fix formatting of help (luke-jr)
+
+### Block and transaction handling
+- #8611 `a9429ca` Reduce default number of blocks to check at startup (sipa)
+- #8634 `3e80ab7` Add policy: null signature for failed CHECK(MULTI)SIG (jl2012)
+- #8525 `1672225` Do not store witness txn in rejection cache (sipa)
+- #8499 `9777fe1` Add several policy limits and disable uncompressed keys for segwit scripts (jl2012)
+- #8526 `0027672` Make non-minimal OP_IF/NOTIF argument non-standard for P2WSH (jl2012)
+- #8524 `b8c79a0` Precompute sighashes (sipa)
+- #8651 `b8c79a0` Predeclare PrecomputedTransactionData as struct (sipa)
+
+### P2P protocol and network code
+- #8740 `42ea51a` No longer send local address in addrMe (laanwj)
+- #8427 `69d1cd2` Ignore `notfound` P2P messages (laanwj)
+- #8573 `4f84082` Set jonasschnellis dns-seeder filter flag (jonasschnelli)
+- #8712 `23feab1` Remove maxuploadtargets recommended minimum (jonasschnelli)
+- #8862 `7ae6242` Fix a few cases where messages were sent after requested disconnect (theuni)
+- #8393 `fe1975a` Support for compact blocks together with segwit (sipa)
+- #8282 `2611ad7` Feeler connections to increase online addrs in the tried table (EthanHeilman)
+- #8612 `2215c22` Check for compatibility with download in FindNextBlocksToDownload (sipa)
+- #8606 `bbf379b` Fix some locks (sipa)
+- #8594 `ab295bb` Do not add random inbound peers to addrman (gmaxwell)
+- #8940 `5b4192b` Add x9 service bit support to dnsseed.bluematt.me, seed.bitcoinstats.com (TheBlueMatt, cdecker)
+- #8944 `685e4c7` Remove bogus assert on number of oubound connections. (TheBlueMatt)
+- #8949 `0dbc48a` Be more agressive in getting connections to peers with relevant services (gmaxwell)
+
+### Build system
+- #8293 `fa5b249` Allow building libbitcoinconsensus without any univalue (luke-jr)
+- #8492 `8b0bdd3` Allow building bench_bitcoin by itself (luke-jr)
+- #8563 `147003c` Add configure check for -latomic (ajtowns)
+- #8626 `ea51b0f` Berkeley DB v6 compatibility fix (netsafe)
+- #8520 `75f2065` Remove check for `openssl/ec.h` (laanwj)
+
+### GUI
+- #8481 `d9f0d4e` Fix minimize and close bugs (adlawren)
+- #8487 `a37cec5` Persist the datadir after option reset (achow101)
+- #8697 `41fd852` Fix op order to append first alert (rodasmith)
+- #8678 `8e03382` Fix UI bug that could result in paying unexpected fee (jonasschnelli)
+- #8911 `7634d8e` Translate all files, even if wallet disabled (laanwj)
+- #8540 `1db3352` Fix random segfault when closing "Choose data directory" dialog (laanwj)
+- #7579 `f1c0d78` Show network/chain errors in the GUI (jonasschnelli)
+
+### Wallet
+- #8443 `464dedd` Trivial cleanup of HD wallet changes (jonasschnelli)
+- #8539 `cb07f19` CDB: fix debug output (crowning-)
+- #8664 `091cdeb` Fix segwit-related wallet bug (sdaftuar)
+- #8693 `c6a6291` Add witness address to address book (instagibbs)
+- #8765 `6288659` Remove "unused" ThreadFlushWalletDB from removeprunedfunds (jonasschnelli)
+
+### Tests and QA
+- #8713 `ae8c7df` create_cache: Delete temp dir when done (MarcoFalke)
+- #8716 `e34374e` Check legacy wallet as well (MarcoFalke)
+- #8750 `d6ebe13` Refactor RPCTestHandler to prevent TimeoutExpired (MarcoFalke)
+- #8652 `63462c2` remove root test directory for RPC tests (yurizhykin)
+- #8724 `da94272` walletbackup: Sync blocks inside the loop (MarcoFalke)
+- #8400 `bea02dc` enable rpcbind_test (yurizhykin)
+- #8417 `f70be14` Add walletdump RPC test (including HD- & encryption-tests) (jonasschnelli)
+- #8419 `a7aa3cc` Enable size accounting in mining unit tests (sdaftuar)
+- #8442 `8bb1efd` Rework hd wallet dump test (MarcoFalke)
+- #8528 `3606b6b` Update p2p-segwit.py to reflect correct behavior (instagibbs)
+- #8531 `a27cdd8` abandonconflict: Use assert_equal (MarcoFalke)
+- #8667 `6b07362` Fix SIGHASH_SINGLE bug in test_framework SignatureHash (jl2012)
+- #8673 `03b0196` Fix obvious assignment/equality error in test (JeremyRubin)
+- #8739 `cef633c` Fix broken sendcmpct test in p2p-compactblocks.py (sdaftuar)
+- #8418 `ff893aa` Add tests for compact blocks (sdaftuar)
+- #8803 `375437c` Ping regularly in p2p-segwit.py to keep connection alive (jl2012)
+- #8827 `9bbe66e` Split up slow RPC calls to avoid pruning test timeouts (sdaftuar)
+- #8829 `2a8bca4` Add bitcoin-tx JSON tests (jnewbery)
+- #8834 `1dd1783` blockstore: Switch to dumb dbm (MarcoFalke)
+- #8835 `d87227d` nulldummy.py: Don't run unused code (MarcoFalke)
+- #8836 `eb18cc1` bitcoin-util-test.py should fail if the output file is empty (jnewbery)
+- #8839 `31ab2f8` Avoid ConnectionResetErrors during RPC tests (laanwj)
+- #8840 `cbc3fe5` Explicitly set encoding to utf8 when opening text files (laanwj)
+- #8841 `3e4abb5` Fix nulldummy test (jl2012)
+- #8854 `624a007` Fix race condition in p2p-compactblocks test (sdaftuar)
+- #8857 `1f60d45` mininode: Only allow named args in wait_until (MarcoFalke)
+- #8860 `0bee740` util: Move wait_bitcoinds() into stop_nodes() (MarcoFalke)
+- #8882 `b73f065` Fix race conditions in p2p-compactblocks.py and sendheaders.py (sdaftuar)
+- #8904 `cc6f551` Fix compact block shortids for a test case (dagurval)
+
+### Documentation
+- #8754 `0e2c6bd` Target protobuf 2.6 in OS X build notes. (fanquake)
+- #8461 `b17a3f9` Document return value of networkhashps for getmininginfo RPC endpoint (jlopp)
+- #8512 `156e305` Corrected JSON typo on setban of net.cpp (sevastos)
+- #8683 `8a7d7ff` Fix incorrect file name bitcoin.qrc (bitcoinsSG)
+- #8891 `5e0dd9e` Update bips.md for Segregated Witness (fanquake)
+- #8545 `863ae74` Update git-subtree-check.sh README (MarcoFalke)
+- #8607 `486650a` Fix doxygen off-by-one comments, fix typos (MarcoFalke)
+- #8560 `c493f43` Fix two VarInt examples in serialize.h (cbarcenas)
+- #8737 `084cae9` UndoReadFromDisk works on undo files (rev), not on block files (paveljanik)
+- #8625 `0a35573` Clarify statement about parallel jobs in rpc-tests.py (isle2983)
+- #8624 `0e6d753` build: Mention curl (MarcoFalke)
+- #8604 `b09e13c` build,doc: Update for 0.13.0+ and OpenBSD 5.9 (laanwj)
+- #8939 `06d15fb` Update implemented bips for 0.13.1 (sipa)
+
+### Miscellaneous
+- #8742 `d31ac72` Specify Protobuf version 2 in paymentrequest.proto (fanquake)
+- #8414,#8558,#8676,#8700,#8701,#8702 Add missing copyright headers (isle2983, kazcw)
+- #8899 `4ed2627` Fix wake from sleep issue with Boost 1.59.0 (fanquake)
+- #8817 `bcf3806` update bitcoin-tx to output witness data (jnewbery)
+- #8513 `4e5fc31` Fix a type error that would not compile on OSX. (JeremyRubin)
+- #8392 `30eac2d` Fix several node initialization issues (sipa)
+- #8548 `305d8ac` Use `__func__` to get function name for output printing (MarcoFalke)
+- #8291 `a987431` [util] CopyrightHolders: Check for untranslated substitution (MarcoFalke)
+
+Credits
+=======
+
+Thanks to everyone who directly contributed to this release:
+
+- adlawren
+- Alexey Vesnin
+- Anders Øyvind Urke-Sætre
+- Andrew Chow
+- Anthony Towns
+- BtcDrak
+- Chris Stewart
+- Christian Barcenas
+- Christian Decker
+- Cory Fields
+- crowning-
+- Dagur Valberg Johannsson
+- David A. Harding
+- Eric Lombrozo
+- Ethan Heilman
+- fanquake
+- Gaurav Rana
+- Gregory Maxwell
+- instagibbs
+- isle2983
+- Jameson Lopp
+- Jeremy Rubin
+- jnewbery
+- Johnson Lau
+- Jonas Schnelli
+- jonnynewbs
+- Justin Camarena
+- Kaz Wesley
+- leijurv
+- Luke Dashjr
+- MarcoFalke
+- Marty Jones
+- Matt Corallo
+- Micha
+- Michael Ford
+- mruddy
+- Pavel Janík
+- Pieter Wuille
+- rodasmith
+- Sev
+- Suhas Daftuar
+- whythat
+- Wladimir J. van der Laan
+
+As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/).
diff --git a/doc/release-process.md b/doc/release-process.md
index 394b159b31..61f05b0771 100644
--- a/doc/release-process.md
+++ b/doc/release-process.md
@@ -5,18 +5,24 @@ Before every release candidate:
* Update translations (ping wumpus on IRC) see [translation_process.md](https://github.com/bitcoin/bitcoin/blob/master/doc/translation_process.md#synchronising-translations).
+* Update manpages, see [gen-manpages.sh](https://github.com/bitcoin/bitcoin/blob/master/contrib/devtools/README.md#gen-manpagessh).
+
Before every minor and major release:
* Update [bips.md](bips.md) to account for changes since the last release.
* Update version in sources (see below)
* Write release notes (see below)
+* Update `src/chainparams.cpp` nMinimumChainWork with information from the getblockchaininfo rpc.
Before every major release:
* Update hardcoded [seeds](/contrib/seeds/README.md), see [this pull request](https://github.com/bitcoin/bitcoin/pull/7415) for an example.
+* Update [`BLOCK_CHAIN_SIZE`](/src/qt/intro.cpp) to the current size plus some overhead.
### First time / New builders
+If you're using the automated script (found in [contrib/gitian-build.sh](/contrib/gitian-build.sh)), then at this point you should run it with the "--setup" command. Otherwise ignore this.
+
Check out the source code in the following directory hierarchy.
cd /path/to/your/toplevel/build
@@ -60,6 +66,8 @@ Tag version (or release candidate) in git
### Setup and perform Gitian builds
+If you're using the automated script (found in [contrib/gitian-build.sh](/contrib/gitian-build.sh)), then at this point you should run it with the "--build" command. Otherwise ignore this.
+
Setup Gitian descriptors:
pushd ./bitcoin
@@ -272,6 +280,8 @@ bitcoin.org (see below for bitcoin.org update instructions).
- Notify BlueMatt so that he can start building [the PPAs](https://launchpad.net/~bitcoin/+archive/ubuntu/bitcoin)
- - Add release notes for the new version to the directory `doc/release-notes` in git master
+ - Archive release notes for the new version to `doc/release-notes/` (branch `master` and branch of the release)
+
+ - Create a [new GitHub release](https://github.com/bitcoin/bitcoin/releases/new) with a link to the archived release notes.
- Celebrate
diff --git a/doc/shared-libraries.md b/doc/shared-libraries.md
index ec6f16c8aa..dc363582cc 100644
--- a/doc/shared-libraries.md
+++ b/doc/shared-libraries.md
@@ -30,12 +30,17 @@ The interface is defined in the C header `bitcoinconsensus.h` located in `src/s
- `bitcoinconsensus_SCRIPT_FLAGS_VERIFY_NONE`
- `bitcoinconsensus_SCRIPT_FLAGS_VERIFY_P2SH` - Evaluate P2SH ([BIP16](https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki)) subscripts
- `bitcoinconsensus_SCRIPT_FLAGS_VERIFY_DERSIG` - Enforce strict DER ([BIP66](https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki)) compliance
+- `bitcoinconsensus_SCRIPT_FLAGS_VERIFY_NULLDUMMY` - Enforce NULLDUMMY ([BIP147](https://github.com/bitcoin/bips/blob/master/bip-0147.mediawiki))
+- `bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY` - Enable CHECKLOCKTIMEVERIFY ([BIP65](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki))
+- `bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKSEQUENCEVERIFY` - Enable CHECKSEQUENCEVERIFY ([BIP112](https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki))
+- `bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS` - Enable WITNESS ([BIP141](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki))
##### Errors
- `bitcoinconsensus_ERR_OK` - No errors with input parameters *(see the return value of `bitcoinconsensus_verify_script` for the verification status)*
- `bitcoinconsensus_ERR_TX_INDEX` - An invalid index for `txTo`
- `bitcoinconsensus_ERR_TX_SIZE_MISMATCH` - `txToLen` did not match with the size of `txTo`
- `bitcoinconsensus_ERR_DESERIALIZE` - An error deserializing `txTo`
+- `bitcoinconsensus_ERR_AMOUNT_REQUIRED` - Input amount is required if WITNESS is used
### Example Implementations
- [NBitcoin](https://github.com/NicolasDorier/NBitcoin/blob/master/NBitcoin/Script.cs#L814) (.NET Bindings)
diff --git a/doc/tor.md b/doc/tor.md
index 79f1563021..a05979fca8 100644
--- a/doc/tor.md
+++ b/doc/tor.md
@@ -99,10 +99,10 @@ This means that if Tor is running (and proper authentication has been configured
Bitcoin Core automatically creates a hidden service to listen on. This will positively
affect the number of available .onion nodes.
-This new feature is enabled by default if Bitcoin Core is listening, and
-a connection to Tor can be made. It can be configured with the `-listenonion`,
-`-torcontrol` and `-torpassword` settings. To show verbose debugging
-information, pass `-debug=tor`.
+This new feature is enabled by default if Bitcoin Core is listening (`-listen`), and
+requires a Tor connection to work. It can be explicitly disabled with `-listenonion=0`
+and, if not disabled, configured using the `-torcontrol` and `-torpassword` settings.
+To show verbose debugging information, pass `-debug=tor`.
Connecting to Tor's control socket API requires one of two authentication methods to be
configured. For cookie authentication the user running bitcoind must have write access
@@ -113,4 +113,13 @@ the user running bitcoind to the same group and setting permissions appropriatel
Debian-based systems the user running bitcoind can be added to the debian-tor group,
which has the appropriate permissions. An alternative authentication method is the use
of the `-torpassword` flag and a `hash-password` which can be enabled and specified in
-Tor configuration. \ No newline at end of file
+Tor configuration.
+
+4. Privacy recommendations
+---------------------------
+
+- Do not add anything but bitcoin ports to the hidden service created in section 2.
+ If you run a web service too, create a new hidden service for that.
+ Otherwise it is trivial to link them, which may reduce privacy. Hidden
+ services created automatically (as in section 3) always have only one port
+ open.
diff --git a/doc/translation_process.md b/doc/translation_process.md
index a443a16fe2..9e9ced2457 100644
--- a/doc/translation_process.md
+++ b/doc/translation_process.md
@@ -6,7 +6,7 @@ The Bitcoin-Core project has been designed to support multiple localisations. Th
### Helping to translate (using Transifex)
Transifex is setup to monitor the Github repo for updates, and when code containing new translations is found, Transifex will process any changes. It may take several hours after a pull-request has been merged, to appear in the Transifex web interface.
-Multiple language support is critical in assisting Bitcoin’s global adoption, and growth. One of Bitcoin’s greatest strengths is cross-boarder money transfers, any help making that easier is greatly appreciated.
+Multiple language support is critical in assisting Bitcoin’s global adoption, and growth. One of Bitcoin’s greatest strengths is cross-border money transfers, any help making that easier is greatly appreciated.
See the [Transifex Bitcoin project](https://www.transifex.com/projects/p/bitcoin/) to assist in translations. You should also join the translation mailing list for announcements - see details below.
diff --git a/doc/travis-ci.txt b/doc/travis-ci.md
index 06410405d6..38085cec35 100644
--- a/doc/travis-ci.txt
+++ b/doc/travis-ci.md
@@ -1,5 +1,8 @@
+Travis CI
+=========
+
Support for using travis-ci has been added in order to automate pull-testing.
-See https://travis-ci.org/ for more info
+See [travis-ci.org](https://travis-ci.org/) for more info
This procedure is different than the pull-tester that came before it in a few
ways.
@@ -7,7 +10,7 @@ ways.
There is nothing to administer. This is a major feature as it means
that builds have no local state. Because there is no ability to login to the
builders to install packages (tools, dependencies, etc), the entire build
-procedure must instead be controlled by a declarative script (.travis.yml).
+procedure must instead be controlled by a declarative script `.travis.yml`.
This script declares each build configuration, creates virtual machines as
necessary, builds, then discards the virtual machines.
@@ -16,7 +19,7 @@ than a single pass/fail. This helps to catch build failures and logic errors
that present on platforms other than the ones the author has tested. This
matrix is defined in the build script and can be changed at any time.
-All builders use the dependency-generator in the depends dir, rather than
+All builders use the dependency-generator in the [depends dir](/depends), rather than
using apt-get to install build dependencies. This guarantees that the tester
is using the same versions as Gitian, so the build results are nearly identical
to what would be found in a final release. However, this also means that builds
diff --git a/doc/unit-tests.md b/doc/unit-tests.md
deleted file mode 100644
index afaece829c..0000000000
--- a/doc/unit-tests.md
+++ /dev/null
@@ -1,18 +0,0 @@
-Compiling/running unit tests
-------------------------------------
-
-Unit tests will be automatically compiled if dependencies were met in `./configure`
-and tests weren't explicitly disabled.
-
-After configuring, they can be run with `make check`.
-
-To run the bitcoind tests manually, launch `src/test/test_bitcoin`.
-
-To add more bitcoind 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.
-
-To run the bitcoin-qt tests manually, launch `src/qt/test/test_bitcoin-qt`
-
-To add more bitcoin-qt tests, add them to the `src/qt/test/` directory and
-the `src/qt/test/test_main.cpp` file.
diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py
index 771f6c7a0f..be31cbbdd3 100755
--- a/qa/pull-tester/rpc-tests.py
+++ b/qa/pull-tester/rpc-tests.py
@@ -100,75 +100,88 @@ if ENABLE_ZMQ:
testScripts = [
# longest test should go first, to favor running tests in parallel
- 'p2p-fullblocktest.py',
+ 'wallet-hd.py',
'walletbackup.py',
- 'bip68-112-113-p2p.py',
+ # vv Tests less than 5m vv
+ 'p2p-fullblocktest.py',
+ 'fundrawtransaction.py',
+ 'p2p-compactblocks.py',
+ 'segwit.py',
+ # vv Tests less than 2m vv
'wallet.py',
'wallet-accounts.py',
- 'wallet-hd.py',
+ 'p2p-segwit.py',
'wallet-dump.py',
'listtransactions.py',
+ # vv Tests less than 60s vv
+ 'sendheaders.py',
+ 'zapwallettxes.py',
+ 'importmulti.py',
+ 'mempool_limit.py',
+ 'merkle_blocks.py',
'receivedby.py',
+ 'abandonconflict.py',
+ 'bip68-112-113-p2p.py',
+ 'rawtransactions.py',
+ 'reindex.py',
+ # vv Tests less than 30s vv
'mempool_resurrect_test.py',
'txn_doublespend.py --mineblock',
- 'p2p-segwit.py',
- 'segwit.py',
'txn_clone.py',
'getchaintips.py',
- 'rawtransactions.py',
'rest.py',
'mempool_spendcoinbase.py',
'mempool_reorg.py',
- 'mempool_limit.py',
'httpbasics.py',
'multi_rpc.py',
- 'zapwallettxes.py',
'proxy_test.py',
- 'merkle_blocks.py',
- 'fundrawtransaction.py',
'signrawtransactions.py',
'nodehandling.py',
- 'reindex.py',
'decodescript.py',
'blockchain.py',
'disablewallet.py',
- 'sendheaders.py',
'keypool.py',
'p2p-mempool.py',
'prioritise_transaction.py',
'invalidblockrequest.py',
'invalidtxrequest.py',
- 'abandonconflict.py',
'p2p-versionbits-warning.py',
+ 'preciousblock.py',
'importprunedfunds.py',
'signmessages.py',
- 'p2p-compactblocks.py',
+ 'nulldummy.py',
+ 'import-rescan.py',
]
if ENABLE_ZMQ:
testScripts.append('zmq_test.py')
testScriptsExt = [
+ 'pruning.py',
+ # vv Tests less than 20m vv
+ 'smartfees.py',
+ # vv Tests less than 5m vv
+ 'maxuploadtarget.py',
+ 'mempool_packages.py',
+ # vv Tests less than 2m vv
+ 'bip68-sequence.py',
+ 'getblocktemplate_longpoll.py',
+ # vv Tests less than 60s vv
'bip9-softforks.py',
+ 'p2p-feefilter.py',
+ 'rpcbind_test.py',
+ # vv Tests less than 30s vv
'bip65-cltv.py',
'bip65-cltv-p2p.py',
- 'bip68-sequence.py',
'bipdersig-p2p.py',
'bipdersig.py',
- 'getblocktemplate_longpoll.py',
'getblocktemplate_proposals.py',
'txn_doublespend.py',
'txn_clone.py --mineblock',
'forknotify.py',
'invalidateblock.py',
- 'rpcbind_test.py',
- 'smartfees.py',
'maxblocksinflight.py',
'p2p-acceptblock.py',
- 'mempool_packages.py',
- 'maxuploadtarget.py',
'replace-by-fee.py',
- 'p2p-feefilter.py',
- 'pruning.py', # leave pruning last as it takes a REALLY long time
]
@@ -215,8 +228,8 @@ def runtests():
time_sum += duration
print('\n' + BOLD[1] + name + BOLD[0] + ":")
- print(stdout)
- print('stderr:\n' if not stderr == '' else '', stderr)
+ print('' if passed else stdout + '\n', end='')
+ print('' if stderr == '' else 'stderr:\n' + stderr + '\n', end='')
results += "%s | %s | %s s\n" % (name.ljust(max_len_name), str(passed).ljust(6), duration)
print("Pass: %s%s%s, Duration: %s s\n" % (BOLD[1], passed, BOLD[0], duration))
results += BOLD[1] + "\n%s | %s | %s s (accumulated)" % ("ALL".ljust(max_len_name), str(all_passed).ljust(6), time_sum) + BOLD[0]
@@ -243,6 +256,10 @@ class RPCTestHandler:
self.test_list = test_list
self.flags = flags
self.num_running = 0
+ # In case there is a graveyard of zombie bitcoinds, we can apply a
+ # pseudorandom offset to hopefully jump over them.
+ # (625 is PORT_RANGE/MAX_NODES)
+ self.portseed_offset = int(time.time() * 1000) % 625
self.jobs = []
def get_next(self):
@@ -250,22 +267,28 @@ class RPCTestHandler:
# Add tests
self.num_running += 1
t = self.test_list.pop(0)
- port_seed = ["--portseed=%s" % len(self.test_list)]
+ port_seed = ["--portseed={}".format(len(self.test_list) + self.portseed_offset)]
+ log_stdout = tempfile.SpooledTemporaryFile(max_size=2**16)
+ log_stderr = tempfile.SpooledTemporaryFile(max_size=2**16)
self.jobs.append((t,
time.time(),
subprocess.Popen((RPC_TESTS_DIR + t).split() + self.flags + port_seed,
universal_newlines=True,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)))
+ stdout=log_stdout,
+ stderr=log_stderr),
+ log_stdout,
+ log_stderr))
if not self.jobs:
raise IndexError('pop from empty list')
while True:
# Return first proc that finishes
time.sleep(.5)
for j in self.jobs:
- (name, time0, proc) = j
+ (name, time0, proc, log_out, log_err) = j
if proc.poll() is not None:
- (stdout, stderr) = proc.communicate(timeout=3)
+ log_out.seek(0), log_err.seek(0)
+ [stdout, stderr] = [l.read().decode('utf-8') for l in (log_out, log_err)]
+ log_out.close(), log_err.close()
passed = stderr == "" and proc.returncode == 0
self.num_running -= 1
self.jobs.remove(j)
diff --git a/qa/pull-tester/run-bitcoind-for-test.sh.in b/qa/pull-tester/run-bitcoind-for-test.sh.in
deleted file mode 100644
index 14ae08e4e5..0000000000
--- a/qa/pull-tester/run-bitcoind-for-test.sh.in
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/bash
-# Copyright (c) 2013-2014 The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#
-DATADIR="@abs_top_builddir@/.bitcoin"
-rm -rf "$DATADIR"
-mkdir -p "$DATADIR"/regtest
-touch "$DATADIR/regtest/debug.log"
-tail -q -n 1 -F "$DATADIR/regtest/debug.log" | grep -m 1 -q "Done loading" &
-WAITER=$!
-PORT=`expr 10000 + $$ % 55536`
-"@abs_top_builddir@/src/bitcoind@EXEEXT@" -connect=0.0.0.0 -datadir="$DATADIR" -rpcuser=user -rpcpassword=pass -listen -keypool=3 -debug -debug=net -logtimestamps -checkmempool=0 -relaypriority=0 -port=$PORT -whitelist=127.0.0.1 -regtest -rpcport=`expr $PORT + 1` &
-BITCOIND=$!
-
-#Install a watchdog.
-(sleep 10 && kill -0 $WAITER 2>/dev/null && kill -9 $BITCOIND $$)&
-wait $WAITER
-
-if [ -n "$TIMEOUT" ]; then
- timeout "$TIMEOUT"s "$@" $PORT
- RETURN=$?
-else
- "$@" $PORT
- RETURN=$?
-fi
-
-(sleep 15 && kill -0 $BITCOIND 2>/dev/null && kill -9 $BITCOIND $$)&
-kill $BITCOIND && wait $BITCOIND
-
-# timeout returns 124 on timeout, otherwise the return value of the child
-
-# If $RETURN is not 0, the test failed. Dump the tail of the debug log.
-if [ $RETURN -ne 0 ]; then tail -n 200 $DATADIR/regtest/debug.log; fi
-
-exit $RETURN
diff --git a/qa/rpc-tests/bip68-112-113-p2p.py b/qa/rpc-tests/bip68-112-113-p2p.py
index 55b3e2a04a..fc3efb1abf 100755
--- a/qa/rpc-tests/bip68-112-113-p2p.py
+++ b/qa/rpc-tests/bip68-112-113-p2p.py
@@ -289,6 +289,7 @@ class BIP68_112_113Test(ComparisonTestFramework):
# BIP113 test transaction will be modified before each use to put in appropriate block time
bip113tx_v1 = self.create_transaction(self.nodes[0], bip113input, self.nodeaddress, Decimal("49.98"))
bip113tx_v1.vin[0].nSequence = 0xFFFFFFFE
+ bip113tx_v1.nVersion = 1
bip113tx_v2 = self.create_transaction(self.nodes[0], bip113input, self.nodeaddress, Decimal("49.98"))
bip113tx_v2.vin[0].nSequence = 0xFFFFFFFE
bip113tx_v2.nVersion = 2
diff --git a/qa/rpc-tests/bip9-softforks.py b/qa/rpc-tests/bip9-softforks.py
index 979d1410c2..c42ed44c25 100755
--- a/qa/rpc-tests/bip9-softforks.py
+++ b/qa/rpc-tests/bip9-softforks.py
@@ -81,6 +81,9 @@ class BIP9SoftForksTest(ComparisonTestFramework):
return info['bip9_softforks'][key]
def test_BIP(self, bipName, activated_version, invalidate, invalidatePostSignature, bitno):
+ assert_equal(self.get_bip9_status(bipName)['status'], 'defined')
+ assert_equal(self.get_bip9_status(bipName)['since'], 0)
+
# generate some coins for later
self.coinbase_blocks = self.nodes[0].generate(2)
self.height = 3 # height of the next block to build
@@ -89,6 +92,7 @@ class BIP9SoftForksTest(ComparisonTestFramework):
self.last_block_time = int(time.time())
assert_equal(self.get_bip9_status(bipName)['status'], 'defined')
+ assert_equal(self.get_bip9_status(bipName)['since'], 0)
tmpl = self.nodes[0].getblocktemplate({})
assert(bipName not in tmpl['rules'])
assert(bipName not in tmpl['vbavailable'])
@@ -101,6 +105,7 @@ class BIP9SoftForksTest(ComparisonTestFramework):
yield TestInstance(test_blocks, sync_every_block=False)
assert_equal(self.get_bip9_status(bipName)['status'], 'started')
+ assert_equal(self.get_bip9_status(bipName)['since'], 144)
tmpl = self.nodes[0].getblocktemplate({})
assert(bipName not in tmpl['rules'])
assert_equal(tmpl['vbavailable'][bipName], bitno)
@@ -117,6 +122,7 @@ class BIP9SoftForksTest(ComparisonTestFramework):
yield TestInstance(test_blocks, sync_every_block=False)
assert_equal(self.get_bip9_status(bipName)['status'], 'started')
+ assert_equal(self.get_bip9_status(bipName)['since'], 144)
tmpl = self.nodes[0].getblocktemplate({})
assert(bipName not in tmpl['rules'])
assert_equal(tmpl['vbavailable'][bipName], bitno)
@@ -133,6 +139,7 @@ class BIP9SoftForksTest(ComparisonTestFramework):
yield TestInstance(test_blocks, sync_every_block=False)
assert_equal(self.get_bip9_status(bipName)['status'], 'locked_in')
+ assert_equal(self.get_bip9_status(bipName)['since'], 432)
tmpl = self.nodes[0].getblocktemplate({})
assert(bipName not in tmpl['rules'])
@@ -142,6 +149,7 @@ class BIP9SoftForksTest(ComparisonTestFramework):
yield TestInstance(test_blocks, sync_every_block=False)
assert_equal(self.get_bip9_status(bipName)['status'], 'locked_in')
+ assert_equal(self.get_bip9_status(bipName)['since'], 432)
tmpl = self.nodes[0].getblocktemplate({})
assert(bipName not in tmpl['rules'])
@@ -167,6 +175,7 @@ class BIP9SoftForksTest(ComparisonTestFramework):
yield TestInstance([[block, True]])
assert_equal(self.get_bip9_status(bipName)['status'], 'active')
+ assert_equal(self.get_bip9_status(bipName)['since'], 576)
tmpl = self.nodes[0].getblocktemplate({})
assert(bipName in tmpl['rules'])
assert(bipName not in tmpl['vbavailable'])
@@ -195,7 +204,6 @@ class BIP9SoftForksTest(ComparisonTestFramework):
# Restart all
self.test.block_store.close()
stop_nodes(self.nodes)
- wait_bitcoinds()
shutil.rmtree(self.options.tmpdir)
self.setup_chain()
self.setup_network()
diff --git a/qa/rpc-tests/create_cache.py b/qa/rpc-tests/create_cache.py
index b6161e0917..1ace6310d0 100755
--- a/qa/rpc-tests/create_cache.py
+++ b/qa/rpc-tests/create_cache.py
@@ -12,9 +12,15 @@ from test_framework.test_framework import BitcoinTestFramework
class CreateCache(BitcoinTestFramework):
+ def __init__(self):
+ super().__init__()
+
+ # Test network and test nodes are not required:
+ self.num_nodes = 0
+ self.nodes = []
+
def setup_network(self):
- # Don't setup any test nodes
- self.options.noshutdown = True
+ pass
def run_test(self):
pass
diff --git a/qa/rpc-tests/forknotify.py b/qa/rpc-tests/forknotify.py
index 5a3f75c808..a1901aedab 100755
--- a/qa/rpc-tests/forknotify.py
+++ b/qa/rpc-tests/forknotify.py
@@ -22,7 +22,7 @@ class ForkNotifyTest(BitcoinTestFramework):
def setup_network(self):
self.nodes = []
self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt")
- with open(self.alert_filename, 'w') as f:
+ with open(self.alert_filename, 'w', encoding='utf8') as f:
pass # Just open then close to create zero-length file
self.nodes.append(start_node(0, self.options.tmpdir,
["-blockversion=2", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""]))
@@ -44,7 +44,7 @@ class ForkNotifyTest(BitcoinTestFramework):
self.nodes[1].generate(1)
self.sync_all()
- with open(self.alert_filename, 'r') as f:
+ with open(self.alert_filename, 'r', encoding='utf8') as f:
alert_text = f.read()
if len(alert_text) == 0:
@@ -56,7 +56,7 @@ class ForkNotifyTest(BitcoinTestFramework):
self.nodes[1].generate(1)
self.sync_all()
- with open(self.alert_filename, 'r') as f:
+ with open(self.alert_filename, 'r', encoding='utf8') as f:
alert_text2 = f.read()
if alert_text != alert_text2:
diff --git a/qa/rpc-tests/fundrawtransaction.py b/qa/rpc-tests/fundrawtransaction.py
index eeb8476634..82e148c55a 100755
--- a/qa/rpc-tests/fundrawtransaction.py
+++ b/qa/rpc-tests/fundrawtransaction.py
@@ -470,7 +470,6 @@ class RawTransactionsTest(BitcoinTestFramework):
self.nodes[1].encryptwallet("test")
self.nodes.pop(1)
stop_nodes(self.nodes)
- wait_bitcoinds()
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)
# This test is not meant to test fee estimation and we'd like
@@ -485,6 +484,23 @@ class RawTransactionsTest(BitcoinTestFramework):
self.is_network_split=False
self.sync_all()
+ # drain the keypool
+ self.nodes[1].getnewaddress()
+ inputs = []
+ outputs = {self.nodes[0].getnewaddress():1.1}
+ rawTx = self.nodes[1].createrawtransaction(inputs, outputs)
+ # fund a transaction that requires a new key for the change output
+ # creating the key must be impossible because the wallet is locked
+ try:
+ fundedTx = self.nodes[1].fundrawtransaction(rawTx)
+ raise AssertionError("Wallet unlocked without passphrase")
+ except JSONRPCException as e:
+ assert('Keypool ran out' in e.error['message'])
+
+ #refill the keypool
+ self.nodes[1].walletpassphrase("test", 100)
+ self.nodes[1].walletlock()
+
try:
self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1.2)
raise AssertionError("Wallet unlocked without passphrase")
@@ -499,7 +515,7 @@ class RawTransactionsTest(BitcoinTestFramework):
fundedTx = self.nodes[1].fundrawtransaction(rawTx)
#now we need to unlock
- self.nodes[1].walletpassphrase("test", 100)
+ self.nodes[1].walletpassphrase("test", 600)
signedTx = self.nodes[1].signrawtransaction(fundedTx['hex'])
txId = self.nodes[1].sendrawtransaction(signedTx['hex'])
self.nodes[1].generate(1)
diff --git a/qa/rpc-tests/import-rescan.py b/qa/rpc-tests/import-rescan.py
new file mode 100755
index 0000000000..e683df26db
--- /dev/null
+++ b/qa/rpc-tests/import-rescan.py
@@ -0,0 +1,155 @@
+#!/usr/bin/env python3
+# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (start_nodes, connect_nodes, sync_blocks, assert_equal)
+from decimal import Decimal
+
+import collections
+import enum
+import itertools
+import functools
+
+Call = enum.Enum("Call", "single multi")
+Data = enum.Enum("Data", "address pub priv")
+ImportNode = collections.namedtuple("ImportNode", "rescan")
+
+
+def call_import_rpc(call, data, address, scriptPubKey, pubkey, key, label, node, rescan):
+ """Helper that calls a wallet import RPC on a bitcoin node."""
+ watchonly = data != Data.priv
+ if call == Call.single:
+ if data == Data.address:
+ response = node.importaddress(address, label, rescan)
+ elif data == Data.pub:
+ response = node.importpubkey(pubkey, label, rescan)
+ elif data == Data.priv:
+ response = node.importprivkey(key, label, rescan)
+ assert_equal(response, None)
+ elif call == Call.multi:
+ response = node.importmulti([{
+ "scriptPubKey": {
+ "address": address
+ },
+ "pubkeys": [pubkey] if data == Data.pub else [],
+ "keys": [key] if data == Data.priv else [],
+ "label": label,
+ "watchonly": watchonly
+ }], {"rescan": rescan})
+ assert_equal(response, [{"success": True}])
+ return watchonly
+
+
+# List of RPCs that import a wallet key or address in various ways.
+IMPORT_RPCS = [functools.partial(call_import_rpc, call, data) for call, data in itertools.product(Call, Data)]
+
+# List of bitcoind nodes that will import keys.
+IMPORT_NODES = [
+ ImportNode(rescan=True),
+ ImportNode(rescan=False),
+]
+
+
+class ImportRescanTest(BitcoinTestFramework):
+ def __init__(self):
+ super().__init__()
+ self.num_nodes = 1 + len(IMPORT_NODES)
+
+ def setup_network(self):
+ extra_args = [["-debug=1"] for _ in range(self.num_nodes)]
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args)
+ for i in range(1, self.num_nodes):
+ connect_nodes(self.nodes[i], 0)
+
+ def run_test(self):
+ # Create one transaction on node 0 with a unique amount and label for
+ # each possible type of wallet import RPC.
+ import_rpc_variants = []
+ for i, import_rpc in enumerate(IMPORT_RPCS):
+ label = "label{}".format(i)
+ addr = self.nodes[0].validateaddress(self.nodes[0].getnewaddress(label))
+ key = self.nodes[0].dumpprivkey(addr["address"])
+ amount = 24.9375 - i * .0625
+ txid = self.nodes[0].sendtoaddress(addr["address"], amount)
+ import_rpc = functools.partial(import_rpc, addr["address"], addr["scriptPubKey"], addr["pubkey"], key,
+ label)
+ import_rpc_variants.append((import_rpc, label, amount, txid, addr))
+
+ self.nodes[0].generate(1)
+ assert_equal(self.nodes[0].getrawmempool(), [])
+ sync_blocks(self.nodes)
+
+ # For each importing node and variation of wallet import RPC, invoke
+ # the RPC and check the results from getbalance and listtransactions.
+ for node, import_node in zip(self.nodes[1:], IMPORT_NODES):
+ for import_rpc, label, amount, txid, addr in import_rpc_variants:
+ watchonly = import_rpc(node, import_node.rescan)
+
+ balance = node.getbalance(label, 0, True)
+ if import_node.rescan:
+ assert_equal(balance, amount)
+ else:
+ assert_equal(balance, 0)
+
+ txs = node.listtransactions(label, 10000, 0, True)
+ if import_node.rescan:
+ assert_equal(len(txs), 1)
+ assert_equal(txs[0]["account"], label)
+ assert_equal(txs[0]["address"], addr["address"])
+ assert_equal(txs[0]["amount"], amount)
+ assert_equal(txs[0]["category"], "receive")
+ assert_equal(txs[0]["label"], label)
+ assert_equal(txs[0]["txid"], txid)
+ assert_equal(txs[0]["confirmations"], 1)
+ assert_equal("trusted" not in txs[0], True)
+ if watchonly:
+ assert_equal(txs[0]["involvesWatchonly"], True)
+ else:
+ assert_equal("involvesWatchonly" not in txs[0], True)
+ else:
+ assert_equal(len(txs), 0)
+
+ # Create spends for all the imported addresses.
+ spend_txids = []
+ fee = self.nodes[0].getnetworkinfo()["relayfee"]
+ for import_rpc, label, amount, txid, addr in import_rpc_variants:
+ raw_tx = self.nodes[0].getrawtransaction(txid)
+ decoded_tx = self.nodes[0].decoderawtransaction(raw_tx)
+ input_vout = next(out["n"] for out in decoded_tx["vout"]
+ if out["scriptPubKey"]["addresses"] == [addr["address"]])
+ inputs = [{"txid": txid, "vout": input_vout}]
+ outputs = {self.nodes[0].getnewaddress(): Decimal(amount) - fee}
+ raw_spend_tx = self.nodes[0].createrawtransaction(inputs, outputs)
+ signed_spend_tx = self.nodes[0].signrawtransaction(raw_spend_tx)
+ spend_txid = self.nodes[0].sendrawtransaction(signed_spend_tx["hex"])
+ spend_txids.append(spend_txid)
+
+ self.nodes[0].generate(1)
+ assert_equal(self.nodes[0].getrawmempool(), [])
+ sync_blocks(self.nodes)
+
+ # Check the results from getbalance and listtransactions after the spends.
+ for node, import_node in zip(self.nodes[1:], IMPORT_NODES):
+ txs = node.listtransactions("*", 10000, 0, True)
+ for (import_rpc, label, amount, txid, addr), spend_txid in zip(import_rpc_variants, spend_txids):
+ balance = node.getbalance(label, 0, True)
+ spend_tx = [tx for tx in txs if tx["txid"] == spend_txid]
+ if import_node.rescan:
+ assert_equal(balance, amount)
+ assert_equal(len(spend_tx), 1)
+ assert_equal(spend_tx[0]["account"], "")
+ assert_equal(spend_tx[0]["amount"] + spend_tx[0]["fee"], -amount)
+ assert_equal(spend_tx[0]["category"], "send")
+ assert_equal("label" not in spend_tx[0], True)
+ assert_equal(spend_tx[0]["confirmations"], 1)
+ assert_equal("trusted" not in spend_tx[0], True)
+ assert_equal("involvesWatchonly" not in txs[0], True)
+ else:
+ assert_equal(balance, 0)
+ assert_equal(spend_tx, [])
+
+
+if __name__ == "__main__":
+ ImportRescanTest().main()
diff --git a/qa/rpc-tests/importmulti.py b/qa/rpc-tests/importmulti.py
new file mode 100755
index 0000000000..5c536f2f49
--- /dev/null
+++ b/qa/rpc-tests/importmulti.py
@@ -0,0 +1,360 @@
+#!/usr/bin/env python3
+# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import *
+
+class ImportMultiTest (BitcoinTestFramework):
+ def __init__(self):
+ super().__init__()
+ self.num_nodes = 2
+ self.setup_clean_chain = True
+
+ def setup_network(self, split=False):
+ self.nodes = start_nodes(2, self.options.tmpdir)
+ self.is_network_split=False
+
+ def run_test (self):
+ print ("Mining blocks...")
+ self.nodes[0].generate(1)
+ self.nodes[1].generate(1)
+
+ # keyword definition
+ PRIV_KEY = 'privkey'
+ PUB_KEY = 'pubkey'
+ ADDRESS_KEY = 'address'
+ SCRIPT_KEY = 'script'
+
+
+ node0_address1 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ node0_address2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ node0_address3 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+
+ #Check only one address
+ assert_equal(node0_address1['ismine'], True)
+
+ #Node 1 sync test
+ assert_equal(self.nodes[1].getblockcount(),1)
+
+ #Address Test - before import
+ address_info = self.nodes[1].validateaddress(node0_address1['address'])
+ assert_equal(address_info['iswatchonly'], False)
+ assert_equal(address_info['ismine'], False)
+
+
+ # RPC importmulti -----------------------------------------------
+
+ # Bitcoin Address
+ print("Should import an address")
+ address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": address['address']
+ }
+ }])
+ assert_equal(result[0]['success'], True)
+ address_assert = self.nodes[1].validateaddress(address['address'])
+ assert_equal(address_assert['iswatchonly'], True)
+ assert_equal(address_assert['ismine'], False)
+
+
+ # ScriptPubKey + internal
+ print("Should import a scriptPubKey with internal flag")
+ address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": address['scriptPubKey'],
+ "internal": True
+ }])
+ assert_equal(result[0]['success'], True)
+ address_assert = self.nodes[1].validateaddress(address['address'])
+ assert_equal(address_assert['iswatchonly'], True)
+ assert_equal(address_assert['ismine'], False)
+
+ # ScriptPubKey + !internal
+ print("Should not import a scriptPubKey without internal flag")
+ address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": address['scriptPubKey']
+ }])
+ assert_equal(result[0]['success'], False)
+ assert_equal(result[0]['error']['code'], -8)
+ assert_equal(result[0]['error']['message'], 'Internal must be set for hex scriptPubKey')
+ address_assert = self.nodes[1].validateaddress(address['address'])
+ assert_equal(address_assert['iswatchonly'], False)
+ assert_equal(address_assert['ismine'], False)
+
+
+ # Address + Public key + !Internal
+ print("Should import an address with public key")
+ address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": address['address']
+ },
+ "pubkeys": [ address['pubkey'] ]
+ }])
+ assert_equal(result[0]['success'], True)
+ address_assert = self.nodes[1].validateaddress(address['address'])
+ assert_equal(address_assert['iswatchonly'], True)
+ assert_equal(address_assert['ismine'], False)
+
+
+ # ScriptPubKey + Public key + internal
+ print("Should import a scriptPubKey with internal and with public key")
+ address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ request = [{
+ "scriptPubKey": address['scriptPubKey'],
+ "pubkeys": [ address['pubkey'] ],
+ "internal": True
+ }];
+ result = self.nodes[1].importmulti(request)
+ assert_equal(result[0]['success'], True)
+ address_assert = self.nodes[1].validateaddress(address['address'])
+ assert_equal(address_assert['iswatchonly'], True)
+ assert_equal(address_assert['ismine'], False)
+
+ # ScriptPubKey + Public key + !internal
+ print("Should not import a scriptPubKey without internal and with public key")
+ address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ request = [{
+ "scriptPubKey": address['scriptPubKey'],
+ "pubkeys": [ address['pubkey'] ]
+ }];
+ result = self.nodes[1].importmulti(request)
+ assert_equal(result[0]['success'], False)
+ assert_equal(result[0]['error']['code'], -8)
+ assert_equal(result[0]['error']['message'], 'Internal must be set for hex scriptPubKey')
+ address_assert = self.nodes[1].validateaddress(address['address'])
+ assert_equal(address_assert['iswatchonly'], False)
+ assert_equal(address_assert['ismine'], False)
+
+ # Address + Private key + !watchonly
+ print("Should import an address with private key")
+ address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": address['address']
+ },
+ "keys": [ self.nodes[0].dumpprivkey(address['address']) ]
+ }])
+ assert_equal(result[0]['success'], True)
+ address_assert = self.nodes[1].validateaddress(address['address'])
+ assert_equal(address_assert['iswatchonly'], False)
+ assert_equal(address_assert['ismine'], True)
+
+ # Address + Private key + watchonly
+ print("Should not import an address with private key and with watchonly")
+ address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": address['address']
+ },
+ "keys": [ self.nodes[0].dumpprivkey(address['address']) ],
+ "watchonly": True
+ }])
+ assert_equal(result[0]['success'], False)
+ assert_equal(result[0]['error']['code'], -8)
+ assert_equal(result[0]['error']['message'], 'Incompatibility found between watchonly and keys')
+ address_assert = self.nodes[1].validateaddress(address['address'])
+ assert_equal(address_assert['iswatchonly'], False)
+ assert_equal(address_assert['ismine'], False)
+
+ # ScriptPubKey + Private key + internal
+ print("Should import a scriptPubKey with internal and with private key")
+ address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": address['scriptPubKey'],
+ "keys": [ self.nodes[0].dumpprivkey(address['address']) ],
+ "internal": True
+ }])
+ assert_equal(result[0]['success'], True)
+ address_assert = self.nodes[1].validateaddress(address['address'])
+ assert_equal(address_assert['iswatchonly'], False)
+ assert_equal(address_assert['ismine'], True)
+
+ # ScriptPubKey + Private key + !internal
+ print("Should not import a scriptPubKey without internal and with private key")
+ address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": address['scriptPubKey'],
+ "keys": [ self.nodes[0].dumpprivkey(address['address']) ]
+ }])
+ assert_equal(result[0]['success'], False)
+ assert_equal(result[0]['error']['code'], -8)
+ assert_equal(result[0]['error']['message'], 'Internal must be set for hex scriptPubKey')
+ address_assert = self.nodes[1].validateaddress(address['address'])
+ assert_equal(address_assert['iswatchonly'], False)
+ assert_equal(address_assert['ismine'], False)
+
+
+ # P2SH address
+ sig_address_1 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ sig_address_2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ sig_address_3 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ multi_sig_script = self.nodes[0].createmultisig(2, [sig_address_1['address'], sig_address_2['address'], sig_address_3['pubkey']])
+ self.nodes[1].generate(100)
+ transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00)
+ self.nodes[1].generate(1)
+ transaction = self.nodes[1].gettransaction(transactionid);
+
+ print("Should import a p2sh")
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": multi_sig_script['address']
+ }
+ }])
+ assert_equal(result[0]['success'], True)
+ address_assert = self.nodes[1].validateaddress(multi_sig_script['address'])
+ assert_equal(address_assert['isscript'], True)
+ assert_equal(address_assert['iswatchonly'], True)
+ p2shunspent = self.nodes[1].listunspent(0,999999, [multi_sig_script['address']])[0]
+ assert_equal(p2shunspent['spendable'], False)
+ assert_equal(p2shunspent['solvable'], False)
+
+
+ # P2SH + Redeem script
+ sig_address_1 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ sig_address_2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ sig_address_3 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ multi_sig_script = self.nodes[0].createmultisig(2, [sig_address_1['address'], sig_address_2['address'], sig_address_3['pubkey']])
+ self.nodes[1].generate(100)
+ transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00)
+ self.nodes[1].generate(1)
+ transaction = self.nodes[1].gettransaction(transactionid);
+
+ print("Should import a p2sh with respective redeem script")
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": multi_sig_script['address']
+ },
+ "redeemscript": multi_sig_script['redeemScript']
+ }])
+ assert_equal(result[0]['success'], True)
+
+ p2shunspent = self.nodes[1].listunspent(0,999999, [multi_sig_script['address']])[0]
+ assert_equal(p2shunspent['spendable'], False)
+ assert_equal(p2shunspent['solvable'], True)
+
+
+ # P2SH + Redeem script + Private Keys + !Watchonly
+ sig_address_1 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ sig_address_2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ sig_address_3 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ multi_sig_script = self.nodes[0].createmultisig(2, [sig_address_1['address'], sig_address_2['address'], sig_address_3['pubkey']])
+ self.nodes[1].generate(100)
+ transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00)
+ self.nodes[1].generate(1)
+ transaction = self.nodes[1].gettransaction(transactionid);
+
+ print("Should import a p2sh with respective redeem script and private keys")
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": multi_sig_script['address']
+ },
+ "redeemscript": multi_sig_script['redeemScript'],
+ "keys": [ self.nodes[0].dumpprivkey(sig_address_1['address']), self.nodes[0].dumpprivkey(sig_address_2['address'])]
+ }])
+ assert_equal(result[0]['success'], True)
+
+ p2shunspent = self.nodes[1].listunspent(0,999999, [multi_sig_script['address']])[0]
+ assert_equal(p2shunspent['spendable'], False)
+ assert_equal(p2shunspent['solvable'], True)
+
+ # P2SH + Redeem script + Private Keys + Watchonly
+ sig_address_1 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ sig_address_2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ sig_address_3 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ multi_sig_script = self.nodes[0].createmultisig(2, [sig_address_1['address'], sig_address_2['address'], sig_address_3['pubkey']])
+ self.nodes[1].generate(100)
+ transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00)
+ self.nodes[1].generate(1)
+ transaction = self.nodes[1].gettransaction(transactionid);
+
+ print("Should import a p2sh with respective redeem script and private keys")
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": multi_sig_script['address']
+ },
+ "redeemscript": multi_sig_script['redeemScript'],
+ "keys": [ self.nodes[0].dumpprivkey(sig_address_1['address']), self.nodes[0].dumpprivkey(sig_address_2['address'])],
+ "watchonly": True
+ }])
+ assert_equal(result[0]['success'], False)
+ assert_equal(result[0]['error']['code'], -8)
+ assert_equal(result[0]['error']['message'], 'Incompatibility found between watchonly and keys')
+
+
+ # Address + Public key + !Internal + Wrong pubkey
+ print("Should not import an address with a wrong public key")
+ address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ address2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": address['address']
+ },
+ "pubkeys": [ address2['pubkey'] ]
+ }])
+ assert_equal(result[0]['success'], False)
+ assert_equal(result[0]['error']['code'], -5)
+ assert_equal(result[0]['error']['message'], 'Consistency check failed')
+ address_assert = self.nodes[1].validateaddress(address['address'])
+ assert_equal(address_assert['iswatchonly'], False)
+ assert_equal(address_assert['ismine'], False)
+
+
+ # ScriptPubKey + Public key + internal + Wrong pubkey
+ print("Should not import a scriptPubKey with internal and with a wrong public key")
+ address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ address2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ request = [{
+ "scriptPubKey": address['scriptPubKey'],
+ "pubkeys": [ address2['pubkey'] ],
+ "internal": True
+ }];
+ result = self.nodes[1].importmulti(request)
+ assert_equal(result[0]['success'], False)
+ assert_equal(result[0]['error']['code'], -5)
+ assert_equal(result[0]['error']['message'], 'Consistency check failed')
+ address_assert = self.nodes[1].validateaddress(address['address'])
+ assert_equal(address_assert['iswatchonly'], False)
+ assert_equal(address_assert['ismine'], False)
+
+
+ # Address + Private key + !watchonly + Wrong private key
+ print("Should not import an address with a wrong private key")
+ address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ address2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": address['address']
+ },
+ "keys": [ self.nodes[0].dumpprivkey(address2['address']) ]
+ }])
+ assert_equal(result[0]['success'], False)
+ assert_equal(result[0]['error']['code'], -5)
+ assert_equal(result[0]['error']['message'], 'Consistency check failed')
+ address_assert = self.nodes[1].validateaddress(address['address'])
+ assert_equal(address_assert['iswatchonly'], False)
+ assert_equal(address_assert['ismine'], False)
+
+
+ # ScriptPubKey + Private key + internal + Wrong private key
+ print("Should not import a scriptPubKey with internal and with a wrong private key")
+ address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ address2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": address['scriptPubKey'],
+ "keys": [ self.nodes[0].dumpprivkey(address2['address']) ],
+ "internal": True
+ }])
+ assert_equal(result[0]['success'], False)
+ assert_equal(result[0]['error']['code'], -5)
+ assert_equal(result[0]['error']['message'], 'Consistency check failed')
+ address_assert = self.nodes[1].validateaddress(address['address'])
+ assert_equal(address_assert['iswatchonly'], False)
+ assert_equal(address_assert['ismine'], False)
+
+if __name__ == '__main__':
+ ImportMultiTest ().main ()
diff --git a/qa/rpc-tests/maxuploadtarget.py b/qa/rpc-tests/maxuploadtarget.py
index 125d4eb275..9340e899eb 100755
--- a/qa/rpc-tests/maxuploadtarget.py
+++ b/qa/rpc-tests/maxuploadtarget.py
@@ -75,55 +75,25 @@ class TestNode(NodeConnCB):
def received_pong():
return (self.last_pong.nonce == self.ping_counter)
self.connection.send_message(msg_ping(nonce=self.ping_counter))
- success = wait_until(received_pong, timeout)
+ success = wait_until(received_pong, timeout=timeout)
self.ping_counter += 1
return success
class MaxUploadTest(BitcoinTestFramework):
- def add_options(self, parser):
- parser.add_option("--testbinary", dest="testbinary",
- default=os.getenv("BITCOIND", "bitcoind"),
- help="bitcoind binary to test")
-
def __init__(self):
super().__init__()
self.setup_clean_chain = True
self.num_nodes = 1
- self.utxo = []
- self.txouts = gen_return_txouts()
+ # Cache for utxos, as the listunspent may take a long time later in the test
+ self.utxo_cache = []
def setup_network(self):
# Start a node with maxuploadtarget of 200 MB (/24h)
self.nodes = []
self.nodes.append(start_node(0, self.options.tmpdir, ["-debug", "-maxuploadtarget=800", "-blockmaxsize=999000"]))
- def mine_full_block(self, node, address):
- # Want to create a full block
- # We'll generate a 66k transaction below, and 14 of them is close to the 1MB block limit
- for j in range(14):
- if len(self.utxo) < 14:
- self.utxo = node.listunspent()
- inputs=[]
- outputs = {}
- t = self.utxo.pop()
- inputs.append({ "txid" : t["txid"], "vout" : t["vout"]})
- remchange = t["amount"] - Decimal("0.001000")
- outputs[address]=remchange
- # Create a basic transaction that will send change back to ourself after account for a fee
- # And then insert the 128 generated transaction outs in the middle rawtx[92] is where the #
- # of txouts is stored and is the only thing we overwrite from the original transaction
- rawtx = node.createrawtransaction(inputs, outputs)
- newtx = rawtx[0:92]
- newtx = newtx + self.txouts
- newtx = newtx + rawtx[94:]
- # Appears to be ever so slightly faster to sign with SIGHASH_NONE
- signresult = node.signrawtransaction(newtx,None,None,"NONE")
- txid = node.sendrawtransaction(signresult["hex"], True)
- # Mine a full sized block which will be these transactions we just created
- node.generate(1)
-
def run_test(self):
# Before we connect anything, we first set the time on the node
# to be in the past, otherwise things break because the CNode
@@ -151,7 +121,7 @@ class MaxUploadTest(BitcoinTestFramework):
# Test logic begins here
# Now mine a big block
- self.mine_full_block(self.nodes[0], self.nodes[0].getnewaddress())
+ mine_large_block(self.nodes[0], self.utxo_cache)
# Store the hash; we'll request this later
big_old_block = self.nodes[0].getbestblockhash()
@@ -162,11 +132,10 @@ class MaxUploadTest(BitcoinTestFramework):
self.nodes[0].setmocktime(int(time.time()) - 2*60*60*24)
# Mine one more block, so that the prior block looks old
- self.mine_full_block(self.nodes[0], self.nodes[0].getnewaddress())
+ mine_large_block(self.nodes[0], self.utxo_cache)
# We'll be requesting this new block too
big_new_block = self.nodes[0].getbestblockhash()
- new_block_size = self.nodes[0].getblock(big_new_block)['size']
big_new_block = int(big_new_block, 16)
# test_nodes[0] will test what happens if we just keep requesting the
diff --git a/qa/rpc-tests/mempool_limit.py b/qa/rpc-tests/mempool_limit.py
index 4438c152df..154ae59c26 100755
--- a/qa/rpc-tests/mempool_limit.py
+++ b/qa/rpc-tests/mempool_limit.py
@@ -26,7 +26,7 @@ class MempoolLimitTest(BitcoinTestFramework):
def run_test(self):
txids = []
- utxos = create_confirmed_utxos(self.relayfee, self.nodes[0], 90)
+ utxos = create_confirmed_utxos(self.relayfee, self.nodes[0], 91)
#create a mempool tx that will be evicted
us0 = utxos.pop()
@@ -41,9 +41,9 @@ class MempoolLimitTest(BitcoinTestFramework):
relayfee = self.nodes[0].getnetworkinfo()['relayfee']
base_fee = relayfee*100
- for i in range (4):
+ for i in range (3):
txids.append([])
- txids[i] = create_lots_of_big_transactions(self.nodes[0], self.txouts, utxos[30*i:30*i+30], (i+1)*base_fee)
+ txids[i] = create_lots_of_big_transactions(self.nodes[0], self.txouts, utxos[30*i:30*i+30], 30, (i+1)*base_fee)
# by now, the tx should be evicted, check confirmation state
assert(txid not in self.nodes[0].getrawmempool())
diff --git a/qa/rpc-tests/mempool_packages.py b/qa/rpc-tests/mempool_packages.py
index 45dc0e65c4..f605e7524f 100755
--- a/qa/rpc-tests/mempool_packages.py
+++ b/qa/rpc-tests/mempool_packages.py
@@ -20,8 +20,8 @@ class MempoolPackagesTest(BitcoinTestFramework):
def setup_network(self):
self.nodes = []
- self.nodes.append(start_node(0, self.options.tmpdir, ["-maxorphantx=1000", "-relaypriority=0", "-debug"]))
- self.nodes.append(start_node(1, self.options.tmpdir, ["-maxorphantx=1000", "-relaypriority=0", "-limitancestorcount=5", "-debug"]))
+ self.nodes.append(start_node(0, self.options.tmpdir, ["-maxorphantx=1000", "-debug"]))
+ self.nodes.append(start_node(1, self.options.tmpdir, ["-maxorphantx=1000", "-limitancestorcount=5", "-debug"]))
connect_nodes(self.nodes[0], 1)
self.is_network_split = False
self.sync_all()
diff --git a/qa/rpc-tests/mempool_reorg.py b/qa/rpc-tests/mempool_reorg.py
index 301b094eb0..dd88aae4f2 100755
--- a/qa/rpc-tests/mempool_reorg.py
+++ b/qa/rpc-tests/mempool_reorg.py
@@ -55,7 +55,7 @@ class MempoolCoinbaseTest(BitcoinTestFramework):
# Create a block-height-locked transaction which will be invalid after reorg
timelock_tx = self.nodes[0].createrawtransaction([{"txid": coinbase_txids[0], "vout": 0}], {node0_address: 49.99})
# Set the time lock
- timelock_tx = timelock_tx.replace("ffffffff", "11111111", 1)
+ timelock_tx = timelock_tx.replace("ffffffff", "11111191", 1)
timelock_tx = timelock_tx[:-8] + hex(self.nodes[0].getblockcount() + 2)[2:] + "000000"
timelock_tx = self.nodes[0].signrawtransaction(timelock_tx)["hex"]
assert_raises(JSONRPCException, self.nodes[0].sendrawtransaction, timelock_tx)
diff --git a/qa/rpc-tests/multi_rpc.py b/qa/rpc-tests/multi_rpc.py
index 24373b257d..95d9090ce2 100755
--- a/qa/rpc-tests/multi_rpc.py
+++ b/qa/rpc-tests/multi_rpc.py
@@ -4,7 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
#
-# Test mulitple rpc user config option rpcauth
+# Test multiple rpc user config option rpcauth
#
from test_framework.test_framework import BitcoinTestFramework
@@ -26,7 +26,7 @@ class HTTPBasicsTest (BitcoinTestFramework):
#Append rpcauth to bitcoin.conf before initialization
rpcauth = "rpcauth=rt:93648e835a54c573682c2eb19f882535$7681e9c5b74bdd85e78166031d2058e1069b3ed7ed967c93fc63abba06f31144"
rpcauth2 = "rpcauth=rt2:f8607b1a88861fac29dfccf9b52ff9f$ff36a0c23c8c62b4846112e50fa888416e94c17bfd4c42f88fd8f55ec6a3137e"
- with open(os.path.join(self.options.tmpdir+"/node0", "bitcoin.conf"), 'a') as f:
+ with open(os.path.join(self.options.tmpdir+"/node0", "bitcoin.conf"), 'a', encoding='utf8') as f:
f.write(rpcauth+"\n")
f.write(rpcauth2+"\n")
diff --git a/qa/rpc-tests/nulldummy.py b/qa/rpc-tests/nulldummy.py
new file mode 100755
index 0000000000..54b7eac376
--- /dev/null
+++ b/qa/rpc-tests/nulldummy.py
@@ -0,0 +1,148 @@
+#!/usr/bin/env python3
+# Copyright (c) 2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import *
+from test_framework.mininode import CTransaction, NetworkThread
+from test_framework.blocktools import create_coinbase, create_block, add_witness_commitment
+from test_framework.script import CScript
+from io import BytesIO
+import time
+
+NULLDUMMY_ERROR = "64: non-mandatory-script-verify-flag (Dummy CHECKMULTISIG argument must be zero)"
+
+def trueDummy(tx):
+ scriptSig = CScript(tx.vin[0].scriptSig)
+ newscript = []
+ for i in scriptSig:
+ if (len(newscript) == 0):
+ assert(len(i) == 0)
+ newscript.append(b'\x51')
+ else:
+ newscript.append(i)
+ tx.vin[0].scriptSig = CScript(newscript)
+ tx.rehash()
+
+'''
+This test is meant to exercise NULLDUMMY softfork.
+Connect to a single node.
+Generate 2 blocks (save the coinbases for later).
+Generate 427 more blocks.
+[Policy/Consensus] Check that NULLDUMMY compliant transactions are accepted in the 430th block.
+[Policy] Check that non-NULLDUMMY transactions are rejected before activation.
+[Consensus] Check that the new NULLDUMMY rules are not enforced on the 431st block.
+[Policy/Consensus] Check that the new NULLDUMMY rules are enforced on the 432nd block.
+'''
+
+class NULLDUMMYTest(BitcoinTestFramework):
+
+ def __init__(self):
+ super().__init__()
+ self.num_nodes = 1
+ self.setup_clean_chain = True
+
+ def setup_network(self):
+ # Must set the blockversion for this test
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir,
+ extra_args=[['-debug', '-whitelist=127.0.0.1', '-walletprematurewitness']])
+
+ def run_test(self):
+ self.address = self.nodes[0].getnewaddress()
+ self.ms_address = self.nodes[0].addmultisigaddress(1,[self.address])
+ self.wit_address = self.nodes[0].addwitnessaddress(self.address)
+ self.wit_ms_address = self.nodes[0].addwitnessaddress(self.ms_address)
+
+ NetworkThread().start() # Start up network handling in another thread
+ self.coinbase_blocks = self.nodes[0].generate(2) # Block 2
+ coinbase_txid = []
+ for i in self.coinbase_blocks:
+ coinbase_txid.append(self.nodes[0].getblock(i)['tx'][0])
+ self.nodes[0].generate(427) # Block 429
+ self.lastblockhash = self.nodes[0].getbestblockhash()
+ self.tip = int("0x" + self.lastblockhash, 0)
+ self.lastblockheight = 429
+ self.lastblocktime = int(time.time()) + 429
+
+ print ("Test 1: NULLDUMMY compliant base transactions should be accepted to mempool and mined before activation [430]")
+ test1txs = [self.create_transaction(self.nodes[0], coinbase_txid[0], self.ms_address, 49)]
+ txid1 = self.tx_submit(self.nodes[0], test1txs[0])
+ test1txs.append(self.create_transaction(self.nodes[0], txid1, self.ms_address, 48))
+ txid2 = self.tx_submit(self.nodes[0], test1txs[1])
+ test1txs.append(self.create_transaction(self.nodes[0], coinbase_txid[1], self.wit_ms_address, 49))
+ txid3 = self.tx_submit(self.nodes[0], test1txs[2])
+ self.block_submit(self.nodes[0], test1txs, False, True)
+
+ print ("Test 2: Non-NULLDUMMY base multisig transaction should not be accepted to mempool before activation")
+ test2tx = self.create_transaction(self.nodes[0], txid2, self.ms_address, 48)
+ trueDummy(test2tx)
+ txid4 = self.tx_submit(self.nodes[0], test2tx, NULLDUMMY_ERROR)
+
+ print ("Test 3: Non-NULLDUMMY base transactions should be accepted in a block before activation [431]")
+ self.block_submit(self.nodes[0], [test2tx], False, True)
+
+ print ("Test 4: Non-NULLDUMMY base multisig transaction is invalid after activation")
+ test4tx = self.create_transaction(self.nodes[0], txid4, self.address, 47)
+ test6txs=[CTransaction(test4tx)]
+ trueDummy(test4tx)
+ self.tx_submit(self.nodes[0], test4tx, NULLDUMMY_ERROR)
+ self.block_submit(self.nodes[0], [test4tx])
+
+ print ("Test 5: Non-NULLDUMMY P2WSH multisig transaction invalid after activation")
+ test5tx = self.create_transaction(self.nodes[0], txid3, self.wit_address, 48)
+ test6txs.append(CTransaction(test5tx))
+ test5tx.wit.vtxinwit[0].scriptWitness.stack[0] = b'\x01'
+ self.tx_submit(self.nodes[0], test5tx, NULLDUMMY_ERROR)
+ self.block_submit(self.nodes[0], [test5tx], True)
+
+ print ("Test 6: NULLDUMMY compliant base/witness transactions should be accepted to mempool and in block after activation [432]")
+ for i in test6txs:
+ self.tx_submit(self.nodes[0], i)
+ self.block_submit(self.nodes[0], test6txs, True, True)
+
+
+ def create_transaction(self, node, txid, to_address, amount):
+ inputs = [{ "txid" : txid, "vout" : 0}]
+ outputs = { to_address : amount }
+ rawtx = node.createrawtransaction(inputs, outputs)
+ signresult = node.signrawtransaction(rawtx)
+ tx = CTransaction()
+ f = BytesIO(hex_str_to_bytes(signresult['hex']))
+ tx.deserialize(f)
+ return tx
+
+
+ def tx_submit(self, node, tx, msg = ""):
+ tx.rehash()
+ try:
+ node.sendrawtransaction(bytes_to_hex_str(tx.serialize_with_witness()), True)
+ except JSONRPCException as exp:
+ assert_equal(exp.error["message"], msg)
+ else:
+ assert_equal('', msg)
+ return tx.hash
+
+
+ def block_submit(self, node, txs, witness = False, accept = False):
+ block = create_block(self.tip, create_coinbase(self.lastblockheight + 1), self.lastblocktime + 1)
+ block.nVersion = 4
+ for tx in txs:
+ tx.rehash()
+ block.vtx.append(tx)
+ block.hashMerkleRoot = block.calc_merkle_root()
+ witness and add_witness_commitment(block)
+ block.rehash()
+ block.solve()
+ node.submitblock(bytes_to_hex_str(block.serialize(True)))
+ if (accept):
+ assert_equal(node.getbestblockhash(), block.hash)
+ self.tip = block.sha256
+ self.lastblockhash = block.hash
+ self.lastblocktime += 1
+ self.lastblockheight += 1
+ else:
+ assert_equal(node.getbestblockhash(), self.lastblockhash)
+
+if __name__ == '__main__':
+ NULLDUMMYTest().main()
diff --git a/qa/rpc-tests/p2p-compactblocks.py b/qa/rpc-tests/p2p-compactblocks.py
index 7fe7ecc16c..fc1f16c6d2 100755
--- a/qa/rpc-tests/p2p-compactblocks.py
+++ b/qa/rpc-tests/p2p-compactblocks.py
@@ -6,31 +6,38 @@
from test_framework.mininode import *
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
-from test_framework.blocktools import create_block, create_coinbase
+from test_framework.blocktools import create_block, create_coinbase, add_witness_commitment
from test_framework.siphash import siphash256
from test_framework.script import CScript, OP_TRUE
'''
CompactBlocksTest -- test compact blocks (BIP 152)
-'''
+Version 1 compact blocks are pre-segwit (txids)
+Version 2 compact blocks are post-segwit (wtxids)
+'''
# TestNode: A peer we use to send messages to bitcoind, and store responses.
class TestNode(SingleNodeConnCB):
def __init__(self):
SingleNodeConnCB.__init__(self)
- self.last_sendcmpct = None
+ self.last_sendcmpct = []
self.last_headers = None
self.last_inv = None
self.last_cmpctblock = None
self.block_announced = False
self.last_getdata = None
+ self.last_getheaders = None
self.last_getblocktxn = None
self.last_block = None
self.last_blocktxn = None
+ # Store the hashes of blocks we've seen announced.
+ # This is for synchronizing the p2p message traffic,
+ # so we can eg wait until a particular block is announced.
+ self.set_announced_blockhashes = set()
def on_sendcmpct(self, conn, message):
- self.last_sendcmpct = message
+ self.last_sendcmpct.append(message)
def on_block(self, conn, message):
self.last_block = message
@@ -38,18 +45,29 @@ class TestNode(SingleNodeConnCB):
def on_cmpctblock(self, conn, message):
self.last_cmpctblock = message
self.block_announced = True
+ self.last_cmpctblock.header_and_shortids.header.calc_sha256()
+ self.set_announced_blockhashes.add(self.last_cmpctblock.header_and_shortids.header.sha256)
def on_headers(self, conn, message):
self.last_headers = message
self.block_announced = True
+ for x in self.last_headers.headers:
+ x.calc_sha256()
+ self.set_announced_blockhashes.add(x.sha256)
def on_inv(self, conn, message):
self.last_inv = message
- self.block_announced = True
+ for x in self.last_inv.inv:
+ if x.type == 2:
+ self.block_announced = True
+ self.set_announced_blockhashes.add(x.hash)
def on_getdata(self, conn, message):
self.last_getdata = message
+ def on_getheaders(self, conn, message):
+ self.last_getheaders = message
+
def on_getblocktxn(self, conn, message):
self.last_getblocktxn = message
@@ -78,34 +96,52 @@ class TestNode(SingleNodeConnCB):
headers_message.headers = [CBlockHeader(b) for b in new_blocks]
self.send_message(headers_message)
+ def request_headers_and_sync(self, locator, hashstop=0):
+ self.clear_block_announcement()
+ self.get_headers(locator, hashstop)
+ assert(wait_until(self.received_block_announcement, timeout=30))
+ assert(self.received_block_announcement())
+ self.clear_block_announcement()
+
+ # Block until a block announcement for a particular block hash is
+ # received.
+ def wait_for_block_announcement(self, block_hash, timeout=30):
+ def received_hash():
+ return (block_hash in self.set_announced_blockhashes)
+ return wait_until(received_hash, timeout=timeout)
class CompactBlocksTest(BitcoinTestFramework):
def __init__(self):
super().__init__()
self.setup_clean_chain = True
- self.num_nodes = 1
+ # Node0 = pre-segwit, node1 = segwit-aware
+ self.num_nodes = 2
self.utxos = []
def setup_network(self):
self.nodes = []
- # Turn off segwit in this test, as compact blocks don't currently work
- # with segwit. (After BIP 152 is updated to support segwit, we can
- # test behavior with and without segwit enabled by adding a second node
- # to the test.)
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, [["-debug", "-logtimemicros=1", "-bip9params=segwit:0:0"]])
+ # Start up node0 to be a version 1, pre-segwit node.
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir,
+ [["-debug", "-logtimemicros=1", "-bip9params=segwit:0:0"],
+ ["-debug", "-logtimemicros", "-txindex"]])
+ connect_nodes(self.nodes[0], 1)
- def build_block_on_tip(self):
- height = self.nodes[0].getblockcount()
- tip = self.nodes[0].getbestblockhash()
- mtp = self.nodes[0].getblockheader(tip)['mediantime']
+ def build_block_on_tip(self, node, segwit=False):
+ height = node.getblockcount()
+ tip = node.getbestblockhash()
+ mtp = node.getblockheader(tip)['mediantime']
block = create_block(int(tip, 16), create_coinbase(height + 1), mtp + 1)
+ block.nVersion = 4
+ if segwit:
+ add_witness_commitment(block)
block.solve()
return block
# Create 10 more anyone-can-spend utxo's for testing.
def make_utxos(self):
- block = self.build_block_on_tip()
+ # Doesn't matter which node we use, just use node0.
+ block = self.build_block_on_tip(self.nodes[0])
self.test_node.send_and_ping(msg_block(block))
assert(int(self.nodes[0].getbestblockhash(), 16) == block.sha256)
self.nodes[0].generate(100)
@@ -118,7 +154,7 @@ class CompactBlocksTest(BitcoinTestFramework):
tx.vout.append(CTxOut(out_value, CScript([OP_TRUE])))
tx.rehash()
- block2 = self.build_block_on_tip()
+ block2 = self.build_block_on_tip(self.nodes[0])
block2.vtx.append(tx)
block2.hashMerkleRoot = block2.calc_merkle_root()
block2.solve()
@@ -127,78 +163,113 @@ class CompactBlocksTest(BitcoinTestFramework):
self.utxos.extend([[tx.sha256, i, out_value] for i in range(10)])
return
- # Test "sendcmpct":
- # - No compact block announcements or getdata(MSG_CMPCT_BLOCK) unless
- # sendcmpct is sent.
- # - If sendcmpct is sent with version > 0, the message is ignored.
+ # Test "sendcmpct" (between peers preferring the same version):
+ # - No compact block announcements unless sendcmpct is sent.
+ # - If sendcmpct is sent with version > preferred_version, the message is ignored.
# - If sendcmpct is sent with boolean 0, then block announcements are not
# made with compact blocks.
# - If sendcmpct is then sent with boolean 1, then new block announcements
# are made with compact blocks.
- def test_sendcmpct(self):
- print("Testing SENDCMPCT p2p message... ")
-
- # Make sure we get a version 0 SENDCMPCT message from our peer
+ # If old_node is passed in, request compact blocks with version=preferred-1
+ # and verify that it receives block announcements via compact block.
+ def test_sendcmpct(self, node, test_node, preferred_version, old_node=None):
+ # Make sure we get a SENDCMPCT message from our peer
def received_sendcmpct():
- return (self.test_node.last_sendcmpct is not None)
+ return (len(test_node.last_sendcmpct) > 0)
got_message = wait_until(received_sendcmpct, timeout=30)
+ assert(received_sendcmpct())
assert(got_message)
- assert_equal(self.test_node.last_sendcmpct.version, 1)
+ with mininode_lock:
+ # Check that the first version received is the preferred one
+ assert_equal(test_node.last_sendcmpct[0].version, preferred_version)
+ # And that we receive versions down to 1.
+ assert_equal(test_node.last_sendcmpct[-1].version, 1)
+ test_node.last_sendcmpct = []
- tip = int(self.nodes[0].getbestblockhash(), 16)
+ tip = int(node.getbestblockhash(), 16)
def check_announcement_of_new_block(node, peer, predicate):
- self.test_node.clear_block_announcement()
- node.generate(1)
- got_message = wait_until(peer.received_block_announcement, timeout=30)
+ peer.clear_block_announcement()
+ block_hash = int(node.generate(1)[0], 16)
+ peer.wait_for_block_announcement(block_hash, timeout=30)
+ assert(peer.block_announced)
assert(got_message)
+
with mininode_lock:
- assert(predicate)
+ assert predicate(peer), (
+ "block_hash={!r}, cmpctblock={!r}, inv={!r}".format(
+ block_hash, peer.last_cmpctblock, peer.last_inv))
# We shouldn't get any block announcements via cmpctblock yet.
- check_announcement_of_new_block(self.nodes[0], self.test_node, lambda: self.test_node.last_cmpctblock is None)
+ check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is None)
# Try one more time, this time after requesting headers.
- self.test_node.clear_block_announcement()
- self.test_node.get_headers(locator=[tip], hashstop=0)
- wait_until(self.test_node.received_block_announcement, timeout=30)
- self.test_node.clear_block_announcement()
+ test_node.request_headers_and_sync(locator=[tip])
+ check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is None and p.last_inv is not None)
- check_announcement_of_new_block(self.nodes[0], self.test_node, lambda: self.test_node.last_cmpctblock is None and self.test_node.last_inv is not None)
+ # Test a few ways of using sendcmpct that should NOT
+ # result in compact block announcements.
+ # Before each test, sync the headers chain.
+ test_node.request_headers_and_sync(locator=[tip])
# Now try a SENDCMPCT message with too-high version
sendcmpct = msg_sendcmpct()
- sendcmpct.version = 2
- self.test_node.send_message(sendcmpct)
+ sendcmpct.version = preferred_version+1
+ sendcmpct.announce = True
+ test_node.send_and_ping(sendcmpct)
+ check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is None)
- check_announcement_of_new_block(self.nodes[0], self.test_node, lambda: self.test_node.last_cmpctblock is None)
+ # Headers sync before next test.
+ test_node.request_headers_and_sync(locator=[tip])
# Now try a SENDCMPCT message with valid version, but announce=False
- self.test_node.send_message(msg_sendcmpct())
- check_announcement_of_new_block(self.nodes[0], self.test_node, lambda: self.test_node.last_cmpctblock is None)
+ sendcmpct.version = preferred_version
+ sendcmpct.announce = False
+ test_node.send_and_ping(sendcmpct)
+ check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is None)
+
+ # Headers sync before next test.
+ test_node.request_headers_and_sync(locator=[tip])
# Finally, try a SENDCMPCT message with announce=True
- sendcmpct.version = 1
+ sendcmpct.version = preferred_version
sendcmpct.announce = True
- self.test_node.send_message(sendcmpct)
- check_announcement_of_new_block(self.nodes[0], self.test_node, lambda: self.test_node.last_cmpctblock is not None)
+ test_node.send_and_ping(sendcmpct)
+ check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is not None)
- # Try one more time
- check_announcement_of_new_block(self.nodes[0], self.test_node, lambda: self.test_node.last_cmpctblock is not None)
+ # Try one more time (no headers sync should be needed!)
+ check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is not None)
# Try one more time, after turning on sendheaders
- self.test_node.send_message(msg_sendheaders())
- check_announcement_of_new_block(self.nodes[0], self.test_node, lambda: self.test_node.last_cmpctblock is not None)
+ test_node.send_and_ping(msg_sendheaders())
+ check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is not None)
+
+ # Try one more time, after sending a version-1, announce=false message.
+ sendcmpct.version = preferred_version-1
+ sendcmpct.announce = False
+ test_node.send_and_ping(sendcmpct)
+ check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is not None)
# Now turn off announcements
+ sendcmpct.version = preferred_version
sendcmpct.announce = False
- check_announcement_of_new_block(self.nodes[0], self.test_node, lambda: self.test_node.last_cmpctblock is None and self.test_node.last_headers is not None)
+ test_node.send_and_ping(sendcmpct)
+ check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is None and p.last_headers is not None)
+
+ if old_node is not None:
+ # Verify that a peer using an older protocol version can receive
+ # announcements from this node.
+ sendcmpct.version = preferred_version-1
+ sendcmpct.announce = True
+ old_node.send_and_ping(sendcmpct)
+ # Header sync
+ old_node.request_headers_and_sync(locator=[tip])
+ check_announcement_of_new_block(node, old_node, lambda p: p.last_cmpctblock is not None)
# This test actually causes bitcoind to (reasonably!) disconnect us, so do this last.
def test_invalid_cmpctblock_message(self):
- print("Testing invalid index in cmpctblock message...")
self.nodes[0].generate(101)
- block = self.build_block_on_tip()
+ block = self.build_block_on_tip(self.nodes[0])
cmpct_block = P2PHeaderAndShortIDs()
cmpct_block.header = CBlockHeader(block)
@@ -211,43 +282,63 @@ class CompactBlocksTest(BitcoinTestFramework):
# Compare the generated shortids to what we expect based on BIP 152, given
# bitcoind's choice of nonce.
- def test_compactblock_construction(self):
- print("Testing compactblock headers and shortIDs are correct...")
-
+ def test_compactblock_construction(self, node, test_node, version, use_witness_address):
# Generate a bunch of transactions.
- self.nodes[0].generate(101)
+ node.generate(101)
num_transactions = 25
- address = self.nodes[0].getnewaddress()
+ address = node.getnewaddress()
+ if use_witness_address:
+ # Want at least one segwit spend, so move all funds to
+ # a witness address.
+ address = node.addwitnessaddress(address)
+ value_to_send = node.getbalance()
+ node.sendtoaddress(address, satoshi_round(value_to_send-Decimal(0.1)))
+ node.generate(1)
+
+ segwit_tx_generated = False
for i in range(num_transactions):
- self.nodes[0].sendtoaddress(address, 0.1)
+ txid = node.sendtoaddress(address, 0.1)
+ hex_tx = node.gettransaction(txid)["hex"]
+ tx = FromHex(CTransaction(), hex_tx)
+ if not tx.wit.is_null():
+ segwit_tx_generated = True
+
+ if use_witness_address:
+ assert(segwit_tx_generated) # check that our test is not broken
+
+ # Wait until we've seen the block announcement for the resulting tip
+ tip = int(node.getbestblockhash(), 16)
+ assert(test_node.wait_for_block_announcement(tip))
# Now mine a block, and look at the resulting compact block.
- self.test_node.clear_block_announcement()
- block_hash = int(self.nodes[0].generate(1)[0], 16)
+ test_node.clear_block_announcement()
+ block_hash = int(node.generate(1)[0], 16)
# Store the raw block in our internal format.
- block = FromHex(CBlock(), self.nodes[0].getblock("%02x" % block_hash, False))
+ block = FromHex(CBlock(), node.getblock("%02x" % block_hash, False))
[tx.calc_sha256() for tx in block.vtx]
block.rehash()
# Don't care which type of announcement came back for this test; just
# request the compact block if we didn't get one yet.
- wait_until(self.test_node.received_block_announcement, timeout=30)
+ wait_until(test_node.received_block_announcement, timeout=30)
+ assert(test_node.received_block_announcement())
with mininode_lock:
- if self.test_node.last_cmpctblock is None:
- self.test_node.clear_block_announcement()
+ if test_node.last_cmpctblock is None:
+ test_node.clear_block_announcement()
inv = CInv(4, block_hash) # 4 == "CompactBlock"
- self.test_node.send_message(msg_getdata([inv]))
+ test_node.send_message(msg_getdata([inv]))
- wait_until(self.test_node.received_block_announcement, timeout=30)
+ wait_until(test_node.received_block_announcement, timeout=30)
+ assert(test_node.received_block_announcement())
# Now we should have the compactblock
header_and_shortids = None
with mininode_lock:
- assert(self.test_node.last_cmpctblock is not None)
+ assert(test_node.last_cmpctblock is not None)
# Convert the on-the-wire representation to absolute indexes
- header_and_shortids = HeaderAndShortIDs(self.test_node.last_cmpctblock.header_and_shortids)
+ header_and_shortids = HeaderAndShortIDs(test_node.last_cmpctblock.header_and_shortids)
# Check that we got the right block!
header_and_shortids.header.calc_sha256()
@@ -260,8 +351,17 @@ class CompactBlocksTest(BitcoinTestFramework):
# Check that all prefilled_txn entries match what's in the block.
for entry in header_and_shortids.prefilled_txn:
entry.tx.calc_sha256()
+ # This checks the non-witness parts of the tx agree
assert_equal(entry.tx.sha256, block.vtx[entry.index].sha256)
+ # And this checks the witness
+ wtxid = entry.tx.calc_sha256(True)
+ if version == 2:
+ assert_equal(wtxid, block.vtx[entry.index].calc_sha256(True))
+ else:
+ # Shouldn't have received a witness
+ assert(entry.tx.wit.is_null())
+
# Check that the cmpctblock message announced all the transactions.
assert_equal(len(header_and_shortids.prefilled_txn) + len(header_and_shortids.shortids), len(block.vtx))
@@ -276,7 +376,10 @@ class CompactBlocksTest(BitcoinTestFramework):
# Already checked prefilled transactions above
header_and_shortids.prefilled_txn.pop(0)
else:
- shortid = calculate_shortid(k0, k1, block.vtx[index].sha256)
+ tx_hash = block.vtx[index].sha256
+ if version == 2:
+ tx_hash = block.vtx[index].calc_sha256(True)
+ shortid = calculate_shortid(k0, k1, tx_hash)
assert_equal(shortid, header_and_shortids.shortids[0])
header_and_shortids.shortids.pop(0)
index += 1
@@ -284,49 +387,61 @@ class CompactBlocksTest(BitcoinTestFramework):
# Test that bitcoind requests compact blocks when we announce new blocks
# via header or inv, and that responding to getblocktxn causes the block
# to be successfully reconstructed.
- def test_compactblock_requests(self):
- print("Testing compactblock requests... ")
-
+ # Post-segwit: upgraded nodes would only make this request of cb-version-2,
+ # NODE_WITNESS peers. Unupgraded nodes would still make this request of
+ # any cb-version-1-supporting peer.
+ def test_compactblock_requests(self, node, test_node, version, segwit):
# Try announcing a block with an inv or header, expect a compactblock
# request
for announce in ["inv", "header"]:
- block = self.build_block_on_tip()
+ block = self.build_block_on_tip(node, segwit=segwit)
with mininode_lock:
- self.test_node.last_getdata = None
+ test_node.last_getdata = None
if announce == "inv":
- self.test_node.send_message(msg_inv([CInv(2, block.sha256)]))
+ test_node.send_message(msg_inv([CInv(2, block.sha256)]))
+ success = wait_until(lambda: test_node.last_getheaders is not None, timeout=30)
+ assert(success)
+ test_node.send_header_for_blocks([block])
else:
- self.test_node.send_header_for_blocks([block])
- success = wait_until(lambda: self.test_node.last_getdata is not None, timeout=30)
+ test_node.send_header_for_blocks([block])
+ success = wait_until(lambda: test_node.last_getdata is not None, timeout=30)
assert(success)
- assert_equal(len(self.test_node.last_getdata.inv), 1)
- assert_equal(self.test_node.last_getdata.inv[0].type, 4)
- assert_equal(self.test_node.last_getdata.inv[0].hash, block.sha256)
+ assert_equal(len(test_node.last_getdata.inv), 1)
+ assert_equal(test_node.last_getdata.inv[0].type, 4)
+ assert_equal(test_node.last_getdata.inv[0].hash, block.sha256)
# Send back a compactblock message that omits the coinbase
comp_block = HeaderAndShortIDs()
comp_block.header = CBlockHeader(block)
comp_block.nonce = 0
- comp_block.shortids = [1] # this is useless, and wrong
- self.test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
- assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.hashPrevBlock)
+ [k0, k1] = comp_block.get_siphash_keys()
+ coinbase_hash = block.vtx[0].sha256
+ if version == 2:
+ coinbase_hash = block.vtx[0].calc_sha256(True)
+ comp_block.shortids = [
+ calculate_shortid(k0, k1, coinbase_hash) ]
+ test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
+ assert_equal(int(node.getbestblockhash(), 16), block.hashPrevBlock)
# Expect a getblocktxn message.
with mininode_lock:
- assert(self.test_node.last_getblocktxn is not None)
- absolute_indexes = self.test_node.last_getblocktxn.block_txn_request.to_absolute()
+ assert(test_node.last_getblocktxn is not None)
+ absolute_indexes = test_node.last_getblocktxn.block_txn_request.to_absolute()
assert_equal(absolute_indexes, [0]) # should be a coinbase request
# Send the coinbase, and verify that the tip advances.
- msg = msg_blocktxn()
+ if version == 2:
+ msg = msg_witness_blocktxn()
+ else:
+ msg = msg_blocktxn()
msg.block_transactions.blockhash = block.sha256
msg.block_transactions.transactions = [block.vtx[0]]
- self.test_node.send_and_ping(msg)
- assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
+ test_node.send_and_ping(msg)
+ assert_equal(int(node.getbestblockhash(), 16), block.sha256)
# Create a chain of transactions from given utxo, and add to a new block.
- def build_block_with_transactions(self, utxo, num_transactions):
- block = self.build_block_on_tip()
+ def build_block_with_transactions(self, node, utxo, num_transactions):
+ block = self.build_block_on_tip(node)
for i in range(num_transactions):
tx = CTransaction()
@@ -343,118 +458,113 @@ class CompactBlocksTest(BitcoinTestFramework):
# Test that we only receive getblocktxn requests for transactions that the
# node needs, and that responding to them causes the block to be
# reconstructed.
- def test_getblocktxn_requests(self):
- print("Testing getblocktxn requests...")
+ def test_getblocktxn_requests(self, node, test_node, version):
+ with_witness = (version==2)
+
+ def test_getblocktxn_response(compact_block, peer, expected_result):
+ msg = msg_cmpctblock(compact_block.to_p2p())
+ peer.send_and_ping(msg)
+ with mininode_lock:
+ assert(peer.last_getblocktxn is not None)
+ absolute_indexes = peer.last_getblocktxn.block_txn_request.to_absolute()
+ assert_equal(absolute_indexes, expected_result)
+
+ def test_tip_after_message(node, peer, msg, tip):
+ peer.send_and_ping(msg)
+ assert_equal(int(node.getbestblockhash(), 16), tip)
# First try announcing compactblocks that won't reconstruct, and verify
# that we receive getblocktxn messages back.
utxo = self.utxos.pop(0)
- block = self.build_block_with_transactions(utxo, 5)
+ block = self.build_block_with_transactions(node, utxo, 5)
self.utxos.append([block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue])
-
comp_block = HeaderAndShortIDs()
- comp_block.initialize_from_block(block)
+ comp_block.initialize_from_block(block, use_witness=with_witness)
- self.test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
- with mininode_lock:
- assert(self.test_node.last_getblocktxn is not None)
- absolute_indexes = self.test_node.last_getblocktxn.block_txn_request.to_absolute()
- assert_equal(absolute_indexes, [1, 2, 3, 4, 5])
- msg = msg_blocktxn()
- msg.block_transactions = BlockTransactions(block.sha256, block.vtx[1:])
- self.test_node.send_and_ping(msg)
- assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
+ test_getblocktxn_response(comp_block, test_node, [1, 2, 3, 4, 5])
+
+ msg_bt = msg_blocktxn()
+ if with_witness:
+ msg_bt = msg_witness_blocktxn() # serialize with witnesses
+ msg_bt.block_transactions = BlockTransactions(block.sha256, block.vtx[1:])
+ test_tip_after_message(node, test_node, msg_bt, block.sha256)
utxo = self.utxos.pop(0)
- block = self.build_block_with_transactions(utxo, 5)
+ block = self.build_block_with_transactions(node, utxo, 5)
self.utxos.append([block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue])
# Now try interspersing the prefilled transactions
- comp_block.initialize_from_block(block, prefill_list=[0, 1, 5])
- self.test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
- with mininode_lock:
- assert(self.test_node.last_getblocktxn is not None)
- absolute_indexes = self.test_node.last_getblocktxn.block_txn_request.to_absolute()
- assert_equal(absolute_indexes, [2, 3, 4])
- msg.block_transactions = BlockTransactions(block.sha256, block.vtx[2:5])
- self.test_node.send_and_ping(msg)
- assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
+ comp_block.initialize_from_block(block, prefill_list=[0, 1, 5], use_witness=with_witness)
+ test_getblocktxn_response(comp_block, test_node, [2, 3, 4])
+ msg_bt.block_transactions = BlockTransactions(block.sha256, block.vtx[2:5])
+ test_tip_after_message(node, test_node, msg_bt, block.sha256)
# Now try giving one transaction ahead of time.
utxo = self.utxos.pop(0)
- block = self.build_block_with_transactions(utxo, 5)
+ block = self.build_block_with_transactions(node, utxo, 5)
self.utxos.append([block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue])
- self.test_node.send_and_ping(msg_tx(block.vtx[1]))
- assert(block.vtx[1].hash in self.nodes[0].getrawmempool())
+ test_node.send_and_ping(msg_tx(block.vtx[1]))
+ assert(block.vtx[1].hash in node.getrawmempool())
# Prefill 4 out of the 6 transactions, and verify that only the one
# that was not in the mempool is requested.
- comp_block.initialize_from_block(block, prefill_list=[0, 2, 3, 4])
- self.test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
- with mininode_lock:
- assert(self.test_node.last_getblocktxn is not None)
- absolute_indexes = self.test_node.last_getblocktxn.block_txn_request.to_absolute()
- assert_equal(absolute_indexes, [5])
+ comp_block.initialize_from_block(block, prefill_list=[0, 2, 3, 4], use_witness=with_witness)
+ test_getblocktxn_response(comp_block, test_node, [5])
- msg.block_transactions = BlockTransactions(block.sha256, [block.vtx[5]])
- self.test_node.send_and_ping(msg)
- assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
+ msg_bt.block_transactions = BlockTransactions(block.sha256, [block.vtx[5]])
+ test_tip_after_message(node, test_node, msg_bt, block.sha256)
# Now provide all transactions to the node before the block is
# announced and verify reconstruction happens immediately.
utxo = self.utxos.pop(0)
- block = self.build_block_with_transactions(utxo, 10)
+ block = self.build_block_with_transactions(node, utxo, 10)
self.utxos.append([block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue])
for tx in block.vtx[1:]:
- self.test_node.send_message(msg_tx(tx))
- self.test_node.sync_with_ping()
+ test_node.send_message(msg_tx(tx))
+ test_node.sync_with_ping()
# Make sure all transactions were accepted.
- mempool = self.nodes[0].getrawmempool()
+ mempool = node.getrawmempool()
for tx in block.vtx[1:]:
assert(tx.hash in mempool)
# Clear out last request.
with mininode_lock:
- self.test_node.last_getblocktxn = None
+ test_node.last_getblocktxn = None
# Send compact block
- comp_block.initialize_from_block(block, prefill_list=[0])
- self.test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
+ comp_block.initialize_from_block(block, prefill_list=[0], use_witness=with_witness)
+ test_tip_after_message(node, test_node, msg_cmpctblock(comp_block.to_p2p()), block.sha256)
with mininode_lock:
# Shouldn't have gotten a request for any transaction
- assert(self.test_node.last_getblocktxn is None)
- # Tip should have updated
- assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
+ assert(test_node.last_getblocktxn is None)
# Incorrectly responding to a getblocktxn shouldn't cause the block to be
# permanently failed.
- def test_incorrect_blocktxn_response(self):
- print("Testing handling of incorrect blocktxn responses...")
-
+ def test_incorrect_blocktxn_response(self, node, test_node, version):
if (len(self.utxos) == 0):
self.make_utxos()
utxo = self.utxos.pop(0)
- block = self.build_block_with_transactions(utxo, 10)
+ block = self.build_block_with_transactions(node, utxo, 10)
self.utxos.append([block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue])
# Relay the first 5 transactions from the block in advance
for tx in block.vtx[1:6]:
- self.test_node.send_message(msg_tx(tx))
- self.test_node.sync_with_ping()
+ test_node.send_message(msg_tx(tx))
+ test_node.sync_with_ping()
# Make sure all transactions were accepted.
- mempool = self.nodes[0].getrawmempool()
+ mempool = node.getrawmempool()
for tx in block.vtx[1:6]:
assert(tx.hash in mempool)
# Send compact block
comp_block = HeaderAndShortIDs()
- comp_block.initialize_from_block(block, prefill_list=[0])
- self.test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
+ comp_block.initialize_from_block(block, prefill_list=[0], use_witness=(version == 2))
+ test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
absolute_indexes = []
with mininode_lock:
- assert(self.test_node.last_getblocktxn is not None)
- absolute_indexes = self.test_node.last_getblocktxn.block_txn_request.to_absolute()
+ assert(test_node.last_getblocktxn is not None)
+ absolute_indexes = test_node.last_getblocktxn.block_txn_request.to_absolute()
assert_equal(absolute_indexes, [6, 7, 8, 9, 10])
# Now give an incorrect response.
@@ -466,100 +576,115 @@ class CompactBlocksTest(BitcoinTestFramework):
# verifying that the block isn't marked bad permanently. This is good
# enough for now.
msg = msg_blocktxn()
+ if version==2:
+ msg = msg_witness_blocktxn()
msg.block_transactions = BlockTransactions(block.sha256, [block.vtx[5]] + block.vtx[7:])
- self.test_node.send_and_ping(msg)
+ test_node.send_and_ping(msg)
# Tip should not have updated
- assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.hashPrevBlock)
+ assert_equal(int(node.getbestblockhash(), 16), block.hashPrevBlock)
# We should receive a getdata request
- success = wait_until(lambda: self.test_node.last_getdata is not None, timeout=10)
+ success = wait_until(lambda: test_node.last_getdata is not None, timeout=10)
assert(success)
- assert_equal(len(self.test_node.last_getdata.inv), 1)
- assert_equal(self.test_node.last_getdata.inv[0].type, 2)
- assert_equal(self.test_node.last_getdata.inv[0].hash, block.sha256)
+ assert_equal(len(test_node.last_getdata.inv), 1)
+ assert(test_node.last_getdata.inv[0].type == 2 or test_node.last_getdata.inv[0].type == 2|MSG_WITNESS_FLAG)
+ assert_equal(test_node.last_getdata.inv[0].hash, block.sha256)
# Deliver the block
- self.test_node.send_and_ping(msg_block(block))
- assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
-
- def test_getblocktxn_handler(self):
- print("Testing getblocktxn handler...")
-
- # bitcoind won't respond for blocks whose height is more than 15 blocks
- # deep.
- MAX_GETBLOCKTXN_DEPTH = 15
- chain_height = self.nodes[0].getblockcount()
+ if version==2:
+ test_node.send_and_ping(msg_witness_block(block))
+ else:
+ test_node.send_and_ping(msg_block(block))
+ assert_equal(int(node.getbestblockhash(), 16), block.sha256)
+
+ def test_getblocktxn_handler(self, node, test_node, version):
+ # bitcoind will not send blocktxn responses for blocks whose height is
+ # more than 10 blocks deep.
+ MAX_GETBLOCKTXN_DEPTH = 10
+ chain_height = node.getblockcount()
current_height = chain_height
while (current_height >= chain_height - MAX_GETBLOCKTXN_DEPTH):
- block_hash = self.nodes[0].getblockhash(current_height)
- block = FromHex(CBlock(), self.nodes[0].getblock(block_hash, False))
+ block_hash = node.getblockhash(current_height)
+ block = FromHex(CBlock(), node.getblock(block_hash, False))
msg = msg_getblocktxn()
msg.block_txn_request = BlockTransactionsRequest(int(block_hash, 16), [])
num_to_request = random.randint(1, len(block.vtx))
msg.block_txn_request.from_absolute(sorted(random.sample(range(len(block.vtx)), num_to_request)))
- self.test_node.send_message(msg)
- success = wait_until(lambda: self.test_node.last_blocktxn is not None, timeout=10)
+ test_node.send_message(msg)
+ success = wait_until(lambda: test_node.last_blocktxn is not None, timeout=10)
assert(success)
[tx.calc_sha256() for tx in block.vtx]
with mininode_lock:
- assert_equal(self.test_node.last_blocktxn.block_transactions.blockhash, int(block_hash, 16))
+ assert_equal(test_node.last_blocktxn.block_transactions.blockhash, int(block_hash, 16))
all_indices = msg.block_txn_request.to_absolute()
for index in all_indices:
- tx = self.test_node.last_blocktxn.block_transactions.transactions.pop(0)
+ tx = test_node.last_blocktxn.block_transactions.transactions.pop(0)
tx.calc_sha256()
assert_equal(tx.sha256, block.vtx[index].sha256)
- self.test_node.last_blocktxn = None
+ if version == 1:
+ # Witnesses should have been stripped
+ assert(tx.wit.is_null())
+ else:
+ # Check that the witness matches
+ assert_equal(tx.calc_sha256(True), block.vtx[index].calc_sha256(True))
+ test_node.last_blocktxn = None
current_height -= 1
- # Next request should be ignored, as we're past the allowed depth.
- block_hash = self.nodes[0].getblockhash(current_height)
+ # Next request should send a full block response, as we're past the
+ # allowed depth for a blocktxn response.
+ block_hash = node.getblockhash(current_height)
msg.block_txn_request = BlockTransactionsRequest(int(block_hash, 16), [0])
- self.test_node.send_and_ping(msg)
with mininode_lock:
- assert_equal(self.test_node.last_blocktxn, None)
-
- def test_compactblocks_not_at_tip(self):
- print("Testing compactblock requests/announcements not at chain tip...")
+ test_node.last_block = None
+ test_node.last_blocktxn = None
+ test_node.send_and_ping(msg)
+ with mininode_lock:
+ test_node.last_block.block.calc_sha256()
+ assert_equal(test_node.last_block.block.sha256, int(block_hash, 16))
+ assert_equal(test_node.last_blocktxn, None)
+ def test_compactblocks_not_at_tip(self, node, test_node):
# Test that requesting old compactblocks doesn't work.
- MAX_CMPCTBLOCK_DEPTH = 11
+ MAX_CMPCTBLOCK_DEPTH = 5
new_blocks = []
- for i in range(MAX_CMPCTBLOCK_DEPTH):
- self.test_node.clear_block_announcement()
- new_blocks.append(self.nodes[0].generate(1)[0])
- wait_until(self.test_node.received_block_announcement, timeout=30)
-
- self.test_node.clear_block_announcement()
- self.test_node.send_message(msg_getdata([CInv(4, int(new_blocks[0], 16))]))
- success = wait_until(lambda: self.test_node.last_cmpctblock is not None, timeout=30)
+ for i in range(MAX_CMPCTBLOCK_DEPTH + 1):
+ test_node.clear_block_announcement()
+ new_blocks.append(node.generate(1)[0])
+ wait_until(test_node.received_block_announcement, timeout=30)
+
+ test_node.clear_block_announcement()
+ test_node.send_message(msg_getdata([CInv(4, int(new_blocks[0], 16))]))
+ success = wait_until(lambda: test_node.last_cmpctblock is not None, timeout=30)
assert(success)
- self.test_node.clear_block_announcement()
- self.nodes[0].generate(1)
- wait_until(self.test_node.received_block_announcement, timeout=30)
- self.test_node.clear_block_announcement()
- self.test_node.send_message(msg_getdata([CInv(4, int(new_blocks[0], 16))]))
- success = wait_until(lambda: self.test_node.last_block is not None, timeout=30)
+ test_node.clear_block_announcement()
+ node.generate(1)
+ wait_until(test_node.received_block_announcement, timeout=30)
+ test_node.clear_block_announcement()
+ with mininode_lock:
+ test_node.last_block = None
+ test_node.send_message(msg_getdata([CInv(4, int(new_blocks[0], 16))]))
+ success = wait_until(lambda: test_node.last_block is not None, timeout=30)
assert(success)
with mininode_lock:
- self.test_node.last_block.block.calc_sha256()
- assert_equal(self.test_node.last_block.block.sha256, int(new_blocks[0], 16))
+ test_node.last_block.block.calc_sha256()
+ assert_equal(test_node.last_block.block.sha256, int(new_blocks[0], 16))
# Generate an old compactblock, and verify that it's not accepted.
- cur_height = self.nodes[0].getblockcount()
- hashPrevBlock = int(self.nodes[0].getblockhash(cur_height-5), 16)
- block = self.build_block_on_tip()
+ cur_height = node.getblockcount()
+ hashPrevBlock = int(node.getblockhash(cur_height-5), 16)
+ block = self.build_block_on_tip(node)
block.hashPrevBlock = hashPrevBlock
block.solve()
comp_block = HeaderAndShortIDs()
comp_block.initialize_from_block(block)
- self.test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
+ test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
- tips = self.nodes[0].getchaintips()
+ tips = node.getchaintips()
found = False
for x in tips:
if x["hash"] == block.hash:
@@ -573,18 +698,136 @@ class CompactBlocksTest(BitcoinTestFramework):
msg = msg_getblocktxn()
msg.block_txn_request = BlockTransactionsRequest(block.sha256, [0])
with mininode_lock:
- self.test_node.last_blocktxn = None
- self.test_node.send_and_ping(msg)
+ test_node.last_blocktxn = None
+ test_node.send_and_ping(msg)
with mininode_lock:
- assert(self.test_node.last_blocktxn is None)
+ assert(test_node.last_blocktxn is None)
+
+ def activate_segwit(self, node):
+ node.generate(144*3)
+ assert_equal(get_bip9_status(node, "segwit")["status"], 'active')
+
+ def test_end_to_end_block_relay(self, node, listeners):
+ utxo = self.utxos.pop(0)
+
+ block = self.build_block_with_transactions(node, utxo, 10)
+
+ [l.clear_block_announcement() for l in listeners]
+
+ # ToHex() won't serialize with witness, but this block has no witnesses
+ # anyway. TODO: repeat this test with witness tx's to a segwit node.
+ node.submitblock(ToHex(block))
+
+ for l in listeners:
+ wait_until(lambda: l.received_block_announcement(), timeout=30)
+ with mininode_lock:
+ for l in listeners:
+ assert(l.last_cmpctblock is not None)
+ l.last_cmpctblock.header_and_shortids.header.calc_sha256()
+ assert_equal(l.last_cmpctblock.header_and_shortids.header.sha256, block.sha256)
+
+ # Test that we don't get disconnected if we relay a compact block with valid header,
+ # but invalid transactions.
+ def test_invalid_tx_in_compactblock(self, node, test_node, use_segwit):
+ assert(len(self.utxos))
+ utxo = self.utxos[0]
+
+ block = self.build_block_with_transactions(node, utxo, 5)
+ del block.vtx[3]
+ block.hashMerkleRoot = block.calc_merkle_root()
+ if use_segwit:
+ # If we're testing with segwit, also drop the coinbase witness,
+ # but include the witness commitment.
+ add_witness_commitment(block)
+ block.vtx[0].wit.vtxinwit = []
+ block.solve()
+
+ # Now send the compact block with all transactions prefilled, and
+ # verify that we don't get disconnected.
+ comp_block = HeaderAndShortIDs()
+ comp_block.initialize_from_block(block, prefill_list=[0, 1, 2, 3, 4], use_witness=use_segwit)
+ msg = msg_cmpctblock(comp_block.to_p2p())
+ test_node.send_and_ping(msg)
+
+ # Check that the tip didn't advance
+ assert(int(node.getbestblockhash(), 16) is not block.sha256)
+ test_node.sync_with_ping()
+
+ # Helper for enabling cb announcements
+ # Send the sendcmpct request and sync headers
+ def request_cb_announcements(self, peer, node, version):
+ tip = node.getbestblockhash()
+ peer.get_headers(locator=[int(tip, 16)], hashstop=0)
+
+ msg = msg_sendcmpct()
+ msg.version = version
+ msg.announce = True
+ peer.send_and_ping(msg)
+
+ def test_compactblock_reconstruction_multiple_peers(self, node, stalling_peer, delivery_peer):
+ assert(len(self.utxos))
+
+ def announce_cmpct_block(node, peer):
+ utxo = self.utxos.pop(0)
+ block = self.build_block_with_transactions(node, utxo, 5)
+
+ cmpct_block = HeaderAndShortIDs()
+ cmpct_block.initialize_from_block(block)
+ msg = msg_cmpctblock(cmpct_block.to_p2p())
+ peer.send_and_ping(msg)
+ with mininode_lock:
+ assert(peer.last_getblocktxn is not None)
+ return block, cmpct_block
+
+ block, cmpct_block = announce_cmpct_block(node, stalling_peer)
+
+ for tx in block.vtx[1:]:
+ delivery_peer.send_message(msg_tx(tx))
+ delivery_peer.sync_with_ping()
+ mempool = node.getrawmempool()
+ for tx in block.vtx[1:]:
+ assert(tx.hash in mempool)
+
+ delivery_peer.send_and_ping(msg_cmpctblock(cmpct_block.to_p2p()))
+ assert_equal(int(node.getbestblockhash(), 16), block.sha256)
+
+ self.utxos.append([block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue])
+
+ # Now test that delivering an invalid compact block won't break relay
+
+ block, cmpct_block = announce_cmpct_block(node, stalling_peer)
+ for tx in block.vtx[1:]:
+ delivery_peer.send_message(msg_tx(tx))
+ delivery_peer.sync_with_ping()
+
+ cmpct_block.prefilled_txn[0].tx.wit.vtxinwit = [ CTxInWitness() ]
+ cmpct_block.prefilled_txn[0].tx.wit.vtxinwit[0].scriptWitness.stack = [ser_uint256(0)]
+
+ cmpct_block.use_witness = True
+ delivery_peer.send_and_ping(msg_cmpctblock(cmpct_block.to_p2p()))
+ assert(int(node.getbestblockhash(), 16) != block.sha256)
+
+ msg = msg_blocktxn()
+ msg.block_transactions.blockhash = block.sha256
+ msg.block_transactions.transactions = block.vtx[1:]
+ stalling_peer.send_and_ping(msg)
+ assert_equal(int(node.getbestblockhash(), 16), block.sha256)
def run_test(self):
# Setup the p2p connections and start up the network thread.
self.test_node = TestNode()
+ self.segwit_node = TestNode()
+ self.old_node = TestNode() # version 1 peer <--> segwit node
connections = []
connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.test_node))
+ connections.append(NodeConn('127.0.0.1', p2p_port(1), self.nodes[1],
+ self.segwit_node, services=NODE_NETWORK|NODE_WITNESS))
+ connections.append(NodeConn('127.0.0.1', p2p_port(1), self.nodes[1],
+ self.old_node, services=NODE_NETWORK))
self.test_node.add_connection(connections[0])
+ self.segwit_node.add_connection(connections[1])
+ self.old_node.add_connection(connections[2])
NetworkThread().start() # Start up network handling in another thread
@@ -594,13 +837,121 @@ class CompactBlocksTest(BitcoinTestFramework):
# We will need UTXOs to construct transactions in later tests.
self.make_utxos()
- self.test_sendcmpct()
- self.test_compactblock_construction()
- self.test_compactblock_requests()
- self.test_getblocktxn_requests()
- self.test_getblocktxn_handler()
- self.test_compactblocks_not_at_tip()
- self.test_incorrect_blocktxn_response()
+ print("Running tests, pre-segwit activation:")
+
+ print("\tTesting SENDCMPCT p2p message... ")
+ self.test_sendcmpct(self.nodes[0], self.test_node, 1)
+ sync_blocks(self.nodes)
+ self.test_sendcmpct(self.nodes[1], self.segwit_node, 2, old_node=self.old_node)
+ sync_blocks(self.nodes)
+
+ print("\tTesting compactblock construction...")
+ self.test_compactblock_construction(self.nodes[0], self.test_node, 1, False)
+ sync_blocks(self.nodes)
+ self.test_compactblock_construction(self.nodes[1], self.segwit_node, 2, False)
+ sync_blocks(self.nodes)
+
+ print("\tTesting compactblock requests... ")
+ self.test_compactblock_requests(self.nodes[0], self.test_node, 1, False)
+ sync_blocks(self.nodes)
+ self.test_compactblock_requests(self.nodes[1], self.segwit_node, 2, False)
+ sync_blocks(self.nodes)
+
+ print("\tTesting getblocktxn requests...")
+ self.test_getblocktxn_requests(self.nodes[0], self.test_node, 1)
+ sync_blocks(self.nodes)
+ self.test_getblocktxn_requests(self.nodes[1], self.segwit_node, 2)
+ sync_blocks(self.nodes)
+
+ print("\tTesting getblocktxn handler...")
+ self.test_getblocktxn_handler(self.nodes[0], self.test_node, 1)
+ sync_blocks(self.nodes)
+ self.test_getblocktxn_handler(self.nodes[1], self.segwit_node, 2)
+ self.test_getblocktxn_handler(self.nodes[1], self.old_node, 1)
+ sync_blocks(self.nodes)
+
+ print("\tTesting compactblock requests/announcements not at chain tip...")
+ self.test_compactblocks_not_at_tip(self.nodes[0], self.test_node)
+ sync_blocks(self.nodes)
+ self.test_compactblocks_not_at_tip(self.nodes[1], self.segwit_node)
+ self.test_compactblocks_not_at_tip(self.nodes[1], self.old_node)
+ sync_blocks(self.nodes)
+
+ print("\tTesting handling of incorrect blocktxn responses...")
+ self.test_incorrect_blocktxn_response(self.nodes[0], self.test_node, 1)
+ sync_blocks(self.nodes)
+ self.test_incorrect_blocktxn_response(self.nodes[1], self.segwit_node, 2)
+ sync_blocks(self.nodes)
+
+ # End-to-end block relay tests
+ print("\tTesting end-to-end block relay...")
+ self.request_cb_announcements(self.test_node, self.nodes[0], 1)
+ self.request_cb_announcements(self.old_node, self.nodes[1], 1)
+ self.request_cb_announcements(self.segwit_node, self.nodes[1], 2)
+ self.test_end_to_end_block_relay(self.nodes[0], [self.segwit_node, self.test_node, self.old_node])
+ self.test_end_to_end_block_relay(self.nodes[1], [self.segwit_node, self.test_node, self.old_node])
+
+ print("\tTesting handling of invalid compact blocks...")
+ self.test_invalid_tx_in_compactblock(self.nodes[0], self.test_node, False)
+ self.test_invalid_tx_in_compactblock(self.nodes[1], self.segwit_node, False)
+ self.test_invalid_tx_in_compactblock(self.nodes[1], self.old_node, False)
+
+ print("\tTesting reconstructing compact blocks from all peers...")
+ self.test_compactblock_reconstruction_multiple_peers(self.nodes[1], self.segwit_node, self.old_node)
+ sync_blocks(self.nodes)
+
+ # Advance to segwit activation
+ print ("\nAdvancing to segwit activation\n")
+ self.activate_segwit(self.nodes[1])
+ print ("Running tests, post-segwit activation...")
+
+ print("\tTesting compactblock construction...")
+ self.test_compactblock_construction(self.nodes[1], self.old_node, 1, True)
+ self.test_compactblock_construction(self.nodes[1], self.segwit_node, 2, True)
+ sync_blocks(self.nodes)
+
+ print("\tTesting compactblock requests (unupgraded node)... ")
+ self.test_compactblock_requests(self.nodes[0], self.test_node, 1, True)
+
+ print("\tTesting getblocktxn requests (unupgraded node)...")
+ self.test_getblocktxn_requests(self.nodes[0], self.test_node, 1)
+
+ # Need to manually sync node0 and node1, because post-segwit activation,
+ # node1 will not download blocks from node0.
+ print("\tSyncing nodes...")
+ assert(self.nodes[0].getbestblockhash() != self.nodes[1].getbestblockhash())
+ while (self.nodes[0].getblockcount() > self.nodes[1].getblockcount()):
+ block_hash = self.nodes[0].getblockhash(self.nodes[1].getblockcount()+1)
+ self.nodes[1].submitblock(self.nodes[0].getblock(block_hash, False))
+ assert_equal(self.nodes[0].getbestblockhash(), self.nodes[1].getbestblockhash())
+
+ print("\tTesting compactblock requests (segwit node)... ")
+ self.test_compactblock_requests(self.nodes[1], self.segwit_node, 2, True)
+
+ print("\tTesting getblocktxn requests (segwit node)...")
+ self.test_getblocktxn_requests(self.nodes[1], self.segwit_node, 2)
+ sync_blocks(self.nodes)
+
+ print("\tTesting getblocktxn handler (segwit node should return witnesses)...")
+ self.test_getblocktxn_handler(self.nodes[1], self.segwit_node, 2)
+ self.test_getblocktxn_handler(self.nodes[1], self.old_node, 1)
+
+ # Test that if we submitblock to node1, we'll get a compact block
+ # announcement to all peers.
+ # (Post-segwit activation, blocks won't propagate from node0 to node1
+ # automatically, so don't bother testing a block announced to node0.)
+ print("\tTesting end-to-end block relay...")
+ self.request_cb_announcements(self.test_node, self.nodes[0], 1)
+ self.request_cb_announcements(self.old_node, self.nodes[1], 1)
+ self.request_cb_announcements(self.segwit_node, self.nodes[1], 2)
+ self.test_end_to_end_block_relay(self.nodes[1], [self.segwit_node, self.test_node, self.old_node])
+
+ print("\tTesting handling of invalid compact blocks...")
+ self.test_invalid_tx_in_compactblock(self.nodes[0], self.test_node, False)
+ self.test_invalid_tx_in_compactblock(self.nodes[1], self.segwit_node, True)
+ self.test_invalid_tx_in_compactblock(self.nodes[1], self.old_node, True)
+
+ print("\tTesting invalid index in cmpctblock message...")
self.test_invalid_cmpctblock_message()
diff --git a/qa/rpc-tests/p2p-fullblocktest.py b/qa/rpc-tests/p2p-fullblocktest.py
index 9aee81164f..e4b889d761 100755
--- a/qa/rpc-tests/p2p-fullblocktest.py
+++ b/qa/rpc-tests/p2p-fullblocktest.py
@@ -351,7 +351,7 @@ class FullBlockTest(ComparisonTestFramework):
block(22, spend=out[5])
yield rejected()
- # Create a block on either side of MAX_BLOCK_SIZE and make sure its accepted/rejected
+ # Create a block on either side of MAX_BLOCK_BASE_SIZE and make sure its accepted/rejected
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6)
# \-> b24 (6) -> b25 (7)
@@ -359,24 +359,24 @@ class FullBlockTest(ComparisonTestFramework):
tip(15)
b23 = block(23, spend=out[6])
tx = CTransaction()
- script_length = MAX_BLOCK_SIZE - len(b23.serialize()) - 69
+ script_length = MAX_BLOCK_BASE_SIZE - len(b23.serialize()) - 69
script_output = CScript([b'\x00' * script_length])
tx.vout.append(CTxOut(0, script_output))
tx.vin.append(CTxIn(COutPoint(b23.vtx[1].sha256, 0)))
b23 = update_block(23, [tx])
# Make sure the math above worked out to produce a max-sized block
- assert_equal(len(b23.serialize()), MAX_BLOCK_SIZE)
+ assert_equal(len(b23.serialize()), MAX_BLOCK_BASE_SIZE)
yield accepted()
save_spendable_output()
# Make the next block one byte bigger and check that it fails
tip(15)
b24 = block(24, spend=out[6])
- script_length = MAX_BLOCK_SIZE - len(b24.serialize()) - 69
+ script_length = MAX_BLOCK_BASE_SIZE - len(b24.serialize()) - 69
script_output = CScript([b'\x00' * (script_length+1)])
tx.vout = [CTxOut(0, script_output)]
b24 = update_block(24, [tx])
- assert_equal(len(b24.serialize()), MAX_BLOCK_SIZE+1)
+ assert_equal(len(b24.serialize()), MAX_BLOCK_BASE_SIZE+1)
yield rejected(RejectResult(16, b'bad-blk-length'))
block(25, spend=out[7])
@@ -523,12 +523,12 @@ class FullBlockTest(ComparisonTestFramework):
tx_new = None
tx_last = tx
total_size=len(b39.serialize())
- while(total_size < MAX_BLOCK_SIZE):
+ while(total_size < MAX_BLOCK_BASE_SIZE):
tx_new = create_tx(tx_last, 1, 1, p2sh_script)
tx_new.vout.append(CTxOut(tx_last.vout[1].nValue - 1, CScript([OP_TRUE])))
tx_new.rehash()
total_size += len(tx_new.serialize())
- if total_size >= MAX_BLOCK_SIZE:
+ if total_size >= MAX_BLOCK_BASE_SIZE:
break
b39.vtx.append(tx_new) # add tx to block
tx_last = tx_new
@@ -877,7 +877,7 @@ class FullBlockTest(ComparisonTestFramework):
# This checks that a block with a bloated VARINT between the block_header and the array of tx such that
- # the block is > MAX_BLOCK_SIZE with the bloated varint, but <= MAX_BLOCK_SIZE without the bloated varint,
+ # the block is > MAX_BLOCK_BASE_SIZE with the bloated varint, but <= MAX_BLOCK_BASE_SIZE without the bloated varint,
# does not cause a subsequent, identical block with canonical encoding to be rejected. The test does not
# care whether the bloated block is accepted or rejected; it only cares that the second block is accepted.
#
@@ -901,12 +901,12 @@ class FullBlockTest(ComparisonTestFramework):
tx = CTransaction()
# use canonical serialization to calculate size
- script_length = MAX_BLOCK_SIZE - len(b64a.normal_serialize()) - 69
+ script_length = MAX_BLOCK_BASE_SIZE - len(b64a.normal_serialize()) - 69
script_output = CScript([b'\x00' * script_length])
tx.vout.append(CTxOut(0, script_output))
tx.vin.append(CTxIn(COutPoint(b64a.vtx[1].sha256, 0)))
b64a = update_block("64a", [tx])
- assert_equal(len(b64a.serialize()), MAX_BLOCK_SIZE + 8)
+ assert_equal(len(b64a.serialize()), MAX_BLOCK_BASE_SIZE + 8)
yield TestInstance([[self.tip, None]])
# comptool workaround: to make sure b64 is delivered, manually erase b64a from blockstore
@@ -916,7 +916,7 @@ class FullBlockTest(ComparisonTestFramework):
b64 = CBlock(b64a)
b64.vtx = copy.deepcopy(b64a.vtx)
assert_equal(b64.hash, b64a.hash)
- assert_equal(len(b64.serialize()), MAX_BLOCK_SIZE)
+ assert_equal(len(b64.serialize()), MAX_BLOCK_BASE_SIZE)
self.blocks[64] = b64
update_block(64, [])
yield accepted()
@@ -1250,12 +1250,12 @@ class FullBlockTest(ComparisonTestFramework):
for i in range(89, LARGE_REORG_SIZE + 89):
b = block(i, spend)
tx = CTransaction()
- script_length = MAX_BLOCK_SIZE - len(b.serialize()) - 69
+ script_length = MAX_BLOCK_BASE_SIZE - len(b.serialize()) - 69
script_output = CScript([b'\x00' * script_length])
tx.vout.append(CTxOut(0, script_output))
tx.vin.append(CTxIn(COutPoint(b.vtx[1].sha256, 0)))
b = update_block(i, [tx])
- assert_equal(len(b.serialize()), MAX_BLOCK_SIZE)
+ assert_equal(len(b.serialize()), MAX_BLOCK_BASE_SIZE)
test1.blocks_and_transactions.append([self.tip, True])
save_spendable_output()
spend = get_spendable_output()
diff --git a/qa/rpc-tests/p2p-mempool.py b/qa/rpc-tests/p2p-mempool.py
index 5c5d778f42..382d7f1e82 100755
--- a/qa/rpc-tests/p2p-mempool.py
+++ b/qa/rpc-tests/p2p-mempool.py
@@ -63,7 +63,7 @@ class TestNode(NodeConnCB):
def received_pong():
return (self.last_pong.nonce == self.ping_counter)
self.connection.send_message(msg_ping(nonce=self.ping_counter))
- success = wait_until(received_pong, timeout)
+ success = wait_until(received_pong, timeout=timeout)
self.ping_counter += 1
return success
diff --git a/qa/rpc-tests/p2p-segwit.py b/qa/rpc-tests/p2p-segwit.py
index 22ec0ad8c8..51adec5cae 100755
--- a/qa/rpc-tests/p2p-segwit.py
+++ b/qa/rpc-tests/p2p-segwit.py
@@ -64,6 +64,9 @@ class TestNode(NodeConnCB):
self.getdataset.add(inv.hash)
self.last_getdata = message
+ def on_getheaders(self, conn, message):
+ self.last_getheaders = message
+
def on_pong(self, conn, message):
self.last_pong = message
@@ -97,6 +100,10 @@ class TestNode(NodeConnCB):
test_function = lambda: self.last_getdata != None
self.sync(test_function, timeout)
+ def wait_for_getheaders(self, timeout=60):
+ test_function = lambda: self.last_getheaders != None
+ self.sync(test_function, timeout)
+
def wait_for_inv(self, expected_inv, timeout=60):
test_function = lambda: self.last_inv != expected_inv
self.sync(test_function, timeout)
@@ -111,12 +118,15 @@ class TestNode(NodeConnCB):
def announce_block_and_wait_for_getdata(self, block, use_header, timeout=60):
with mininode_lock:
self.last_getdata = None
+ self.last_getheaders = None
+ msg = msg_headers()
+ msg.headers = [ CBlockHeader(block) ]
if use_header:
- msg = msg_headers()
- msg.headers = [ CBlockHeader(block) ]
self.send_message(msg)
else:
self.send_message(msg_inv(inv=[CInv(2, block.sha256)]))
+ self.wait_for_getheaders()
+ self.send_message(msg)
self.wait_for_getdata()
return
@@ -166,6 +176,17 @@ class UTXO(object):
self.n = n
self.nValue = nValue
+# Helper for getting the script associated with a P2PKH
+def GetP2PKHScript(pubkeyhash):
+ return CScript([CScriptOp(OP_DUP), CScriptOp(OP_HASH160), pubkeyhash, CScriptOp(OP_EQUALVERIFY), CScriptOp(OP_CHECKSIG)])
+
+# Add signature for a P2PK witness program.
+def sign_P2PK_witness_input(script, txTo, inIdx, hashtype, value, key):
+ tx_hash = SegwitVersion1SignatureHash(script, txTo, inIdx, hashtype, value)
+ signature = key.sign(tx_hash) + chr(hashtype).encode('latin-1')
+ txTo.wit.vtxinwit[inIdx].scriptWitness.stack = [signature, script]
+ txTo.rehash()
+
class SegWitTest(BitcoinTestFramework):
@@ -174,11 +195,6 @@ class SegWitTest(BitcoinTestFramework):
self.setup_clean_chain = True
self.num_nodes = 3
- def add_options(self, parser):
- parser.add_option("--oldbinary", dest="oldbinary",
- default=None,
- help="pre-segwit bitcoind binary for upgrade testing")
-
def setup_network(self):
self.nodes = []
self.nodes.append(start_node(0, self.options.tmpdir, ["-debug", "-logtimemicros=1", "-whitelist=127.0.0.1"]))
@@ -186,12 +202,9 @@ class SegWitTest(BitcoinTestFramework):
self.nodes.append(start_node(1, self.options.tmpdir, ["-debug", "-logtimemicros=1", "-whitelist=127.0.0.1", "-acceptnonstdtxn=0"]))
connect_nodes(self.nodes[0], 1)
- # If an old bitcoind is given, do the upgrade-after-activation test.
- self.test_upgrade = False
- if (self.options.oldbinary != None):
- self.nodes.append(start_node(2, self.options.tmpdir, ["-debug", "-whitelist=127.0.0.1"], binary=self.options.oldbinary))
- connect_nodes(self.nodes[0], 2)
- self.test_upgrade = True
+ # Disable segwit's bip9 parameter to simulate upgrading after activation.
+ self.nodes.append(start_node(2, self.options.tmpdir, ["-debug", "-whitelist=127.0.0.1", "-bip9params=segwit:0:0"]))
+ connect_nodes(self.nodes[0], 2)
''' Helpers '''
# Build a block on top of node0's tip.
@@ -477,7 +490,7 @@ class SegWitTest(BitcoinTestFramework):
block.solve()
block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.append(b'a'*5000000)
- assert(get_virtual_size(block) > MAX_BLOCK_SIZE)
+ assert(get_virtual_size(block) > MAX_BLOCK_BASE_SIZE)
# We can't send over the p2p network, because this is too big to relay
# TODO: repeat this test with a block that can be relayed
@@ -486,7 +499,7 @@ class SegWitTest(BitcoinTestFramework):
assert(self.nodes[0].getbestblockhash() != block.hash)
block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.pop()
- assert(get_virtual_size(block) < MAX_BLOCK_SIZE)
+ assert(get_virtual_size(block) < MAX_BLOCK_BASE_SIZE)
self.nodes[0].submitblock(bytes_to_hex_str(block.serialize(True)))
assert(self.nodes[0].getbestblockhash() == block.hash)
@@ -551,10 +564,10 @@ class SegWitTest(BitcoinTestFramework):
self.update_witness_block_with_transactions(block, [parent_tx, child_tx])
vsize = get_virtual_size(block)
- additional_bytes = (MAX_BLOCK_SIZE - vsize)*4
+ additional_bytes = (MAX_BLOCK_BASE_SIZE - vsize)*4
i = 0
while additional_bytes > 0:
- # Add some more bytes to each input until we hit MAX_BLOCK_SIZE+1
+ # Add some more bytes to each input until we hit MAX_BLOCK_BASE_SIZE+1
extra_bytes = min(additional_bytes+1, 55)
block.vtx[-1].wit.vtxinwit[int(i/(2*NUM_DROPS))].scriptWitness.stack[i%(2*NUM_DROPS)] = b'a'*(195+extra_bytes)
additional_bytes -= extra_bytes
@@ -564,7 +577,7 @@ class SegWitTest(BitcoinTestFramework):
add_witness_commitment(block)
block.solve()
vsize = get_virtual_size(block)
- assert_equal(vsize, MAX_BLOCK_SIZE + 1)
+ assert_equal(vsize, MAX_BLOCK_BASE_SIZE + 1)
# Make sure that our test case would exceed the old max-network-message
# limit
assert(len(block.serialize(True)) > 2*1024*1024)
@@ -577,7 +590,7 @@ class SegWitTest(BitcoinTestFramework):
block.vtx[0].vout.pop()
add_witness_commitment(block)
block.solve()
- assert(get_virtual_size(block) == MAX_BLOCK_SIZE)
+ assert(get_virtual_size(block) == MAX_BLOCK_BASE_SIZE)
self.test_node.test_witness_block(block, accepted=True)
@@ -912,14 +925,6 @@ class SegWitTest(BitcoinTestFramework):
# But eliminating the witness should fix it
self.test_node.test_transaction_acceptance(tx, with_witness=False, accepted=True)
- # Verify that inv's to test_node come with getdata's for non-witness tx's
- # Just tweak the transaction, announce it, and verify we get a getdata
- # for a normal tx
- tx.vout[0].scriptPubKey = CScript([OP_TRUE, OP_TRUE])
- tx.rehash()
- self.test_node.announce_tx_and_wait_for_getdata(tx)
- assert(self.test_node.last_getdata.inv[0].type == 1)
-
# Cleanup: mine the first transaction and update utxo
self.nodes[0].generate(1)
assert_equal(len(self.nodes[0].getrawmempool()), 0)
@@ -1025,7 +1030,7 @@ class SegWitTest(BitcoinTestFramework):
def test_block_relay(self, segwit_activated):
print("\tTesting block relay")
- blocktype = 2|MSG_WITNESS_FLAG if segwit_activated else 2
+ blocktype = 2|MSG_WITNESS_FLAG
# test_node has set NODE_WITNESS, so all getdata requests should be for
# witness blocks.
@@ -1164,7 +1169,7 @@ class SegWitTest(BitcoinTestFramework):
if segwit_activated:
# tx and tx2 were both accepted. Don't bother trying to reclaim the
# P2PKH output; just send tx's first output back to an anyone-can-spend.
- sync_mempools(self.nodes)
+ sync_mempools([self.nodes[0], self.nodes[1]])
tx3.vin = [CTxIn(COutPoint(tx.sha256, 0), b"")]
tx3.vout = [CTxOut(tx.vout[0].nValue-1000, CScript([OP_TRUE]))]
tx3.wit.vtxinwit.append(CTxInWitness())
@@ -1331,13 +1336,6 @@ class SegWitTest(BitcoinTestFramework):
sync_blocks(self.nodes)
self.utxo.pop(0)
- # Add signature for a P2PK witness program.
- def sign_P2PK_witness_input(script, txTo, inIdx, hashtype, value, key):
- tx_hash = SegwitVersion1SignatureHash(script, txTo, inIdx, hashtype, value)
- signature = key.sign(tx_hash) + chr(hashtype).encode('latin-1')
- txTo.wit.vtxinwit[inIdx].scriptWitness.stack = [signature, script]
- txTo.rehash()
-
# Test each hashtype
prev_utxo = UTXO(tx.sha256, 0, tx.vout[0].nValue)
for sigflag in [ 0, SIGHASH_ANYONECANPAY ]:
@@ -1392,6 +1390,9 @@ class SegWitTest(BitcoinTestFramework):
block = self.build_next_block()
used_sighash_single_out_of_bounds = False
for i in range(NUM_TESTS):
+ # Ping regularly to keep the connection alive
+ if (not i % 100):
+ self.test_node.sync_with_ping()
# Choose random number of inputs to use.
num_inputs = random.randint(1, 10)
# Create a slight bias for producing more utxos
@@ -1424,7 +1425,7 @@ class SegWitTest(BitcoinTestFramework):
block.vtx.append(tx)
# Test the block periodically, if we're close to maxblocksize
- if (get_virtual_size(block) > MAX_BLOCK_SIZE - 1000):
+ if (get_virtual_size(block) > MAX_BLOCK_BASE_SIZE - 1000):
self.update_witness_block_with_transactions(block, [])
self.test_node.test_witness_block(block, accepted=True)
block = self.build_next_block()
@@ -1448,7 +1449,7 @@ class SegWitTest(BitcoinTestFramework):
tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b""))
tx2.vout.append(CTxOut(tx.vout[0].nValue, CScript([OP_TRUE])))
- script = CScript([CScriptOp(OP_DUP), CScriptOp(OP_HASH160), pubkeyhash, CScriptOp(OP_EQUALVERIFY), CScriptOp(OP_CHECKSIG)])
+ script = GetP2PKHScript(pubkeyhash)
sig_hash = SegwitVersion1SignatureHash(script, tx2, 0, SIGHASH_ALL, tx.vout[0].nValue)
signature = key.sign(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL
@@ -1697,19 +1698,258 @@ class SegWitTest(BitcoinTestFramework):
def test_getblocktemplate_before_lockin(self):
print("\tTesting getblocktemplate setting of segwit versionbit (before lockin)")
- block_version = (self.nodes[0].getblocktemplate())['version']
- assert_equal(block_version & (1 << VB_WITNESS_BIT), 0)
+ # Node0 is segwit aware, node2 is not.
+ for node in [self.nodes[0], self.nodes[2]]:
+ gbt_results = node.getblocktemplate()
+ block_version = gbt_results['version']
+ # If we're not indicating segwit support, we should not be signalling
+ # for segwit activation, nor should we get a witness commitment.
+ assert_equal(block_version & (1 << VB_WITNESS_BIT), 0)
+ assert('default_witness_commitment' not in gbt_results)
# Workaround:
# Can either change the tip, or change the mempool and wait 5 seconds
# to trigger a recomputation of getblocktemplate.
- self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)
+ txid = int(self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1), 16)
# Using mocktime lets us avoid sleep()
+ sync_mempools(self.nodes)
self.nodes[0].setmocktime(int(time.time())+10)
+ self.nodes[2].setmocktime(int(time.time())+10)
+
+ for node in [self.nodes[0], self.nodes[2]]:
+ gbt_results = node.getblocktemplate({"rules" : ["segwit"]})
+ block_version = gbt_results['version']
+ if node == self.nodes[2]:
+ # If this is a non-segwit node, we should still not get a witness
+ # commitment, nor a version bit signalling segwit.
+ assert_equal(block_version & (1 << VB_WITNESS_BIT), 0)
+ assert('default_witness_commitment' not in gbt_results)
+ else:
+ # For segwit-aware nodes, check the version bit and the witness
+ # commitment are correct.
+ assert(block_version & (1 << VB_WITNESS_BIT) != 0)
+ assert('default_witness_commitment' in gbt_results)
+ witness_commitment = gbt_results['default_witness_commitment']
+
+ # TODO: this duplicates some code from blocktools.py, would be nice
+ # to refactor.
+ # Check that default_witness_commitment is present.
+ block = CBlock()
+ witness_root = block.get_merkle_root([ser_uint256(0), ser_uint256(txid)])
+ check_commitment = uint256_from_str(hash256(ser_uint256(witness_root)+ser_uint256(0)))
+ from test_framework.blocktools import WITNESS_COMMITMENT_HEADER
+ output_data = WITNESS_COMMITMENT_HEADER + ser_uint256(check_commitment)
+ script = CScript([OP_RETURN, output_data])
+ assert_equal(witness_commitment, bytes_to_hex_str(script))
+
+ # undo mocktime
+ self.nodes[0].setmocktime(0)
+ self.nodes[2].setmocktime(0)
+
+ # Uncompressed pubkeys are no longer supported in default relay policy,
+ # but (for now) are still valid in blocks.
+ def test_uncompressed_pubkey(self):
+ print("\tTesting uncompressed pubkeys")
+ # Segwit transactions using uncompressed pubkeys are not accepted
+ # under default policy, but should still pass consensus.
+ key = CECKey()
+ key.set_secretbytes(b"9")
+ key.set_compressed(False)
+ pubkey = CPubKey(key.get_pubkey())
+ assert_equal(len(pubkey), 65) # This should be an uncompressed pubkey
+
+ assert(len(self.utxo) > 0)
+ utxo = self.utxo.pop(0)
+
+ # Test 1: P2WPKH
+ # First create a P2WPKH output that uses an uncompressed pubkey
+ pubkeyhash = hash160(pubkey)
+ scriptPKH = CScript([OP_0, pubkeyhash])
+ tx = CTransaction()
+ tx.vin.append(CTxIn(COutPoint(utxo.sha256, utxo.n), b""))
+ tx.vout.append(CTxOut(utxo.nValue-1000, scriptPKH))
+ tx.rehash()
+
+ # Confirm it in a block.
+ block = self.build_next_block()
+ self.update_witness_block_with_transactions(block, [tx])
+ self.test_node.test_witness_block(block, accepted=True)
+
+ # Now try to spend it. Send it to a P2WSH output, which we'll
+ # use in the next test.
+ witness_program = CScript([pubkey, CScriptOp(OP_CHECKSIG)])
+ witness_hash = sha256(witness_program)
+ scriptWSH = CScript([OP_0, witness_hash])
+
+ tx2 = CTransaction()
+ tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b""))
+ tx2.vout.append(CTxOut(tx.vout[0].nValue-1000, scriptWSH))
+ script = GetP2PKHScript(pubkeyhash)
+ sig_hash = SegwitVersion1SignatureHash(script, tx2, 0, SIGHASH_ALL, tx.vout[0].nValue)
+ signature = key.sign(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL
+ tx2.wit.vtxinwit.append(CTxInWitness())
+ tx2.wit.vtxinwit[0].scriptWitness.stack = [ signature, pubkey ]
+ tx2.rehash()
+
+ # Should fail policy test.
+ self.test_node.test_transaction_acceptance(tx2, True, False, b'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)')
+ # But passes consensus.
+ block = self.build_next_block()
+ self.update_witness_block_with_transactions(block, [tx2])
+ self.test_node.test_witness_block(block, accepted=True)
+
+ # Test 2: P2WSH
+ # Try to spend the P2WSH output created in last test.
+ # Send it to a P2SH(P2WSH) output, which we'll use in the next test.
+ p2sh_witness_hash = hash160(scriptWSH)
+ scriptP2SH = CScript([OP_HASH160, p2sh_witness_hash, OP_EQUAL])
+ scriptSig = CScript([scriptWSH])
+
+ tx3 = CTransaction()
+ tx3.vin.append(CTxIn(COutPoint(tx2.sha256, 0), b""))
+ tx3.vout.append(CTxOut(tx2.vout[0].nValue-1000, scriptP2SH))
+ tx3.wit.vtxinwit.append(CTxInWitness())
+ sign_P2PK_witness_input(witness_program, tx3, 0, SIGHASH_ALL, tx2.vout[0].nValue, key)
+
+ # Should fail policy test.
+ self.test_node.test_transaction_acceptance(tx3, True, False, b'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)')
+ # But passes consensus.
+ block = self.build_next_block()
+ self.update_witness_block_with_transactions(block, [tx3])
+ self.test_node.test_witness_block(block, accepted=True)
+
+ # Test 3: P2SH(P2WSH)
+ # Try to spend the P2SH output created in the last test.
+ # Send it to a P2PKH output, which we'll use in the next test.
+ scriptPubKey = GetP2PKHScript(pubkeyhash)
+ tx4 = CTransaction()
+ tx4.vin.append(CTxIn(COutPoint(tx3.sha256, 0), scriptSig))
+ tx4.vout.append(CTxOut(tx3.vout[0].nValue-1000, scriptPubKey))
+ tx4.wit.vtxinwit.append(CTxInWitness())
+ sign_P2PK_witness_input(witness_program, tx4, 0, SIGHASH_ALL, tx3.vout[0].nValue, key)
+
+ # Should fail policy test.
+ self.test_node.test_transaction_acceptance(tx4, True, False, b'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)')
+ block = self.build_next_block()
+ self.update_witness_block_with_transactions(block, [tx4])
+ self.test_node.test_witness_block(block, accepted=True)
+
+ # Test 4: Uncompressed pubkeys should still be valid in non-segwit
+ # transactions.
+ tx5 = CTransaction()
+ tx5.vin.append(CTxIn(COutPoint(tx4.sha256, 0), b""))
+ tx5.vout.append(CTxOut(tx4.vout[0].nValue-1000, CScript([OP_TRUE])))
+ (sig_hash, err) = SignatureHash(scriptPubKey, tx5, 0, SIGHASH_ALL)
+ signature = key.sign(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL
+ tx5.vin[0].scriptSig = CScript([signature, pubkey])
+ tx5.rehash()
+ # Should pass policy and consensus.
+ self.test_node.test_transaction_acceptance(tx5, True, True)
+ block = self.build_next_block()
+ self.update_witness_block_with_transactions(block, [tx5])
+ self.test_node.test_witness_block(block, accepted=True)
+ self.utxo.append(UTXO(tx5.sha256, 0, tx5.vout[0].nValue))
+
+ def test_non_standard_witness(self):
+ print("\tTesting detection of non-standard P2WSH witness")
+ pad = chr(1).encode('latin-1')
+
+ # Create scripts for tests
+ scripts = []
+ scripts.append(CScript([OP_DROP] * 100))
+ scripts.append(CScript([OP_DROP] * 99))
+ scripts.append(CScript([pad * 59] * 59 + [OP_DROP] * 60))
+ scripts.append(CScript([pad * 59] * 59 + [OP_DROP] * 61))
+
+ p2wsh_scripts = []
+
+ assert(len(self.utxo))
+ tx = CTransaction()
+ tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b""))
+
+ # For each script, generate a pair of P2WSH and P2SH-P2WSH output.
+ outputvalue = (self.utxo[0].nValue - 1000) // (len(scripts) * 2)
+ for i in scripts:
+ p2wsh = CScript([OP_0, sha256(i)])
+ p2sh = hash160(p2wsh)
+ p2wsh_scripts.append(p2wsh)
+ tx.vout.append(CTxOut(outputvalue, p2wsh))
+ tx.vout.append(CTxOut(outputvalue, CScript([OP_HASH160, p2sh, OP_EQUAL])))
+ tx.rehash()
+ txid = tx.sha256
+ self.test_node.test_transaction_acceptance(tx, with_witness=False, accepted=True)
+
+ self.nodes[0].generate(1)
+ sync_blocks(self.nodes)
+
+ # Creating transactions for tests
+ p2wsh_txs = []
+ p2sh_txs = []
+ for i in range(len(scripts)):
+ p2wsh_tx = CTransaction()
+ p2wsh_tx.vin.append(CTxIn(COutPoint(txid,i*2)))
+ p2wsh_tx.vout.append(CTxOut(outputvalue - 5000, CScript([OP_0, hash160(hex_str_to_bytes(""))])))
+ p2wsh_tx.wit.vtxinwit.append(CTxInWitness())
+ p2wsh_tx.rehash()
+ p2wsh_txs.append(p2wsh_tx)
+ p2sh_tx = CTransaction()
+ p2sh_tx.vin.append(CTxIn(COutPoint(txid,i*2+1), CScript([p2wsh_scripts[i]])))
+ p2sh_tx.vout.append(CTxOut(outputvalue - 5000, CScript([OP_0, hash160(hex_str_to_bytes(""))])))
+ p2sh_tx.wit.vtxinwit.append(CTxInWitness())
+ p2sh_tx.rehash()
+ p2sh_txs.append(p2sh_tx)
+
+ # Testing native P2WSH
+ # Witness stack size, excluding witnessScript, over 100 is non-standard
+ p2wsh_txs[0].wit.vtxinwit[0].scriptWitness.stack = [pad] * 101 + [scripts[0]]
+ self.std_node.test_transaction_acceptance(p2wsh_txs[0], True, False, b'bad-witness-nonstandard')
+ # Non-standard nodes should accept
+ self.test_node.test_transaction_acceptance(p2wsh_txs[0], True, True)
+
+ # Stack element size over 80 bytes is non-standard
+ p2wsh_txs[1].wit.vtxinwit[0].scriptWitness.stack = [pad * 81] * 100 + [scripts[1]]
+ self.std_node.test_transaction_acceptance(p2wsh_txs[1], True, False, b'bad-witness-nonstandard')
+ # Non-standard nodes should accept
+ self.test_node.test_transaction_acceptance(p2wsh_txs[1], True, True)
+ # Standard nodes should accept if element size is not over 80 bytes
+ p2wsh_txs[1].wit.vtxinwit[0].scriptWitness.stack = [pad * 80] * 100 + [scripts[1]]
+ self.std_node.test_transaction_acceptance(p2wsh_txs[1], True, True)
+
+ # witnessScript size at 3600 bytes is standard
+ p2wsh_txs[2].wit.vtxinwit[0].scriptWitness.stack = [pad, pad, scripts[2]]
+ self.test_node.test_transaction_acceptance(p2wsh_txs[2], True, True)
+ self.std_node.test_transaction_acceptance(p2wsh_txs[2], True, True)
+
+ # witnessScript size at 3601 bytes is non-standard
+ p2wsh_txs[3].wit.vtxinwit[0].scriptWitness.stack = [pad, pad, pad, scripts[3]]
+ self.std_node.test_transaction_acceptance(p2wsh_txs[3], True, False, b'bad-witness-nonstandard')
+ # Non-standard nodes should accept
+ self.test_node.test_transaction_acceptance(p2wsh_txs[3], True, True)
+
+ # Repeating the same tests with P2SH-P2WSH
+ p2sh_txs[0].wit.vtxinwit[0].scriptWitness.stack = [pad] * 101 + [scripts[0]]
+ self.std_node.test_transaction_acceptance(p2sh_txs[0], True, False, b'bad-witness-nonstandard')
+ self.test_node.test_transaction_acceptance(p2sh_txs[0], True, True)
+ p2sh_txs[1].wit.vtxinwit[0].scriptWitness.stack = [pad * 81] * 100 + [scripts[1]]
+ self.std_node.test_transaction_acceptance(p2sh_txs[1], True, False, b'bad-witness-nonstandard')
+ self.test_node.test_transaction_acceptance(p2sh_txs[1], True, True)
+ p2sh_txs[1].wit.vtxinwit[0].scriptWitness.stack = [pad * 80] * 100 + [scripts[1]]
+ self.std_node.test_transaction_acceptance(p2sh_txs[1], True, True)
+ p2sh_txs[2].wit.vtxinwit[0].scriptWitness.stack = [pad, pad, scripts[2]]
+ self.test_node.test_transaction_acceptance(p2sh_txs[2], True, True)
+ self.std_node.test_transaction_acceptance(p2sh_txs[2], True, True)
+ p2sh_txs[3].wit.vtxinwit[0].scriptWitness.stack = [pad, pad, pad, scripts[3]]
+ self.std_node.test_transaction_acceptance(p2sh_txs[3], True, False, b'bad-witness-nonstandard')
+ self.test_node.test_transaction_acceptance(p2sh_txs[3], True, True)
+
+ self.nodes[0].generate(1) # Mine and clean up the mempool of non-standard node
+ # Valid but non-standard transactions in a block should be accepted by standard node
+ sync_blocks(self.nodes)
+ assert_equal(len(self.nodes[0].getrawmempool()), 0)
+ assert_equal(len(self.nodes[1].getrawmempool()), 0)
+
+ self.utxo.pop(0)
- block_version = self.nodes[0].getblocktemplate({"rules" : ["segwit"]})['version']
- assert(block_version & (1 << VB_WITNESS_BIT) != 0)
- self.nodes[0].setmocktime(0) # undo mocktime
def run_test(self):
# Setup the p2p connections and start up the network thread.
@@ -1744,6 +1984,7 @@ class SegWitTest(BitcoinTestFramework):
# Advance to segwit being 'started'
self.advance_to_segwit_started()
+ sync_blocks(self.nodes)
self.test_getblocktemplate_before_lockin()
sync_blocks(self.nodes)
@@ -1782,12 +2023,11 @@ class SegWitTest(BitcoinTestFramework):
self.test_standardness_v0(segwit_activated=True)
self.test_segwit_versions()
self.test_premature_coinbase_witness_spend()
+ self.test_uncompressed_pubkey()
self.test_signature_version_1()
+ self.test_non_standard_witness()
sync_blocks(self.nodes)
- if self.test_upgrade:
- self.test_upgrade_after_activation(self.nodes[2], 2)
- else:
- print("\tSkipping upgrade-after-activation test (use --oldbinary to enable)")
+ self.test_upgrade_after_activation(self.nodes[2], 2)
self.test_witness_sigops()
diff --git a/qa/rpc-tests/p2p-versionbits-warning.py b/qa/rpc-tests/p2p-versionbits-warning.py
index 962cafef0b..fc3eddddee 100755
--- a/qa/rpc-tests/p2p-versionbits-warning.py
+++ b/qa/rpc-tests/p2p-versionbits-warning.py
@@ -6,6 +6,7 @@
from test_framework.mininode import *
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
+import re
import time
from test_framework.blocktools import create_block, create_coinbase
@@ -21,6 +22,10 @@ VB_THRESHOLD = 108 # versionbits activation threshold for regtest
VB_TOP_BITS = 0x20000000
VB_UNKNOWN_BIT = 27 # Choose a bit unassigned to any deployment
+WARN_UNKNOWN_RULES_MINED = "Unknown block versions being mined! It's possible unknown rules are in effect"
+WARN_UNKNOWN_RULES_ACTIVE = "unknown new rules activated (versionbit {})".format(VB_UNKNOWN_BIT)
+VB_PATTERN = re.compile("^Warning.*versionbit")
+
# TestNode: bare-bones "peer". Used mostly as a conduit for a test to sending
# p2p messages to a node, generating the messages in the main testing logic.
class TestNode(NodeConnCB):
@@ -65,16 +70,12 @@ class VersionBitsWarningTest(BitcoinTestFramework):
self.num_nodes = 1
def setup_network(self):
- self.nodes = []
self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt")
# Open and close to create zero-length file
- with open(self.alert_filename, 'w') as f:
+ with open(self.alert_filename, 'w', encoding='utf8') as _:
pass
- self.node_options = ["-debug", "-logtimemicros=1", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""]
- self.nodes.append(start_node(0, self.options.tmpdir, self.node_options))
-
- import re
- self.vb_pattern = re.compile("^Warning.*versionbit")
+ self.extra_args = [["-debug", "-logtimemicros=1", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""]]
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args)
# Send numblocks blocks via peer with nVersionToUse set.
def send_blocks_with_version(self, peer, numblocks, nVersionToUse):
@@ -83,7 +84,7 @@ class VersionBitsWarningTest(BitcoinTestFramework):
block_time = self.nodes[0].getblockheader(tip)["time"]+1
tip = int(tip, 16)
- for i in range(numblocks):
+ for _ in range(numblocks):
block = create_block(tip, create_coinbase(height+1), block_time)
block.nVersion = nVersionToUse
block.solve()
@@ -94,9 +95,9 @@ class VersionBitsWarningTest(BitcoinTestFramework):
peer.sync_with_ping()
def test_versionbits_in_alert_file(self):
- with open(self.alert_filename, 'r') as f:
+ with open(self.alert_filename, 'r', encoding='utf8') as f:
alert_text = f.read()
- assert(self.vb_pattern.match(alert_text))
+ assert(VB_PATTERN.match(alert_text))
def run_test(self):
# Setup the p2p connection and start up the network thread.
@@ -122,8 +123,10 @@ class VersionBitsWarningTest(BitcoinTestFramework):
# Fill rest of period with regular version blocks
self.nodes[0].generate(VB_PERIOD - VB_THRESHOLD + 1)
# Check that we're not getting any versionbit-related errors in
- # getinfo()
- assert(not self.vb_pattern.match(self.nodes[0].getinfo()["errors"]))
+ # get*info()
+ assert(not VB_PATTERN.match(self.nodes[0].getinfo()["errors"]))
+ assert(not VB_PATTERN.match(self.nodes[0].getmininginfo()["errors"]))
+ assert(not VB_PATTERN.match(self.nodes[0].getnetworkinfo()["warnings"]))
# 3. Now build one period of blocks with >= VB_THRESHOLD blocks signaling
# some unknown bit
@@ -132,30 +135,31 @@ class VersionBitsWarningTest(BitcoinTestFramework):
# Might not get a versionbits-related alert yet, as we should
# have gotten a different alert due to more than 51/100 blocks
# being of unexpected version.
- # Check that getinfo() shows some kind of error.
- assert(len(self.nodes[0].getinfo()["errors"]) != 0)
+ # Check that get*info() shows some kind of error.
+ assert(WARN_UNKNOWN_RULES_MINED in self.nodes[0].getinfo()["errors"])
+ assert(WARN_UNKNOWN_RULES_MINED in self.nodes[0].getmininginfo()["errors"])
+ assert(WARN_UNKNOWN_RULES_MINED in self.nodes[0].getnetworkinfo()["warnings"])
# Mine a period worth of expected blocks so the generic block-version warning
# is cleared, and restart the node. This should move the versionbit state
# to ACTIVE.
self.nodes[0].generate(VB_PERIOD)
- stop_node(self.nodes[0], 0)
- wait_bitcoinds()
+ stop_nodes(self.nodes)
# Empty out the alert file
- with open(self.alert_filename, 'w') as f:
+ with open(self.alert_filename, 'w', encoding='utf8') as _:
pass
- self.nodes[0] = start_node(0, self.options.tmpdir, ["-debug", "-logtimemicros=1", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""])
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args)
# Connecting one block should be enough to generate an error.
self.nodes[0].generate(1)
- assert(len(self.nodes[0].getinfo()["errors"]) != 0)
- stop_node(self.nodes[0], 0)
- wait_bitcoinds()
+ assert(WARN_UNKNOWN_RULES_ACTIVE in self.nodes[0].getinfo()["errors"])
+ assert(WARN_UNKNOWN_RULES_ACTIVE in self.nodes[0].getmininginfo()["errors"])
+ assert(WARN_UNKNOWN_RULES_ACTIVE in self.nodes[0].getnetworkinfo()["warnings"])
+ stop_nodes(self.nodes)
self.test_versionbits_in_alert_file()
# Test framework expects the node to still be running...
- self.nodes[0] = start_node(0, self.options.tmpdir, ["-debug", "-logtimemicros=1", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""])
-
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args)
if __name__ == '__main__':
VersionBitsWarningTest().main()
diff --git a/qa/rpc-tests/preciousblock.py b/qa/rpc-tests/preciousblock.py
new file mode 100755
index 0000000000..3cefa51c0a
--- /dev/null
+++ b/qa/rpc-tests/preciousblock.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python3
+# Copyright (c) 2015 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#
+# Test PreciousBlock code
+#
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ connect_nodes_bi,
+ sync_chain,
+ sync_blocks,
+)
+
+def unidirectional_node_sync_via_rpc(node_src, node_dest):
+ blocks_to_copy = []
+ blockhash = node_src.getbestblockhash()
+ while True:
+ try:
+ assert(len(node_dest.getblock(blockhash, False)) > 0)
+ break
+ except:
+ blocks_to_copy.append(blockhash)
+ blockhash = node_src.getblockheader(blockhash, True)['previousblockhash']
+ blocks_to_copy.reverse()
+ for blockhash in blocks_to_copy:
+ blockdata = node_src.getblock(blockhash, False)
+ assert(node_dest.submitblock(blockdata) in (None, 'inconclusive'))
+
+def node_sync_via_rpc(nodes):
+ for node_src in nodes:
+ for node_dest in nodes:
+ if node_src is node_dest:
+ continue
+ unidirectional_node_sync_via_rpc(node_src, node_dest)
+
+class PreciousTest(BitcoinTestFramework):
+ def __init__(self):
+ super().__init__()
+ self.setup_clean_chain = True
+ self.num_nodes = 3
+ self.extra_args = [["-debug"]] * self.num_nodes
+
+ def setup_network(self):
+ self.nodes = self.setup_nodes()
+
+ def run_test(self):
+ print("Ensure submitblock can in principle reorg to a competing chain")
+ self.nodes[0].generate(1)
+ assert_equal(self.nodes[0].getblockcount(), 1)
+ (hashY, hashZ) = self.nodes[1].generate(2)
+ assert_equal(self.nodes[1].getblockcount(), 2)
+ node_sync_via_rpc(self.nodes[0:3])
+ assert_equal(self.nodes[0].getbestblockhash(), hashZ)
+
+ print("Mine blocks A-B-C on Node 0")
+ (hashA, hashB, hashC) = self.nodes[0].generate(3)
+ assert_equal(self.nodes[0].getblockcount(), 5)
+ print("Mine competing blocks E-F-G on Node 1")
+ (hashE, hashF, hashG) = self.nodes[1].generate(3)
+ assert_equal(self.nodes[1].getblockcount(), 5)
+ assert(hashC != hashG)
+ print("Connect nodes and check no reorg occurs")
+ # Submit competing blocks via RPC so any reorg should occur before we proceed (no way to wait on inaction for p2p sync)
+ node_sync_via_rpc(self.nodes[0:2])
+ connect_nodes_bi(self.nodes,0,1)
+ assert_equal(self.nodes[0].getbestblockhash(), hashC)
+ assert_equal(self.nodes[1].getbestblockhash(), hashG)
+ print("Make Node0 prefer block G")
+ self.nodes[0].preciousblock(hashG)
+ assert_equal(self.nodes[0].getbestblockhash(), hashG)
+ print("Make Node0 prefer block C again")
+ self.nodes[0].preciousblock(hashC)
+ assert_equal(self.nodes[0].getbestblockhash(), hashC)
+ print("Make Node1 prefer block C")
+ self.nodes[1].preciousblock(hashC)
+ sync_chain(self.nodes[0:2]) # wait because node 1 may not have downloaded hashC
+ assert_equal(self.nodes[1].getbestblockhash(), hashC)
+ print("Make Node1 prefer block G again")
+ self.nodes[1].preciousblock(hashG)
+ assert_equal(self.nodes[1].getbestblockhash(), hashG)
+ print("Make Node0 prefer block G again")
+ self.nodes[0].preciousblock(hashG)
+ assert_equal(self.nodes[0].getbestblockhash(), hashG)
+ print("Make Node1 prefer block C again")
+ self.nodes[1].preciousblock(hashC)
+ assert_equal(self.nodes[1].getbestblockhash(), hashC)
+ print("Mine another block (E-F-G-)H on Node 0 and reorg Node 1")
+ self.nodes[0].generate(1)
+ assert_equal(self.nodes[0].getblockcount(), 6)
+ sync_blocks(self.nodes[0:2])
+ hashH = self.nodes[0].getbestblockhash()
+ assert_equal(self.nodes[1].getbestblockhash(), hashH)
+ print("Node1 should not be able to prefer block C anymore")
+ self.nodes[1].preciousblock(hashC)
+ assert_equal(self.nodes[1].getbestblockhash(), hashH)
+ print("Mine competing blocks I-J-K-L on Node 2")
+ self.nodes[2].generate(4)
+ assert_equal(self.nodes[2].getblockcount(), 6)
+ hashL = self.nodes[2].getbestblockhash()
+ print("Connect nodes and check no reorg occurs")
+ node_sync_via_rpc(self.nodes[0:3])
+ connect_nodes_bi(self.nodes,1,2)
+ connect_nodes_bi(self.nodes,0,2)
+ assert_equal(self.nodes[0].getbestblockhash(), hashH)
+ assert_equal(self.nodes[1].getbestblockhash(), hashH)
+ assert_equal(self.nodes[2].getbestblockhash(), hashL)
+ print("Make Node1 prefer block L")
+ self.nodes[1].preciousblock(hashL)
+ assert_equal(self.nodes[1].getbestblockhash(), hashL)
+ print("Make Node2 prefer block H")
+ self.nodes[2].preciousblock(hashH)
+ assert_equal(self.nodes[2].getbestblockhash(), hashH)
+
+if __name__ == '__main__':
+ PreciousTest().main()
diff --git a/qa/rpc-tests/prioritise_transaction.py b/qa/rpc-tests/prioritise_transaction.py
index e1771231c0..b7459c80cb 100755
--- a/qa/rpc-tests/prioritise_transaction.py
+++ b/qa/rpc-tests/prioritise_transaction.py
@@ -9,7 +9,7 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
-from test_framework.mininode import COIN, MAX_BLOCK_SIZE
+from test_framework.mininode import COIN, MAX_BLOCK_BASE_SIZE
class PrioritiseTransactionTest(BitcoinTestFramework):
@@ -39,10 +39,10 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
txids.append([])
start_range = i * range_size
end_range = start_range + range_size
- txids[i] = create_lots_of_big_transactions(self.nodes[0], self.txouts, utxos[start_range:end_range], (i+1)*base_fee)
+ txids[i] = create_lots_of_big_transactions(self.nodes[0], self.txouts, utxos[start_range:end_range], end_range - start_range, (i+1)*base_fee)
# Make sure that the size of each group of transactions exceeds
- # MAX_BLOCK_SIZE -- otherwise the test needs to be revised to create
+ # MAX_BLOCK_BASE_SIZE -- otherwise the test needs to be revised to create
# more transactions.
mempool = self.nodes[0].getrawmempool(True)
sizes = [0, 0, 0]
@@ -50,7 +50,7 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
for j in txids[i]:
assert(j in mempool)
sizes[i] += mempool[j]['size']
- assert(sizes[i] > MAX_BLOCK_SIZE) # Fail => raise utxo_count
+ assert(sizes[i] > MAX_BLOCK_BASE_SIZE) # Fail => raise utxo_count
# add a fee delta to something in the cheapest bucket and make sure it gets mined
# also check that a different entry in the cheapest bucket is NOT mined (lower
diff --git a/qa/rpc-tests/proxy_test.py b/qa/rpc-tests/proxy_test.py
index 27160cae07..9ccc0ffbb0 100755
--- a/qa/rpc-tests/proxy_test.py
+++ b/qa/rpc-tests/proxy_test.py
@@ -4,10 +4,16 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
import socket
+import os
from test_framework.socks5 import Socks5Configuration, Socks5Command, Socks5Server, AddressType
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import *
+from test_framework.util import (
+ PORT_MIN,
+ PORT_RANGE,
+ start_nodes,
+ assert_equal,
+)
from test_framework.netutil import test_ipv6_local
'''
Test plan:
@@ -33,6 +39,8 @@ addnode connect to onion
addnode connect to generic DNS name
'''
+RANGE_BEGIN = PORT_MIN + 2 * PORT_RANGE # Start after p2p and rpc ports
+
class ProxyTest(BitcoinTestFramework):
def __init__(self):
@@ -44,19 +52,19 @@ class ProxyTest(BitcoinTestFramework):
# Create two proxies on different ports
# ... one unauthenticated
self.conf1 = Socks5Configuration()
- self.conf1.addr = ('127.0.0.1', 13000 + (os.getpid() % 1000))
+ self.conf1.addr = ('127.0.0.1', RANGE_BEGIN + (os.getpid() % 1000))
self.conf1.unauth = True
self.conf1.auth = False
# ... one supporting authenticated and unauthenticated (Tor)
self.conf2 = Socks5Configuration()
- self.conf2.addr = ('127.0.0.1', 14000 + (os.getpid() % 1000))
+ self.conf2.addr = ('127.0.0.1', RANGE_BEGIN + 1000 + (os.getpid() % 1000))
self.conf2.unauth = True
self.conf2.auth = True
if self.have_ipv6:
# ... one on IPv6 with similar configuration
self.conf3 = Socks5Configuration()
self.conf3.af = socket.AF_INET6
- self.conf3.addr = ('::1', 15000 + (os.getpid() % 1000))
+ self.conf3.addr = ('::1', RANGE_BEGIN + 2000 + (os.getpid() % 1000))
self.conf3.unauth = True
self.conf3.auth = True
else:
diff --git a/qa/rpc-tests/pruning.py b/qa/rpc-tests/pruning.py
index 7cbe69c29b..78b8938e4a 100755
--- a/qa/rpc-tests/pruning.py
+++ b/qa/rpc-tests/pruning.py
@@ -13,6 +13,9 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
+import time
+import os
+
def calc_usage(blockdir):
return sum(os.path.getsize(blockdir+f) for f in os.listdir(blockdir) if os.path.isfile(blockdir+f)) / (1024. * 1024.)
@@ -24,9 +27,9 @@ class PruneTest(BitcoinTestFramework):
self.setup_clean_chain = True
self.num_nodes = 3
- self.utxo = []
- self.address = ["",""]
- self.txouts = gen_return_txouts()
+ # Cache for utxos, as the listunspent may take a long time later in the test
+ self.utxo_cache_0 = []
+ self.utxo_cache_1 = []
def setup_network(self):
self.nodes = []
@@ -40,12 +43,6 @@ class PruneTest(BitcoinTestFramework):
self.nodes.append(start_node(2, self.options.tmpdir, ["-debug","-maxreceivebuffer=20000","-prune=550"], timewait=900))
self.prunedir = self.options.tmpdir+"/node2/regtest/blocks/"
- self.address[0] = self.nodes[0].getnewaddress()
- self.address[1] = self.nodes[1].getnewaddress()
-
- # Determine default relay fee
- self.relayfee = self.nodes[0].getnetworkinfo()["relayfee"]
-
connect_nodes(self.nodes[0], 1)
connect_nodes(self.nodes[1], 2)
connect_nodes(self.nodes[2], 0)
@@ -58,7 +55,7 @@ class PruneTest(BitcoinTestFramework):
self.nodes[0].generate(150)
# Then mine enough full blocks to create more than 550MiB of data
for i in range(645):
- self.mine_full_block(self.nodes[0], self.address[0])
+ mine_large_block(self.nodes[0], self.utxo_cache_0)
sync_blocks(self.nodes[0:3])
@@ -70,7 +67,7 @@ class PruneTest(BitcoinTestFramework):
print("Mining 25 more blocks should cause the first block file to be pruned")
# Pruning doesn't run until we're allocating another chunk, 20 full blocks past the height cutoff will ensure this
for i in range(25):
- self.mine_full_block(self.nodes[0],self.address[0])
+ mine_large_block(self.nodes[0], self.utxo_cache_0)
waitstart = time.time()
while os.path.isfile(self.prunedir+"blk00000.dat"):
@@ -95,17 +92,15 @@ class PruneTest(BitcoinTestFramework):
stop_node(self.nodes[0],0)
self.nodes[0]=start_node(0, self.options.tmpdir, ["-debug","-maxreceivebuffer=20000","-blockmaxsize=999000", "-checkblocks=5"], timewait=900)
# Mine 24 blocks in node 1
- self.utxo = self.nodes[1].listunspent()
for i in range(24):
if j == 0:
- self.mine_full_block(self.nodes[1],self.address[1])
+ mine_large_block(self.nodes[1], self.utxo_cache_1)
else:
self.nodes[1].generate(1) #tx's already in mempool from previous disconnects
# Reorg back with 25 block chain from node 0
- self.utxo = self.nodes[0].listunspent()
for i in range(25):
- self.mine_full_block(self.nodes[0],self.address[0])
+ mine_large_block(self.nodes[0], self.utxo_cache_0)
# Create connections in the order so both nodes can see the reorg at the same time
connect_nodes(self.nodes[1], 0)
@@ -157,7 +152,10 @@ class PruneTest(BitcoinTestFramework):
print("Usage possibly still high bc of stale blocks in block files:", calc_usage(self.prunedir))
print("Mine 220 more blocks so we have requisite history (some blocks will be big and cause pruning of previous chain)")
- self.nodes[0].generate(220) #node 0 has many large tx's in its mempool from the disconnects
+ for i in range(22):
+ # This can be slow, so do this in multiple RPC calls to avoid
+ # RPC timeouts.
+ self.nodes[0].generate(10) #node 0 has many large tx's in its mempool from the disconnects
sync_blocks(self.nodes[0:3], timeout=300)
usage = calc_usage(self.prunedir)
@@ -214,31 +212,6 @@ class PruneTest(BitcoinTestFramework):
# Verify we can now have the data for a block previously pruned
assert(self.nodes[2].getblock(self.forkhash)["height"] == self.forkheight)
- def mine_full_block(self, node, address):
- # Want to create a full block
- # We'll generate a 66k transaction below, and 14 of them is close to the 1MB block limit
- for j in range(14):
- if len(self.utxo) < 14:
- self.utxo = node.listunspent()
- inputs=[]
- outputs = {}
- t = self.utxo.pop()
- inputs.append({ "txid" : t["txid"], "vout" : t["vout"]})
- remchange = t["amount"] - 100*self.relayfee # Fee must be above min relay rate for 66kb tx
- outputs[address]=remchange
- # Create a basic transaction that will send change back to ourself after account for a fee
- # And then insert the 128 generated transaction outs in the middle rawtx[92] is where the #
- # of txouts is stored and is the only thing we overwrite from the original transaction
- rawtx = node.createrawtransaction(inputs, outputs)
- newtx = rawtx[0:92]
- newtx = newtx + self.txouts
- newtx = newtx + rawtx[94:]
- # Appears to be ever so slightly faster to sign with SIGHASH_NONE
- signresult = node.signrawtransaction(newtx,None,None,"NONE")
- txid = node.sendrawtransaction(signresult["hex"], True)
- # Mine a full sized block which will be these transactions we just created
- node.generate(1)
-
def run_test(self):
print("Warning! This test requires 4GB of disk space and takes over 30 mins (up to 2 hours)")
diff --git a/qa/rpc-tests/rawtransactions.py b/qa/rpc-tests/rawtransactions.py
index ab6d2e8def..33a6f2b0cc 100755
--- a/qa/rpc-tests/rawtransactions.py
+++ b/qa/rpc-tests/rawtransactions.py
@@ -2,11 +2,15 @@
# Copyright (c) 2014-2016 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""rawtranscation RPCs QA test.
-#
-# Test re-org scenarios with a mempool that contains transactions
-# that spend (directly or indirectly) coinbase transactions.
-#
+# Tests the following RPCs:
+# - createrawtransaction
+# - signrawtransaction
+# - sendrawtransaction
+# - decoderawtransaction
+# - getrawtransaction
+"""
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
@@ -138,6 +142,33 @@ class RawTransactionsTest(BitcoinTestFramework):
self.sync_all()
assert_equal(self.nodes[0].getbalance(), bal+Decimal('50.00000000')+Decimal('2.19000000')) #block reward + tx
+ # getrawtransaction tests
+ # 1. valid parameters - only supply txid
+ txHash = rawTx["hash"]
+ assert_equal(self.nodes[0].getrawtransaction(txHash), rawTxSigned['hex'])
+
+ # 2. valid parameters - supply txid and 0 for non-verbose
+ assert_equal(self.nodes[0].getrawtransaction(txHash, 0), rawTxSigned['hex'])
+
+ # 3. valid parameters - supply txid and False for non-verbose
+ assert_equal(self.nodes[0].getrawtransaction(txHash, False), rawTxSigned['hex'])
+
+ # 4. valid parameters - supply txid and 1 for verbose.
+ # We only check the "hex" field of the output so we don't need to update this test every time the output format changes.
+ assert_equal(self.nodes[0].getrawtransaction(txHash, 1)["hex"], rawTxSigned['hex'])
+
+ # 5. valid parameters - supply txid and True for non-verbose
+ assert_equal(self.nodes[0].getrawtransaction(txHash, True)["hex"], rawTxSigned['hex'])
+
+ # 6. invalid parameters - supply txid and string "Flase"
+ assert_raises(JSONRPCException, self.nodes[0].getrawtransaction, txHash, "Flase")
+
+ # 7. invalid parameters - supply txid and empty array
+ assert_raises(JSONRPCException, self.nodes[0].getrawtransaction, txHash, [])
+
+ # 8. invalid parameters - supply txid and empty dict
+ assert_raises(JSONRPCException, self.nodes[0].getrawtransaction, txHash, {})
+
inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : 1000}]
outputs = { self.nodes[0].getnewaddress() : 1 }
rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
diff --git a/qa/rpc-tests/reindex.py b/qa/rpc-tests/reindex.py
index abbbb10336..25cf4c1679 100755
--- a/qa/rpc-tests/reindex.py
+++ b/qa/rpc-tests/reindex.py
@@ -7,7 +7,11 @@
# Test -reindex and -reindex-chainstate with CheckBlockIndex
#
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import *
+from test_framework.util import (
+ start_nodes,
+ stop_nodes,
+ assert_equal,
+)
import time
class ReindexTest(BitcoinTestFramework):
@@ -18,16 +22,14 @@ class ReindexTest(BitcoinTestFramework):
self.num_nodes = 1
def setup_network(self):
- self.nodes = []
- self.is_network_split = False
- self.nodes.append(start_node(0, self.options.tmpdir))
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)
def reindex(self, justchainstate=False):
self.nodes[0].generate(3)
blockcount = self.nodes[0].getblockcount()
- stop_node(self.nodes[0], 0)
- wait_bitcoinds()
- self.nodes[0]=start_node(0, self.options.tmpdir, ["-debug", "-reindex-chainstate" if justchainstate else "-reindex", "-checkblockindex=1"])
+ stop_nodes(self.nodes)
+ extra_args = [["-debug", "-reindex-chainstate" if justchainstate else "-reindex", "-checkblockindex=1"]]
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args)
while self.nodes[0].getblockcount() < blockcount:
time.sleep(0.1)
assert_equal(self.nodes[0].getblockcount(), blockcount)
diff --git a/qa/rpc-tests/replace-by-fee.py b/qa/rpc-tests/replace-by-fee.py
index 34c0f9d795..8aba06c60c 100755
--- a/qa/rpc-tests/replace-by-fee.py
+++ b/qa/rpc-tests/replace-by-fee.py
@@ -76,7 +76,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
def setup_network(self):
self.nodes = []
self.nodes.append(start_node(0, self.options.tmpdir, ["-maxorphantx=1000", "-debug",
- "-relaypriority=0", "-whitelist=127.0.0.1",
+ "-whitelist=127.0.0.1",
"-limitancestorcount=50",
"-limitancestorsize=101",
"-limitdescendantcount=200",
diff --git a/qa/rpc-tests/rpcbind_test.py b/qa/rpc-tests/rpcbind_test.py
index 3ac32140ba..d78d0b884e 100755
--- a/qa/rpc-tests/rpcbind_test.py
+++ b/qa/rpc-tests/rpcbind_test.py
@@ -40,11 +40,10 @@ class RPCBindTest(BitcoinTestFramework):
assert_equal(set(get_bind_addrs(pid)), set(expected))
finally:
stop_nodes(self.nodes)
- wait_bitcoinds()
def run_allowip_test(self, allow_ips, rpchost, rpcport):
'''
- Start a node with rpcwallow IP, and request getinfo
+ Start a node with rpcallow IP, and request getnetworkinfo
at a non-localhost IP.
'''
base_args = ['-disablewallet', '-nolisten'] + ['-rpcallowip='+x for x in allow_ips]
@@ -52,11 +51,10 @@ class RPCBindTest(BitcoinTestFramework):
try:
# connect to node through non-loopback interface
node = get_rpc_proxy(rpc_url(0, "%s:%d" % (rpchost, rpcport)), 0)
- node.getinfo()
+ node.getnetworkinfo()
finally:
node = None # make sure connection will be garbage collected and closed
stop_nodes(self.nodes)
- wait_bitcoinds()
def run_test(self):
# due to OS-specific network stats queries, this test works only on Linux
diff --git a/qa/rpc-tests/segwit.py b/qa/rpc-tests/segwit.py
index 745a1d4750..be6fae5088 100755
--- a/qa/rpc-tests/segwit.py
+++ b/qa/rpc-tests/segwit.py
@@ -9,7 +9,11 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
-from test_framework.mininode import sha256, ripemd160
+from test_framework.mininode import sha256, ripemd160, CTransaction, CTxIn, COutPoint, CTxOut
+from test_framework.address import script_to_p2sh, key_to_p2pkh
+from test_framework.script import CScript, OP_HASH160, OP_CHECKSIG, OP_0, hash160, OP_EQUAL, OP_DUP, OP_EQUALVERIFY, OP_1, OP_2, OP_CHECKMULTISIG
+from io import BytesIO
+from test_framework.mininode import FromHex
NODE_0 = 0
NODE_1 = 1
@@ -81,8 +85,8 @@ class SegWitTest(BitcoinTestFramework):
def setup_network(self):
self.nodes = []
- self.nodes.append(start_node(0, self.options.tmpdir, ["-logtimemicros", "-debug", "-walletprematurewitness"]))
- self.nodes.append(start_node(1, self.options.tmpdir, ["-logtimemicros", "-debug", "-blockversion=4", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness"]))
+ self.nodes.append(start_node(0, self.options.tmpdir, ["-logtimemicros", "-debug", "-walletprematurewitness", "-rpcserialversion=0"]))
+ self.nodes.append(start_node(1, self.options.tmpdir, ["-logtimemicros", "-debug", "-blockversion=4", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness", "-rpcserialversion=1"]))
self.nodes.append(start_node(2, self.options.tmpdir, ["-logtimemicros", "-debug", "-blockversion=536870915", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness"]))
connect_nodes(self.nodes[1], 0)
connect_nodes(self.nodes[2], 1)
@@ -208,7 +212,19 @@ class SegWitTest(BitcoinTestFramework):
block = self.nodes[2].generate(1) #block 432 (first block with new rules; 432 = 144 * 3)
sync_blocks(self.nodes)
assert_equal(len(self.nodes[2].getrawmempool()), 0)
- assert_equal(len(self.nodes[2].getblock(block[0])["tx"]), 5)
+ segwit_tx_list = self.nodes[2].getblock(block[0])["tx"]
+ assert_equal(len(segwit_tx_list), 5)
+
+ print("Verify block and transaction serialization rpcs return differing serializations depending on rpc serialization flag")
+ assert(self.nodes[2].getblock(block[0], False) != self.nodes[0].getblock(block[0], False))
+ assert(self.nodes[1].getblock(block[0], False) == self.nodes[2].getblock(block[0], False))
+ for i in range(len(segwit_tx_list)):
+ tx = FromHex(CTransaction(), self.nodes[2].gettransaction(segwit_tx_list[i])["hex"])
+ assert(self.nodes[2].getrawtransaction(segwit_tx_list[i]) != self.nodes[0].getrawtransaction(segwit_tx_list[i]))
+ assert(self.nodes[1].getrawtransaction(segwit_tx_list[i], 0) == self.nodes[2].getrawtransaction(segwit_tx_list[i]))
+ assert(self.nodes[0].getrawtransaction(segwit_tx_list[i]) != self.nodes[2].gettransaction(segwit_tx_list[i])["hex"])
+ assert(self.nodes[1].getrawtransaction(segwit_tx_list[i]) == self.nodes[2].gettransaction(segwit_tx_list[i])["hex"])
+ assert(self.nodes[0].getrawtransaction(segwit_tx_list[i]) == bytes_to_hex_str(tx.serialize_without_witness()))
print("Verify witness txs without witness data are invalid after the fork")
self.fail_mine(self.nodes[2], wit_ids[NODE_2][WIT_V0][2], False)
@@ -242,5 +258,360 @@ class SegWitTest(BitcoinTestFramework):
# This is an acceptable outcome
pass
+ print("Verify behaviour of importaddress, addwitnessaddress and listunspent")
+
+ # Some public keys to be used later
+ pubkeys = [
+ "0363D44AABD0F1699138239DF2F042C3282C0671CC7A76826A55C8203D90E39242", # cPiM8Ub4heR9NBYmgVzJQiUH1if44GSBGiqaeJySuL2BKxubvgwb
+ "02D3E626B3E616FC8662B489C123349FECBFC611E778E5BE739B257EAE4721E5BF", # cPpAdHaD6VoYbW78kveN2bsvb45Q7G5PhaPApVUGwvF8VQ9brD97
+ "04A47F2CBCEFFA7B9BCDA184E7D5668D3DA6F9079AD41E422FA5FD7B2D458F2538A62F5BD8EC85C2477F39650BD391EA6250207065B2A81DA8B009FC891E898F0E", # 91zqCU5B9sdWxzMt1ca3VzbtVm2YM6Hi5Rxn4UDtxEaN9C9nzXV
+ "02A47F2CBCEFFA7B9BCDA184E7D5668D3DA6F9079AD41E422FA5FD7B2D458F2538", # cPQFjcVRpAUBG8BA9hzr2yEzHwKoMgLkJZBBtK9vJnvGJgMjzTbd
+ "036722F784214129FEB9E8129D626324F3F6716555B603FFE8300BBCB882151228", # cQGtcm34xiLjB1v7bkRa4V3aAc9tS2UTuBZ1UnZGeSeNy627fN66
+ "0266A8396EE936BF6D99D17920DB21C6C7B1AB14C639D5CD72B300297E416FD2EC", # cTW5mR5M45vHxXkeChZdtSPozrFwFgmEvTNnanCW6wrqwaCZ1X7K
+ "0450A38BD7F0AC212FEBA77354A9B036A32E0F7C81FC4E0C5ADCA7C549C4505D2522458C2D9AE3CEFD684E039194B72C8A10F9CB9D4764AB26FCC2718D421D3B84", # 92h2XPssjBpsJN5CqSP7v9a7cf2kgDunBC6PDFwJHMACM1rrVBJ
+ ]
+
+ # Import a compressed key and an uncompressed key, generate some multisig addresses
+ self.nodes[0].importprivkey("92e6XLo5jVAVwrQKPNTs93oQco8f8sDNBcpv73Dsrs397fQtFQn")
+ uncompressed_spendable_address = ["mvozP4UwyGD2mGZU4D2eMvMLPB9WkMmMQu"]
+ self.nodes[0].importprivkey("cNC8eQ5dg3mFAVePDX4ddmPYpPbw41r9bm2jd1nLJT77e6RrzTRR")
+ compressed_spendable_address = ["mmWQubrDomqpgSYekvsU7HWEVjLFHAakLe"]
+ assert ((self.nodes[0].validateaddress(uncompressed_spendable_address[0])['iscompressed'] == False))
+ assert ((self.nodes[0].validateaddress(compressed_spendable_address[0])['iscompressed'] == True))
+
+ self.nodes[0].importpubkey(pubkeys[0])
+ compressed_solvable_address = [key_to_p2pkh(pubkeys[0])]
+ self.nodes[0].importpubkey(pubkeys[1])
+ compressed_solvable_address.append(key_to_p2pkh(pubkeys[1]))
+ self.nodes[0].importpubkey(pubkeys[2])
+ uncompressed_solvable_address = [key_to_p2pkh(pubkeys[2])]
+
+ spendable_anytime = [] # These outputs should be seen anytime after importprivkey and addmultisigaddress
+ spendable_after_importaddress = [] # These outputs should be seen after importaddress
+ solvable_after_importaddress = [] # These outputs should be seen after importaddress but not spendable
+ unsolvable_after_importaddress = [] # These outputs should be unsolvable after importaddress
+ solvable_anytime = [] # These outputs should be solvable after importpubkey
+ unseen_anytime = [] # These outputs should never be seen
+
+ uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], compressed_spendable_address[0]]))
+ uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], uncompressed_spendable_address[0]]))
+ compressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_spendable_address[0]]))
+ uncompressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], uncompressed_solvable_address[0]]))
+ compressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_solvable_address[0]]))
+ compressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_solvable_address[0], compressed_solvable_address[1]]))
+ unknown_address = ["mtKKyoHabkk6e4ppT7NaM7THqPUt7AzPrT", "2NDP3jLWAFT8NDAiUa9qiE6oBt2awmMq7Dx"]
+
+ # Test multisig_without_privkey
+ # We have 2 public keys without private keys, use addmultisigaddress to add to wallet.
+ # Money sent to P2SH of multisig of this should only be seen after importaddress with the BASE58 P2SH address.
+
+ multisig_without_privkey_address = self.nodes[0].addmultisigaddress(2, [pubkeys[3], pubkeys[4]])
+ script = CScript([OP_2, hex_str_to_bytes(pubkeys[3]), hex_str_to_bytes(pubkeys[4]), OP_2, OP_CHECKMULTISIG])
+ solvable_after_importaddress.append(CScript([OP_HASH160, hash160(script), OP_EQUAL]))
+
+ for i in compressed_spendable_address:
+ v = self.nodes[0].validateaddress(i)
+ if (v['isscript']):
+ [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
+ # bare and p2sh multisig with compressed keys should always be spendable
+ spendable_anytime.extend([bare, p2sh])
+ # P2WSH and P2SH(P2WSH) multisig with compressed keys are spendable after direct importaddress
+ spendable_after_importaddress.extend([p2wsh, p2sh_p2wsh])
+ else:
+ [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
+ # normal P2PKH and P2PK with compressed keys should always be spendable
+ spendable_anytime.extend([p2pkh, p2pk])
+ # P2SH_P2PK, P2SH_P2PKH, and witness with compressed keys are spendable after direct importaddress
+ spendable_after_importaddress.extend([p2wpkh, p2sh_p2wpkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
+
+ for i in uncompressed_spendable_address:
+ v = self.nodes[0].validateaddress(i)
+ if (v['isscript']):
+ [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
+ # bare and p2sh multisig with uncompressed keys should always be spendable
+ spendable_anytime.extend([bare, p2sh])
+ # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen
+ unseen_anytime.extend([p2wsh, p2sh_p2wsh])
+ else:
+ [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
+ # normal P2PKH and P2PK with uncompressed keys should always be spendable
+ spendable_anytime.extend([p2pkh, p2pk])
+ # P2SH_P2PK and P2SH_P2PKH are spendable after direct importaddress
+ spendable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh])
+ # witness with uncompressed keys are never seen
+ unseen_anytime.extend([p2wpkh, p2sh_p2wpkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
+
+ for i in compressed_solvable_address:
+ v = self.nodes[0].validateaddress(i)
+ if (v['isscript']):
+ # Multisig without private is not seen after addmultisigaddress, but seen after importaddress
+ [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
+ solvable_after_importaddress.extend([bare, p2sh, p2wsh, p2sh_p2wsh])
+ else:
+ [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
+ # normal P2PKH and P2PK with compressed keys should always be seen
+ solvable_anytime.extend([p2pkh, p2pk])
+ # P2SH_P2PK, P2SH_P2PKH, and witness with compressed keys are seen after direct importaddress
+ solvable_after_importaddress.extend([p2wpkh, p2sh_p2wpkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
+
+ for i in uncompressed_solvable_address:
+ v = self.nodes[0].validateaddress(i)
+ if (v['isscript']):
+ [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
+ # Base uncompressed multisig without private is not seen after addmultisigaddress, but seen after importaddress
+ solvable_after_importaddress.extend([bare, p2sh])
+ # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen
+ unseen_anytime.extend([p2wsh, p2sh_p2wsh])
+ else:
+ [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
+ # normal P2PKH and P2PK with uncompressed keys should always be seen
+ solvable_anytime.extend([p2pkh, p2pk])
+ # P2SH_P2PK, P2SH_P2PKH with uncompressed keys are seen after direct importaddress
+ solvable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh])
+ # witness with uncompressed keys are never seen
+ unseen_anytime.extend([p2wpkh, p2sh_p2wpkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
+
+ op1 = CScript([OP_1])
+ op0 = CScript([OP_0])
+ # 2N7MGY19ti4KDMSzRfPAssP6Pxyuxoi6jLe is the P2SH(P2PKH) version of mjoE3sSrb8ByYEvgnC3Aox86u1CHnfJA4V
+ unsolvable_address = ["mjoE3sSrb8ByYEvgnC3Aox86u1CHnfJA4V", "2N7MGY19ti4KDMSzRfPAssP6Pxyuxoi6jLe", script_to_p2sh(op1), script_to_p2sh(op0)]
+ unsolvable_address_key = hex_str_to_bytes("02341AEC7587A51CDE5279E0630A531AEA2615A9F80B17E8D9376327BAEAA59E3D")
+ unsolvablep2pkh = CScript([OP_DUP, OP_HASH160, hash160(unsolvable_address_key), OP_EQUALVERIFY, OP_CHECKSIG])
+ unsolvablep2wshp2pkh = CScript([OP_0, sha256(unsolvablep2pkh)])
+ p2shop0 = CScript([OP_HASH160, hash160(op0), OP_EQUAL])
+ p2wshop1 = CScript([OP_0, sha256(op1)])
+ unsolvable_after_importaddress.append(unsolvablep2pkh)
+ unsolvable_after_importaddress.append(unsolvablep2wshp2pkh)
+ unsolvable_after_importaddress.append(op1) # OP_1 will be imported as script
+ unsolvable_after_importaddress.append(p2wshop1)
+ unseen_anytime.append(op0) # OP_0 will be imported as P2SH address with no script provided
+ unsolvable_after_importaddress.append(p2shop0)
+
+ spendable_txid = []
+ solvable_txid = []
+ spendable_txid.append(self.mine_and_test_listunspent(spendable_anytime, 2))
+ solvable_txid.append(self.mine_and_test_listunspent(solvable_anytime, 1))
+ self.mine_and_test_listunspent(spendable_after_importaddress + solvable_after_importaddress + unseen_anytime + unsolvable_after_importaddress, 0)
+
+ importlist = []
+ for i in compressed_spendable_address + uncompressed_spendable_address + compressed_solvable_address + uncompressed_solvable_address:
+ v = self.nodes[0].validateaddress(i)
+ if (v['isscript']):
+ bare = hex_str_to_bytes(v['hex'])
+ importlist.append(bytes_to_hex_str(bare))
+ importlist.append(bytes_to_hex_str(CScript([OP_0, sha256(bare)])))
+ else:
+ pubkey = hex_str_to_bytes(v['pubkey'])
+ p2pk = CScript([pubkey, OP_CHECKSIG])
+ p2pkh = CScript([OP_DUP, OP_HASH160, hash160(pubkey), OP_EQUALVERIFY, OP_CHECKSIG])
+ importlist.append(bytes_to_hex_str(p2pk))
+ importlist.append(bytes_to_hex_str(p2pkh))
+ importlist.append(bytes_to_hex_str(CScript([OP_0, hash160(pubkey)])))
+ importlist.append(bytes_to_hex_str(CScript([OP_0, sha256(p2pk)])))
+ importlist.append(bytes_to_hex_str(CScript([OP_0, sha256(p2pkh)])))
+
+ importlist.append(bytes_to_hex_str(unsolvablep2pkh))
+ importlist.append(bytes_to_hex_str(unsolvablep2wshp2pkh))
+ importlist.append(bytes_to_hex_str(op1))
+ importlist.append(bytes_to_hex_str(p2wshop1))
+
+ for i in importlist:
+ try:
+ self.nodes[0].importaddress(i,"",False,True)
+ except JSONRPCException as exp:
+ assert_equal(exp.error["message"], "The wallet already contains the private key for this address or script")
+
+ self.nodes[0].importaddress(script_to_p2sh(op0)) # import OP_0 as address only
+ self.nodes[0].importaddress(multisig_without_privkey_address) # Test multisig_without_privkey
+
+ spendable_txid.append(self.mine_and_test_listunspent(spendable_anytime + spendable_after_importaddress, 2))
+ solvable_txid.append(self.mine_and_test_listunspent(solvable_anytime + solvable_after_importaddress, 1))
+ self.mine_and_test_listunspent(unsolvable_after_importaddress, 1)
+ self.mine_and_test_listunspent(unseen_anytime, 0)
+
+ # addwitnessaddress should refuse to return a witness address if an uncompressed key is used or the address is
+ # not in the wallet
+ # note that no witness address should be returned by unsolvable addresses
+ # the multisig_without_privkey_address will fail because its keys were not added with importpubkey
+ for i in uncompressed_spendable_address + uncompressed_solvable_address + unknown_address + unsolvable_address + [multisig_without_privkey_address]:
+ try:
+ self.nodes[0].addwitnessaddress(i)
+ except JSONRPCException as exp:
+ assert_equal(exp.error["message"], "Public key or redeemscript not known to wallet, or the key is uncompressed")
+ else:
+ assert(False)
+
+ for i in compressed_spendable_address + compressed_solvable_address:
+ witaddress = self.nodes[0].addwitnessaddress(i)
+ # addwitnessaddress should return the same address if it is a known P2SH-witness address
+ assert_equal(witaddress, self.nodes[0].addwitnessaddress(witaddress))
+
+ spendable_txid.append(self.mine_and_test_listunspent(spendable_anytime + spendable_after_importaddress, 2))
+ solvable_txid.append(self.mine_and_test_listunspent(solvable_anytime + solvable_after_importaddress, 1))
+ self.mine_and_test_listunspent(unsolvable_after_importaddress, 1)
+ self.mine_and_test_listunspent(unseen_anytime, 0)
+
+ # Repeat some tests. This time we don't add witness scripts with importaddress
+ # Import a compressed key and an uncompressed key, generate some multisig addresses
+ self.nodes[0].importprivkey("927pw6RW8ZekycnXqBQ2JS5nPyo1yRfGNN8oq74HeddWSpafDJH")
+ uncompressed_spendable_address = ["mguN2vNSCEUh6rJaXoAVwY3YZwZvEmf5xi"]
+ self.nodes[0].importprivkey("cMcrXaaUC48ZKpcyydfFo8PxHAjpsYLhdsp6nmtB3E2ER9UUHWnw")
+ compressed_spendable_address = ["n1UNmpmbVUJ9ytXYXiurmGPQ3TRrXqPWKL"]
+
+ self.nodes[0].importpubkey(pubkeys[5])
+ compressed_solvable_address = [key_to_p2pkh(pubkeys[5])]
+ self.nodes[0].importpubkey(pubkeys[6])
+ uncompressed_solvable_address = [key_to_p2pkh(pubkeys[6])]
+
+ spendable_after_addwitnessaddress = [] # These outputs should be seen after importaddress
+ solvable_after_addwitnessaddress=[] # These outputs should be seen after importaddress but not spendable
+ unseen_anytime = [] # These outputs should never be seen
+
+ uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], compressed_spendable_address[0]]))
+ uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], uncompressed_spendable_address[0]]))
+ compressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_spendable_address[0]]))
+ uncompressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_solvable_address[0], uncompressed_solvable_address[0]]))
+ compressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_solvable_address[0]]))
+
+ premature_witaddress = []
+
+ for i in compressed_spendable_address:
+ v = self.nodes[0].validateaddress(i)
+ if (v['isscript']):
+ [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
+ # P2WSH and P2SH(P2WSH) multisig with compressed keys are spendable after addwitnessaddress
+ spendable_after_addwitnessaddress.extend([p2wsh, p2sh_p2wsh])
+ premature_witaddress.append(script_to_p2sh(p2wsh))
+ else:
+ [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
+ # P2WPKH, P2SH_P2WPKH are spendable after addwitnessaddress
+ spendable_after_addwitnessaddress.extend([p2wpkh, p2sh_p2wpkh])
+ premature_witaddress.append(script_to_p2sh(p2wpkh))
+
+ for i in uncompressed_spendable_address + uncompressed_solvable_address:
+ v = self.nodes[0].validateaddress(i)
+ if (v['isscript']):
+ [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
+ # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen
+ unseen_anytime.extend([p2wsh, p2sh_p2wsh])
+ else:
+ [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
+ # P2WPKH, P2SH_P2WPKH with uncompressed keys are never seen
+ unseen_anytime.extend([p2wpkh, p2sh_p2wpkh])
+
+ for i in compressed_solvable_address:
+ v = self.nodes[0].validateaddress(i)
+ if (v['isscript']):
+ # P2WSH multisig without private key are seen after addwitnessaddress
+ [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
+ solvable_after_addwitnessaddress.extend([p2wsh, p2sh_p2wsh])
+ premature_witaddress.append(script_to_p2sh(p2wsh))
+ else:
+ [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
+ # P2SH_P2PK, P2SH_P2PKH with compressed keys are seen after addwitnessaddress
+ solvable_after_addwitnessaddress.extend([p2wpkh, p2sh_p2wpkh])
+ premature_witaddress.append(script_to_p2sh(p2wpkh))
+
+ self.mine_and_test_listunspent(spendable_after_addwitnessaddress + solvable_after_addwitnessaddress + unseen_anytime, 0)
+
+ # addwitnessaddress should refuse to return a witness address if an uncompressed key is used
+ # note that a multisig address returned by addmultisigaddress is not solvable until it is added with importaddress
+ # premature_witaddress are not accepted until the script is added with addwitnessaddress first
+ for i in uncompressed_spendable_address + uncompressed_solvable_address + premature_witaddress + [compressed_solvable_address[1]]:
+ try:
+ self.nodes[0].addwitnessaddress(i)
+ except JSONRPCException as exp:
+ assert_equal(exp.error["message"], "Public key or redeemscript not known to wallet, or the key is uncompressed")
+ else:
+ assert(False)
+
+ # after importaddress it should pass addwitnessaddress
+ v = self.nodes[0].validateaddress(compressed_solvable_address[1])
+ self.nodes[0].importaddress(v['hex'],"",False,True)
+ for i in compressed_spendable_address + compressed_solvable_address + premature_witaddress:
+ witaddress = self.nodes[0].addwitnessaddress(i)
+ assert_equal(witaddress, self.nodes[0].addwitnessaddress(witaddress))
+
+ spendable_txid.append(self.mine_and_test_listunspent(spendable_after_addwitnessaddress, 2))
+ solvable_txid.append(self.mine_and_test_listunspent(solvable_after_addwitnessaddress, 1))
+ self.mine_and_test_listunspent(unseen_anytime, 0)
+
+ # Check that spendable outputs are really spendable
+ self.create_and_mine_tx_from_txids(spendable_txid)
+
+ # import all the private keys so solvable addresses become spendable
+ self.nodes[0].importprivkey("cPiM8Ub4heR9NBYmgVzJQiUH1if44GSBGiqaeJySuL2BKxubvgwb")
+ self.nodes[0].importprivkey("cPpAdHaD6VoYbW78kveN2bsvb45Q7G5PhaPApVUGwvF8VQ9brD97")
+ self.nodes[0].importprivkey("91zqCU5B9sdWxzMt1ca3VzbtVm2YM6Hi5Rxn4UDtxEaN9C9nzXV")
+ self.nodes[0].importprivkey("cPQFjcVRpAUBG8BA9hzr2yEzHwKoMgLkJZBBtK9vJnvGJgMjzTbd")
+ self.nodes[0].importprivkey("cQGtcm34xiLjB1v7bkRa4V3aAc9tS2UTuBZ1UnZGeSeNy627fN66")
+ self.nodes[0].importprivkey("cTW5mR5M45vHxXkeChZdtSPozrFwFgmEvTNnanCW6wrqwaCZ1X7K")
+ self.create_and_mine_tx_from_txids(solvable_txid)
+
+ def mine_and_test_listunspent(self, script_list, ismine):
+ utxo = find_unspent(self.nodes[0], 50)
+ tx = CTransaction()
+ tx.vin.append(CTxIn(COutPoint(int('0x'+utxo['txid'],0), utxo['vout'])))
+ for i in script_list:
+ tx.vout.append(CTxOut(10000000, i))
+ tx.rehash()
+ signresults = self.nodes[0].signrawtransaction(bytes_to_hex_str(tx.serialize_without_witness()))['hex']
+ txid = self.nodes[0].sendrawtransaction(signresults, True)
+ self.nodes[0].generate(1)
+ sync_blocks(self.nodes)
+ watchcount = 0
+ spendcount = 0
+ for i in self.nodes[0].listunspent():
+ if (i['txid'] == txid):
+ watchcount += 1
+ if (i['spendable'] == True):
+ spendcount += 1
+ if (ismine == 2):
+ assert_equal(spendcount, len(script_list))
+ elif (ismine == 1):
+ assert_equal(watchcount, len(script_list))
+ assert_equal(spendcount, 0)
+ else:
+ assert_equal(watchcount, 0)
+ return txid
+
+ def p2sh_address_to_script(self,v):
+ bare = CScript(hex_str_to_bytes(v['hex']))
+ p2sh = CScript(hex_str_to_bytes(v['scriptPubKey']))
+ p2wsh = CScript([OP_0, sha256(bare)])
+ p2sh_p2wsh = CScript([OP_HASH160, hash160(p2wsh), OP_EQUAL])
+ return([bare, p2sh, p2wsh, p2sh_p2wsh])
+
+ def p2pkh_address_to_script(self,v):
+ pubkey = hex_str_to_bytes(v['pubkey'])
+ p2wpkh = CScript([OP_0, hash160(pubkey)])
+ p2sh_p2wpkh = CScript([OP_HASH160, hash160(p2wpkh), OP_EQUAL])
+ p2pk = CScript([pubkey, OP_CHECKSIG])
+ p2pkh = CScript(hex_str_to_bytes(v['scriptPubKey']))
+ p2sh_p2pk = CScript([OP_HASH160, hash160(p2pk), OP_EQUAL])
+ p2sh_p2pkh = CScript([OP_HASH160, hash160(p2pkh), OP_EQUAL])
+ p2wsh_p2pk = CScript([OP_0, sha256(p2pk)])
+ p2wsh_p2pkh = CScript([OP_0, sha256(p2pkh)])
+ p2sh_p2wsh_p2pk = CScript([OP_HASH160, hash160(p2wsh_p2pk), OP_EQUAL])
+ p2sh_p2wsh_p2pkh = CScript([OP_HASH160, hash160(p2wsh_p2pkh), OP_EQUAL])
+ return [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh]
+
+ def create_and_mine_tx_from_txids(self, txids, success = True):
+ tx = CTransaction()
+ for i in txids:
+ txtmp = CTransaction()
+ txraw = self.nodes[0].getrawtransaction(i)
+ f = BytesIO(hex_str_to_bytes(txraw))
+ txtmp.deserialize(f)
+ for j in range(len(txtmp.vout)):
+ tx.vin.append(CTxIn(COutPoint(int('0x'+i,0), j)))
+ tx.vout.append(CTxOut(0, CScript()))
+ tx.rehash()
+ signresults = self.nodes[0].signrawtransaction(bytes_to_hex_str(tx.serialize_without_witness()))['hex']
+ self.nodes[0].sendrawtransaction(signresults, True)
+ self.nodes[0].generate(1)
+ sync_blocks(self.nodes)
+
+
if __name__ == '__main__':
SegWitTest().main()
diff --git a/qa/rpc-tests/sendheaders.py b/qa/rpc-tests/sendheaders.py
index c3f3180b6b..37b98c576e 100755
--- a/qa/rpc-tests/sendheaders.py
+++ b/qa/rpc-tests/sendheaders.py
@@ -80,20 +80,19 @@ e. Announce one more that doesn't connect.
Expect: disconnect.
'''
-class BaseNode(NodeConnCB):
+direct_fetch_response_time = 0.05
+
+class BaseNode(SingleNodeConnCB):
def __init__(self):
- NodeConnCB.__init__(self)
- self.connection = None
+ SingleNodeConnCB.__init__(self)
self.last_inv = None
self.last_headers = None
self.last_block = None
- self.ping_counter = 1
- self.last_pong = msg_pong(0)
self.last_getdata = None
- self.sleep_time = 0.05
self.block_announced = False
self.last_getheaders = None
self.disconnected = False
+ self.last_blockhash_announced = None
def clear_last_announcement(self):
with mininode_lock:
@@ -101,9 +100,6 @@ class BaseNode(NodeConnCB):
self.last_inv = None
self.last_headers = None
- def add_connection(self, conn):
- self.connection = conn
-
# Request data for a list of block hashes
def get_data(self, block_hashes):
msg = msg_getdata()
@@ -122,17 +118,17 @@ class BaseNode(NodeConnCB):
msg.inv = [CInv(2, blockhash)]
self.connection.send_message(msg)
- # Wrapper for the NodeConn's send_message function
- def send_message(self, message):
- self.connection.send_message(message)
-
def on_inv(self, conn, message):
self.last_inv = message
self.block_announced = True
+ self.last_blockhash_announced = message.inv[-1].hash
def on_headers(self, conn, message):
self.last_headers = message
- self.block_announced = True
+ if len(message.headers):
+ self.block_announced = True
+ message.headers[-1].calc_sha256()
+ self.last_blockhash_announced = message.headers[-1].sha256
def on_block(self, conn, message):
self.last_block = message.block
@@ -141,9 +137,6 @@ class BaseNode(NodeConnCB):
def on_getdata(self, conn, message):
self.last_getdata = message
- def on_pong(self, conn, message):
- self.last_pong = message
-
def on_getheaders(self, conn, message):
self.last_getheaders = message
@@ -157,7 +150,7 @@ class BaseNode(NodeConnCB):
expect_headers = headers if headers != None else []
expect_inv = inv if inv != None else []
test_function = lambda: self.block_announced
- self.sync(test_function)
+ assert(wait_until(test_function, timeout=60))
with mininode_lock:
self.block_announced = False
@@ -180,30 +173,14 @@ class BaseNode(NodeConnCB):
return success
# Syncing helpers
- def sync(self, test_function, timeout=60):
- while timeout > 0:
- with mininode_lock:
- if test_function():
- return
- time.sleep(self.sleep_time)
- timeout -= self.sleep_time
- raise AssertionError("Sync failed to complete")
-
- def sync_with_ping(self, timeout=60):
- self.send_message(msg_ping(nonce=self.ping_counter))
- test_function = lambda: self.last_pong.nonce == self.ping_counter
- self.sync(test_function, timeout)
- self.ping_counter += 1
- return
-
def wait_for_block(self, blockhash, timeout=60):
test_function = lambda: self.last_block != None and self.last_block.sha256 == blockhash
- self.sync(test_function, timeout)
+ assert(wait_until(test_function, timeout=timeout))
return
def wait_for_getheaders(self, timeout=60):
test_function = lambda: self.last_getheaders != None
- self.sync(test_function, timeout)
+ assert(wait_until(test_function, timeout=timeout))
return
def wait_for_getdata(self, hash_list, timeout=60):
@@ -211,12 +188,17 @@ class BaseNode(NodeConnCB):
return
test_function = lambda: self.last_getdata != None and [x.hash for x in self.last_getdata.inv] == hash_list
- self.sync(test_function, timeout)
+ assert(wait_until(test_function, timeout=timeout))
return
def wait_for_disconnect(self, timeout=60):
test_function = lambda: self.disconnected
- self.sync(test_function, timeout)
+ assert(wait_until(test_function, timeout=timeout))
+ return
+
+ def wait_for_block_announcement(self, block_hash, timeout=60):
+ test_function = lambda: self.last_blockhash_announced == block_hash
+ assert(wait_until(test_function, timeout=timeout))
return
def send_header_for_blocks(self, new_blocks):
@@ -266,7 +248,9 @@ class SendHeadersTest(BitcoinTestFramework):
def mine_reorg(self, length):
self.nodes[0].generate(length) # make sure all invalidated blocks are node0's
sync_blocks(self.nodes, wait=0.1)
- [x.clear_last_announcement() for x in self.p2p_connections]
+ for x in self.p2p_connections:
+ x.wait_for_block_announcement(int(self.nodes[0].getbestblockhash(), 16))
+ x.clear_last_announcement()
tip_height = self.nodes[1].getblockcount()
hash_to_invalidate = self.nodes[1].getblockhash(tip_height-(length-1))
@@ -364,14 +348,13 @@ class SendHeadersTest(BitcoinTestFramework):
if j == 0:
# Announce via inv
test_node.send_block_inv(tip)
- test_node.wait_for_getdata([tip], timeout=5)
+ test_node.wait_for_getheaders(timeout=5)
+ # Should have received a getheaders now
+ test_node.send_header_for_blocks(blocks)
# Test that duplicate inv's won't result in duplicate
# getdata requests, or duplicate headers announcements
- inv_node.send_block_inv(tip)
- # Should have received a getheaders as well!
- test_node.send_header_for_blocks(blocks)
- test_node.wait_for_getdata([x.sha256 for x in blocks[0:-1]], timeout=5)
- [ inv_node.send_block_inv(x.sha256) for x in blocks[0:-1] ]
+ [ inv_node.send_block_inv(x.sha256) for x in blocks ]
+ test_node.wait_for_getdata([x.sha256 for x in blocks], timeout=5)
inv_node.sync_with_ping()
else:
# Announce via headers
@@ -495,7 +478,7 @@ class SendHeadersTest(BitcoinTestFramework):
test_node.send_header_for_blocks(blocks)
test_node.sync_with_ping()
- test_node.wait_for_getdata([x.sha256 for x in blocks], timeout=test_node.sleep_time)
+ test_node.wait_for_getdata([x.sha256 for x in blocks], timeout=direct_fetch_response_time)
[ test_node.send_message(msg_block(x)) for x in blocks ]
@@ -526,13 +509,13 @@ class SendHeadersTest(BitcoinTestFramework):
# both blocks (same work as tip)
test_node.send_header_for_blocks(blocks[1:2])
test_node.sync_with_ping()
- test_node.wait_for_getdata([x.sha256 for x in blocks[0:2]], timeout=test_node.sleep_time)
+ test_node.wait_for_getdata([x.sha256 for x in blocks[0:2]], timeout=direct_fetch_response_time)
# Announcing 16 more headers should trigger direct fetch for 14 more
# blocks
test_node.send_header_for_blocks(blocks[2:18])
test_node.sync_with_ping()
- test_node.wait_for_getdata([x.sha256 for x in blocks[2:16]], timeout=test_node.sleep_time)
+ test_node.wait_for_getdata([x.sha256 for x in blocks[2:16]], timeout=direct_fetch_response_time)
# Announcing 1 more header should not trigger any response
test_node.last_getdata = None
diff --git a/qa/rpc-tests/smartfees.py b/qa/rpc-tests/smartfees.py
index d76fba4b07..2c56f954a2 100755
--- a/qa/rpc-tests/smartfees.py
+++ b/qa/rpc-tests/smartfees.py
@@ -159,7 +159,7 @@ class EstimateFeeTest(BitcoinTestFramework):
self.nodes = []
# Use node0 to mine blocks for input splitting
self.nodes.append(start_node(0, self.options.tmpdir, ["-maxorphantx=1000",
- "-relaypriority=0", "-whitelist=127.0.0.1"]))
+ "-whitelist=127.0.0.1"]))
print("This test is time consuming, please be patient")
print("Splitting inputs to small size so we can generate low priority tx's")
@@ -197,12 +197,12 @@ class EstimateFeeTest(BitcoinTestFramework):
# (17k is room enough for 110 or so transactions)
self.nodes.append(start_node(1, self.options.tmpdir,
["-blockprioritysize=1500", "-blockmaxsize=17000",
- "-maxorphantx=1000", "-relaypriority=0", "-debug=estimatefee"]))
+ "-maxorphantx=1000", "-debug=estimatefee"]))
connect_nodes(self.nodes[1], 0)
# Node2 is a stingy miner, that
# produces too small blocks (room for only 55 or so transactions)
- node2args = ["-blockprioritysize=0", "-blockmaxsize=8000", "-maxorphantx=1000", "-relaypriority=0"]
+ node2args = ["-blockprioritysize=0", "-blockmaxsize=8000", "-maxorphantx=1000"]
self.nodes.append(start_node(2, self.options.tmpdir, node2args))
connect_nodes(self.nodes[0], 2)
@@ -225,9 +225,9 @@ class EstimateFeeTest(BitcoinTestFramework):
self.memutxo, Decimal("0.005"), min_fee, min_fee)
tx_kbytes = (len(txhex) // 2) / 1000.0
self.fees_per_kb.append(float(fee)/tx_kbytes)
- sync_mempools(self.nodes[0:3],.1)
+ sync_mempools(self.nodes[0:3], wait=.1)
mined = mining_node.getblock(mining_node.generate(1)[0],True)["tx"]
- sync_blocks(self.nodes[0:3],.1)
+ sync_blocks(self.nodes[0:3], wait=.1)
# update which txouts are confirmed
newmem = []
for utx in self.memutxo:
@@ -259,7 +259,7 @@ class EstimateFeeTest(BitcoinTestFramework):
while len(self.nodes[1].getrawmempool()) > 0:
self.nodes[1].generate(1)
- sync_blocks(self.nodes[0:3],.1)
+ sync_blocks(self.nodes[0:3], wait=.1)
print("Final estimates after emptying mempools")
check_estimates(self.nodes[1], self.fees_per_kb, 2)
diff --git a/qa/rpc-tests/test_framework/address.py b/qa/rpc-tests/test_framework/address.py
new file mode 100644
index 0000000000..50b999be61
--- /dev/null
+++ b/qa/rpc-tests/test_framework/address.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env python3
+# Copyright (c) 2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#
+# address.py
+#
+# This file encodes and decodes BASE58 P2PKH and P2SH addresses
+#
+
+from .script import hash256, hash160, sha256, CScript, OP_0
+from .util import bytes_to_hex_str, hex_str_to_bytes
+
+chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
+
+def byte_to_base58(b, version):
+ result = ''
+ str = bytes_to_hex_str(b)
+ str = bytes_to_hex_str(chr(version).encode('latin-1')) + str
+ checksum = bytes_to_hex_str(hash256(hex_str_to_bytes(str)))
+ str += checksum[:8]
+ value = int('0x'+str,0)
+ while value > 0:
+ result = chars[value % 58] + result
+ value //= 58
+ while (str[:2] == '00'):
+ result = chars[0] + result
+ str = str[2:]
+ return result
+
+# TODO: def base58_decode
+
+def keyhash_to_p2pkh(hash, main = False):
+ assert (len(hash) == 20)
+ version = 0 if main else 111
+ return byte_to_base58(hash, version)
+
+def scripthash_to_p2sh(hash, main = False):
+ assert (len(hash) == 20)
+ version = 5 if main else 196
+ return byte_to_base58(hash, version)
+
+def key_to_p2pkh(key, main = False):
+ key = check_key(key)
+ return keyhash_to_p2pkh(hash160(key), main)
+
+def script_to_p2sh(script, main = False):
+ script = check_script(script)
+ return scripthash_to_p2sh(hash160(script), main)
+
+def key_to_p2sh_p2wpkh(key, main = False):
+ key = check_key(key)
+ p2shscript = CScript([OP_0, hash160(key)])
+ return script_to_p2sh(p2shscript, main)
+
+def script_to_p2sh_p2wsh(script, main = False):
+ script = check_script(script)
+ p2shscript = CScript([OP_0, sha256(script)])
+ return script_to_p2sh(p2shscript, main)
+
+def check_key(key):
+ if (type(key) is str):
+ key = hex_str_to_bytes(key) # Assuming this is hex string
+ if (type(key) is bytes and (len(key) == 33 or len(key) == 65)):
+ return key
+ assert(False)
+
+def check_script(script):
+ if (type(script) is str):
+ script = hex_str_to_bytes(script) # Assuming this is hex string
+ if (type(script) is bytes or type(script) is CScript):
+ return script
+ assert(False)
diff --git a/qa/rpc-tests/test_framework/authproxy.py b/qa/rpc-tests/test_framework/authproxy.py
index d095a56ce7..9bee1962e2 100644
--- a/qa/rpc-tests/test_framework/authproxy.py
+++ b/qa/rpc-tests/test_framework/authproxy.py
@@ -1,6 +1,6 @@
"""
- Copyright 2011 Jeff Garzik
+ Copyright (c) 2011 Jeff Garzik
AuthServiceProxy has the following improvements over python-jsonrpc's
ServiceProxy class:
@@ -42,6 +42,7 @@ import base64
import decimal
import json
import logging
+import socket
try:
import urllib.parse as urlparse
except ImportError:
@@ -55,7 +56,11 @@ log = logging.getLogger("BitcoinRPC")
class JSONRPCException(Exception):
def __init__(self, rpc_error):
- Exception.__init__(self)
+ try:
+ errmsg = '%(message)s (%(code)i)' % rpc_error
+ except (KeyError, TypeError):
+ errmsg = ''
+ Exception.__init__(self, errmsg)
self.error = rpc_error
@@ -126,8 +131,9 @@ class AuthServiceProxy(object):
return self._get_response()
else:
raise
- except BrokenPipeError:
- # Python 3.5+ raises this instead of BadStatusLine when the connection was reset
+ except (BrokenPipeError,ConnectionResetError):
+ # Python 3.5+ raises BrokenPipeError instead of BadStatusLine when the connection was reset
+ # ConnectionResetError happens on FreeBSD with Python 3.4
self.__conn.close()
self.__conn.request(method, path, postdata, headers)
return self._get_response()
@@ -156,7 +162,15 @@ class AuthServiceProxy(object):
return self._request('POST', self.__url.path, postdata.encode('utf-8'))
def _get_response(self):
- http_response = self.__conn.getresponse()
+ try:
+ http_response = self.__conn.getresponse()
+ except socket.timeout as e:
+ raise JSONRPCException({
+ 'code': -344,
+ 'message': '%r RPC took longer than %f seconds. Consider '
+ 'using larger timeout for calls that take '
+ 'longer to return.' % (self._service_name,
+ self.__conn.timeout)})
if http_response is None:
raise JSONRPCException({
'code': -342, 'message': 'missing HTTP response from server'})
diff --git a/qa/rpc-tests/test_framework/blockstore.py b/qa/rpc-tests/test_framework/blockstore.py
index 6120dd574b..28a6b92b81 100644
--- a/qa/rpc-tests/test_framework/blockstore.py
+++ b/qa/rpc-tests/test_framework/blockstore.py
@@ -1,4 +1,7 @@
#!/usr/bin/env python3
+# Copyright (c) 2015-2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
# BlockStore: a helper class that keeps a map of blocks and implements
# helper functions for responding to getheaders and getdata,
# and for constructing a getheaders message
@@ -6,11 +9,11 @@
from .mininode import *
from io import BytesIO
-import dbm.ndbm
+import dbm.dumb as dbmd
class BlockStore(object):
def __init__(self, datadir):
- self.blockDB = dbm.ndbm.open(datadir + "/blocks", 'c')
+ self.blockDB = dbmd.open(datadir + "/blocks", 'c')
self.currentBlock = 0
self.headers_map = dict()
@@ -120,7 +123,7 @@ class BlockStore(object):
class TxStore(object):
def __init__(self, datadir):
- self.txDB = dbm.ndbm.open(datadir + "/transactions", 'c')
+ self.txDB = dbmd.open(datadir + "/transactions", 'c')
def close(self):
self.txDB.close()
diff --git a/qa/rpc-tests/test_framework/comptool.py b/qa/rpc-tests/test_framework/comptool.py
index 7c92d3f828..17679fc7e1 100755
--- a/qa/rpc-tests/test_framework/comptool.py
+++ b/qa/rpc-tests/test_framework/comptool.py
@@ -111,6 +111,11 @@ class TestNode(NodeConnCB):
m.locator = self.block_store.get_locator(self.bestblockhash)
self.conn.send_message(m)
+ def send_header(self, header):
+ m = msg_headers()
+ m.headers.append(header)
+ self.conn.send_message(m)
+
# This assumes BIP31
def send_ping(self, nonce):
self.pingMap[nonce] = True
@@ -345,8 +350,16 @@ class TestManager(object):
# Either send inv's to each node and sync, or add
# to invqueue for later inv'ing.
if (test_instance.sync_every_block):
- [ c.cb.send_inv(block) for c in self.connections ]
- self.sync_blocks(block.sha256, 1)
+ # if we expect success, send inv and sync every block
+ # if we expect failure, just push the block and see what happens.
+ if outcome == True:
+ [ c.cb.send_inv(block) for c in self.connections ]
+ self.sync_blocks(block.sha256, 1)
+ else:
+ [ c.send_message(msg_block(block)) for c in self.connections ]
+ [ c.cb.send_ping(self.ping_counter) for c in self.connections ]
+ self.wait_for_pings(self.ping_counter)
+ self.ping_counter += 1
if (not self.check_results(tip, outcome)):
raise AssertionError("Test failed at test %d" % test_number)
else:
@@ -354,6 +367,8 @@ class TestManager(object):
elif isinstance(b_or_t, CBlockHeader):
block_header = b_or_t
self.block_store.add_header(block_header)
+ [ c.cb.send_header(block_header) for c in self.connections ]
+
else: # Tx test runner
assert(isinstance(b_or_t, CTransaction))
tx = b_or_t
diff --git a/qa/rpc-tests/test_framework/coverage.py b/qa/rpc-tests/test_framework/coverage.py
index 23fce61014..13b33869f5 100644
--- a/qa/rpc-tests/test_framework/coverage.py
+++ b/qa/rpc-tests/test_framework/coverage.py
@@ -50,7 +50,7 @@ class AuthServiceProxyWrapper(object):
rpc_method = self.auth_service_proxy_instance._service_name
if self.coverage_logfile:
- with open(self.coverage_logfile, 'a+') as f:
+ with open(self.coverage_logfile, 'a+', encoding='utf8') as f:
f.write("%s\n" % rpc_method)
return return_val
@@ -100,7 +100,7 @@ def write_all_rpc_commands(dirname, node):
if line and not line.startswith('='):
commands.add("%s\n" % line.split()[0])
- with open(filename, 'w') as f:
+ with open(filename, 'w', encoding='utf8') as f:
f.writelines(list(commands))
return True
diff --git a/qa/rpc-tests/test_framework/key.py b/qa/rpc-tests/test_framework/key.py
index ba3038fe04..c63a15c1e0 100644
--- a/qa/rpc-tests/test_framework/key.py
+++ b/qa/rpc-tests/test_framework/key.py
@@ -75,6 +75,9 @@ ssl.EC_POINT_mul.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p,
# this specifies the curve used with ECDSA.
NID_secp256k1 = 714 # from openssl/obj_mac.h
+SECP256K1_ORDER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
+SECP256K1_ORDER_HALF = SECP256K1_ORDER // 2
+
# Thx to Sam Devlin for the ctypes magic 64-bit fix.
def _check_result(val, func, args):
if val == 0:
@@ -147,7 +150,7 @@ class CECKey(object):
r = self.get_raw_ecdh_key(other_pubkey)
return kdf(r)
- def sign(self, hash):
+ def sign(self, hash, low_s = True):
# FIXME: need unit tests for below cases
if not isinstance(hash, bytes):
raise TypeError('Hash must be bytes instance; got %r' % hash.__class__)
@@ -159,7 +162,25 @@ class CECKey(object):
mb_sig = ctypes.create_string_buffer(sig_size0.value)
result = ssl.ECDSA_sign(0, hash, len(hash), mb_sig, ctypes.byref(sig_size0), self.k)
assert 1 == result
- return mb_sig.raw[:sig_size0.value]
+ assert mb_sig.raw[0] == 0x30
+ assert mb_sig.raw[1] == sig_size0.value - 2
+ total_size = mb_sig.raw[1]
+ assert mb_sig.raw[2] == 2
+ r_size = mb_sig.raw[3]
+ assert mb_sig.raw[4 + r_size] == 2
+ s_size = mb_sig.raw[5 + r_size]
+ s_value = int.from_bytes(mb_sig.raw[6+r_size:6+r_size+s_size], byteorder='big')
+ if (not low_s) or s_value <= SECP256K1_ORDER_HALF:
+ return mb_sig.raw[:sig_size0.value]
+ else:
+ low_s_value = SECP256K1_ORDER - s_value
+ low_s_bytes = (low_s_value).to_bytes(33, byteorder='big')
+ while len(low_s_bytes) > 1 and low_s_bytes[0] == 0 and low_s_bytes[1] < 0x80:
+ low_s_bytes = low_s_bytes[1:]
+ new_s_size = len(low_s_bytes)
+ new_total_size_byte = (total_size + new_s_size - s_size).to_bytes(1,byteorder='big')
+ new_s_size_byte = (new_s_size).to_bytes(1,byteorder='big')
+ return b'\x30' + new_total_size_byte + mb_sig.raw[2:5+r_size] + new_s_size_byte + low_s_bytes
def verify(self, hash, sig):
"""Verify a DER signature"""
diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py
index caffab3535..91daa4ab1f 100755
--- a/qa/rpc-tests/test_framework/mininode.py
+++ b/qa/rpc-tests/test_framework/mininode.py
@@ -41,9 +41,10 @@ from test_framework.siphash import siphash256
BIP0031_VERSION = 60000
MY_VERSION = 70014 # past bip-31 for ping/pong
MY_SUBVERSION = b"/python-mininode-tester:0.0.3/"
+MY_RELAY = 1 # from version 70001 onwards, fRelay should be appended to version messages (BIP37)
MAX_INV_SZ = 50000
-MAX_BLOCK_SIZE = 1000000
+MAX_BLOCK_BASE_SIZE = 1000000
COIN = 100000000 # 1 btc in satoshis
@@ -452,7 +453,7 @@ class CTransaction(object):
else:
self.vout = deser_vector(f, CTxOut)
if flags != 0:
- self.wit.vtxinwit = [CTxInWitness()]*len(self.vin)
+ self.wit.vtxinwit = [CTxInWitness() for i in range(len(self.vin))]
self.wit.deserialize(f)
self.nLockTime = struct.unpack("<I", f.read(4))[0]
self.sha256 = None
@@ -518,8 +519,8 @@ class CTransaction(object):
return True
def __repr__(self):
- return "CTransaction(nVersion=%i vin=%s vout=%s nLockTime=%i)" \
- % (self.nVersion, repr(self.vin), repr(self.vout), self.nLockTime)
+ return "CTransaction(nVersion=%i vin=%s vout=%s wit=%s nLockTime=%i)" \
+ % (self.nVersion, repr(self.vin), repr(self.vout), repr(self.wit), self.nLockTime)
class CBlockHeader(object):
@@ -755,6 +756,9 @@ class PrefilledTransaction(object):
r += self.tx.serialize_without_witness()
return r
+ def serialize_with_witness(self):
+ return self.serialize(with_witness=True)
+
def __repr__(self):
return "PrefilledTransaction(index=%d, tx=%s)" % (self.index, repr(self.tx))
@@ -779,6 +783,7 @@ class P2PHeaderAndShortIDs(object):
self.prefilled_txn = deser_vector(f, PrefilledTransaction)
self.prefilled_txn_length = len(self.prefilled_txn)
+ # When using version 2 compact blocks, we must serialize with_witness.
def serialize(self, with_witness=False):
r = b""
r += self.header.serialize()
@@ -787,12 +792,20 @@ class P2PHeaderAndShortIDs(object):
for x in self.shortids:
# We only want the first 6 bytes
r += struct.pack("<Q", x)[0:6]
- r += ser_vector(self.prefilled_txn)
+ if with_witness:
+ r += ser_vector(self.prefilled_txn, "serialize_with_witness")
+ else:
+ r += ser_vector(self.prefilled_txn)
return r
def __repr__(self):
return "P2PHeaderAndShortIDs(header=%s, nonce=%d, shortids_length=%d, shortids=%s, prefilled_txn_length=%d, prefilledtxn=%s" % (repr(self.header), self.nonce, self.shortids_length, repr(self.shortids), self.prefilled_txn_length, repr(self.prefilled_txn))
+# P2P version of the above that will use witness serialization (for compact
+# block version 2)
+class P2PHeaderAndShortWitnessIDs(P2PHeaderAndShortIDs):
+ def serialize(self):
+ return super(P2PHeaderAndShortWitnessIDs, self).serialize(with_witness=True)
# Calculate the BIP 152-compact blocks shortid for a given transaction hash
def calculate_shortid(k0, k1, tx_hash):
@@ -808,6 +821,7 @@ class HeaderAndShortIDs(object):
self.nonce = 0
self.shortids = []
self.prefilled_txn = []
+ self.use_witness = False
if p2pheaders_and_shortids != None:
self.header = p2pheaders_and_shortids.header
@@ -819,7 +833,10 @@ class HeaderAndShortIDs(object):
last_index = self.prefilled_txn[-1].index
def to_p2p(self):
- ret = P2PHeaderAndShortIDs()
+ if self.use_witness:
+ ret = P2PHeaderAndShortWitnessIDs()
+ else:
+ ret = P2PHeaderAndShortIDs()
ret.header = self.header
ret.nonce = self.nonce
ret.shortids_length = len(self.shortids)
@@ -840,15 +857,20 @@ class HeaderAndShortIDs(object):
key1 = struct.unpack("<Q", hash_header_nonce_as_str[8:16])[0]
return [ key0, key1 ]
- def initialize_from_block(self, block, nonce=0, prefill_list = [0]):
+ # Version 2 compact blocks use wtxid in shortids (rather than txid)
+ def initialize_from_block(self, block, nonce=0, prefill_list = [0], use_witness = False):
self.header = CBlockHeader(block)
self.nonce = nonce
self.prefilled_txn = [ PrefilledTransaction(i, block.vtx[i]) for i in prefill_list ]
self.shortids = []
+ self.use_witness = use_witness
[k0, k1] = self.get_siphash_keys()
for i in range(len(block.vtx)):
if i not in prefill_list:
- self.shortids.append(calculate_shortid(k0, k1, block.vtx[i].sha256))
+ tx_hash = block.vtx[i].sha256
+ if use_witness:
+ tx_hash = block.vtx[i].calc_sha256(with_witness=True)
+ self.shortids.append(calculate_shortid(k0, k1, tx_hash))
def __repr__(self):
return "HeaderAndShortIDs(header=%s, nonce=%d, shortids=%s, prefilledtxn=%s" % (repr(self.header), self.nonce, repr(self.shortids), repr(self.prefilled_txn))
@@ -930,6 +952,7 @@ class msg_version(object):
self.nNonce = random.getrandbits(64)
self.strSubVer = MY_SUBVERSION
self.nStartingHeight = -1
+ self.nRelay = MY_RELAY
def deserialize(self, f):
self.nVersion = struct.unpack("<i", f.read(4))[0]
@@ -939,21 +962,32 @@ class msg_version(object):
self.nTime = struct.unpack("<q", f.read(8))[0]
self.addrTo = CAddress()
self.addrTo.deserialize(f)
+
if self.nVersion >= 106:
self.addrFrom = CAddress()
self.addrFrom.deserialize(f)
self.nNonce = struct.unpack("<Q", f.read(8))[0]
self.strSubVer = deser_string(f)
- if self.nVersion >= 209:
- self.nStartingHeight = struct.unpack("<i", f.read(4))[0]
- else:
- self.nStartingHeight = None
else:
self.addrFrom = None
self.nNonce = None
self.strSubVer = None
self.nStartingHeight = None
+ if self.nVersion >= 209:
+ self.nStartingHeight = struct.unpack("<i", f.read(4))[0]
+ else:
+ self.nStartingHeight = None
+
+ if self.nVersion >= 70001:
+ # Relay field is optional for version 70001 onwards
+ try:
+ self.nRelay = struct.unpack("<b", f.read(1))[0]
+ except:
+ self.nRelay = 0
+ else:
+ self.nRelay = 0
+
def serialize(self):
r = b""
r += struct.pack("<i", self.nVersion)
@@ -964,13 +998,14 @@ class msg_version(object):
r += struct.pack("<Q", self.nNonce)
r += ser_string(self.strSubVer)
r += struct.pack("<i", self.nStartingHeight)
+ r += struct.pack("<b", self.nRelay)
return r
def __repr__(self):
- return 'msg_version(nVersion=%i nServices=%i nTime=%s addrTo=%s addrFrom=%s nNonce=0x%016X strSubVer=%s nStartingHeight=%i)' \
+ return 'msg_version(nVersion=%i nServices=%i nTime=%s addrTo=%s addrFrom=%s nNonce=0x%016X strSubVer=%s nStartingHeight=%i nRelay=%i)' \
% (self.nVersion, self.nServices, time.ctime(self.nTime),
repr(self.addrTo), repr(self.addrFrom), self.nNonce,
- self.strSubVer, self.nStartingHeight)
+ self.strSubVer, self.nStartingHeight, self.nRelay)
class msg_verack(object):
@@ -1320,7 +1355,7 @@ class msg_reject(object):
% (self.message, self.code, self.reason, self.data)
# Helper function
-def wait_until(predicate, attempts=float('inf'), timeout=float('inf')):
+def wait_until(predicate, *, attempts=float('inf'), timeout=float('inf')):
attempt = 0
elapsed = 0
@@ -1424,6 +1459,12 @@ class msg_blocktxn(object):
def __repr__(self):
return "msg_blocktxn(block_transactions=%s)" % (repr(self.block_transactions))
+class msg_witness_blocktxn(msg_blocktxn):
+ def serialize(self):
+ r = b""
+ r += self.block_transactions.serialize(with_witness=True)
+ return r
+
# This is what a callback should look like for NodeConn
# Reimplement the on_* functions to provide handling for events
class NodeConnCB(object):
@@ -1536,7 +1577,7 @@ class SingleNodeConnCB(NodeConnCB):
def received_pong():
return (self.last_pong.nonce == self.ping_counter)
self.send_message(msg_ping(nonce=self.ping_counter))
- success = wait_until(received_pong, timeout)
+ success = wait_until(received_pong, timeout=timeout)
self.ping_counter += 1
return success
diff --git a/qa/rpc-tests/test_framework/netutil.py b/qa/rpc-tests/test_framework/netutil.py
index 573b06772d..b92a9f6e1c 100644
--- a/qa/rpc-tests/test_framework/netutil.py
+++ b/qa/rpc-tests/test_framework/netutil.py
@@ -58,7 +58,7 @@ def netstat(typ='tcp'):
To get pid of all network process running on system, you must run this script
as superuser
'''
- with open('/proc/net/'+typ,'r') as f:
+ with open('/proc/net/'+typ,'r',encoding='utf8') as f:
content = f.readlines()
content.pop(0)
result = []
diff --git a/qa/rpc-tests/test_framework/test_framework.py b/qa/rpc-tests/test_framework/test_framework.py
index 186cf866cf..98c4f6070b 100755
--- a/qa/rpc-tests/test_framework/test_framework.py
+++ b/qa/rpc-tests/test_framework/test_framework.py
@@ -21,7 +21,6 @@ from .util import (
sync_mempools,
stop_nodes,
stop_node,
- wait_bitcoinds,
enable_coverage,
check_json_precision,
initialize_chain_clean,
@@ -81,7 +80,6 @@ class BitcoinTestFramework(object):
"""
assert not self.is_network_split
stop_nodes(self.nodes)
- wait_bitcoinds()
self.setup_network(True)
def sync_all(self):
@@ -100,7 +98,6 @@ class BitcoinTestFramework(object):
"""
assert self.is_network_split
stop_nodes(self.nodes)
- wait_bitcoinds()
self.setup_network(False)
def main(self):
@@ -142,16 +139,11 @@ class BitcoinTestFramework(object):
success = False
try:
- if not os.path.isdir(self.options.tmpdir):
- os.makedirs(self.options.tmpdir)
+ os.makedirs(self.options.tmpdir, exist_ok=False)
self.setup_chain()
-
self.setup_network()
-
self.run_test()
-
success = True
-
except JSONRPCException as e:
print("JSONRPC error: "+e.error['message'])
traceback.print_tb(sys.exc_info()[2])
@@ -170,7 +162,6 @@ class BitcoinTestFramework(object):
if not self.options.noshutdown:
print("Stopping nodes")
stop_nodes(self.nodes)
- wait_bitcoinds()
else:
print("Note: bitcoinds were not stopped and may still be running")
@@ -181,7 +172,16 @@ class BitcoinTestFramework(object):
os.rmdir(self.options.root)
else:
print("Not cleaning up dir %s" % self.options.tmpdir)
-
+ if os.getenv("PYTHON_DEBUG", ""):
+ # Dump the end of the debug logs, to aid in debugging rare
+ # travis failures.
+ import glob
+ filenames = glob.glob(self.options.tmpdir + "/node*/regtest/debug.log")
+ MAX_LINES_TO_PRINT = 1000
+ for f in filenames:
+ print("From" , f, ":")
+ from collections import deque
+ print("".join(deque(open(f), MAX_LINES_TO_PRINT)))
if success:
print("Tests successful")
sys.exit(0)
diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py
index eee77f1a10..85898d9f32 100644
--- a/qa/rpc-tests/test_framework/util.py
+++ b/qa/rpc-tests/test_framework/util.py
@@ -121,23 +121,44 @@ def hex_str_to_bytes(hex_str):
def str_to_b64str(string):
return b64encode(string.encode('utf-8')).decode('ascii')
-def sync_blocks(rpc_connections, wait=1, timeout=60):
+def sync_blocks(rpc_connections, *, wait=1, timeout=60):
"""
- Wait until everybody has the same tip
+ Wait until everybody has the same tip.
+
+ sync_blocks needs to be called with an rpc_connections set that has least
+ one node already synced to the latest, stable tip, otherwise there's a
+ chance it might return before all nodes are stably synced.
+ """
+ # Use getblockcount() instead of waitforblockheight() to determine the
+ # initial max height because the two RPCs look at different internal global
+ # variables (chainActive vs latestBlock) and the former gets updated
+ # earlier.
+ maxheight = max(x.getblockcount() for x in rpc_connections)
+ start_time = cur_time = time.time()
+ while cur_time <= start_time + timeout:
+ tips = [r.waitforblockheight(maxheight, int(wait * 1000)) for r in rpc_connections]
+ if all(t["height"] == maxheight for t in tips):
+ if all(t["hash"] == tips[0]["hash"] for t in tips):
+ return
+ raise AssertionError("Block sync failed, mismatched block hashes:{}".format(
+ "".join("\n {!r}".format(tip) for tip in tips)))
+ cur_time = time.time()
+ raise AssertionError("Block sync to height {} timed out:{}".format(
+ maxheight, "".join("\n {!r}".format(tip) for tip in tips)))
+
+def sync_chain(rpc_connections, *, wait=1, timeout=60):
+ """
+ Wait until everybody has the same best block
"""
- maxheight = 0
while timeout > 0:
- tips = [ x.waitforblockheight(maxheight, int(wait * 1000)) for x in rpc_connections ]
- heights = [ x["height"] for x in tips ]
- if tips == [ tips[0] ]*len(tips):
- return True
- if heights == [ heights[0] ]*len(heights): #heights are the same but hashes are not
- raise AssertionError("Block sync failed")
+ best_hash = [x.getbestblockhash() for x in rpc_connections]
+ if best_hash == [best_hash[0]]*len(best_hash):
+ return
+ time.sleep(wait)
timeout -= wait
- maxheight = max(heights)
- raise AssertionError("Block sync failed")
+ raise AssertionError("Chain sync failed: Best block hashes don't match")
-def sync_mempools(rpc_connections, wait=1, timeout=60):
+def sync_mempools(rpc_connections, *, wait=1, timeout=60):
"""
Wait until everybody has the same transactions in their memory
pools
@@ -149,7 +170,7 @@ def sync_mempools(rpc_connections, wait=1, timeout=60):
if set(rpc_connections[i].getrawmempool()) == pool:
num_match = num_match+1
if num_match == len(rpc_connections):
- return True
+ return
time.sleep(wait)
timeout -= wait
raise AssertionError("Mempool sync failed")
@@ -161,7 +182,7 @@ def initialize_datadir(dirname, n):
if not os.path.isdir(datadir):
os.makedirs(datadir)
rpc_u, rpc_p = rpc_auth_pair(n)
- with open(os.path.join(datadir, "bitcoin.conf"), 'w') as f:
+ with open(os.path.join(datadir, "bitcoin.conf"), 'w', encoding='utf8') as f:
f.write("regtest=1\n")
f.write("rpcuser=" + rpc_u + "\n")
f.write("rpcpassword=" + rpc_p + "\n")
@@ -202,7 +223,7 @@ def wait_for_bitcoind_start(process, url, i):
raise # unknown IO error
except JSONRPCException as e: # Initialization phase
if e.error['code'] != -28: # RPC in warmup?
- raise # unkown JSON RPC exception
+ raise # unknown JSON RPC exception
time.sleep(0.25)
def initialize_chain(test_dir, num_nodes, cachedir):
@@ -236,7 +257,7 @@ def initialize_chain(test_dir, num_nodes, cachedir):
print("initialize_chain: bitcoind started, waiting for RPC to come up")
wait_for_bitcoind_start(bitcoind_processes[i], rpc_url(i), i)
if os.getenv("PYTHON_DEBUG", ""):
- print("initialize_chain: RPC succesfully started")
+ print("initialize_chain: RPC successfully started")
rpcs = []
for i in range(MAX_NODES):
@@ -266,7 +287,6 @@ def initialize_chain(test_dir, num_nodes, cachedir):
# Shut them down, and clean up cache directories:
stop_nodes(rpcs)
- wait_bitcoinds()
disable_mocktime()
for i in range(MAX_NODES):
os.remove(log_filename(cachedir, i, "debug.log"))
@@ -324,7 +344,7 @@ def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=
url = rpc_url(i, rpchost)
wait_for_bitcoind_start(bitcoind_processes[i], url, i)
if os.getenv("PYTHON_DEBUG", ""):
- print("start_node: RPC succesfully started")
+ print("start_node: RPC successfully started")
proxy = get_rpc_proxy(url, i, timeout=timewait)
if COVERAGE_DIR:
@@ -332,7 +352,7 @@ def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=
return proxy
-def start_nodes(num_nodes, dirname, extra_args=None, rpchost=None, binary=None):
+def start_nodes(num_nodes, dirname, extra_args=None, rpchost=None, timewait=None, binary=None):
"""
Start multiple bitcoinds, return RPC connections to them
"""
@@ -341,7 +361,7 @@ def start_nodes(num_nodes, dirname, extra_args=None, rpchost=None, binary=None):
rpcs = []
try:
for i in range(num_nodes):
- rpcs.append(start_node(i, dirname, extra_args[i], rpchost, binary=binary[i]))
+ rpcs.append(start_node(i, dirname, extra_args[i], rpchost, timewait=timewait, binary=binary[i]))
except: # If one node failed to start, stop the others
stop_nodes(rpcs)
raise
@@ -365,6 +385,7 @@ def stop_nodes(nodes):
except http.client.CannotSendRequest as e:
print("WARN: Unable to stop node: " + repr(e))
del nodes[:] # Emptying array closes connections as a side effect
+ wait_bitcoinds()
def set_node_times(nodes, t):
for node in nodes:
@@ -512,10 +533,14 @@ def assert_greater_than(thing1, thing2):
raise AssertionError("%s <= %s"%(str(thing1),str(thing2)))
def assert_raises(exc, fun, *args, **kwds):
+ assert_raises_message(exc, None, fun, *args, **kwds)
+
+def assert_raises_message(exc, message, fun, *args, **kwds):
try:
fun(*args, **kwds)
- except exc:
- pass
+ except exc as e:
+ if message is not None and message not in e.error['message']:
+ raise AssertionError("Expected substring not found:"+e.error['message'])
except Exception as e:
raise AssertionError("Unexpected exception raised: "+type(e).__name__)
else:
@@ -629,16 +654,15 @@ def create_tx(node, coinbase, to_address, amount):
# Create a spend of each passed-in utxo, splicing in "txouts" to each raw
# transaction to make it large. See gen_return_txouts() above.
-def create_lots_of_big_transactions(node, txouts, utxos, fee):
+def create_lots_of_big_transactions(node, txouts, utxos, num, fee):
addr = node.getnewaddress()
txids = []
- for i in range(len(utxos)):
+ for _ in range(num):
t = utxos.pop()
- inputs = []
- inputs.append({ "txid" : t["txid"], "vout" : t["vout"]})
+ inputs=[{ "txid" : t["txid"], "vout" : t["vout"]}]
outputs = {}
- send_value = t['amount'] - fee
- outputs[addr] = satoshi_round(send_value)
+ change = t['amount'] - fee
+ outputs[addr] = satoshi_round(change)
rawtx = node.createrawtransaction(inputs, outputs)
newtx = rawtx[0:92]
newtx = newtx + txouts
@@ -648,6 +672,19 @@ def create_lots_of_big_transactions(node, txouts, utxos, fee):
txids.append(txid)
return txids
+def mine_large_block(node, utxos=None):
+ # generate a 66k transaction,
+ # and 14 of them is close to the 1MB block limit
+ num = 14
+ txouts = gen_return_txouts()
+ utxos = utxos if utxos is not None else []
+ if len(utxos) < num:
+ utxos.clear()
+ utxos.extend(node.listunspent())
+ fee = 100 * node.getnetworkinfo()["relayfee"]
+ create_lots_of_big_transactions(node, txouts, utxos, num, fee=fee)
+ node.generate(1)
+
def get_bip9_status(node, key):
info = node.getblockchaininfo()
return info['bip9_softforks'][key]
diff --git a/qa/rpc-tests/wallet-dump.py b/qa/rpc-tests/wallet-dump.py
index 6028d2c20b..c6dc2e3d10 100755
--- a/qa/rpc-tests/wallet-dump.py
+++ b/qa/rpc-tests/wallet-dump.py
@@ -12,7 +12,7 @@ def read_dump(file_name, addrs, hd_master_addr_old):
Read the given dump, count the addrs that match, count change and reserve.
Also check that the old hd_master is inactive
"""
- with open(file_name) as inputfile:
+ with open(file_name, encoding='utf8') as inputfile:
found_addr = 0
found_addr_chg = 0
found_addr_rsv = 0
@@ -61,7 +61,11 @@ class WalletDumpTest(BitcoinTestFramework):
self.extra_args = [["-keypool=90"]]
def setup_network(self, split=False):
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args)
+ # Use 1 minute timeout because the initial getnewaddress RPC can take
+ # longer than the default 30 seconds due to an expensive
+ # CWallet::TopUpKeyPool call, and the encryptwallet RPC made later in
+ # the test often takes even longer.
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args, timewait=60)
def run_test (self):
tmpdir = self.options.tmpdir
diff --git a/qa/rpc-tests/wallet.py b/qa/rpc-tests/wallet.py
index 5d96e7a6e5..992fb8a2d6 100755
--- a/qa/rpc-tests/wallet.py
+++ b/qa/rpc-tests/wallet.py
@@ -18,9 +18,10 @@ class WalletTest (BitcoinTestFramework):
super().__init__()
self.setup_clean_chain = True
self.num_nodes = 4
+ self.extra_args = [['-usehd={:d}'.format(i%2==0)] for i in range(4)]
def setup_network(self, split=False):
- self.nodes = start_nodes(3, self.options.tmpdir)
+ self.nodes = start_nodes(3, self.options.tmpdir, self.extra_args[:3])
connect_nodes_bi(self.nodes,0,1)
connect_nodes_bi(self.nodes,1,2)
connect_nodes_bi(self.nodes,0,2)
@@ -70,7 +71,7 @@ class WalletTest (BitcoinTestFramework):
unspent_0 = self.nodes[2].listunspent()[0]
unspent_0 = {"txid": unspent_0["txid"], "vout": unspent_0["vout"]}
self.nodes[2].lockunspent(False, [unspent_0])
- assert_raises(JSONRPCException, self.nodes[2].sendtoaddress, self.nodes[2].getnewaddress(), 20)
+ assert_raises_message(JSONRPCException, "Insufficient funds", self.nodes[2].sendtoaddress, self.nodes[2].getnewaddress(), 20)
assert_equal([unspent_0], self.nodes[2].listlockunspent())
self.nodes[2].lockunspent(True, [unspent_0])
assert_equal(len(self.nodes[2].listlockunspent()), 0)
@@ -154,7 +155,7 @@ class WalletTest (BitcoinTestFramework):
txid2 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1)
sync_mempools(self.nodes)
- self.nodes.append(start_node(3, self.options.tmpdir))
+ self.nodes.append(start_node(3, self.options.tmpdir, self.extra_args[3]))
connect_nodes_bi(self.nodes, 0, 3)
sync_blocks(self.nodes)
@@ -198,7 +199,6 @@ class WalletTest (BitcoinTestFramework):
#do some -walletbroadcast tests
stop_nodes(self.nodes)
- wait_bitcoinds()
self.nodes = start_nodes(3, self.options.tmpdir, [["-walletbroadcast=0"],["-walletbroadcast=0"],["-walletbroadcast=0"]])
connect_nodes_bi(self.nodes,0,1)
connect_nodes_bi(self.nodes,1,2)
@@ -224,7 +224,6 @@ class WalletTest (BitcoinTestFramework):
#restart the nodes with -walletbroadcast=1
stop_nodes(self.nodes)
- wait_bitcoinds()
self.nodes = start_nodes(3, self.options.tmpdir)
connect_nodes_bi(self.nodes,0,1)
connect_nodes_bi(self.nodes,1,2)
@@ -331,11 +330,12 @@ class WalletTest (BitcoinTestFramework):
# disabled until issue is fixed: https://github.com/bitcoin/bitcoin/issues/7463
# '-salvagewallet',
]
+ chainlimit = 6
for m in maintenance:
print("check " + m)
stop_nodes(self.nodes)
- wait_bitcoinds()
- self.nodes = start_nodes(3, self.options.tmpdir, [[m]] * 3)
+ # set lower ancestor limit for later
+ self.nodes = start_nodes(3, self.options.tmpdir, [[m, "-limitancestorcount="+str(chainlimit)]] * 3)
while m == '-reindex' and [block_count] * 3 != [self.nodes[i].getblockcount() for i in range(3)]:
# reindex will leave rpc warm up "early"; Wait for it to finish
time.sleep(0.1)
@@ -348,5 +348,26 @@ class WalletTest (BitcoinTestFramework):
assert_equal(coinbase_tx_1["transactions"][0]["blockhash"], blocks[1])
assert_equal(len(self.nodes[0].listsinceblock(blocks[1])["transactions"]), 0)
+ # ==Check that wallet prefers to use coins that don't exceed mempool limits =====
+
+ # Get all non-zero utxos together
+ chain_addrs = [self.nodes[0].getnewaddress(), self.nodes[0].getnewaddress()]
+ singletxid = self.nodes[0].sendtoaddress(chain_addrs[0], self.nodes[0].getbalance(), "", "", True)
+ self.nodes[0].generate(1)
+ node0_balance = self.nodes[0].getbalance()
+ # Split into two chains
+ rawtx = self.nodes[0].createrawtransaction([{"txid":singletxid, "vout":0}], {chain_addrs[0]:node0_balance/2-Decimal('0.01'), chain_addrs[1]:node0_balance/2-Decimal('0.01')})
+ signedtx = self.nodes[0].signrawtransaction(rawtx)
+ singletxid = self.nodes[0].sendrawtransaction(signedtx["hex"])
+ txids = [singletxid, singletxid]
+ self.nodes[0].generate(1)
+
+ # Make a long chain of unconfirmed payments without hitting mempool limit
+ txid_list = []
+ for i in range(chainlimit*2):
+ txid_list.append(self.nodes[0].sendtoaddress(chain_addrs[0], Decimal('0.0001')))
+ assert_equal(self.nodes[0].getmempoolinfo()['size'], chainlimit*2)
+ assert_equal(len(txid_list), chainlimit*2)
+
if __name__ == '__main__':
WalletTest().main()
diff --git a/qa/rpc-tests/walletbackup.py b/qa/rpc-tests/walletbackup.py
index b991d5c761..e12cb10a50 100755
--- a/qa/rpc-tests/walletbackup.py
+++ b/qa/rpc-tests/walletbackup.py
@@ -45,12 +45,12 @@ class WalletBackupTest(BitcoinTestFramework):
super().__init__()
self.setup_clean_chain = True
self.num_nodes = 4
+ # nodes 1, 2,3 are spenders, let's give them a keypool=100
+ self.extra_args = [["-keypool=100"], ["-keypool=100"], ["-keypool=100"], []]
# This mirrors how the network was setup in the bash test
def setup_network(self, split=False):
- # nodes 1, 2,3 are spenders, let's give them a keypool=100
- extra_args = [["-keypool=100"], ["-keypool=100"], ["-keypool=100"], []]
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args)
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args)
connect_nodes(self.nodes[0], 3)
connect_nodes(self.nodes[1], 3)
connect_nodes(self.nodes[2], 3)
@@ -79,6 +79,7 @@ class WalletBackupTest(BitcoinTestFramework):
# Must sync mempools before mining.
sync_mempools(self.nodes)
self.nodes[3].generate(1)
+ sync_blocks(self.nodes)
# As above, this mirrors the original bash test.
def start_three(self):
diff --git a/share/genbuild.sh b/share/genbuild.sh
index 1ef77d706f..eecac4bd00 100755
--- a/share/genbuild.sh
+++ b/share/genbuild.sh
@@ -1,4 +1,8 @@
#!/bin/sh
+# Copyright (c) 2012-2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
if [ $# -gt 1 ]; then
cd "$2"
fi
diff --git a/share/qt/Info.plist.in b/share/qt/Info.plist.in
index 6a34d64cd5..5ca6d9d015 100644
--- a/share/qt/Info.plist.in
+++ b/share/qt/Info.plist.in
@@ -3,7 +3,7 @@
<plist version="0.9">
<dict>
<key>LSMinimumSystemVersion</key>
- <string>10.7.0</string>
+ <string>10.8.0</string>
<key>LSArchitecturePriority</key>
<array>
diff --git a/share/qt/extract_strings_qt.py b/share/qt/extract_strings_qt.py
index 9624abf1fc..92e0df259e 100755
--- a/share/qt/extract_strings_qt.py
+++ b/share/qt/extract_strings_qt.py
@@ -1,4 +1,7 @@
#!/usr/bin/env python
+# Copyright (c) 2012-2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
'''
Extract _("...") strings for translation and convert to Qt stringdefs so that
they can be picked up by Qt linguist.
diff --git a/share/qt/protobuf.pri b/share/qt/protobuf.pri
deleted file mode 100644
index 865fe86555..0000000000
--- a/share/qt/protobuf.pri
+++ /dev/null
@@ -1,35 +0,0 @@
-# Based on: http://code.google.com/p/ostinato/source/browse/protobuf.pri
-#
-# Qt qmake integration with Google Protocol Buffers compiler protoc
-#
-# To compile protocol buffers with qt qmake, specify PROTOS variable and
-# include this file
-#
-# Example:
-# PROTOS = a.proto b.proto
-# include(protobuf.pri)
-#
-# Set PROTO_PATH if you need to set the protoc --proto_path search path
-# Set PROTOC to the path to the protoc compiler if it is not in your $PATH
-#
-
-isEmpty(PROTO_DIR):PROTO_DIR = .
-isEmpty(PROTOC):PROTOC = protoc
-
-PROTOPATHS =
-for(p, PROTO_PATH):PROTOPATHS += --proto_path=$${p}
-
-protobuf_decl.name = protobuf header
-protobuf_decl.input = PROTOS
-protobuf_decl.output = $${PROTO_DIR}/${QMAKE_FILE_BASE}.pb.h
-protobuf_decl.commands = $${PROTOC} --cpp_out="$${PROTO_DIR}" $${PROTOPATHS} --proto_path=${QMAKE_FILE_IN_PATH} ${QMAKE_FILE_NAME}
-protobuf_decl.variable_out = GENERATED_FILES
-QMAKE_EXTRA_COMPILERS += protobuf_decl
-
-protobuf_impl.name = protobuf implementation
-protobuf_impl.input = PROTOS
-protobuf_impl.output = $${PROTO_DIR}/${QMAKE_FILE_BASE}.pb.cc
-protobuf_impl.depends = $${PROTO_DIR}/${QMAKE_FILE_BASE}.pb.h
-protobuf_impl.commands = $$escape_expand(\\n)
-protobuf_impl.variable_out = GENERATED_SOURCES
-QMAKE_EXTRA_COMPILERS += protobuf_impl
diff --git a/share/rpcuser/rpcuser.py b/share/rpcuser/rpcuser.py
index 9fd176908b..f806a810e0 100755
--- a/share/rpcuser/rpcuser.py
+++ b/share/rpcuser/rpcuser.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python2
-# Copyright (c) 2015 The Bitcoin Core developers
+# Copyright (c) 2015 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/Makefile.am b/src/Makefile.am
index ebdddc87f5..89b90e6dff 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,3 +1,7 @@
+# Copyright (c) 2013-2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
DIST_SUBDIRS = secp256k1 univalue
AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS)
@@ -57,6 +61,7 @@ EXTRA_LIBRARIES += \
lib_LTLIBRARIES = $(LIBBITCOINCONSENSUS)
bin_PROGRAMS =
+noinst_PROGRAMS =
TESTS =
BENCHMARKS =
@@ -83,7 +88,6 @@ BITCOIN_CORE_H = \
checkpoints.h \
checkqueue.h \
clientversion.h \
- coincontrol.h \
coins.h \
compat.h \
compat/byteswap.h \
@@ -93,6 +97,7 @@ BITCOIN_CORE_H = \
consensus/consensus.h \
core_io.h \
core_memusage.h \
+ cuckoocache.h \
httprpc.h \
httpserver.h \
indirectmap.h \
@@ -101,13 +106,14 @@ BITCOIN_CORE_H = \
keystore.h \
dbwrapper.h \
limitedmap.h \
- main.h \
memusage.h \
merkleblock.h \
miner.h \
net.h \
+ net_processing.h \
netaddress.h \
netbase.h \
+ netmessagemaker.h \
noui.h \
policy/fees.h \
policy/policy.h \
@@ -129,7 +135,7 @@ BITCOIN_CORE_H = \
support/allocators/secure.h \
support/allocators/zeroafterfree.h \
support/cleanse.h \
- support/pagelocker.h \
+ support/lockedpool.h \
sync.h \
threadsafety.h \
timedata.h \
@@ -141,13 +147,16 @@ BITCOIN_CORE_H = \
util.h \
utilmoneystr.h \
utiltime.h \
+ validation.h \
validationinterface.h \
versionbits.h \
+ wallet/coincontrol.h \
wallet/crypter.h \
wallet/db.h \
wallet/rpcwallet.h \
wallet/wallet.h \
wallet/walletdb.h \
+ warnings.h \
zmq/zmqabstractnotifier.h \
zmq/zmqconfig.h\
zmq/zmqnotificationinterface.h \
@@ -174,10 +183,10 @@ libbitcoin_server_a_SOURCES = \
httpserver.cpp \
init.cpp \
dbwrapper.cpp \
- main.cpp \
merkleblock.cpp \
miner.cpp \
net.cpp \
+ net_processing.cpp \
noui.cpp \
policy/fees.cpp \
policy/policy.cpp \
@@ -196,6 +205,7 @@ libbitcoin_server_a_SOURCES = \
txdb.cpp \
txmempool.cpp \
ui_interface.cpp \
+ validation.cpp \
validationinterface.cpp \
versionbits.cpp \
$(BITCOIN_CORE_H)
@@ -298,6 +308,7 @@ libbitcoin_common_a_SOURCES = \
scheduler.cpp \
script/sign.cpp \
script/standard.cpp \
+ warnings.cpp \
$(BITCOIN_CORE_H)
# util: shared between all executables.
@@ -306,7 +317,7 @@ libbitcoin_common_a_SOURCES = \
libbitcoin_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
libbitcoin_util_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_util_a_SOURCES = \
- support/pagelocker.cpp \
+ support/lockedpool.cpp \
chainparamsbase.cpp \
clientversion.cpp \
compat/glibc_sanity.cpp \
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index 4067ceb399..e58bd9dfbf 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -1,16 +1,32 @@
+# Copyright (c) 2015-2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
bin_PROGRAMS += bench/bench_bitcoin
BENCH_SRCDIR = bench
BENCH_BINARY = bench/bench_bitcoin$(EXEEXT)
+RAW_TEST_FILES = \
+ bench/data/block413567.raw
+GENERATED_TEST_FILES = $(RAW_TEST_FILES:.raw=.raw.h)
bench_bench_bitcoin_SOURCES = \
bench/bench_bitcoin.cpp \
bench/bench.cpp \
bench/bench.h \
+ bench/checkblock.cpp \
bench/Examples.cpp \
bench/rollingbloom.cpp \
bench/crypto_hash.cpp \
- bench/base58.cpp
+ bench/ccoins_caching.cpp \
+ bench/mempool_eviction.cpp \
+ bench/verify_script.cpp \
+ bench/base58.cpp \
+ bench/lockedpool.cpp \
+ bench/perf.cpp \
+ bench/perf.h
+
+nodist_bench_bench_bitcoin_SOURCES = $(GENERATED_TEST_FILES)
bench_bench_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CLFAGS) $(EVENT_PTHREADS_CFLAGS) -I$(builddir)/bench/
bench_bench_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
@@ -30,16 +46,19 @@ bench_bench_bitcoin_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
endif
if ENABLE_WALLET
-bench_bench_bitcoin_LDADD += $(LIBBITCOIN_WALLET)
+bench_bench_bitcoin_SOURCES += bench/coin_selection.cpp
+bench_bench_bitcoin_LDADD += $(LIBBITCOIN_WALLET) $(LIBBITCOIN_CRYPTO)
endif
bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
bench_bench_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno
+CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno $(GENERATED_TEST_FILES)
CLEANFILES += $(CLEAN_BITCOIN_BENCH)
+bench/checkblock.cpp: bench/data/block413567.raw.h
+
bitcoin_bench: $(BENCH_BINARY)
bench: $(BENCH_BINARY) FORCE
@@ -47,3 +66,12 @@ bench: $(BENCH_BINARY) FORCE
bitcoin_bench_clean : FORCE
rm -f $(CLEAN_BITCOIN_BENCH) $(bench_bench_bitcoin_OBJECTS) $(BENCH_BINARY)
+
+%.raw.h: %.raw
+ @$(MKDIR_P) $(@D)
+ @{ \
+ echo "static unsigned const char $(*F)[] = {" && \
+ $(HEXDUMP) -v -e '8/1 "0x%02x, "' -e '"\n"' $< | $(SED) -e 's/0x ,//g' && \
+ echo "};"; \
+ } > "$@.new" && mv -f "$@.new" "$@"
+ @echo "Generated $@"
diff --git a/src/Makefile.leveldb.include b/src/Makefile.leveldb.include
index 4b3cd6364a..358f39cbef 100644
--- a/src/Makefile.leveldb.include
+++ b/src/Makefile.leveldb.include
@@ -1,3 +1,7 @@
+# Copyright (c) 2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
LIBLEVELDB_INT = leveldb/libleveldb.a
LIBMEMENV_INT = leveldb/libmemenv.a
@@ -113,7 +117,6 @@ leveldb_libleveldb_a_SOURCES += leveldb/util/comparator.cc
leveldb_libleveldb_a_SOURCES += leveldb/util/crc32c.cc
leveldb_libleveldb_a_SOURCES += leveldb/util/env.cc
leveldb_libleveldb_a_SOURCES += leveldb/util/env_posix.cc
-leveldb_libleveldb_a_SOURCES += leveldb/util/env_win.cc
leveldb_libleveldb_a_SOURCES += leveldb/util/filter_policy.cc
leveldb_libleveldb_a_SOURCES += leveldb/util/hash.cc
leveldb_libleveldb_a_SOURCES += leveldb/util/histogram.cc
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 8947aeaca0..48abb9b4a2 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -1,3 +1,7 @@
+# Copyright (c) 2013-2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
bin_PROGRAMS += qt/bitcoin-qt
EXTRA_LIBRARIES += qt/libbitcoinqt.a
@@ -96,6 +100,7 @@ QT_FORMS_UI = \
qt/forms/editaddressdialog.ui \
qt/forms/helpmessagedialog.ui \
qt/forms/intro.ui \
+ qt/forms/modaloverlay.ui \
qt/forms/openuridialog.ui \
qt/forms/optionsdialog.ui \
qt/forms/overviewpage.ui \
@@ -125,6 +130,7 @@ QT_MOC_CPP = \
qt/moc_intro.cpp \
qt/moc_macdockiconhandler.cpp \
qt/moc_macnotificationhandler.cpp \
+ qt/moc_modaloverlay.cpp \
qt/moc_notificator.cpp \
qt/moc_openuridialog.cpp \
qt/moc_optionsdialog.cpp \
@@ -192,6 +198,7 @@ BITCOIN_QT_H = \
qt/intro.h \
qt/macdockiconhandler.h \
qt/macnotificationhandler.h \
+ qt/modaloverlay.h \
qt/networkstyle.h \
qt/notificator.h \
qt/openuridialog.h \
@@ -264,6 +271,7 @@ RES_ICONS = \
qt/res/icons/key.png \
qt/res/icons/lock_closed.png \
qt/res/icons/lock_open.png \
+ qt/res/icons/network_disabled.png \
qt/res/icons/open.png \
qt/res/icons/overview.png \
qt/res/icons/quit.png \
@@ -282,7 +290,7 @@ RES_ICONS = \
qt/res/icons/warning.png \
qt/res/icons/verify.png
-BITCOIN_QT_CPP = \
+BITCOIN_QT_BASE_CPP = \
qt/bantablemodel.cpp \
qt/bitcoinaddressvalidator.cpp \
qt/bitcoinamountfield.cpp \
@@ -292,6 +300,7 @@ BITCOIN_QT_CPP = \
qt/csvmodelwriter.cpp \
qt/guiutil.cpp \
qt/intro.cpp \
+ qt/modaloverlay.cpp \
qt/networkstyle.cpp \
qt/notificator.cpp \
qt/optionsdialog.cpp \
@@ -305,12 +314,9 @@ BITCOIN_QT_CPP = \
qt/trafficgraphwidget.cpp \
qt/utilitydialog.cpp
-if TARGET_WINDOWS
-BITCOIN_QT_CPP += qt/winshutdownmonitor.cpp
-endif
+BITCOIN_QT_WINDOWS_CPP = qt/winshutdownmonitor.cpp
-if ENABLE_WALLET
-BITCOIN_QT_CPP += \
+BITCOIN_QT_WALLET_CPP = \
qt/addressbookpage.cpp \
qt/addresstablemodel.cpp \
qt/askpassphrasedialog.cpp \
@@ -337,6 +343,13 @@ BITCOIN_QT_CPP += \
qt/walletmodel.cpp \
qt/walletmodeltransaction.cpp \
qt/walletview.cpp
+
+BITCOIN_QT_CPP = $(BITCOIN_QT_BASE_CPP)
+if TARGET_WINDOWS
+BITCOIN_QT_CPP += $(BITCOIN_QT_WINDOWS_CPP)
+endif
+if ENABLE_WALLET
+BITCOIN_QT_CPP += $(BITCOIN_QT_WALLET_CPP)
endif
RES_IMAGES =
@@ -405,7 +418,7 @@ $(srcdir)/qt/bitcoinstrings.cpp: $(libbitcoin_server_a_SOURCES) $(libbitcoin_wal
@test -n $(XGETTEXT) || echo "xgettext is required for updating translations"
$(AM_V_GEN) cd $(srcdir); XGETTEXT=$(XGETTEXT) PACKAGE_NAME="$(PACKAGE_NAME)" COPYRIGHT_HOLDERS="$(COPYRIGHT_HOLDERS)" COPYRIGHT_HOLDERS_SUBSTITUTION="$(COPYRIGHT_HOLDERS_SUBSTITUTION)" $(PYTHON) ../share/qt/extract_strings_qt.py $^
-translate: $(srcdir)/qt/bitcoinstrings.cpp $(QT_FORMS_UI) $(QT_FORMS_UI) $(BITCOIN_QT_CPP) $(BITCOIN_QT_H) $(BITCOIN_MM)
+translate: $(srcdir)/qt/bitcoinstrings.cpp $(QT_FORMS_UI) $(QT_FORMS_UI) $(BITCOIN_QT_BASE_CPP) $(BITCOIN_QT_WINDOWS_CPP) $(BITCOIN_QT_WALLET_CPP) $(BITCOIN_QT_H) $(BITCOIN_MM)
@test -n $(LUPDATE) || echo "lupdate is required for updating translations"
$(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(LUPDATE) $^ -locations relative -no-obsolete -ts $(srcdir)/qt/locale/bitcoin_en.ts
diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include
index 813a343ffa..039f8ac547 100644
--- a/src/Makefile.qttest.include
+++ b/src/Makefile.qttest.include
@@ -1,13 +1,22 @@
+# Copyright (c) 2013-2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
bin_PROGRAMS += qt/test/test_bitcoin-qt
TESTS += qt/test/test_bitcoin-qt
-TEST_QT_MOC_CPP = qt/test/moc_uritests.cpp
+TEST_QT_MOC_CPP = \
+ qt/test/moc_compattests.cpp \
+ qt/test/moc_rpcnestedtests.cpp \
+ qt/test/moc_uritests.cpp
if ENABLE_WALLET
TEST_QT_MOC_CPP += qt/test/moc_paymentservertests.cpp
endif
TEST_QT_H = \
+ qt/test/compattests.h \
+ qt/test/rpcnestedtests.h \
qt/test/uritests.h \
qt/test/paymentrequestdata.h \
qt/test/paymentservertests.h
@@ -16,6 +25,8 @@ qt_test_test_bitcoin_qt_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_
$(QT_INCLUDES) $(QT_TEST_INCLUDES) $(PROTOBUF_CFLAGS)
qt_test_test_bitcoin_qt_SOURCES = \
+ qt/test/compattests.cpp \
+ qt/test/rpcnestedtests.cpp \
qt/test/test_main.cpp \
qt/test/uritests.cpp \
$(TEST_QT_H)
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 0748d1a39d..7a5bb07b6c 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -1,5 +1,10 @@
+# Copyright (c) 2013-2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
TESTS += test/test_bitcoin
bin_PROGRAMS += test/test_bitcoin
+noinst_PROGRAMS += test/test_bitcoin_fuzzy
TEST_SRCDIR = test
TEST_BINARY=test/test_bitcoin$(EXEEXT)
@@ -8,7 +13,6 @@ EXTRA_DIST += \
test/bctest.py \
test/bitcoin-util-test.py \
test/data/bitcoin-util-test.json \
- test/data/blanktx.hex \
test/data/tt-delin1-out.hex \
test/data/tt-delout1-out.hex \
test/data/tt-locktime317000-out.hex \
@@ -17,7 +21,7 @@ EXTRA_DIST += \
test/data/txcreate2.hex \
test/data/txcreatedata1.hex \
test/data/txcreatedata2.hex \
- test/data/txcreatesign.hex \
+ test/data/txcreatesignv1.hex \
test/data/txcreatedata_seq0.hex \
test/data/txcreatedata_seq1.hex
@@ -34,6 +38,7 @@ RAW_TEST_FILES =
GENERATED_TEST_FILES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.raw.h)
+# test_bitcoin binary #
BITCOIN_TESTS =\
test/arith_uint256_tests.cpp \
test/scriptnum10.h \
@@ -46,10 +51,11 @@ BITCOIN_TESTS =\
test/bip32_tests.cpp \
test/blockencodings_tests.cpp \
test/bloom_tests.cpp \
- test/Checkpoints_tests.cpp \
+ test/bswap_tests.cpp \
test/coins_tests.cpp \
test/compress_tests.cpp \
test/crypto_tests.cpp \
+ test/cuckoocache_tests.cpp \
test/DoS_tests.cpp \
test/getarg_tests.cpp \
test/hash_tests.cpp \
@@ -81,6 +87,7 @@ BITCOIN_TESTS =\
test/streams_tests.cpp \
test/test_bitcoin.cpp \
test/test_bitcoin.h \
+ test/test_random.h \
test/testutil.cpp \
test/testutil.h \
test/timedata_tests.cpp \
@@ -115,6 +122,25 @@ test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -s
if ENABLE_ZMQ
test_test_bitcoin_LDADD += $(ZMQ_LIBS)
endif
+#
+
+# test_bitcoin_fuzzy binary #
+test_test_bitcoin_fuzzy_SOURCES = test/test_bitcoin_fuzzy.cpp
+test_test_bitcoin_fuzzy_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_test_bitcoin_fuzzy_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_test_bitcoin_fuzzy_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+
+test_test_bitcoin_fuzzy_LDADD = \
+ $(LIBUNIVALUE) \
+ $(LIBBITCOIN_SERVER) \
+ $(LIBBITCOIN_COMMON) \
+ $(LIBBITCOIN_UTIL) \
+ $(LIBBITCOIN_CONSENSUS) \
+ $(LIBBITCOIN_CRYPTO) \
+ $(LIBSECP256K1)
+
+test_test_bitcoin_fuzzy_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+#
nodist_test_test_bitcoin_SOURCES = $(GENERATED_TEST_FILES)
@@ -145,16 +171,10 @@ endif
%.json.h: %.json
@$(MKDIR_P) $(@D)
- @echo "namespace json_tests{" > $@
- @echo "static unsigned const char $(*F)[] = {" >> $@
- @$(HEXDUMP) -v -e '8/1 "0x%02x, "' -e '"\n"' $< | $(SED) -e 's/0x ,//g' >> $@
- @echo "};};" >> $@
- @echo "Generated $@"
-
-%.raw.h: %.raw
- @$(MKDIR_P) $(@D)
- @echo "namespace alert_tests{" > $@
- @echo "static unsigned const char $(*F)[] = {" >> $@
- @$(HEXDUMP) -v -e '8/1 "0x%02x, "' -e '"\n"' $< | $(SED) -e 's/0x ,//g' >> $@
- @echo "};};" >> $@
+ @{ \
+ echo "namespace json_tests{" && \
+ echo "static unsigned const char $(*F)[] = {" && \
+ $(HEXDUMP) -v -e '8/1 "0x%02x, "' -e '"\n"' $< | $(SED) -e 's/0x ,//g' && \
+ echo "};};"; \
+ } > "$@.new" && mv -f "$@.new" "$@"
@echo "Generated $@"
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
index ddf41f92de..23715bbea4 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -103,7 +103,7 @@ bool CBanDB::Read(banmap_t& banSet)
if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp)))
return error("%s: Invalid network magic number", __func__);
- // de-serialize address data into one CAddrMan object
+ // de-serialize ban data
ssBanlist >> banSet;
}
catch (const std::exception& e) {
diff --git a/src/addrdb.h b/src/addrdb.h
index d8c66d872b..339943ca5a 100644
--- a/src/addrdb.h
+++ b/src/addrdb.h
@@ -46,9 +46,8 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(this->nVersion);
- nVersion = this->nVersion;
READWRITE(nCreateTime);
READWRITE(nBanUntil);
READWRITE(banReason);
diff --git a/src/addrman.cpp b/src/addrman.cpp
index cebb1c8e5e..2016523212 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -255,6 +255,11 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP
int nId;
CAddrInfo* pinfo = Find(addr, &nId);
+ // Do not set a penality for a source's self-announcement
+ if (addr == source) {
+ nTimePenalty = 0;
+ }
+
if (pinfo) {
// periodically update nTime
bool fCurrentlyOnline = (GetAdjustedTime() - addr.nTime < 24 * 60 * 60);
@@ -353,8 +358,8 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
int nKBucket = RandomInt(ADDRMAN_TRIED_BUCKET_COUNT);
int nKBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE);
while (vvTried[nKBucket][nKBucketPos] == -1) {
- nKBucket = (nKBucket + insecure_rand()) % ADDRMAN_TRIED_BUCKET_COUNT;
- nKBucketPos = (nKBucketPos + insecure_rand()) % ADDRMAN_BUCKET_SIZE;
+ nKBucket = (nKBucket + insecure_rand.rand32()) % ADDRMAN_TRIED_BUCKET_COUNT;
+ nKBucketPos = (nKBucketPos + insecure_rand.rand32()) % ADDRMAN_BUCKET_SIZE;
}
int nId = vvTried[nKBucket][nKBucketPos];
assert(mapInfo.count(nId) == 1);
@@ -370,8 +375,8 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
int nUBucket = RandomInt(ADDRMAN_NEW_BUCKET_COUNT);
int nUBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE);
while (vvNew[nUBucket][nUBucketPos] == -1) {
- nUBucket = (nUBucket + insecure_rand()) % ADDRMAN_NEW_BUCKET_COUNT;
- nUBucketPos = (nUBucketPos + insecure_rand()) % ADDRMAN_BUCKET_SIZE;
+ nUBucket = (nUBucket + insecure_rand.rand32()) % ADDRMAN_NEW_BUCKET_COUNT;
+ nUBucketPos = (nUBucketPos + insecure_rand.rand32()) % ADDRMAN_BUCKET_SIZE;
}
int nId = vvNew[nUBucket][nUBucketPos];
assert(mapInfo.count(nId) == 1);
diff --git a/src/addrman.h b/src/addrman.h
index 9bab39049d..76e7b210f7 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -58,7 +58,7 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(*(CAddress*)this);
READWRITE(source);
READWRITE(nLastSuccess);
@@ -211,6 +211,9 @@ protected:
//! secret key to randomize bucket select with
uint256 nKey;
+ //! Source of random numbers for randomization in inner loops
+ FastRandomContext insecure_rand;
+
//! Find an entry.
CAddrInfo* Find(const CNetAddr& addr, int *pnId = NULL);
@@ -290,7 +293,7 @@ public:
* very little in common.
*/
template<typename Stream>
- void Serialize(Stream &s, int nType, int nVersionDummy) const
+ void Serialize(Stream &s) const
{
LOCK(cs);
@@ -340,7 +343,7 @@ public:
}
template<typename Stream>
- void Unserialize(Stream& s, int nType, int nVersionDummy)
+ void Unserialize(Stream& s)
{
LOCK(cs);
@@ -445,11 +448,6 @@ public:
Check();
}
- unsigned int GetSerializeSize(int nType, int nVersion) const
- {
- return (CSizeComputer(nType, nVersion) << *this).size();
- }
-
void Clear()
{
std::vector<int>().swap(vRandom);
@@ -484,6 +482,7 @@ public:
//! Return the number of (unique) addresses in all tables.
size_t size() const
{
+ LOCK(cs); // TODO: Cache this in an atomic to avoid this overhead
return vRandom.size();
}
@@ -503,13 +502,11 @@ public:
//! Add a single address.
bool Add(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty = 0)
{
+ LOCK(cs);
bool fRet = false;
- {
- LOCK(cs);
- Check();
- fRet |= Add_(addr, source, nTimePenalty);
- Check();
- }
+ Check();
+ fRet |= Add_(addr, source, nTimePenalty);
+ Check();
if (fRet)
LogPrint("addrman", "Added %s from %s: %i tried, %i new\n", addr.ToStringIPPort(), source.ToString(), nTried, nNew);
return fRet;
@@ -518,14 +515,12 @@ public:
//! Add multiple addresses.
bool Add(const std::vector<CAddress> &vAddr, const CNetAddr& source, int64_t nTimePenalty = 0)
{
+ LOCK(cs);
int nAdd = 0;
- {
- LOCK(cs);
- Check();
- for (std::vector<CAddress>::const_iterator it = vAddr.begin(); it != vAddr.end(); it++)
- nAdd += Add_(*it, source, nTimePenalty) ? 1 : 0;
- Check();
- }
+ Check();
+ for (std::vector<CAddress>::const_iterator it = vAddr.begin(); it != vAddr.end(); it++)
+ nAdd += Add_(*it, source, nTimePenalty) ? 1 : 0;
+ Check();
if (nAdd)
LogPrint("addrman", "Added %i addresses from %s: %i tried, %i new\n", nAdd, source.ToString(), nTried, nNew);
return nAdd > 0;
@@ -534,23 +529,19 @@ public:
//! Mark an entry as accessible.
void Good(const CService &addr, int64_t nTime = GetAdjustedTime())
{
- {
- LOCK(cs);
- Check();
- Good_(addr, nTime);
- Check();
- }
+ LOCK(cs);
+ Check();
+ Good_(addr, nTime);
+ Check();
}
//! Mark an entry as connection attempted to.
void Attempt(const CService &addr, bool fCountFailure, int64_t nTime = GetAdjustedTime())
{
- {
- LOCK(cs);
- Check();
- Attempt_(addr, fCountFailure, nTime);
- Check();
- }
+ LOCK(cs);
+ Check();
+ Attempt_(addr, fCountFailure, nTime);
+ Check();
}
/**
@@ -584,12 +575,10 @@ public:
//! Mark an entry as currently-connected-to.
void Connected(const CService &addr, int64_t nTime = GetAdjustedTime())
{
- {
- LOCK(cs);
- Check();
- Connected_(addr, nTime);
- Check();
- }
+ LOCK(cs);
+ Check();
+ Connected_(addr, nTime);
+ Check();
}
void SetServices(const CService &addr, ServiceFlags nServices)
diff --git a/src/amount.h b/src/amount.h
index 5e52f37f23..ba0c86040f 100644
--- a/src/amount.h
+++ b/src/amount.h
@@ -64,7 +64,7 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(nSatoshisPerK);
}
};
diff --git a/src/arith_uint256.cpp b/src/arith_uint256.cpp
index 2e61363576..a58ad01b5a 100644
--- a/src/arith_uint256.cpp
+++ b/src/arith_uint256.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2014 The Bitcoin developers
+// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/arith_uint256.h b/src/arith_uint256.h
index ba3d620158..5cc52f6e72 100644
--- a/src/arith_uint256.h
+++ b/src/arith_uint256.h
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2015 The Bitcoin developers
+// Copyright (c) 2009-2015 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/base58.cpp b/src/base58.cpp
index d1d60a6f1d..f7768b5a64 100644
--- a/src/base58.cpp
+++ b/src/base58.cpp
@@ -25,12 +25,14 @@ bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch)
psz++;
// Skip and count leading '1's.
int zeroes = 0;
+ int length = 0;
while (*psz == '1') {
zeroes++;
psz++;
}
// Allocate enough space in big-endian base256 representation.
- std::vector<unsigned char> b256(strlen(psz) * 733 / 1000 + 1); // log(58) / log(256), rounded up.
+ int size = strlen(psz) * 733 /1000 + 1; // log(58) / log(256), rounded up.
+ std::vector<unsigned char> b256(size);
// Process the characters.
while (*psz && !isspace(*psz)) {
// Decode base58 character
@@ -39,12 +41,14 @@ bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch)
return false;
// Apply "b256 = b256 * 58 + ch".
int carry = ch - pszBase58;
- for (std::vector<unsigned char>::reverse_iterator it = b256.rbegin(); it != b256.rend(); it++) {
+ int i = 0;
+ for (std::vector<unsigned char>::reverse_iterator it = b256.rbegin(); (carry != 0 || i < length) && (it != b256.rend()); ++it, ++i) {
carry += 58 * (*it);
*it = carry % 256;
carry /= 256;
}
assert(carry == 0);
+ length = i;
psz++;
}
// Skip trailing spaces.
@@ -53,7 +57,7 @@ bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch)
if (*psz != 0)
return false;
// Skip leading zeroes in b256.
- std::vector<unsigned char>::iterator it = b256.begin();
+ std::vector<unsigned char>::iterator it = b256.begin() + (size - length);
while (it != b256.end() && *it == 0)
it++;
// Copy result into output vector.
diff --git a/src/bench/Examples.cpp b/src/bench/Examples.cpp
index b6b020a971..9f35a1ea04 100644
--- a/src/bench/Examples.cpp
+++ b/src/bench/Examples.cpp
@@ -3,7 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "bench.h"
-#include "main.h"
+#include "validation.h"
#include "utiltime.h"
// Sanity test: this should loop ten times, and
diff --git a/src/bench/base58.cpp b/src/bench/base58.cpp
index 1279c3e7df..3319c179bf 100644
--- a/src/bench/base58.cpp
+++ b/src/bench/base58.cpp
@@ -1,10 +1,10 @@
-// Copyright (c) 2016 the Bitcoin Core developers
+// Copyright (c) 2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "bench.h"
-#include "main.h"
+#include "validation.h"
#include "base58.h"
#include <vector>
diff --git a/src/bench/bench.cpp b/src/bench/bench.cpp
index 227546a7a7..af3d152c9a 100644
--- a/src/bench/bench.cpp
+++ b/src/bench/bench.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "bench.h"
+#include "perf.h"
#include <iostream>
#include <iomanip>
@@ -26,7 +27,9 @@ BenchRunner::BenchRunner(std::string name, BenchFunction func)
void
BenchRunner::RunAll(double elapsedTimeForOne)
{
- std::cout << "#Benchmark" << "," << "count" << "," << "min" << "," << "max" << "," << "average" << "\n";
+ perf_init();
+ std::cout << "#Benchmark" << "," << "count" << "," << "min" << "," << "max" << "," << "average" << ","
+ << "min_cycles" << "," << "max_cycles" << "," << "average_cycles" << "\n";
for (std::map<std::string,BenchFunction>::iterator it = benchmarks.begin();
it != benchmarks.end(); ++it) {
@@ -35,6 +38,7 @@ BenchRunner::RunAll(double elapsedTimeForOne)
BenchFunction& func = it->second;
func(state);
}
+ perf_fini();
}
bool State::KeepRunning()
@@ -44,8 +48,10 @@ bool State::KeepRunning()
return true;
}
double now;
+ uint64_t nowCycles;
if (count == 0) {
lastTime = beginTime = now = gettimedouble();
+ lastCycles = beginCycles = nowCycles = perf_cpucycles();
}
else {
now = gettimedouble();
@@ -53,6 +59,13 @@ bool State::KeepRunning()
double elapsedOne = elapsed * countMaskInv;
if (elapsedOne < minTime) minTime = elapsedOne;
if (elapsedOne > maxTime) maxTime = elapsedOne;
+
+ // We only use relative values, so don't have to handle 64-bit wrap-around specially
+ nowCycles = perf_cpucycles();
+ uint64_t elapsedOneCycles = (nowCycles - lastCycles) * countMaskInv;
+ if (elapsedOneCycles < minCycles) minCycles = elapsedOneCycles;
+ if (elapsedOneCycles > maxCycles) maxCycles = elapsedOneCycles;
+
if (elapsed*128 < maxElapsed) {
// If the execution was much too fast (1/128th of maxElapsed), increase the count mask by 8x and restart timing.
// The restart avoids including the overhead of this code in the measurement.
@@ -61,14 +74,20 @@ bool State::KeepRunning()
count = 0;
minTime = std::numeric_limits<double>::max();
maxTime = std::numeric_limits<double>::min();
+ minCycles = std::numeric_limits<uint64_t>::max();
+ maxCycles = std::numeric_limits<uint64_t>::min();
return true;
}
if (elapsed*16 < maxElapsed) {
- countMask = ((countMask<<1)|1) & ((1LL<<60)-1);
- countMaskInv = 1./(countMask+1);
+ uint64_t newCountMask = ((countMask<<1)|1) & ((1LL<<60)-1);
+ if ((count & newCountMask)==0) {
+ countMask = newCountMask;
+ countMaskInv = 1./(countMask+1);
+ }
}
}
lastTime = now;
+ lastCycles = nowCycles;
++count;
if (now - beginTime < maxElapsed) return true; // Keep going
@@ -77,7 +96,9 @@ bool State::KeepRunning()
// Output results
double average = (now-beginTime)/count;
- std::cout << std::fixed << std::setprecision(15) << name << "," << count << "," << minTime << "," << maxTime << "," << average << "\n";
+ int64_t averageCycles = (nowCycles-beginCycles)/count;
+ std::cout << std::fixed << std::setprecision(15) << name << "," << count << "," << minTime << "," << maxTime << "," << average << ","
+ << minCycles << "," << maxCycles << "," << averageCycles << "\n";
return false;
}
diff --git a/src/bench/bench.h b/src/bench/bench.h
index f13b145aaf..caf73e949b 100644
--- a/src/bench/bench.h
+++ b/src/bench/bench.h
@@ -41,12 +41,18 @@ namespace benchmark {
double maxElapsed;
double beginTime;
double lastTime, minTime, maxTime, countMaskInv;
- int64_t count;
- int64_t countMask;
+ uint64_t count;
+ uint64_t countMask;
+ uint64_t beginCycles;
+ uint64_t lastCycles;
+ uint64_t minCycles;
+ uint64_t maxCycles;
public:
State(std::string _name, double _maxElapsed) : name(_name), maxElapsed(_maxElapsed), count(0) {
minTime = std::numeric_limits<double>::max();
maxTime = std::numeric_limits<double>::min();
+ minCycles = std::numeric_limits<uint64_t>::max();
+ maxCycles = std::numeric_limits<uint64_t>::min();
countMask = 1;
countMaskInv = 1./(countMask + 1);
}
diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp
index db1402216d..bd768180c6 100644
--- a/src/bench/bench_bitcoin.cpp
+++ b/src/bench/bench_bitcoin.cpp
@@ -5,7 +5,7 @@
#include "bench.h"
#include "key.h"
-#include "main.h"
+#include "validation.h"
#include "util.h"
int
diff --git a/src/bench/ccoins_caching.cpp b/src/bench/ccoins_caching.cpp
new file mode 100644
index 0000000000..1e8e3d462f
--- /dev/null
+++ b/src/bench/ccoins_caching.cpp
@@ -0,0 +1,87 @@
+// Copyright (c) 2016 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "bench.h"
+#include "coins.h"
+#include "policy/policy.h"
+#include "wallet/crypter.h"
+
+#include <vector>
+
+// FIXME: Dedup with SetupDummyInputs in test/transaction_tests.cpp.
+//
+// Helper: create two dummy transactions, each with
+// two outputs. The first has 11 and 50 CENT outputs
+// paid to a TX_PUBKEY, the second 21 and 22 CENT outputs
+// paid to a TX_PUBKEYHASH.
+//
+static std::vector<CMutableTransaction>
+SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet)
+{
+ std::vector<CMutableTransaction> dummyTransactions;
+ dummyTransactions.resize(2);
+
+ // Add some keys to the keystore:
+ CKey key[4];
+ for (int i = 0; i < 4; i++) {
+ key[i].MakeNewKey(i % 2);
+ keystoreRet.AddKey(key[i]);
+ }
+
+ // Create some dummy input transactions
+ dummyTransactions[0].vout.resize(2);
+ dummyTransactions[0].vout[0].nValue = 11 * CENT;
+ dummyTransactions[0].vout[0].scriptPubKey << ToByteVector(key[0].GetPubKey()) << OP_CHECKSIG;
+ dummyTransactions[0].vout[1].nValue = 50 * CENT;
+ dummyTransactions[0].vout[1].scriptPubKey << ToByteVector(key[1].GetPubKey()) << OP_CHECKSIG;
+ coinsRet.ModifyCoins(dummyTransactions[0].GetHash())->FromTx(dummyTransactions[0], 0);
+
+ dummyTransactions[1].vout.resize(2);
+ dummyTransactions[1].vout[0].nValue = 21 * CENT;
+ dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(key[2].GetPubKey().GetID());
+ dummyTransactions[1].vout[1].nValue = 22 * CENT;
+ dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(key[3].GetPubKey().GetID());
+ coinsRet.ModifyCoins(dummyTransactions[1].GetHash())->FromTx(dummyTransactions[1], 0);
+
+ return dummyTransactions;
+}
+
+// Microbenchmark for simple accesses to a CCoinsViewCache database. Note from
+// laanwj, "replicating the actual usage patterns of the client is hard though,
+// many times micro-benchmarks of the database showed completely different
+// characteristics than e.g. reindex timings. But that's not a requirement of
+// every benchmark."
+// (https://github.com/bitcoin/bitcoin/issues/7883#issuecomment-224807484)
+static void CCoinsCaching(benchmark::State& state)
+{
+ CBasicKeyStore keystore;
+ CCoinsView coinsDummy;
+ CCoinsViewCache coins(&coinsDummy);
+ std::vector<CMutableTransaction> dummyTransactions = SetupDummyInputs(keystore, coins);
+
+ CMutableTransaction t1;
+ t1.vin.resize(3);
+ t1.vin[0].prevout.hash = dummyTransactions[0].GetHash();
+ t1.vin[0].prevout.n = 1;
+ t1.vin[0].scriptSig << std::vector<unsigned char>(65, 0);
+ t1.vin[1].prevout.hash = dummyTransactions[1].GetHash();
+ t1.vin[1].prevout.n = 0;
+ t1.vin[1].scriptSig << std::vector<unsigned char>(65, 0) << std::vector<unsigned char>(33, 4);
+ t1.vin[2].prevout.hash = dummyTransactions[1].GetHash();
+ t1.vin[2].prevout.n = 1;
+ t1.vin[2].scriptSig << std::vector<unsigned char>(65, 0) << std::vector<unsigned char>(33, 4);
+ t1.vout.resize(2);
+ t1.vout[0].nValue = 90 * CENT;
+ t1.vout[0].scriptPubKey << OP_1;
+
+ // Benchmark.
+ while (state.KeepRunning()) {
+ bool success = AreInputsStandard(t1, coins);
+ assert(success);
+ CAmount value = coins.GetValueIn(t1);
+ assert(value == (50 + 21 + 22) * CENT);
+ }
+}
+
+BENCHMARK(CCoinsCaching);
diff --git a/src/bench/checkblock.cpp b/src/bench/checkblock.cpp
new file mode 100644
index 0000000000..230e4ca773
--- /dev/null
+++ b/src/bench/checkblock.cpp
@@ -0,0 +1,56 @@
+// Copyright (c) 2016 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "bench.h"
+
+#include "chainparams.h"
+#include "validation.h"
+#include "streams.h"
+#include "consensus/validation.h"
+
+namespace block_bench {
+#include "bench/data/block413567.raw.h"
+}
+
+// These are the two major time-sinks which happen after we have fully received
+// a block off the wire, but before we can relay the block on to peers using
+// compact block relay.
+
+static void DeserializeBlockTest(benchmark::State& state)
+{
+ CDataStream stream((const char*)block_bench::block413567,
+ (const char*)&block_bench::block413567[sizeof(block_bench::block413567)],
+ SER_NETWORK, PROTOCOL_VERSION);
+ char a;
+ stream.write(&a, 1); // Prevent compaction
+
+ while (state.KeepRunning()) {
+ CBlock block;
+ stream >> block;
+ assert(stream.Rewind(sizeof(block_bench::block413567)));
+ }
+}
+
+static void DeserializeAndCheckBlockTest(benchmark::State& state)
+{
+ CDataStream stream((const char*)block_bench::block413567,
+ (const char*)&block_bench::block413567[sizeof(block_bench::block413567)],
+ SER_NETWORK, PROTOCOL_VERSION);
+ char a;
+ stream.write(&a, 1); // Prevent compaction
+
+ Consensus::Params params = Params(CBaseChainParams::MAIN).GetConsensus();
+
+ while (state.KeepRunning()) {
+ CBlock block; // Note that CBlock caches its checked state, so we need to recreate it here
+ stream >> block;
+ assert(stream.Rewind(sizeof(block_bench::block413567)));
+
+ CValidationState validationState;
+ assert(CheckBlock(block, validationState, params));
+ }
+}
+
+BENCHMARK(DeserializeBlockTest);
+BENCHMARK(DeserializeAndCheckBlockTest);
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
new file mode 100644
index 0000000000..cacead4a59
--- /dev/null
+++ b/src/bench/coin_selection.cpp
@@ -0,0 +1,62 @@
+// Copyright (c) 2012-2015 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "bench.h"
+#include "wallet/wallet.h"
+
+#include <boost/foreach.hpp>
+#include <set>
+
+using namespace std;
+
+static void addCoin(const CAmount& nValue, const CWallet& wallet, vector<COutput>& vCoins)
+{
+ int nInput = 0;
+
+ static int nextLockTime = 0;
+ CMutableTransaction tx;
+ tx.nLockTime = nextLockTime++; // so all transactions get different hashes
+ tx.vout.resize(nInput + 1);
+ tx.vout[nInput].nValue = nValue;
+ CWalletTx* wtx = new CWalletTx(&wallet, MakeTransactionRef(std::move(tx)));
+
+ int nAge = 6 * 24;
+ COutput output(wtx, nInput, nAge, true, true);
+ vCoins.push_back(output);
+}
+
+// Simple benchmark for wallet coin selection. Note that it maybe be necessary
+// to build up more complicated scenarios in order to get meaningful
+// measurements of performance. From laanwj, "Wallet coin selection is probably
+// the hardest, as you need a wider selection of scenarios, just testing the
+// same one over and over isn't too useful. Generating random isn't useful
+// either for measurements."
+// (https://github.com/bitcoin/bitcoin/issues/7883#issuecomment-224807484)
+static void CoinSelection(benchmark::State& state)
+{
+ const CWallet wallet;
+ vector<COutput> vCoins;
+ LOCK(wallet.cs_wallet);
+
+ while (state.KeepRunning()) {
+ // Empty wallet.
+ BOOST_FOREACH (COutput output, vCoins)
+ delete output.tx;
+ vCoins.clear();
+
+ // Add coins.
+ for (int i = 0; i < 1000; i++)
+ addCoin(1000 * COIN, wallet, vCoins);
+ addCoin(3 * COIN, wallet, vCoins);
+
+ set<pair<const CWalletTx*, unsigned int> > setCoinsRet;
+ CAmount nValueRet;
+ bool success = wallet.SelectCoinsMinConf(1003 * COIN, 1, 6, 0, vCoins, setCoinsRet, nValueRet);
+ assert(success);
+ assert(nValueRet == 1003 * COIN);
+ assert(setCoinsRet.size() == 2);
+ }
+}
+
+BENCHMARK(CoinSelection);
diff --git a/src/bench/crypto_hash.cpp b/src/bench/crypto_hash.cpp
index 168006154f..737d3572ae 100644
--- a/src/bench/crypto_hash.cpp
+++ b/src/bench/crypto_hash.cpp
@@ -22,7 +22,7 @@ static void RIPEMD160(benchmark::State& state)
uint8_t hash[CRIPEMD160::OUTPUT_SIZE];
std::vector<uint8_t> in(BUFFER_SIZE,0);
while (state.KeepRunning())
- CRIPEMD160().Write(begin_ptr(in), in.size()).Finalize(hash);
+ CRIPEMD160().Write(in.data(), in.size()).Finalize(hash);
}
static void SHA1(benchmark::State& state)
@@ -30,7 +30,7 @@ static void SHA1(benchmark::State& state)
uint8_t hash[CSHA1::OUTPUT_SIZE];
std::vector<uint8_t> in(BUFFER_SIZE,0);
while (state.KeepRunning())
- CSHA1().Write(begin_ptr(in), in.size()).Finalize(hash);
+ CSHA1().Write(in.data(), in.size()).Finalize(hash);
}
static void SHA256(benchmark::State& state)
@@ -38,7 +38,7 @@ static void SHA256(benchmark::State& state)
uint8_t hash[CSHA256::OUTPUT_SIZE];
std::vector<uint8_t> in(BUFFER_SIZE,0);
while (state.KeepRunning())
- CSHA256().Write(begin_ptr(in), in.size()).Finalize(hash);
+ CSHA256().Write(in.data(), in.size()).Finalize(hash);
}
static void SHA256_32b(benchmark::State& state)
@@ -46,7 +46,7 @@ static void SHA256_32b(benchmark::State& state)
std::vector<uint8_t> in(32,0);
while (state.KeepRunning()) {
for (int i = 0; i < 1000000; i++) {
- CSHA256().Write(begin_ptr(in), in.size()).Finalize(&in[0]);
+ CSHA256().Write(in.data(), in.size()).Finalize(&in[0]);
}
}
}
@@ -56,7 +56,7 @@ static void SHA512(benchmark::State& state)
uint8_t hash[CSHA512::OUTPUT_SIZE];
std::vector<uint8_t> in(BUFFER_SIZE,0);
while (state.KeepRunning())
- CSHA512().Write(begin_ptr(in), in.size()).Finalize(hash);
+ CSHA512().Write(in.data(), in.size()).Finalize(hash);
}
static void SipHash_32b(benchmark::State& state)
diff --git a/src/bench/data/block413567.raw b/src/bench/data/block413567.raw
new file mode 100644
index 0000000000..67d2d5d382
--- /dev/null
+++ b/src/bench/data/block413567.raw
Binary files differ
diff --git a/src/bench/lockedpool.cpp b/src/bench/lockedpool.cpp
new file mode 100644
index 0000000000..5df5b1ac6e
--- /dev/null
+++ b/src/bench/lockedpool.cpp
@@ -0,0 +1,47 @@
+// Copyright (c) 2016 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "bench.h"
+
+#include "support/lockedpool.h"
+
+#include <iostream>
+#include <vector>
+
+#define ASIZE 2048
+#define BITER 5000
+#define MSIZE 2048
+
+static void LockedPool(benchmark::State& state)
+{
+ void *synth_base = reinterpret_cast<void*>(0x08000000);
+ const size_t synth_size = 1024*1024;
+ Arena b(synth_base, synth_size, 16);
+
+ std::vector<void*> addr;
+ for (int x=0; x<ASIZE; ++x)
+ addr.push_back(0);
+ uint32_t s = 0x12345678;
+ while (state.KeepRunning()) {
+ for (int x=0; x<BITER; ++x) {
+ int idx = s & (addr.size()-1);
+ if (s & 0x80000000) {
+ b.free(addr[idx]);
+ addr[idx] = 0;
+ } else if(!addr[idx]) {
+ addr[idx] = b.alloc((s >> 16) & (MSIZE-1));
+ }
+ bool lsb = s & 1;
+ s >>= 1;
+ if (lsb)
+ s ^= 0xf00f00f0; // LFSR period 0xf7ffffe0
+ }
+ }
+ for (void *ptr: addr)
+ b.free(ptr);
+ addr.clear();
+}
+
+BENCHMARK(LockedPool);
+
diff --git a/src/bench/mempool_eviction.cpp b/src/bench/mempool_eviction.cpp
new file mode 100644
index 0000000000..0ae69c75fc
--- /dev/null
+++ b/src/bench/mempool_eviction.cpp
@@ -0,0 +1,115 @@
+// Copyright (c) 2011-2015 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "bench.h"
+#include "policy/policy.h"
+#include "txmempool.h"
+
+#include <list>
+#include <vector>
+
+static void AddTx(const CTransaction& tx, const CAmount& nFee, CTxMemPool& pool)
+{
+ int64_t nTime = 0;
+ double dPriority = 10.0;
+ unsigned int nHeight = 1;
+ bool spendsCoinbase = false;
+ unsigned int sigOpCost = 4;
+ LockPoints lp;
+ pool.addUnchecked(tx.GetHash(), CTxMemPoolEntry(
+ tx, nFee, nTime, dPriority, nHeight, pool.HasNoInputsOf(tx),
+ tx.GetValueOut(), spendsCoinbase, sigOpCost, lp));
+}
+
+// Right now this is only testing eviction performance in an extremely small
+// mempool. Code needs to be written to generate a much wider variety of
+// unique transactions for a more meaningful performance measurement.
+static void MempoolEviction(benchmark::State& state)
+{
+ CMutableTransaction tx1 = CMutableTransaction();
+ tx1.vin.resize(1);
+ tx1.vin[0].scriptSig = CScript() << OP_1;
+ tx1.vout.resize(1);
+ tx1.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL;
+ tx1.vout[0].nValue = 10 * COIN;
+
+ CMutableTransaction tx2 = CMutableTransaction();
+ tx2.vin.resize(1);
+ tx2.vin[0].scriptSig = CScript() << OP_2;
+ tx2.vout.resize(1);
+ tx2.vout[0].scriptPubKey = CScript() << OP_2 << OP_EQUAL;
+ tx2.vout[0].nValue = 10 * COIN;
+
+ CMutableTransaction tx3 = CMutableTransaction();
+ tx3.vin.resize(1);
+ tx3.vin[0].prevout = COutPoint(tx2.GetHash(), 0);
+ tx3.vin[0].scriptSig = CScript() << OP_2;
+ tx3.vout.resize(1);
+ tx3.vout[0].scriptPubKey = CScript() << OP_3 << OP_EQUAL;
+ tx3.vout[0].nValue = 10 * COIN;
+
+ CMutableTransaction tx4 = CMutableTransaction();
+ tx4.vin.resize(2);
+ tx4.vin[0].prevout.SetNull();
+ tx4.vin[0].scriptSig = CScript() << OP_4;
+ tx4.vin[1].prevout.SetNull();
+ tx4.vin[1].scriptSig = CScript() << OP_4;
+ tx4.vout.resize(2);
+ tx4.vout[0].scriptPubKey = CScript() << OP_4 << OP_EQUAL;
+ tx4.vout[0].nValue = 10 * COIN;
+ tx4.vout[1].scriptPubKey = CScript() << OP_4 << OP_EQUAL;
+ tx4.vout[1].nValue = 10 * COIN;
+
+ CMutableTransaction tx5 = CMutableTransaction();
+ tx5.vin.resize(2);
+ tx5.vin[0].prevout = COutPoint(tx4.GetHash(), 0);
+ tx5.vin[0].scriptSig = CScript() << OP_4;
+ tx5.vin[1].prevout.SetNull();
+ tx5.vin[1].scriptSig = CScript() << OP_5;
+ tx5.vout.resize(2);
+ tx5.vout[0].scriptPubKey = CScript() << OP_5 << OP_EQUAL;
+ tx5.vout[0].nValue = 10 * COIN;
+ tx5.vout[1].scriptPubKey = CScript() << OP_5 << OP_EQUAL;
+ tx5.vout[1].nValue = 10 * COIN;
+
+ CMutableTransaction tx6 = CMutableTransaction();
+ tx6.vin.resize(2);
+ tx6.vin[0].prevout = COutPoint(tx4.GetHash(), 1);
+ tx6.vin[0].scriptSig = CScript() << OP_4;
+ tx6.vin[1].prevout.SetNull();
+ tx6.vin[1].scriptSig = CScript() << OP_6;
+ tx6.vout.resize(2);
+ tx6.vout[0].scriptPubKey = CScript() << OP_6 << OP_EQUAL;
+ tx6.vout[0].nValue = 10 * COIN;
+ tx6.vout[1].scriptPubKey = CScript() << OP_6 << OP_EQUAL;
+ tx6.vout[1].nValue = 10 * COIN;
+
+ CMutableTransaction tx7 = CMutableTransaction();
+ tx7.vin.resize(2);
+ tx7.vin[0].prevout = COutPoint(tx5.GetHash(), 0);
+ tx7.vin[0].scriptSig = CScript() << OP_5;
+ tx7.vin[1].prevout = COutPoint(tx6.GetHash(), 0);
+ tx7.vin[1].scriptSig = CScript() << OP_6;
+ tx7.vout.resize(2);
+ tx7.vout[0].scriptPubKey = CScript() << OP_7 << OP_EQUAL;
+ tx7.vout[0].nValue = 10 * COIN;
+ tx7.vout[1].scriptPubKey = CScript() << OP_7 << OP_EQUAL;
+ tx7.vout[1].nValue = 10 * COIN;
+
+ CTxMemPool pool(CFeeRate(1000));
+
+ while (state.KeepRunning()) {
+ AddTx(tx1, 10000LL, pool);
+ AddTx(tx2, 5000LL, pool);
+ AddTx(tx3, 20000LL, pool);
+ AddTx(tx4, 7000LL, pool);
+ AddTx(tx5, 1000LL, pool);
+ AddTx(tx6, 1100LL, pool);
+ AddTx(tx7, 9000LL, pool);
+ pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4);
+ pool.TrimToSize(GetVirtualTransactionSize(tx1));
+ }
+}
+
+BENCHMARK(MempoolEviction);
diff --git a/src/bench/perf.cpp b/src/bench/perf.cpp
new file mode 100644
index 0000000000..1f43e5d3ac
--- /dev/null
+++ b/src/bench/perf.cpp
@@ -0,0 +1,53 @@
+// Copyright (c) 2016 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "perf.h"
+
+#if defined(__i386__) || defined(__x86_64__)
+
+/* These architectures support quering the cycle counter
+ * from user space, no need for any syscall overhead.
+ */
+void perf_init(void) { }
+void perf_fini(void) { }
+
+#elif defined(__linux__)
+
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <linux/perf_event.h>
+
+static int fd = -1;
+static struct perf_event_attr attr;
+
+void perf_init(void)
+{
+ attr.type = PERF_TYPE_HARDWARE;
+ attr.config = PERF_COUNT_HW_CPU_CYCLES;
+ fd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0);
+}
+
+void perf_fini(void)
+{
+ if (fd != -1) {
+ close(fd);
+ }
+}
+
+uint64_t perf_cpucycles(void)
+{
+ uint64_t result = 0;
+ if (fd == -1 || read(fd, &result, sizeof(result)) < (ssize_t)sizeof(result)) {
+ return 0;
+ }
+ return result;
+}
+
+#else /* Unhandled platform */
+
+void perf_init(void) { }
+void perf_fini(void) { }
+uint64_t perf_cpucycles(void) { return 0; }
+
+#endif
diff --git a/src/bench/perf.h b/src/bench/perf.h
new file mode 100644
index 0000000000..681bd0c8a2
--- /dev/null
+++ b/src/bench/perf.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2016 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+/** Functions for measurement of CPU cycles */
+#ifndef H_PERF
+#define H_PERF
+
+#include <stdint.h>
+
+#if defined(__i386__)
+
+static inline uint64_t perf_cpucycles(void)
+{
+ uint64_t x;
+ __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
+ return x;
+}
+
+#elif defined(__x86_64__)
+
+static inline uint64_t perf_cpucycles(void)
+{
+ uint32_t hi, lo;
+ __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
+ return ((uint64_t)lo)|(((uint64_t)hi)<<32);
+}
+#else
+
+uint64_t perf_cpucycles(void);
+
+#endif
+
+void perf_init(void);
+void perf_fini(void);
+
+#endif // H_PERF
diff --git a/src/bench/verify_script.cpp b/src/bench/verify_script.cpp
new file mode 100644
index 0000000000..91b8c44472
--- /dev/null
+++ b/src/bench/verify_script.cpp
@@ -0,0 +1,103 @@
+// Copyright (c) 2016 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "bench.h"
+#include "key.h"
+#if defined(HAVE_CONSENSUS_LIB)
+#include "script/bitcoinconsensus.h"
+#endif
+#include "script/script.h"
+#include "script/sign.h"
+#include "streams.h"
+
+// FIXME: Dedup with BuildCreditingTransaction in test/script_tests.cpp.
+static CMutableTransaction BuildCreditingTransaction(const CScript& scriptPubKey)
+{
+ CMutableTransaction txCredit;
+ txCredit.nVersion = 1;
+ txCredit.nLockTime = 0;
+ txCredit.vin.resize(1);
+ txCredit.vout.resize(1);
+ txCredit.vin[0].prevout.SetNull();
+ txCredit.vin[0].scriptSig = CScript() << CScriptNum(0) << CScriptNum(0);
+ txCredit.vin[0].nSequence = CTxIn::SEQUENCE_FINAL;
+ txCredit.vout[0].scriptPubKey = scriptPubKey;
+ txCredit.vout[0].nValue = 1;
+
+ return txCredit;
+}
+
+// FIXME: Dedup with BuildSpendingTransaction in test/script_tests.cpp.
+static CMutableTransaction BuildSpendingTransaction(const CScript& scriptSig, const CMutableTransaction& txCredit)
+{
+ CMutableTransaction txSpend;
+ txSpend.nVersion = 1;
+ txSpend.nLockTime = 0;
+ txSpend.vin.resize(1);
+ txSpend.vout.resize(1);
+ txSpend.wit.vtxinwit.resize(1);
+ txSpend.vin[0].prevout.hash = txCredit.GetHash();
+ txSpend.vin[0].prevout.n = 0;
+ txSpend.vin[0].scriptSig = scriptSig;
+ txSpend.vin[0].nSequence = CTxIn::SEQUENCE_FINAL;
+ txSpend.vout[0].scriptPubKey = CScript();
+ txSpend.vout[0].nValue = txCredit.vout[0].nValue;
+
+ return txSpend;
+}
+
+// Microbenchmark for verification of a basic P2WPKH script. Can be easily
+// modified to measure performance of other types of scripts.
+static void VerifyScriptBench(benchmark::State& state)
+{
+ const int flags = SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH;
+ const int witnessversion = 0;
+
+ // Keypair.
+ CKey key;
+ const unsigned char vchKey[32] = {0, 0, 0, 0, 0, 0, 0, 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};
+ key.Set(vchKey, vchKey + 32, false);
+ CPubKey pubkey = key.GetPubKey();
+ uint160 pubkeyHash;
+ CHash160().Write(pubkey.begin(), pubkey.size()).Finalize(pubkeyHash.begin());
+
+ // Script.
+ CScript scriptPubKey = CScript() << witnessversion << ToByteVector(pubkeyHash);
+ CScript scriptSig;
+ CScript witScriptPubkey = CScript() << OP_DUP << OP_HASH160 << ToByteVector(pubkeyHash) << OP_EQUALVERIFY << OP_CHECKSIG;
+ CTransaction txCredit = BuildCreditingTransaction(scriptPubKey);
+ CMutableTransaction txSpend = BuildSpendingTransaction(scriptSig, txCredit);
+ CScriptWitness& witness = txSpend.wit.vtxinwit[0].scriptWitness;
+ witness.stack.emplace_back();
+ key.Sign(SignatureHash(witScriptPubkey, txSpend, 0, SIGHASH_ALL, txCredit.vout[0].nValue, SIGVERSION_WITNESS_V0), witness.stack.back(), 0);
+ witness.stack.back().push_back(static_cast<unsigned char>(SIGHASH_ALL));
+ witness.stack.push_back(ToByteVector(pubkey));
+
+ // Benchmark.
+ while (state.KeepRunning()) {
+ ScriptError err;
+ bool success = VerifyScript(
+ txSpend.vin[0].scriptSig,
+ txCredit.vout[0].scriptPubKey,
+ &txSpend.wit.vtxinwit[0].scriptWitness,
+ flags,
+ MutableTransactionSignatureChecker(&txSpend, 0, txCredit.vout[0].nValue),
+ &err);
+ assert(err == SCRIPT_ERR_OK);
+ assert(success);
+
+#if defined(HAVE_CONSENSUS_LIB)
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ stream << txSpend;
+ int csuccess = bitcoinconsensus_verify_script_with_amount(
+ txCredit.vout[0].scriptPubKey.data(),
+ txCredit.vout[0].scriptPubKey.size(),
+ txCredit.vout[0].nValue,
+ (const unsigned char*)&stream[0], stream.size(), 0, flags, nullptr);
+ assert(csuccess == 1);
+#endif
+ }
+}
+
+BENCHMARK(VerifyScriptBench);
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index a04101d3ed..596cc8eada 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -24,14 +24,13 @@
#include <univalue.h>
-using namespace std;
-
static const char DEFAULT_RPCCONNECT[] = "127.0.0.1";
static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;
+static const int CONTINUE_EXECUTION=-1;
std::string HelpMessageCli()
{
- string strUsage;
+ std::string strUsage;
strUsage += HelpMessageGroup(_("Options:"));
strUsage += HelpMessageOpt("-?", _("This help message"));
strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME));
@@ -67,7 +66,11 @@ public:
};
-static bool AppInitRPC(int argc, char* argv[])
+//
+// This function returns either one of EXIT_ codes when it's expected to stop the process or
+// CONTINUE_EXECUTION when it's expected to continue further.
+//
+static int AppInitRPC(int argc, char* argv[])
{
//
// Parameters
@@ -85,48 +88,77 @@ static bool AppInitRPC(int argc, char* argv[])
}
fprintf(stdout, "%s", strUsage.c_str());
- return false;
+ if (argc < 2) {
+ fprintf(stderr, "Error: too few parameters\n");
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
}
if (!boost::filesystem::is_directory(GetDataDir(false))) {
fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str());
- return false;
+ return EXIT_FAILURE;
}
try {
- ReadConfigFile(mapArgs, mapMultiArgs);
+ ReadConfigFile(GetArg("-conf", BITCOIN_CONF_FILENAME), mapArgs, mapMultiArgs);
} catch (const std::exception& e) {
fprintf(stderr,"Error reading configuration file: %s\n", e.what());
- return false;
+ return EXIT_FAILURE;
}
// Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause)
try {
SelectBaseParams(ChainNameFromCommandLine());
} catch (const std::exception& e) {
fprintf(stderr, "Error: %s\n", e.what());
- return false;
+ return EXIT_FAILURE;
}
if (GetBoolArg("-rpcssl", false))
{
fprintf(stderr, "Error: SSL mode for RPC (-rpcssl) is no longer supported.\n");
- return false;
+ return EXIT_FAILURE;
}
- return true;
+ return CONTINUE_EXECUTION;
}
/** Reply structure for request_done to fill in */
struct HTTPReply
{
+ HTTPReply(): status(0), error(-1) {}
+
int status;
+ int error;
std::string body;
};
+const char *http_errorstring(int code)
+{
+ switch(code) {
+#if LIBEVENT_VERSION_NUMBER >= 0x02010300
+ case EVREQ_HTTP_TIMEOUT:
+ return "timeout reached";
+ case EVREQ_HTTP_EOF:
+ return "EOF reached";
+ case EVREQ_HTTP_INVALID_HEADER:
+ return "error while reading header, or invalid header";
+ case EVREQ_HTTP_BUFFER_ERROR:
+ return "error encountered while reading or writing";
+ case EVREQ_HTTP_REQUEST_CANCEL:
+ return "request was canceled";
+ case EVREQ_HTTP_DATA_TOO_LONG:
+ return "response body is larger than allowed";
+#endif
+ default:
+ return "unknown";
+ }
+}
+
static void http_request_done(struct evhttp_request *req, void *ctx)
{
HTTPReply *reply = static_cast<HTTPReply*>(ctx);
if (req == NULL) {
- /* If req is NULL, it means an error occurred while connecting, but
- * I'm not sure how to find out which one. We also don't really care.
+ /* If req is NULL, it means an error occurred while connecting: the
+ * error code will have been passed to http_error_cb.
*/
reply->status = 0;
return;
@@ -145,7 +177,15 @@ static void http_request_done(struct evhttp_request *req, void *ctx)
}
}
-UniValue CallRPC(const string& strMethod, const UniValue& params)
+#if LIBEVENT_VERSION_NUMBER >= 0x02010300
+static void http_error_cb(enum evhttp_request_error err, void *ctx)
+{
+ HTTPReply *reply = static_cast<HTTPReply*>(ctx);
+ reply->error = err;
+}
+#endif
+
+UniValue CallRPC(const std::string& strMethod, const UniValue& params)
{
std::string host = GetArg("-rpcconnect", DEFAULT_RPCCONNECT);
int port = GetArg("-rpcport", BaseParams().RPCPort());
@@ -153,27 +193,30 @@ UniValue CallRPC(const string& strMethod, const UniValue& params)
// Create event base
struct event_base *base = event_base_new(); // TODO RAII
if (!base)
- throw runtime_error("cannot create event_base");
+ throw std::runtime_error("cannot create event_base");
// Synchronously look up hostname
struct evhttp_connection *evcon = evhttp_connection_base_new(base, NULL, host.c_str(), port); // TODO RAII
if (evcon == NULL)
- throw runtime_error("create connection failed");
+ throw std::runtime_error("create connection failed");
evhttp_connection_set_timeout(evcon, GetArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT));
HTTPReply response;
struct evhttp_request *req = evhttp_request_new(http_request_done, (void*)&response); // TODO RAII
if (req == NULL)
- throw runtime_error("create http request failed");
+ throw std::runtime_error("create http request failed");
+#if LIBEVENT_VERSION_NUMBER >= 0x02010300
+ evhttp_request_set_error_cb(req, http_error_cb);
+#endif
// Get credentials
std::string strRPCUserColonPass;
if (mapArgs["-rpcpassword"] == "") {
// Try fall back to cookie-based authentication if no password is provided
if (!GetAuthCookie(&strRPCUserColonPass)) {
- throw runtime_error(strprintf(
+ throw std::runtime_error(strprintf(
_("Could not locate RPC credentials. No authentication cookie could be found, and no rpcpassword is set in the configuration file (%s)"),
- GetConfigFile().string().c_str()));
+ GetConfigFile(GetArg("-conf", BITCOIN_CONF_FILENAME)).string().c_str()));
}
} else {
@@ -187,7 +230,7 @@ UniValue CallRPC(const string& strMethod, const UniValue& params)
evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str());
// Attach request data
- std::string strRequest = JSONRPCRequest(strMethod, params, 1);
+ std::string strRequest = JSONRPCRequestObj(strMethod, params, 1).write() + "\n";
struct evbuffer * output_buffer = evhttp_request_get_output_buffer(req);
assert(output_buffer);
evbuffer_add(output_buffer, strRequest.data(), strRequest.size());
@@ -204,28 +247,28 @@ UniValue CallRPC(const string& strMethod, const UniValue& params)
event_base_free(base);
if (response.status == 0)
- throw CConnectionFailed("couldn't connect to server");
+ throw CConnectionFailed(strprintf("couldn't connect to server: %s (code %d)\n(make sure server is running and you are connecting to the correct RPC port)", http_errorstring(response.error), response.error));
else if (response.status == HTTP_UNAUTHORIZED)
- throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
+ throw std::runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR)
- throw runtime_error(strprintf("server returned HTTP error %d", response.status));
+ throw std::runtime_error(strprintf("server returned HTTP error %d", response.status));
else if (response.body.empty())
- throw runtime_error("no response from server");
+ throw std::runtime_error("no response from server");
// Parse reply
UniValue valReply(UniValue::VSTR);
if (!valReply.read(response.body))
- throw runtime_error("couldn't parse reply from server");
+ throw std::runtime_error("couldn't parse reply from server");
const UniValue& reply = valReply.get_obj();
if (reply.empty())
- throw runtime_error("expected reply to have result, error and id properties");
+ throw std::runtime_error("expected reply to have result, error and id properties");
return reply;
}
int CommandLineRPC(int argc, char *argv[])
{
- string strPrint;
+ std::string strPrint;
int nRet = 0;
try {
// Skip switches
@@ -241,7 +284,7 @@ int CommandLineRPC(int argc, char *argv[])
args.push_back(line);
}
if (args.size() < 1)
- throw runtime_error("too few parameters (need at least command)");
+ throw std::runtime_error("too few parameters (need at least command)");
std::string strMethod = args[0];
UniValue params = RPCConvertValues(strMethod, std::vector<std::string>(args.begin()+1, args.end()));
@@ -295,7 +338,7 @@ int CommandLineRPC(int argc, char *argv[])
throw;
}
catch (const std::exception& e) {
- strPrint = string("error: ") + e.what();
+ strPrint = std::string("error: ") + e.what();
nRet = EXIT_FAILURE;
}
catch (...) {
@@ -318,8 +361,9 @@ int main(int argc, char* argv[])
}
try {
- if(!AppInitRPC(argc, argv))
- return EXIT_FAILURE;
+ int ret = AppInitRPC(argc, argv);
+ if (ret != CONTINUE_EXECUTION)
+ return ret;
}
catch (const std::exception& e) {
PrintExceptionContinue(&e, "AppInitRPC()");
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index cb863bda19..1ed1449f03 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -26,12 +26,15 @@
#include <boost/algorithm/string.hpp>
#include <boost/assign/list_of.hpp>
-using namespace std;
-
static bool fCreateBlank;
-static map<string,UniValue> registers;
-
-static bool AppInitRawTx(int argc, char* argv[])
+static std::map<std::string,UniValue> registers;
+static const int CONTINUE_EXECUTION=-1;
+
+//
+// This function returns either one of EXIT_ codes when it's expected to stop the process or
+// CONTINUE_EXECUTION when it's expected to continue further.
+//
+static int AppInitRawTx(int argc, char* argv[])
{
//
// Parameters
@@ -43,7 +46,7 @@ static bool AppInitRawTx(int argc, char* argv[])
SelectParams(ChainNameFromCommandLine());
} catch (const std::exception& e) {
fprintf(stderr, "Error: %s\n", e.what());
- return false;
+ return EXIT_FAILURE;
}
fCreateBlank = GetBoolArg("-create", false);
@@ -89,57 +92,61 @@ static bool AppInitRawTx(int argc, char* argv[])
strUsage += HelpMessageOpt("set=NAME:JSON-STRING", _("Set register NAME to given JSON-STRING"));
fprintf(stdout, "%s", strUsage.c_str());
- return false;
+ if (argc < 2) {
+ fprintf(stderr, "Error: too few parameters\n");
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
}
- return true;
+ return CONTINUE_EXECUTION;
}
-static void RegisterSetJson(const string& key, const string& rawJson)
+static void RegisterSetJson(const std::string& key, const std::string& rawJson)
{
UniValue val;
if (!val.read(rawJson)) {
- string strErr = "Cannot parse JSON for key " + key;
- throw runtime_error(strErr);
+ std::string strErr = "Cannot parse JSON for key " + key;
+ throw std::runtime_error(strErr);
}
registers[key] = val;
}
-static void RegisterSet(const string& strInput)
+static void RegisterSet(const std::string& strInput)
{
// separate NAME:VALUE in string
size_t pos = strInput.find(':');
- if ((pos == string::npos) ||
+ if ((pos == std::string::npos) ||
(pos == 0) ||
(pos == (strInput.size() - 1)))
- throw runtime_error("Register input requires NAME:VALUE");
+ throw std::runtime_error("Register input requires NAME:VALUE");
- string key = strInput.substr(0, pos);
- string valStr = strInput.substr(pos + 1, string::npos);
+ std::string key = strInput.substr(0, pos);
+ std::string valStr = strInput.substr(pos + 1, std::string::npos);
RegisterSetJson(key, valStr);
}
-static void RegisterLoad(const string& strInput)
+static void RegisterLoad(const std::string& strInput)
{
// separate NAME:FILENAME in string
size_t pos = strInput.find(':');
- if ((pos == string::npos) ||
+ if ((pos == std::string::npos) ||
(pos == 0) ||
(pos == (strInput.size() - 1)))
- throw runtime_error("Register load requires NAME:FILENAME");
+ throw std::runtime_error("Register load requires NAME:FILENAME");
- string key = strInput.substr(0, pos);
- string filename = strInput.substr(pos + 1, string::npos);
+ std::string key = strInput.substr(0, pos);
+ std::string filename = strInput.substr(pos + 1, std::string::npos);
FILE *f = fopen(filename.c_str(), "r");
if (!f) {
- string strErr = "Cannot open file " + filename;
- throw runtime_error(strErr);
+ std::string strErr = "Cannot open file " + filename;
+ throw std::runtime_error(strErr);
}
// load file chunks into one big buffer
- string valStr;
+ std::string valStr;
while ((!feof(f)) && (!ferror(f))) {
char buf[4096];
int bread = fread(buf, 1, sizeof(buf), f);
@@ -153,55 +160,55 @@ static void RegisterLoad(const string& strInput)
fclose(f);
if (error) {
- string strErr = "Error reading file " + filename;
- throw runtime_error(strErr);
+ std::string strErr = "Error reading file " + filename;
+ throw std::runtime_error(strErr);
}
// evaluate as JSON buffer register
RegisterSetJson(key, valStr);
}
-static void MutateTxVersion(CMutableTransaction& tx, const string& cmdVal)
+static void MutateTxVersion(CMutableTransaction& tx, const std::string& cmdVal)
{
int64_t newVersion = atoi64(cmdVal);
- if (newVersion < 1 || newVersion > CTransaction::CURRENT_VERSION)
- throw runtime_error("Invalid TX version requested");
+ if (newVersion < 1 || newVersion > CTransaction::MAX_STANDARD_VERSION)
+ throw std::runtime_error("Invalid TX version requested");
tx.nVersion = (int) newVersion;
}
-static void MutateTxLocktime(CMutableTransaction& tx, const string& cmdVal)
+static void MutateTxLocktime(CMutableTransaction& tx, const std::string& cmdVal)
{
int64_t newLocktime = atoi64(cmdVal);
if (newLocktime < 0LL || newLocktime > 0xffffffffLL)
- throw runtime_error("Invalid TX locktime requested");
+ throw std::runtime_error("Invalid TX locktime requested");
tx.nLockTime = (unsigned int) newLocktime;
}
-static void MutateTxAddInput(CMutableTransaction& tx, const string& strInput)
+static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInput)
{
std::vector<std::string> vStrInputParts;
boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
// separate TXID:VOUT in string
if (vStrInputParts.size()<2)
- throw runtime_error("TX input missing separator");
+ throw std::runtime_error("TX input missing separator");
// extract and validate TXID
- string strTxid = vStrInputParts[0];
+ std::string strTxid = vStrInputParts[0];
if ((strTxid.size() != 64) || !IsHex(strTxid))
- throw runtime_error("invalid TX input txid");
+ throw std::runtime_error("invalid TX input txid");
uint256 txid(uint256S(strTxid));
static const unsigned int minTxOutSz = 9;
static const unsigned int maxVout = MAX_BLOCK_BASE_SIZE / minTxOutSz;
// extract and validate vout
- string strVout = vStrInputParts[1];
+ std::string strVout = vStrInputParts[1];
int vout = atoi(strVout);
if ((vout < 0) || (vout > (int)maxVout))
- throw runtime_error("invalid TX input vout");
+ throw std::runtime_error("invalid TX input vout");
// extract the optional sequence number
uint32_t nSequenceIn=std::numeric_limits<unsigned int>::max();
@@ -213,26 +220,26 @@ static void MutateTxAddInput(CMutableTransaction& tx, const string& strInput)
tx.vin.push_back(txin);
}
-static void MutateTxAddOutAddr(CMutableTransaction& tx, const string& strInput)
+static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strInput)
{
// separate VALUE:ADDRESS in string
size_t pos = strInput.find(':');
- if ((pos == string::npos) ||
+ if ((pos == std::string::npos) ||
(pos == 0) ||
(pos == (strInput.size() - 1)))
- throw runtime_error("TX output missing separator");
+ throw std::runtime_error("TX output missing separator");
// extract and validate VALUE
- string strValue = strInput.substr(0, pos);
+ std::string strValue = strInput.substr(0, pos);
CAmount value;
if (!ParseMoney(strValue, value))
- throw runtime_error("invalid TX output value");
+ throw std::runtime_error("invalid TX output value");
// extract and validate ADDRESS
- string strAddr = strInput.substr(pos + 1, string::npos);
+ std::string strAddr = strInput.substr(pos + 1, std::string::npos);
CBitcoinAddress addr(strAddr);
if (!addr.IsValid())
- throw runtime_error("invalid TX output address");
+ throw std::runtime_error("invalid TX output address");
// build standard output script via GetScriptForDestination()
CScript scriptPubKey = GetScriptForDestination(addr.Get());
@@ -242,7 +249,7 @@ static void MutateTxAddOutAddr(CMutableTransaction& tx, const string& strInput)
tx.vout.push_back(txout);
}
-static void MutateTxAddOutData(CMutableTransaction& tx, const string& strInput)
+static void MutateTxAddOutData(CMutableTransaction& tx, const std::string& strInput)
{
CAmount value = 0;
@@ -250,20 +257,20 @@ static void MutateTxAddOutData(CMutableTransaction& tx, const string& strInput)
size_t pos = strInput.find(':');
if (pos==0)
- throw runtime_error("TX output value not specified");
+ throw std::runtime_error("TX output value not specified");
- if (pos != string::npos) {
+ if (pos != std::string::npos) {
// extract and validate VALUE
- string strValue = strInput.substr(0, pos);
+ std::string strValue = strInput.substr(0, pos);
if (!ParseMoney(strValue, value))
- throw runtime_error("invalid TX output value");
+ throw std::runtime_error("invalid TX output value");
}
// extract and validate DATA
- string strData = strInput.substr(pos + 1, string::npos);
+ std::string strData = strInput.substr(pos + 1, std::string::npos);
if (!IsHex(strData))
- throw runtime_error("invalid TX output data");
+ throw std::runtime_error("invalid TX output data");
std::vector<unsigned char> data = ParseHex(strData);
@@ -271,22 +278,22 @@ static void MutateTxAddOutData(CMutableTransaction& tx, const string& strInput)
tx.vout.push_back(txout);
}
-static void MutateTxAddOutScript(CMutableTransaction& tx, const string& strInput)
+static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& strInput)
{
// separate VALUE:SCRIPT in string
size_t pos = strInput.find(':');
- if ((pos == string::npos) ||
+ if ((pos == std::string::npos) ||
(pos == 0))
- throw runtime_error("TX output missing separator");
+ throw std::runtime_error("TX output missing separator");
// extract and validate VALUE
- string strValue = strInput.substr(0, pos);
+ std::string strValue = strInput.substr(0, pos);
CAmount value;
if (!ParseMoney(strValue, value))
- throw runtime_error("invalid TX output value");
+ throw std::runtime_error("invalid TX output value");
// extract and validate script
- string strScript = strInput.substr(pos + 1, string::npos);
+ std::string strScript = strInput.substr(pos + 1, std::string::npos);
CScript scriptPubKey = ParseScript(strScript); // throws on err
// construct TxOut, append to transaction output list
@@ -294,26 +301,26 @@ static void MutateTxAddOutScript(CMutableTransaction& tx, const string& strInput
tx.vout.push_back(txout);
}
-static void MutateTxDelInput(CMutableTransaction& tx, const string& strInIdx)
+static void MutateTxDelInput(CMutableTransaction& tx, const std::string& strInIdx)
{
// parse requested deletion index
int inIdx = atoi(strInIdx);
if (inIdx < 0 || inIdx >= (int)tx.vin.size()) {
- string strErr = "Invalid TX input index '" + strInIdx + "'";
- throw runtime_error(strErr.c_str());
+ std::string strErr = "Invalid TX input index '" + strInIdx + "'";
+ throw std::runtime_error(strErr.c_str());
}
// delete input from transaction
tx.vin.erase(tx.vin.begin() + inIdx);
}
-static void MutateTxDelOutput(CMutableTransaction& tx, const string& strOutIdx)
+static void MutateTxDelOutput(CMutableTransaction& tx, const std::string& strOutIdx)
{
// parse requested deletion index
int outIdx = atoi(strOutIdx);
if (outIdx < 0 || outIdx >= (int)tx.vout.size()) {
- string strErr = "Invalid TX output index '" + strOutIdx + "'";
- throw runtime_error(strErr.c_str());
+ std::string strErr = "Invalid TX output index '" + strOutIdx + "'";
+ throw std::runtime_error(strErr.c_str());
}
// delete output from transaction
@@ -333,7 +340,7 @@ static const struct {
{"SINGLE|ANYONECANPAY", SIGHASH_SINGLE|SIGHASH_ANYONECANPAY},
};
-static bool findSighashFlags(int& flags, const string& flagStr)
+static bool findSighashFlags(int& flags, const std::string& flagStr)
{
flags = 0;
@@ -347,17 +354,17 @@ static bool findSighashFlags(int& flags, const string& flagStr)
return false;
}
-uint256 ParseHashUO(map<string,UniValue>& o, string strKey)
+uint256 ParseHashUO(std::map<std::string,UniValue>& o, std::string strKey)
{
if (!o.count(strKey))
return uint256();
return ParseHashUV(o[strKey], strKey);
}
-vector<unsigned char> ParseHexUO(map<string,UniValue>& o, string strKey)
+std::vector<unsigned char> ParseHexUO(std::map<std::string,UniValue>& o, std::string strKey)
{
if (!o.count(strKey)) {
- vector<unsigned char> emptyVec;
+ std::vector<unsigned char> emptyVec;
return emptyVec;
}
return ParseHexUV(o[strKey], strKey);
@@ -366,24 +373,24 @@ vector<unsigned char> ParseHexUO(map<string,UniValue>& o, string strKey)
static CAmount AmountFromValue(const UniValue& value)
{
if (!value.isNum() && !value.isStr())
- throw runtime_error("Amount is not a number or string");
+ throw std::runtime_error("Amount is not a number or string");
CAmount amount;
if (!ParseFixedPoint(value.getValStr(), 8, &amount))
- throw runtime_error("Invalid amount");
+ throw std::runtime_error("Invalid amount");
if (!MoneyRange(amount))
- throw runtime_error("Amount out of range");
+ throw std::runtime_error("Amount out of range");
return amount;
}
-static void MutateTxSign(CMutableTransaction& tx, const string& flagStr)
+static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
{
int nHashType = SIGHASH_ALL;
if (flagStr.size() > 0)
if (!findSighashFlags(nHashType, flagStr))
- throw runtime_error("unknown sighash flag/sign option");
+ throw std::runtime_error("unknown sighash flag/sign option");
- vector<CTransaction> txVariants;
+ std::vector<CTransaction> txVariants;
txVariants.push_back(tx);
// mergedTx will end up with all the signatures; it
@@ -394,17 +401,17 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr)
CCoinsViewCache view(&viewDummy);
if (!registers.count("privatekeys"))
- throw runtime_error("privatekeys register variable must be set.");
+ throw std::runtime_error("privatekeys register variable must be set.");
CBasicKeyStore tempKeystore;
UniValue keysObj = registers["privatekeys"];
for (unsigned int kidx = 0; kidx < keysObj.size(); kidx++) {
if (!keysObj[kidx].isStr())
- throw runtime_error("privatekey not a string");
+ throw std::runtime_error("privatekey not a std::string");
CBitcoinSecret vchSecret;
bool fGood = vchSecret.SetString(keysObj[kidx].getValStr());
if (!fGood)
- throw runtime_error("privatekey not valid");
+ throw std::runtime_error("privatekey not valid");
CKey key = vchSecret.GetKey();
tempKeystore.AddKey(key);
@@ -412,34 +419,34 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr)
// Add previous txouts given in the RPC call:
if (!registers.count("prevtxs"))
- throw runtime_error("prevtxs register variable must be set.");
+ throw std::runtime_error("prevtxs register variable must be set.");
UniValue prevtxsObj = registers["prevtxs"];
{
for (unsigned int previdx = 0; previdx < prevtxsObj.size(); previdx++) {
UniValue prevOut = prevtxsObj[previdx];
if (!prevOut.isObject())
- throw runtime_error("expected prevtxs internal object");
+ throw std::runtime_error("expected prevtxs internal object");
- map<string,UniValue::VType> types = boost::assign::map_list_of("txid", UniValue::VSTR)("vout",UniValue::VNUM)("scriptPubKey",UniValue::VSTR);
+ std::map<std::string,UniValue::VType> types = boost::assign::map_list_of("txid", UniValue::VSTR)("vout",UniValue::VNUM)("scriptPubKey",UniValue::VSTR);
if (!prevOut.checkObject(types))
- throw runtime_error("prevtxs internal object typecheck fail");
+ throw std::runtime_error("prevtxs internal object typecheck fail");
uint256 txid = ParseHashUV(prevOut["txid"], "txid");
int nOut = atoi(prevOut["vout"].getValStr());
if (nOut < 0)
- throw runtime_error("vout must be positive");
+ throw std::runtime_error("vout must be positive");
- vector<unsigned char> pkData(ParseHexUV(prevOut["scriptPubKey"], "scriptPubKey"));
+ std::vector<unsigned char> pkData(ParseHexUV(prevOut["scriptPubKey"], "scriptPubKey"));
CScript scriptPubKey(pkData.begin(), pkData.end());
{
CCoinsModifier coins = view.ModifyCoins(txid);
if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) {
- string err("Previous output scriptPubKey mismatch:\n");
+ std::string err("Previous output scriptPubKey mismatch:\n");
err = err + ScriptToAsmStr(coins->vout[nOut].scriptPubKey) + "\nvs:\n"+
ScriptToAsmStr(scriptPubKey);
- throw runtime_error(err);
+ throw std::runtime_error(err);
}
if ((unsigned int)nOut >= coins->vout.size())
coins->vout.resize(nOut+1);
@@ -455,7 +462,7 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr)
if ((scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash()) &&
prevOut.exists("redeemScript")) {
UniValue v = prevOut["redeemScript"];
- vector<unsigned char> rsData(ParseHexUV(v, "redeemScript"));
+ std::vector<unsigned char> rsData(ParseHexUV(v, "redeemScript"));
CScript redeemScript(rsData.begin(), rsData.end());
tempKeystore.AddCScript(redeemScript);
}
@@ -512,8 +519,8 @@ public:
}
};
-static void MutateTx(CMutableTransaction& tx, const string& command,
- const string& commandVal)
+static void MutateTx(CMutableTransaction& tx, const std::string& command,
+ const std::string& commandVal)
{
std::unique_ptr<Secp256k1Init> ecc;
@@ -548,7 +555,7 @@ static void MutateTx(CMutableTransaction& tx, const string& command,
RegisterSet(commandVal);
else
- throw runtime_error("unknown command");
+ throw std::runtime_error("unknown command");
}
static void OutputTxJSON(const CTransaction& tx)
@@ -556,20 +563,20 @@ static void OutputTxJSON(const CTransaction& tx)
UniValue entry(UniValue::VOBJ);
TxToUniv(tx, uint256(), entry);
- string jsonOutput = entry.write(4);
+ std::string jsonOutput = entry.write(4);
fprintf(stdout, "%s\n", jsonOutput.c_str());
}
static void OutputTxHash(const CTransaction& tx)
{
- string strHexHash = tx.GetHash().GetHex(); // the hex-encoded transaction hash (aka the transaction id)
+ std::string strHexHash = tx.GetHash().GetHex(); // the hex-encoded transaction hash (aka the transaction id)
fprintf(stdout, "%s\n", strHexHash.c_str());
}
static void OutputTxHex(const CTransaction& tx)
{
- string strHex = EncodeHexTx(tx);
+ std::string strHex = EncodeHexTx(tx);
fprintf(stdout, "%s\n", strHex.c_str());
}
@@ -584,10 +591,10 @@ static void OutputTx(const CTransaction& tx)
OutputTxHex(tx);
}
-static string readStdin()
+static std::string readStdin()
{
char buf[4096];
- string ret;
+ std::string ret;
while (!feof(stdin)) {
size_t bread = fread(buf, 1, sizeof(buf), stdin);
@@ -597,7 +604,7 @@ static string readStdin()
}
if (ferror(stdin))
- throw runtime_error("error reading stdin");
+ throw std::runtime_error("error reading stdin");
boost::algorithm::trim_right(ret);
@@ -606,7 +613,7 @@ static string readStdin()
static int CommandLineRawTx(int argc, char* argv[])
{
- string strPrint;
+ std::string strPrint;
int nRet = 0;
try {
// Skip switches; Permit common stdin convention "-"
@@ -616,33 +623,31 @@ static int CommandLineRawTx(int argc, char* argv[])
argv++;
}
- CTransaction txDecodeTmp;
+ CMutableTransaction tx;
int startArg;
if (!fCreateBlank) {
// require at least one param
if (argc < 2)
- throw runtime_error("too few parameters");
+ throw std::runtime_error("too few parameters");
// param: hex-encoded bitcoin transaction
- string strHexTx(argv[1]);
+ std::string strHexTx(argv[1]);
if (strHexTx == "-") // "-" implies standard input
strHexTx = readStdin();
- if (!DecodeHexTx(txDecodeTmp, strHexTx))
- throw runtime_error("invalid transaction encoding");
+ if (!DecodeHexTx(tx, strHexTx, true))
+ throw std::runtime_error("invalid transaction encoding");
startArg = 2;
} else
startArg = 1;
- CMutableTransaction tx(txDecodeTmp);
-
for (int i = startArg; i < argc; i++) {
- string arg = argv[i];
- string key, value;
+ std::string arg = argv[i];
+ std::string key, value;
size_t eqpos = arg.find('=');
- if (eqpos == string::npos)
+ if (eqpos == std::string::npos)
key = arg;
else {
key = arg.substr(0, eqpos);
@@ -659,7 +664,7 @@ static int CommandLineRawTx(int argc, char* argv[])
throw;
}
catch (const std::exception& e) {
- strPrint = string("error: ") + e.what();
+ strPrint = std::string("error: ") + e.what();
nRet = EXIT_FAILURE;
}
catch (...) {
@@ -678,8 +683,9 @@ int main(int argc, char* argv[])
SetupEnvironment();
try {
- if(!AppInitRawTx(argc, argv))
- return EXIT_FAILURE;
+ int ret = AppInitRawTx(argc, argv);
+ if (ret != CONTINUE_EXECUTION)
+ return ret;
}
catch (const std::exception& e) {
PrintExceptionContinue(&e, "AppInitRawTx()");
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index 322298d1b3..ba3ccac615 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -9,6 +9,7 @@
#include "chainparams.h"
#include "clientversion.h"
+#include "compat.h"
#include "rpc/server.h"
#include "init.h"
#include "noui.h"
@@ -91,7 +92,7 @@ bool AppInit(int argc, char* argv[])
}
fprintf(stdout, "%s", strUsage.c_str());
- return false;
+ return true;
}
try
@@ -103,7 +104,7 @@ bool AppInit(int argc, char* argv[])
}
try
{
- ReadConfigFile(mapArgs, mapMultiArgs);
+ ReadConfigFile(GetArg("-conf", BITCOIN_CONF_FILENAME), mapArgs, mapMultiArgs);
} catch (const std::exception& e) {
fprintf(stderr,"Error reading configuration file: %s\n", e.what());
return false;
@@ -125,37 +126,45 @@ bool AppInit(int argc, char* argv[])
if (fCommandLine)
{
fprintf(stderr, "Error: There is no RPC client functionality in bitcoind anymore. Use the bitcoin-cli utility instead.\n");
+ exit(EXIT_FAILURE);
+ }
+ // -server defaults to true for bitcoind but not for the GUI so do this here
+ SoftSetBoolArg("-server", true);
+ // Set this early so that parameter interactions go to console
+ InitLogging();
+ InitParameterInteraction();
+ if (!AppInitBasicSetup())
+ {
+ // InitError will have been called with detailed error, which ends up on console
+ exit(1);
+ }
+ if (!AppInitParameterInteraction())
+ {
+ // InitError will have been called with detailed error, which ends up on console
+ exit(1);
+ }
+ if (!AppInitSanityChecks())
+ {
+ // InitError will have been called with detailed error, which ends up on console
exit(1);
}
-#ifndef WIN32
if (GetBoolArg("-daemon", false))
{
+#if HAVE_DECL_DAEMON
fprintf(stdout, "Bitcoin server starting\n");
// Daemonize
- pid_t pid = fork();
- if (pid < 0)
- {
- fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
+ if (daemon(1, 0)) { // don't chdir (1), do close FDs (0)
+ fprintf(stderr, "Error: daemon() failed: %s\n", strerror(errno));
return false;
}
- if (pid > 0) // Parent process, pid is child process id
- {
- 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);
+#else
+ fprintf(stderr, "Error: -daemon is not supported on this operating system\n");
+ return false;
+#endif // HAVE_DECL_DAEMON
}
-#endif
- SoftSetBoolArg("-server", true);
- // Set this early so that parameter interactions go to console
- InitLogging();
- InitParameterInteraction();
- fRet = AppInit2(threadGroup, scheduler);
+ fRet = AppInitMain(threadGroup, scheduler);
}
catch (const std::exception& e) {
PrintExceptionContinue(&e, "AppInit()");
@@ -184,5 +193,5 @@ int main(int argc, char* argv[])
// Connect bitcoind signal handlers
noui_connect();
- return (AppInit(argc, argv) ? 0 : 1);
+ return (AppInit(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE);
}
diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp
index df237f8f26..914af0c670 100644
--- a/src/blockencodings.cpp
+++ b/src/blockencodings.cpp
@@ -10,22 +10,22 @@
#include "random.h"
#include "streams.h"
#include "txmempool.h"
-#include "main.h"
+#include "validation.h"
#include "util.h"
#include <unordered_map>
#define MIN_TRANSACTION_BASE_SIZE (::GetSerializeSize(CTransaction(), SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS))
-CBlockHeaderAndShortTxIDs::CBlockHeaderAndShortTxIDs(const CBlock& block) :
+CBlockHeaderAndShortTxIDs::CBlockHeaderAndShortTxIDs(const CBlock& block, bool fUseWTXID) :
nonce(GetRand(std::numeric_limits<uint64_t>::max())),
shorttxids(block.vtx.size() - 1), prefilledtxn(1), header(block) {
FillShortTxIDSelector();
//TODO: Use our mempool prior to block acceptance to predictively fill more than just the coinbase
prefilledtxn[0] = {0, block.vtx[0]};
for (size_t i = 1; i < block.vtx.size(); i++) {
- const CTransaction& tx = block.vtx[i];
- shorttxids[i - 1] = GetShortID(tx.GetHash());
+ const CTransaction& tx = *block.vtx[i];
+ shorttxids[i - 1] = GetShortID(fUseWTXID ? tx.GetWitnessHash() : tx.GetHash());
}
}
@@ -59,10 +59,10 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c
int32_t lastprefilledindex = -1;
for (size_t i = 0; i < cmpctblock.prefilledtxn.size(); i++) {
- if (cmpctblock.prefilledtxn[i].tx.IsNull())
+ if (cmpctblock.prefilledtxn[i].tx->IsNull())
return READ_STATUS_INVALID;
- lastprefilledindex += cmpctblock.prefilledtxn[i].index + 1; //index is a uint16_t, so cant overflow here
+ lastprefilledindex += cmpctblock.prefilledtxn[i].index + 1; //index is a uint16_t, so can't overflow here
if (lastprefilledindex > std::numeric_limits<uint16_t>::max())
return READ_STATUS_INVALID;
if ((uint32_t)lastprefilledindex > cmpctblock.shorttxids.size() + i) {
@@ -71,7 +71,7 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c
// have neither a prefilled txn or a shorttxid!
return READ_STATUS_INVALID;
}
- txn_available[lastprefilledindex] = std::make_shared<CTransaction>(cmpctblock.prefilledtxn[i].tx);
+ txn_available[lastprefilledindex] = cmpctblock.prefilledtxn[i].tx;
}
prefilled_count = cmpctblock.prefilledtxn.size();
@@ -131,7 +131,7 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c
break;
}
- LogPrint("cmpctblock", "Initialized PartiallyDownloadedBlock for block %s using a cmpctblock of size %lu\n", cmpctblock.header.GetHash().ToString(), cmpctblock.GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION));
+ LogPrint("cmpctblock", "Initialized PartiallyDownloadedBlock for block %s using a cmpctblock of size %lu\n", cmpctblock.header.GetHash().ToString(), GetSerializeSize(cmpctblock, SER_NETWORK, PROTOCOL_VERSION));
return READ_STATUS_OK;
}
@@ -142,7 +142,7 @@ bool PartiallyDownloadedBlock::IsTxAvailable(size_t index) const {
return txn_available[index] ? true : false;
}
-ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<CTransaction>& vtx_missing) const {
+ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<CTransactionRef>& vtx_missing) const {
assert(!header.IsNull());
block = header;
block.vtx.resize(txn_available.size());
@@ -154,7 +154,7 @@ ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<
return READ_STATUS_INVALID;
block.vtx[i] = vtx_missing[tx_missing_offset++];
} else
- block.vtx[i] = *txn_available[i];
+ block.vtx[i] = txn_available[i];
}
if (vtx_missing.size() != tx_missing_offset)
return READ_STATUS_INVALID;
@@ -167,13 +167,13 @@ ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<
// check its own merkle root and cache that check.
if (state.CorruptionPossible())
return READ_STATUS_FAILED; // Possible Short ID collision
- return READ_STATUS_INVALID;
+ return READ_STATUS_CHECKBLOCK_FAILED;
}
LogPrint("cmpctblock", "Successfully reconstructed block %s with %lu txn prefilled, %lu txn from mempool and %lu txn requested\n", header.GetHash().ToString(), prefilled_count, mempool_count, vtx_missing.size());
if (vtx_missing.size() < 5) {
- for(const CTransaction& tx : vtx_missing)
- LogPrint("cmpctblock", "Reconstructed block %s required tx %s\n", header.GetHash().ToString(), tx.GetHash().ToString());
+ for (const auto& tx : vtx_missing)
+ LogPrint("cmpctblock", "Reconstructed block %s required tx %s\n", header.GetHash().ToString(), tx->GetHash().ToString());
}
return READ_STATUS_OK;
diff --git a/src/blockencodings.h b/src/blockencodings.h
index 349fcbd50f..27baf1f8f8 100644
--- a/src/blockencodings.h
+++ b/src/blockencodings.h
@@ -14,14 +14,14 @@ class CTxMemPool;
// Dumb helper to handle CTransaction compression at serialize-time
struct TransactionCompressor {
private:
- CTransaction& tx;
+ CTransactionRef& tx;
public:
- TransactionCompressor(CTransaction& txIn) : tx(txIn) {}
+ TransactionCompressor(CTransactionRef& txIn) : tx(txIn) {}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(tx); //TODO: Compress tx encoding
}
};
@@ -35,7 +35,7 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(blockhash);
uint64_t indexes_size = (uint64_t)indexes.size();
READWRITE(COMPACTSIZE(indexes_size));
@@ -72,7 +72,7 @@ class BlockTransactions {
public:
// A BlockTransactions message
uint256 blockhash;
- std::vector<CTransaction> txn;
+ std::vector<CTransactionRef> txn;
BlockTransactions() {}
BlockTransactions(const BlockTransactionsRequest& req) :
@@ -81,7 +81,7 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(blockhash);
uint64_t txn_size = (uint64_t)txn.size();
READWRITE(COMPACTSIZE(txn_size));
@@ -104,12 +104,12 @@ struct PrefilledTransaction {
// Used as an offset since last prefilled tx in CBlockHeaderAndShortTxIDs,
// as a proper transaction-in-block-index in PartiallyDownloadedBlock
uint16_t index;
- CTransaction tx;
+ CTransactionRef tx;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
uint64_t idx = index;
READWRITE(COMPACTSIZE(idx));
if (idx > std::numeric_limits<uint16_t>::max())
@@ -124,6 +124,8 @@ typedef enum ReadStatus_t
READ_STATUS_OK,
READ_STATUS_INVALID, // Invalid object, peer is sending bogus crap
READ_STATUS_FAILED, // Failed to process object
+ READ_STATUS_CHECKBLOCK_FAILED, // Used only by FillBlock to indicate a
+ // failure in CheckBlock.
} ReadStatus;
class CBlockHeaderAndShortTxIDs {
@@ -146,7 +148,7 @@ public:
// Dummy for deserialization
CBlockHeaderAndShortTxIDs() {}
- CBlockHeaderAndShortTxIDs(const CBlock& block);
+ CBlockHeaderAndShortTxIDs(const CBlock& block, bool fUseWTXID);
uint64_t GetShortID(const uint256& txhash) const;
@@ -155,7 +157,7 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(header);
READWRITE(nonce);
@@ -191,7 +193,7 @@ public:
class PartiallyDownloadedBlock {
protected:
- std::vector<std::shared_ptr<const CTransaction> > txn_available;
+ std::vector<CTransactionRef> txn_available;
size_t prefilled_count = 0, mempool_count = 0;
CTxMemPool* pool;
public:
@@ -200,7 +202,7 @@ public:
ReadStatus InitData(const CBlockHeaderAndShortTxIDs& cmpctblock);
bool IsTxAvailable(size_t index) const;
- ReadStatus FillBlock(CBlock& block, const std::vector<CTransaction>& vtx_missing) const;
+ ReadStatus FillBlock(CBlock& block, const std::vector<CTransactionRef>& vtx_missing) const;
};
#endif
diff --git a/src/bloom.cpp b/src/bloom.cpp
index fd328e8e96..d00befc61c 100644
--- a/src/bloom.cpp
+++ b/src/bloom.cpp
@@ -34,7 +34,7 @@ CBloomFilter::CBloomFilter(unsigned int nElements, double nFPRate, unsigned int
* See https://en.wikipedia.org/wiki/Bloom_filter for an explanation of these formulas
*/
isFull(false),
- isEmpty(false),
+ isEmpty(true),
nHashFuncs(min((unsigned int)(vData.size() * 8 / nElements * LN2), MAX_HASH_FUNCS)),
nTweak(nTweakIn),
nFlags(nFlagsIn)
@@ -280,8 +280,8 @@ void CRollingBloomFilter::insert(const std::vector<unsigned char>& vKey)
void CRollingBloomFilter::insert(const uint256& hash)
{
- vector<unsigned char> data(hash.begin(), hash.end());
- insert(data);
+ vector<unsigned char> vData(hash.begin(), hash.end());
+ insert(vData);
}
bool CRollingBloomFilter::contains(const std::vector<unsigned char>& vKey) const
@@ -300,8 +300,8 @@ bool CRollingBloomFilter::contains(const std::vector<unsigned char>& vKey) const
bool CRollingBloomFilter::contains(const uint256& hash) const
{
- vector<unsigned char> data(hash.begin(), hash.end());
- return contains(data);
+ vector<unsigned char> vData(hash.begin(), hash.end());
+ return contains(vData);
}
void CRollingBloomFilter::reset()
diff --git a/src/bloom.h b/src/bloom.h
index ad6de625d8..d3a017371f 100644
--- a/src/bloom.h
+++ b/src/bloom.h
@@ -73,7 +73,7 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(vData);
READWRITE(nHashFuncs);
READWRITE(nTweak);
diff --git a/src/chain.cpp b/src/chain.cpp
index 77e924e703..1e611906d1 100644
--- a/src/chain.cpp
+++ b/src/chain.cpp
@@ -61,6 +61,13 @@ const CBlockIndex *CChain::FindFork(const CBlockIndex *pindex) const {
return pindex;
}
+CBlockIndex* CChain::FindLatestBefore(int64_t nTime) const
+{
+ std::vector<CBlockIndex*>::const_iterator lower = std::lower_bound(vChain.begin(), vChain.end(), nTime,
+ [](CBlockIndex* pBlock, const int64_t& time) -> bool { return pBlock->GetBlockTime() < time; });
+ return (lower == vChain.end() ? NULL : *lower);
+}
+
/** Turn the lowest '1' bit in the binary representation of a number into a '0'. */
int static inline InvertLowestOne(int n) { return n & (n - 1); }
diff --git a/src/chain.h b/src/chain.h
index 6588e8f57d..0aac5de5c2 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -28,7 +28,7 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(VARINT(nBlocks));
READWRITE(VARINT(nSize));
READWRITE(VARINT(nUndoSize));
@@ -76,7 +76,7 @@ struct CDiskBlockPos
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(VARINT(nFile));
READWRITE(VARINT(nPos));
}
@@ -200,7 +200,7 @@ public:
unsigned int nNonce;
//! (memory only) Sequential id assigned to distinguish order in which blocks are received.
- uint32_t nSequenceId;
+ int32_t nSequenceId;
void SetNull()
{
@@ -357,8 +357,9 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
- if (!(nType & SER_GETHASH))
+ inline void SerializationOp(Stream& s, Operation ser_action) {
+ int nVersion = s.GetVersion();
+ if (!(s.GetType() & SER_GETHASH))
READWRITE(VARINT(nVersion));
READWRITE(VARINT(nHeight));
@@ -459,6 +460,9 @@ public:
/** Find the last common block between this chain and a block index entry. */
const CBlockIndex *FindFork(const CBlockIndex *pindex) const;
+
+ /** Find the most recent block with timestamp lower than the given. */
+ CBlockIndex* FindLatestBefore(int64_t nTime) const;
};
#endif // BITCOIN_CHAIN_H
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index e6be1b5d5b..3b3c0a5d3e 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -31,7 +31,7 @@ static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesi
genesis.nBits = nBits;
genesis.nNonce = nNonce;
genesis.nVersion = nVersion;
- genesis.vtx.push_back(txNew);
+ genesis.vtx.push_back(MakeTransactionRef(std::move(txNew)));
genesis.hashPrevBlock.SetNull();
genesis.hashMerkleRoot = BlockMerkleRoot(genesis);
return genesis;
@@ -91,10 +91,13 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 1462060800; // May 1st, 2016
consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 1493596800; // May 1st, 2017
- // Deployment of SegWit (BIP141 and BIP143)
+ // Deployment of SegWit (BIP141, BIP143, and BIP147)
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].bit = 1;
- consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = 0;
- consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = 0; // Never / undefined
+ consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = 1479168000; // November 15th, 2016.
+ consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = 1510704000; // November 15th, 2017.
+
+ // The best chain should have at least this much work.
+ consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000002cb971dd56d1c583c20f90");
/**
* The message start string is designed to be unlikely to occur in normal data.
@@ -113,12 +116,13 @@ public:
assert(consensus.hashGenesisBlock == uint256S("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"));
assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"));
- vSeeds.push_back(CDNSSeedData("bitcoin.sipa.be", "seed.bitcoin.sipa.be", true)); // Pieter Wuille
- vSeeds.push_back(CDNSSeedData("bluematt.me", "dnsseed.bluematt.me")); // Matt Corallo
+ // Note that of those with the service bits flag, most only support a subset of possible options
+ vSeeds.push_back(CDNSSeedData("bitcoin.sipa.be", "seed.bitcoin.sipa.be", true)); // Pieter Wuille, only supports x1, x5, x9, and xd
+ vSeeds.push_back(CDNSSeedData("bluematt.me", "dnsseed.bluematt.me", true)); // Matt Corallo, only supports x9
vSeeds.push_back(CDNSSeedData("dashjr.org", "dnsseed.bitcoin.dashjr.org")); // Luke Dashjr
- vSeeds.push_back(CDNSSeedData("bitcoinstats.com", "seed.bitcoinstats.com")); // Christian Decker
+ vSeeds.push_back(CDNSSeedData("bitcoinstats.com", "seed.bitcoinstats.com", true)); // Christian Decker, supports x1 - xf
vSeeds.push_back(CDNSSeedData("xf2.org", "bitseed.xf2.org")); // Jeff Garzik
- vSeeds.push_back(CDNSSeedData("bitcoin.jonasschnelli.ch", "seed.bitcoin.jonasschnelli.ch", true)); // Jonas Schnelli
+ vSeeds.push_back(CDNSSeedData("bitcoin.jonasschnelli.ch", "seed.bitcoin.jonasschnelli.ch", true)); // Jonas Schnelli, only supports x1, x5, x9, and xd
base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,0);
base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,5);
@@ -132,7 +136,6 @@ public:
fDefaultConsistencyChecks = false;
fRequireStandard = true;
fMineBlocksOnDemand = false;
- fTestnetToBeDeprecatedFieldRPC = false;
checkpointData = (CCheckpointData) {
boost::assign::map_list_of
@@ -186,11 +189,14 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 1456790400; // March 1st, 2016
consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 1493596800; // May 1st, 2017
- // Deployment of SegWit (BIP141 and BIP143)
+ // Deployment of SegWit (BIP141, BIP143, and BIP147)
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].bit = 1;
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = 1462060800; // May 1st 2016
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = 1493596800; // May 1st 2017
+ // The best chain should have at least this much work.
+ consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000000198b4def2baa9338d6");
+
pchMessageStart[0] = 0x0b;
pchMessageStart[1] = 0x11;
pchMessageStart[2] = 0x09;
@@ -223,7 +229,7 @@ public:
fDefaultConsistencyChecks = false;
fRequireStandard = false;
fMineBlocksOnDemand = false;
- fTestnetToBeDeprecatedFieldRPC = true;
+
checkpointData = (CCheckpointData) {
boost::assign::map_list_of
@@ -266,6 +272,9 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = 0;
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = 999999999999ULL;
+ // The best chain should have at least this much work.
+ consensus.nMinimumChainWork = uint256S("0x00");
+
pchMessageStart[0] = 0xfa;
pchMessageStart[1] = 0xbf;
pchMessageStart[2] = 0xb5;
@@ -285,7 +294,6 @@ public:
fDefaultConsistencyChecks = true;
fRequireStandard = false;
fMineBlocksOnDemand = true;
- fTestnetToBeDeprecatedFieldRPC = false;
checkpointData = (CCheckpointData){
boost::assign::map_list_of
diff --git a/src/chainparams.h b/src/chainparams.h
index 0c3820b7c6..633fbd5120 100644
--- a/src/chainparams.h
+++ b/src/chainparams.h
@@ -67,8 +67,6 @@ public:
uint64_t PruneAfterHeight() const { return nPruneAfterHeight; }
/** Make miner stop after a block is found. In RPC, don't return until nGenProcLimit blocks are generated */
bool MineBlocksOnDemand() const { return fMineBlocksOnDemand; }
- /** In the future use NetworkIDString() for RPC fields */
- bool TestnetToBeDeprecatedFieldRPC() const { return fTestnetToBeDeprecatedFieldRPC; }
/** Return the BIP70 network string (main, test or regtest) */
std::string NetworkIDString() const { return strNetworkID; }
const std::vector<CDNSSeedData>& DNSSeeds() const { return vSeeds; }
@@ -91,7 +89,6 @@ protected:
bool fDefaultConsistencyChecks;
bool fRequireStandard;
bool fMineBlocksOnDemand;
- bool fTestnetToBeDeprecatedFieldRPC;
CCheckpointData checkpointData;
};
diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp
index aefddce464..a487fb1dc4 100644
--- a/src/checkpoints.cpp
+++ b/src/checkpoints.cpp
@@ -6,7 +6,7 @@
#include "chain.h"
#include "chainparams.h"
-#include "main.h"
+#include "validation.h"
#include "uint256.h"
#include <stdint.h>
@@ -55,16 +55,6 @@ namespace Checkpoints {
return fWorkBefore / (fWorkBefore + fWorkAfter);
}
- int GetTotalBlocksEstimate(const CCheckpointData& data)
- {
- const MapCheckpoints& checkpoints = data.mapCheckpoints;
-
- if (checkpoints.empty())
- return 0;
-
- return checkpoints.rbegin()->first;
- }
-
CBlockIndex* GetLastCheckpoint(const CCheckpointData& data)
{
const MapCheckpoints& checkpoints = data.mapCheckpoints;
diff --git a/src/checkpoints.h b/src/checkpoints.h
index cd25ea5379..04346f35ff 100644
--- a/src/checkpoints.h
+++ b/src/checkpoints.h
@@ -19,9 +19,6 @@ struct CCheckpointData;
namespace Checkpoints
{
-//! Return conservative estimate of total number of blocks, 0 if unknown
-int GetTotalBlocksEstimate(const CCheckpointData& data);
-
//! Returns last CBlockIndex* in mapBlockIndex that is a checkpoint
CBlockIndex* GetLastCheckpoint(const CCheckpointData& data);
diff --git a/src/coins.cpp b/src/coins.cpp
index 39db7dedfb..8ff652b474 100644
--- a/src/coins.cpp
+++ b/src/coins.cpp
@@ -275,7 +275,7 @@ double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight, CAmount
assert(coins);
if (!coins->IsAvailable(txin.prevout.n)) continue;
if (coins->nHeight <= nHeight) {
- dResult += coins->vout[txin.prevout.n].nValue * (nHeight-coins->nHeight);
+ dResult += (double)(coins->vout[txin.prevout.n].nValue) * (nHeight-coins->nHeight);
inChainInputValue += coins->vout[txin.prevout.n].nValue;
}
}
diff --git a/src/coins.h b/src/coins.h
index 033651a435..dd6ef6cc3a 100644
--- a/src/coins.h
+++ b/src/coins.h
@@ -153,31 +153,8 @@ public:
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<typename Stream>
- void Serialize(Stream &s, int nType, int nVersion) const {
+ void Serialize(Stream &s) const {
unsigned int nMaskSize = 0, nMaskCode = 0;
CalcMaskSize(nMaskSize, nMaskCode);
bool fFirst = vout.size() > 0 && !vout[0].IsNull();
@@ -185,33 +162,33 @@ public:
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);
+ ::Serialize(s, VARINT(this->nVersion));
// header code
- ::Serialize(s, VARINT(nCode), nType, nVersion);
+ ::Serialize(s, VARINT(nCode));
// spentness bitmask
for (unsigned int b = 0; b<nMaskSize; b++) {
unsigned char chAvail = 0;
for (unsigned int i = 0; i < 8 && 2+b*8+i < vout.size(); i++)
if (!vout[2+b*8+i].IsNull())
chAvail |= (1 << i);
- ::Serialize(s, chAvail, nType, nVersion);
+ ::Serialize(s, chAvail);
}
// txouts themself
for (unsigned int i = 0; i < vout.size(); i++) {
if (!vout[i].IsNull())
- ::Serialize(s, CTxOutCompressor(REF(vout[i])), nType, nVersion);
+ ::Serialize(s, CTxOutCompressor(REF(vout[i])));
}
// coinbase height
- ::Serialize(s, VARINT(nHeight), nType, nVersion);
+ ::Serialize(s, VARINT(nHeight));
}
template<typename Stream>
- void Unserialize(Stream &s, int nType, int nVersion) {
+ void Unserialize(Stream &s) {
unsigned int nCode = 0;
// version
- ::Unserialize(s, VARINT(this->nVersion), nType, nVersion);
+ ::Unserialize(s, VARINT(this->nVersion));
// header code
- ::Unserialize(s, VARINT(nCode), nType, nVersion);
+ ::Unserialize(s, VARINT(nCode));
fCoinBase = nCode & 1;
std::vector<bool> vAvail(2, false);
vAvail[0] = (nCode & 2) != 0;
@@ -220,7 +197,7 @@ public:
// spentness bitmask
while (nMaskCode > 0) {
unsigned char chAvail = 0;
- ::Unserialize(s, chAvail, nType, nVersion);
+ ::Unserialize(s, chAvail);
for (unsigned int p = 0; p < 8; p++) {
bool f = (chAvail & (1 << p)) != 0;
vAvail.push_back(f);
@@ -232,10 +209,10 @@ public:
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);
+ ::Unserialize(s, REF(CTxOutCompressor(vout[i])));
}
// coinbase height
- ::Unserialize(s, VARINT(nHeight), nType, nVersion);
+ ::Unserialize(s, VARINT(nHeight));
Cleanup();
}
@@ -490,7 +467,6 @@ public:
friend class CCoinsModifier;
private:
- CCoinsMap::iterator FetchCoins(const uint256 &txid);
CCoinsMap::const_iterator FetchCoins(const uint256 &txid) const;
/**
diff --git a/src/compat.h b/src/compat.h
index 79a297e5e4..2578d6d344 100644
--- a/src/compat.h
+++ b/src/compat.h
@@ -34,6 +34,7 @@
#else
#include <sys/fcntl.h>
#include <sys/mman.h>
+#include <sys/select.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <net/if.h>
diff --git a/src/compat/byteswap.h b/src/compat/byteswap.h
index 899220bdc5..56f4240e49 100644
--- a/src/compat/byteswap.h
+++ b/src/compat/byteswap.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014 The Bitcoin developers
+// Copyright (c) 2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -15,6 +15,23 @@
#include <byteswap.h>
#endif
+#if defined(__APPLE__)
+
+#if !defined(bswap_16)
+
+// Mac OS X / Darwin features; we include a check for bswap_16 because if it is already defined, protobuf has
+// defined these macros for us already; if it isn't, we do it ourselves. In either case, we get the exact same
+// result regardless which path was taken
+#include <libkern/OSByteOrder.h>
+#define bswap_16(x) OSSwapInt16(x)
+#define bswap_32(x) OSSwapInt32(x)
+#define bswap_64(x) OSSwapInt64(x)
+
+#endif // !defined(bswap_16)
+
+#else
+// Non-Mac OS X / non-Darwin
+
#if HAVE_DECL_BSWAP_16 == 0
inline uint16_t bswap_16(uint16_t x)
{
@@ -44,4 +61,6 @@ inline uint64_t bswap_64(uint64_t x)
}
#endif // HAVE_DECL_BSWAP64
+#endif // defined(__APPLE__)
+
#endif // BITCOIN_COMPAT_BYTESWAP_H
diff --git a/src/compat/endian.h b/src/compat/endian.h
index 6bfae42c77..f7c1f9318a 100644
--- a/src/compat/endian.h
+++ b/src/compat/endian.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2015 The Bitcoin developers
+// Copyright (c) 2014-2015 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/compressor.h b/src/compressor.h
index fa702f0dfa..961365d261 100644
--- a/src/compressor.h
+++ b/src/compressor.h
@@ -55,16 +55,8 @@ protected:
public:
CScriptCompressor(CScript &scriptIn) : script(scriptIn) { }
- unsigned int GetSerializeSize(int nType, int nVersion) const {
- std::vector<unsigned char> compr;
- if (Compress(compr))
- return compr.size();
- unsigned int nSize = script.size() + nSpecialScripts;
- return script.size() + VARINT(nSize).GetSerializeSize(nType, nVersion);
- }
-
template<typename Stream>
- void Serialize(Stream &s, int nType, int nVersion) const {
+ void Serialize(Stream &s) const {
std::vector<unsigned char> compr;
if (Compress(compr)) {
s << CFlatData(compr);
@@ -76,7 +68,7 @@ public:
}
template<typename Stream>
- void Unserialize(Stream &s, int nType, int nVersion) {
+ void Unserialize(Stream &s) {
unsigned int nSize = 0;
s >> VARINT(nSize);
if (nSize < nSpecialScripts) {
@@ -112,7 +104,7 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
if (!ser_action.ForRead()) {
uint64_t nVal = CompressAmount(txout.nValue);
READWRITE(VARINT(nVal));
diff --git a/src/consensus/merkle.cpp b/src/consensus/merkle.cpp
index 35f7d2e05a..6fa96ddf45 100644
--- a/src/consensus/merkle.cpp
+++ b/src/consensus/merkle.cpp
@@ -160,7 +160,7 @@ uint256 BlockMerkleRoot(const CBlock& block, bool* mutated)
std::vector<uint256> leaves;
leaves.resize(block.vtx.size());
for (size_t s = 0; s < block.vtx.size(); s++) {
- leaves[s] = block.vtx[s].GetHash();
+ leaves[s] = block.vtx[s]->GetHash();
}
return ComputeMerkleRoot(leaves, mutated);
}
@@ -171,7 +171,7 @@ uint256 BlockWitnessMerkleRoot(const CBlock& block, bool* mutated)
leaves.resize(block.vtx.size());
leaves[0].SetNull(); // The witness hash of the coinbase is 0.
for (size_t s = 1; s < block.vtx.size(); s++) {
- leaves[s] = block.vtx[s].GetWitnessHash();
+ leaves[s] = block.vtx[s]->GetWitnessHash();
}
return ComputeMerkleRoot(leaves, mutated);
}
@@ -181,7 +181,7 @@ std::vector<uint256> BlockMerkleBranch(const CBlock& block, uint32_t position)
std::vector<uint256> leaves;
leaves.resize(block.vtx.size());
for (size_t s = 0; s < block.vtx.size(); s++) {
- leaves[s] = block.vtx[s].GetHash();
+ leaves[s] = block.vtx[s]->GetHash();
}
return ComputeMerkleBranch(leaves, position);
}
diff --git a/src/consensus/params.h b/src/consensus/params.h
index 5b2f49184f..20efc68ade 100644
--- a/src/consensus/params.h
+++ b/src/consensus/params.h
@@ -16,7 +16,7 @@ enum DeploymentPos
{
DEPLOYMENT_TESTDUMMY,
DEPLOYMENT_CSV, // Deployment of BIP68, BIP112, and BIP113.
- DEPLOYMENT_SEGWIT, // Deployment of BIP141 and BIP143
+ DEPLOYMENT_SEGWIT, // Deployment of BIP141, BIP143, and BIP147.
// NOTE: Also add new deployments to VersionBitsDeploymentInfo in versionbits.cpp
MAX_VERSION_BITS_DEPLOYMENTS
};
@@ -61,6 +61,7 @@ struct Params {
int64_t nPowTargetSpacing;
int64_t nPowTargetTimespan;
int64_t DifficultyAdjustmentInterval() const { return nPowTargetTimespan / nPowTargetSpacing; }
+ uint256 nMinimumChainWork;
};
} // namespace Consensus
diff --git a/src/core_io.h b/src/core_io.h
index b559d44bf5..7642bc6d6e 100644
--- a/src/core_io.h
+++ b/src/core_io.h
@@ -11,22 +11,23 @@
class CBlock;
class CScript;
class CTransaction;
+struct CMutableTransaction;
class uint256;
class UniValue;
// core_read.cpp
-extern CScript ParseScript(const std::string& s);
-extern std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode = false);
-extern bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx, bool fTryNoWitness = false);
-extern bool DecodeHexBlk(CBlock&, const std::string& strHexBlk);
-extern uint256 ParseHashUV(const UniValue& v, const std::string& strName);
-extern uint256 ParseHashStr(const std::string&, const std::string& strName);
-extern std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strName);
+CScript ParseScript(const std::string& s);
+std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode = false);
+bool DecodeHexTx(CMutableTransaction& tx, const std::string& strHexTx, bool fTryNoWitness = false);
+bool DecodeHexBlk(CBlock&, const std::string& strHexBlk);
+uint256 ParseHashUV(const UniValue& v, const std::string& strName);
+uint256 ParseHashStr(const std::string&, const std::string& strName);
+std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strName);
// core_write.cpp
-extern std::string FormatScript(const CScript& script);
-extern std::string EncodeHexTx(const CTransaction& tx);
-extern void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex);
-extern void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry);
+std::string FormatScript(const CScript& script);
+std::string EncodeHexTx(const CTransaction& tx, const int serializeFlags = 0);
+void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex);
+void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry);
#endif // BITCOIN_CORE_IO_H
diff --git a/src/core_memusage.h b/src/core_memusage.h
index b8e0f08bbf..0dcc24c40c 100644
--- a/src/core_memusage.h
+++ b/src/core_memusage.h
@@ -69,8 +69,8 @@ static inline size_t RecursiveDynamicUsage(const CMutableTransaction& tx) {
static inline size_t RecursiveDynamicUsage(const CBlock& block) {
size_t mem = memusage::DynamicUsage(block.vtx);
- for (std::vector<CTransaction>::const_iterator it = block.vtx.begin(); it != block.vtx.end(); it++) {
- mem += RecursiveDynamicUsage(*it);
+ for (const auto& tx : block.vtx) {
+ mem += memusage::DynamicUsage(tx) + RecursiveDynamicUsage(*tx);
}
return mem;
}
diff --git a/src/core_read.cpp b/src/core_read.cpp
index 7cfda6dd6d..b6d0285459 100644
--- a/src/core_read.cpp
+++ b/src/core_read.cpp
@@ -90,7 +90,7 @@ CScript ParseScript(const std::string& s)
return result;
}
-bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx, bool fTryNoWitness)
+bool DecodeHexTx(CMutableTransaction& tx, const std::string& strHexTx, bool fTryNoWitness)
{
if (!IsHex(strHexTx))
return false;
diff --git a/src/core_write.cpp b/src/core_write.cpp
index 6f9e2266a3..9f859ba9e1 100644
--- a/src/core_write.cpp
+++ b/src/core_write.cpp
@@ -116,9 +116,9 @@ string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode)
return str;
}
-string EncodeHexTx(const CTransaction& tx)
+string EncodeHexTx(const CTransaction& tx, const int serialFlags)
{
- CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | serialFlags);
ssTx << tx;
return HexStr(ssTx.begin(), ssTx.end());
}
@@ -151,11 +151,13 @@ void ScriptPubKeyToUniv(const CScript& scriptPubKey,
void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry)
{
entry.pushKV("txid", tx.GetHash().GetHex());
+ entry.pushKV("hash", tx.GetWitnessHash().GetHex());
entry.pushKV("version", tx.nVersion);
entry.pushKV("locktime", (int64_t)tx.nLockTime);
UniValue vin(UniValue::VARR);
- BOOST_FOREACH(const CTxIn& txin, tx.vin) {
+ for (unsigned int i = 0; i < tx.vin.size(); i++) {
+ const CTxIn& txin = tx.vin[i];
UniValue in(UniValue::VOBJ);
if (tx.IsCoinBase())
in.pushKV("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
@@ -166,6 +168,13 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry)
o.pushKV("asm", ScriptToAsmStr(txin.scriptSig, true));
o.pushKV("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
in.pushKV("scriptSig", o);
+ if (!tx.wit.IsNull() && i < tx.wit.vtxinwit.size() && !tx.wit.vtxinwit[i].IsNull()) {
+ UniValue txinwitness(UniValue::VARR);
+ for (const auto& item : tx.wit.vtxinwit[i].scriptWitness.stack) {
+ txinwitness.push_back(HexStr(item.begin(), item.end()));
+ }
+ in.pushKV("txinwitness", txinwitness);
+ }
}
in.pushKV("sequence", (int64_t)txin.nSequence);
vin.push_back(in);
diff --git a/src/crypto/ctaes/ctaes.c b/src/crypto/ctaes/ctaes.c
index 2389fc0bb2..55962bf252 100644
--- a/src/crypto/ctaes/ctaes.c
+++ b/src/crypto/ctaes/ctaes.c
@@ -134,7 +134,7 @@ static void SubBytes(AES_state *s, int inv) {
D = U7;
}
- /* Non-linear transformation (identical to the code in SubBytes) */
+ /* Non-linear transformation (shared between the forward and backward case) */
M1 = T13 & T6;
M6 = T3 & T16;
M11 = T1 & T15;
@@ -469,9 +469,9 @@ static void AES_encrypt(const AES_state* rounds, int nrounds, unsigned char* cip
static void AES_decrypt(const AES_state* rounds, int nrounds, unsigned char* plain16, const unsigned char* cipher16) {
/* Most AES decryption implementations use the alternate scheme
- * (the Equivalent Inverse Cipher), which looks more like encryption, but
- * needs different round constants. We can't reuse any code here anyway, so
- * don't bother. */
+ * (the Equivalent Inverse Cipher), which allows for more code reuse between
+ * the encryption and decryption code, but requires separate setup for both.
+ */
AES_state s = {{0}};
int round;
diff --git a/src/crypto/ctaes/test.c b/src/crypto/ctaes/test.c
index fce1696acd..21439a16f1 100644
--- a/src/crypto/ctaes/test.c
+++ b/src/crypto/ctaes/test.c
@@ -102,7 +102,7 @@ int main(void) {
}
}
if (fail == 0) {
- fprintf(stderr, "All tests succesful\n");
+ fprintf(stderr, "All tests successful\n");
} else {
fprintf(stderr, "%i tests failed\n", fail);
}
diff --git a/src/cuckoocache.h b/src/cuckoocache.h
new file mode 100644
index 0000000000..efd6a820b5
--- /dev/null
+++ b/src/cuckoocache.h
@@ -0,0 +1,457 @@
+// Copyright (c) 2016 Jeremy Rubin
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef _BITCOIN_CUCKOOCACHE_H_
+#define _BITCOIN_CUCKOOCACHE_H_
+
+#include <array>
+#include <algorithm>
+#include <atomic>
+#include <cstring>
+#include <cmath>
+#include <memory>
+#include <vector>
+
+
+/** namespace CuckooCache provides high performance cache primitives
+ *
+ * Summary:
+ *
+ * 1) bit_packed_atomic_flags is bit-packed atomic flags for garbage collection
+ *
+ * 2) cache is a cache which is performant in memory usage and lookup speed. It
+ * is lockfree for erase operations. Elements are lazily erased on the next
+ * insert.
+ */
+namespace CuckooCache
+{
+/** bit_packed_atomic_flags implements a container for garbage collection flags
+ * that is only thread unsafe on calls to setup. This class bit-packs collection
+ * flags for memory efficiency.
+ *
+ * All operations are std::memory_order_relaxed so external mechanisms must
+ * ensure that writes and reads are properly synchronized.
+ *
+ * On setup(n), all bits up to n are marked as collected.
+ *
+ * Under the hood, because it is an 8-bit type, it makes sense to use a multiple
+ * of 8 for setup, but it will be safe if that is not the case as well.
+ *
+ */
+class bit_packed_atomic_flags
+{
+ std::unique_ptr<std::atomic<uint8_t>[]> mem;
+
+public:
+ /** No default constructor as there must be some size */
+ bit_packed_atomic_flags() = delete;
+
+ /**
+ * bit_packed_atomic_flags constructor creates memory to sufficiently
+ * keep track of garbage collection information for size entries.
+ *
+ * @param size the number of elements to allocate space for
+ *
+ * @post bit_set, bit_unset, and bit_is_set function properly forall x. x <
+ * size
+ * @post All calls to bit_is_set (without subsequent bit_unset) will return
+ * true.
+ */
+ bit_packed_atomic_flags(uint32_t size)
+ {
+ // pad out the size if needed
+ size = (size + 7) / 8;
+ mem.reset(new std::atomic<uint8_t>[size]);
+ for (uint32_t i = 0; i < size; ++i)
+ mem[i].store(0xFF);
+ };
+
+ /** setup marks all entries and ensures that bit_packed_atomic_flags can store
+ * at least size entries
+ *
+ * @param b the number of elements to allocate space for
+ * @post bit_set, bit_unset, and bit_is_set function properly forall x. x <
+ * b
+ * @post All calls to bit_is_set (without subsequent bit_unset) will return
+ * true.
+ */
+ inline void setup(uint32_t b)
+ {
+ bit_packed_atomic_flags d(b);
+ std::swap(mem, d.mem);
+ }
+
+ /** bit_set sets an entry as discardable.
+ *
+ * @param s the index of the entry to bit_set.
+ * @post immediately subsequent call (assuming proper external memory
+ * ordering) to bit_is_set(s) == true.
+ *
+ */
+ inline void bit_set(uint32_t s)
+ {
+ mem[s >> 3].fetch_or(1 << (s & 7), std::memory_order_relaxed);
+ }
+
+ /** bit_unset marks an entry as something that should not be overwritten
+ *
+ * @param s the index of the entry to bit_unset.
+ * @post immediately subsequent call (assuming proper external memory
+ * ordering) to bit_is_set(s) == false.
+ */
+ inline void bit_unset(uint32_t s)
+ {
+ mem[s >> 3].fetch_and(~(1 << (s & 7)), std::memory_order_relaxed);
+ }
+
+ /** bit_is_set queries the table for discardability at s
+ *
+ * @param s the index of the entry to read.
+ * @returns if the bit at index s was set.
+ * */
+ inline bool bit_is_set(uint32_t s) const
+ {
+ return (1 << (s & 7)) & mem[s >> 3].load(std::memory_order_relaxed);
+ }
+};
+
+/** cache implements a cache with properties similar to a cuckoo-set
+ *
+ * The cache is able to hold up to (~(uint32_t)0) - 1 elements.
+ *
+ * Read Operations:
+ * - contains(*, false)
+ *
+ * Read+Erase Operations:
+ * - contains(*, true)
+ *
+ * Erase Operations:
+ * - allow_erase()
+ *
+ * Write Operations:
+ * - setup()
+ * - setup_bytes()
+ * - insert()
+ * - please_keep()
+ *
+ * Synchronization Free Operations:
+ * - invalid()
+ * - compute_hashes()
+ *
+ * User Must Guarantee:
+ *
+ * 1) Write Requires synchronized access (e.g., a lock)
+ * 2) Read Requires no concurrent Write, synchronized with the last insert.
+ * 3) Erase requires no concurrent Write, synchronized with last insert.
+ * 4) An Erase caller must release all memory before allowing a new Writer.
+ *
+ *
+ * Note on function names:
+ * - The name "allow_erase" is used because the real discard happens later.
+ * - The name "please_keep" is used because elements may be erased anyways on insert.
+ *
+ * @tparam Element should be a movable and copyable type
+ * @tparam Hash should be a function/callable which takes a template parameter
+ * hash_select and an Element and extracts a hash from it. Should return
+ * high-entropy hashes for `Hash h; h<0>(e) ... h<7>(e)`.
+ */
+template <typename Element, typename Hash>
+class cache
+{
+private:
+ /** table stores all the elements */
+ std::vector<Element> table;
+
+ /** size stores the total available slots in the hash table */
+ uint32_t size;
+
+ /** The bit_packed_atomic_flags array is marked mutable because we want
+ * garbage collection to be allowed to occur from const methods */
+ mutable bit_packed_atomic_flags collection_flags;
+
+ /** epoch_flags tracks how recently an element was inserted into
+ * the cache. true denotes recent, false denotes not-recent. See insert()
+ * method for full semantics.
+ */
+ mutable std::vector<bool> epoch_flags;
+
+ /** epoch_heuristic_counter is used to determine when a epoch might be aged
+ * & an expensive scan should be done. epoch_heuristic_counter is
+ * decremented on insert and reset to the new number of inserts which would
+ * cause the epoch to reach epoch_size when it reaches zero.
+ */
+ uint32_t epoch_heuristic_counter;
+
+ /** epoch_size is set to be the number of elements supposed to be in a
+ * epoch. When the number of non-erased elements in a epoch
+ * exceeds epoch_size, a new epoch should be started and all
+ * current entries demoted. epoch_size is set to be 45% of size because
+ * we want to keep load around 90%, and we support 3 epochs at once --
+ * one "dead" which has been erased, one "dying" which has been marked to be
+ * erased next, and one "living" which new inserts add to.
+ */
+ uint32_t epoch_size;
+
+ /** hash_mask should be set to appropriately mask out a hash such that every
+ * masked hash is [0,size), eg, if floor(log2(size)) == 20, then hash_mask
+ * should be (1<<20)-1
+ */
+ uint32_t hash_mask;
+
+ /** depth_limit determines how many elements insert should try to replace.
+ * Should be set to log2(n)*/
+ uint8_t depth_limit;
+
+ /** hash_function is a const instance of the hash function. It cannot be
+ * static or initialized at call time as it may have internal state (such as
+ * a nonce).
+ * */
+ const Hash hash_function;
+
+ /** compute_hashes is convenience for not having to write out this
+ * expression everywhere we use the hash values of an Element.
+ *
+ * @param e the element whose hashes will be returned
+ * @returns std::array<uint32_t, 8> of deterministic hashes derived from e
+ */
+ inline std::array<uint32_t, 8> compute_hashes(const Element& e) const
+ {
+ return {{hash_function.template operator()<0>(e) & hash_mask,
+ hash_function.template operator()<1>(e) & hash_mask,
+ hash_function.template operator()<2>(e) & hash_mask,
+ hash_function.template operator()<3>(e) & hash_mask,
+ hash_function.template operator()<4>(e) & hash_mask,
+ hash_function.template operator()<5>(e) & hash_mask,
+ hash_function.template operator()<6>(e) & hash_mask,
+ hash_function.template operator()<7>(e) & hash_mask}};
+ }
+
+ /* end
+ * @returns a constexpr index that can never be inserted to */
+ constexpr uint32_t invalid() const
+ {
+ return ~(uint32_t)0;
+ }
+
+ /** allow_erase marks the element at index n as discardable. Threadsafe
+ * without any concurrent insert.
+ * @param n the index to allow erasure of
+ */
+ inline void allow_erase(uint32_t n) const
+ {
+ collection_flags.bit_set(n);
+ }
+
+ /** please_keep marks the element at index n as an entry that should be kept.
+ * Threadsafe without any concurrent insert.
+ * @param n the index to prioritize keeping
+ */
+ inline void please_keep(uint32_t n) const
+ {
+ collection_flags.bit_unset(n);
+ }
+
+ /** epoch_check handles the changing of epochs for elements stored in the
+ * cache. epoch_check should be run before every insert.
+ *
+ * First, epoch_check decrements and checks the cheap heuristic, and then does
+ * a more expensive scan if the cheap heuristic runs out. If the expensive
+ * scan suceeds, the epochs are aged and old elements are allow_erased. The
+ * cheap heuristic is reset to retrigger after the worst case growth of the
+ * current epoch's elements would exceed the epoch_size.
+ */
+ void epoch_check()
+ {
+ if (epoch_heuristic_counter != 0) {
+ --epoch_heuristic_counter;
+ return;
+ }
+ // count the number of elements from the latest epoch which
+ // have not been erased.
+ uint32_t epoch_unused_count = 0;
+ for (uint32_t i = 0; i < size; ++i)
+ epoch_unused_count += epoch_flags[i] &&
+ !collection_flags.bit_is_set(i);
+ // If there are more non-deleted entries in the current epoch than the
+ // epoch size, then allow_erase on all elements in the old epoch (marked
+ // false) and move all elements in the current epoch to the old epoch
+ // but do not call allow_erase on their indices.
+ if (epoch_unused_count >= epoch_size) {
+ for (uint32_t i = 0; i < size; ++i)
+ if (epoch_flags[i])
+ epoch_flags[i] = false;
+ else
+ allow_erase(i);
+ epoch_heuristic_counter = epoch_size;
+ } else
+ // reset the epoch_heuristic_counter to next do a scan when worst
+ // case behavior (no intermittent erases) would exceed epoch size,
+ // with a reasonable minimum scan size.
+ // Ordinarily, we would have to sanity check std::min(epoch_size,
+ // epoch_unused_count), but we already know that `epoch_unused_count
+ // < epoch_size` in this branch
+ epoch_heuristic_counter = std::max(1u, std::max(epoch_size / 16,
+ epoch_size - epoch_unused_count));
+ }
+
+public:
+ /** You must always construct a cache with some elements via a subsequent
+ * call to setup or setup_bytes, otherwise operations may segfault.
+ */
+ cache() : table(), size(), collection_flags(0), epoch_flags(),
+ epoch_heuristic_counter(), epoch_size(), depth_limit(0), hash_function()
+ {
+ }
+
+ /** setup initializes the container to store no more than new_size
+ * elements. setup rounds down to a power of two size.
+ *
+ * setup should only be called once.
+ *
+ * @param new_size the desired number of elements to store
+ * @returns the maximum number of elements storable
+ **/
+ uint32_t setup(uint32_t new_size)
+ {
+ // depth_limit must be at least one otherwise errors can occur.
+ depth_limit = static_cast<uint8_t>(std::log2(static_cast<float>(std::max((uint32_t)2, new_size))));
+ size = 1 << depth_limit;
+ hash_mask = size-1;
+ table.resize(size);
+ collection_flags.setup(size);
+ epoch_flags.resize(size);
+ // Set to 45% as described above
+ epoch_size = std::max((uint32_t)1, (45 * size) / 100);
+ // Initially set to wait for a whole epoch
+ epoch_heuristic_counter = epoch_size;
+ return size;
+ }
+
+ /** setup_bytes is a convenience function which accounts for internal memory
+ * usage when deciding how many elements to store. It isn't perfect because
+ * it doesn't account for any overhead (struct size, MallocUsage, collection
+ * and epoch flags). This was done to simplify selecting a power of two
+ * size. In the expected use case, an extra two bits per entry should be
+ * negligible compared to the size of the elements.
+ *
+ * @param bytes the approximate number of bytes to use for this data
+ * structure.
+ * @returns the maximum number of elements storable (see setup()
+ * documentation for more detail)
+ */
+ uint32_t setup_bytes(size_t bytes)
+ {
+ return setup(bytes/sizeof(Element));
+ }
+
+ /** insert loops at most depth_limit times trying to insert a hash
+ * at various locations in the table via a variant of the Cuckoo Algorithm
+ * with eight hash locations.
+ *
+ * It drops the last tried element if it runs out of depth before
+ * encountering an open slot.
+ *
+ * Thus
+ *
+ * insert(x);
+ * return contains(x, false);
+ *
+ * is not guaranteed to return true.
+ *
+ * @param e the element to insert
+ * @post one of the following: All previously inserted elements and e are
+ * now in the table, one previously inserted element is evicted from the
+ * table, the entry attempted to be inserted is evicted.
+ *
+ */
+ inline void insert(Element e)
+ {
+ epoch_check();
+ uint32_t last_loc = invalid();
+ bool last_epoch = true;
+ std::array<uint32_t, 8> locs = compute_hashes(e);
+ // Make sure we have not already inserted this element
+ // If we have, make sure that it does not get deleted
+ for (uint32_t loc : locs)
+ if (table[loc] == e) {
+ please_keep(loc);
+ epoch_flags[loc] = last_epoch;
+ return;
+ }
+ for (uint8_t depth = 0; depth < depth_limit; ++depth) {
+ // First try to insert to an empty slot, if one exists
+ for (uint32_t loc : locs) {
+ if (!collection_flags.bit_is_set(loc))
+ continue;
+ table[loc] = std::move(e);
+ please_keep(loc);
+ epoch_flags[loc] = last_epoch;
+ return;
+ }
+ /** Swap with the element at the location that was
+ * not the last one looked at. Example:
+ *
+ * 1) On first iteration, last_loc == invalid(), find returns last, so
+ * last_loc defaults to locs[0].
+ * 2) On further iterations, where last_loc == locs[k], last_loc will
+ * go to locs[k+1 % 8], i.e., next of the 8 indicies wrapping around
+ * to 0 if needed.
+ *
+ * This prevents moving the element we just put in.
+ *
+ * The swap is not a move -- we must switch onto the evicted element
+ * for the next iteration.
+ */
+ last_loc = locs[(1 + (std::find(locs.begin(), locs.end(), last_loc) - locs.begin())) & 7];
+ std::swap(table[last_loc], e);
+ // Can't std::swap a std::vector<bool>::reference and a bool&.
+ bool epoch = last_epoch;
+ last_epoch = epoch_flags[last_loc];
+ epoch_flags[last_loc] = epoch;
+
+ // Recompute the locs -- unfortunately happens one too many times!
+ locs = compute_hashes(e);
+ }
+ }
+
+ /* contains iterates through the hash locations for a given element
+ * and checks to see if it is present.
+ *
+ * contains does not check garbage collected state (in other words,
+ * garbage is only collected when the space is needed), so:
+ *
+ * insert(x);
+ * if (contains(x, true))
+ * return contains(x, false);
+ * else
+ * return true;
+ *
+ * executed on a single thread will always return true!
+ *
+ * This is a great property for re-org performance for example.
+ *
+ * contains returns a bool set true if the element was found.
+ *
+ * @param e the element to check
+ * @param erase
+ *
+ * @post if erase is true and the element is found, then the garbage collect
+ * flag is set
+ * @returns true if the element is found, false otherwise
+ */
+ inline bool contains(const Element& e, const bool erase) const
+ {
+ std::array<uint32_t, 8> locs = compute_hashes(e);
+ for (uint32_t loc : locs)
+ if (table[loc] == e) {
+ if (erase)
+ allow_erase(loc);
+ return true;
+ }
+ return false;
+ }
+};
+} // namespace CuckooCache
+
+#endif
diff --git a/src/dbwrapper.h b/src/dbwrapper.h
index 47bdb31b5b..08c72c25dd 100644
--- a/src/dbwrapper.h
+++ b/src/dbwrapper.h
@@ -17,6 +17,9 @@
#include <leveldb/db.h>
#include <leveldb/write_batch.h>
+static const size_t DBWRAPPER_PREALLOC_KEY_SIZE = 64;
+static const size_t DBWRAPPER_PREALLOC_VALUE_SIZE = 1024;
+
class dbwrapper_error : public std::runtime_error
{
public:
@@ -50,38 +53,41 @@ private:
const CDBWrapper &parent;
leveldb::WriteBatch batch;
+ CDataStream ssKey;
+ CDataStream ssValue;
+
public:
/**
* @param[in] _parent CDBWrapper that this batch is to be submitted to
*/
- CDBBatch(const CDBWrapper &_parent) : parent(_parent) { };
+ CDBBatch(const CDBWrapper &_parent) : parent(_parent), ssKey(SER_DISK, CLIENT_VERSION), ssValue(SER_DISK, CLIENT_VERSION) { };
template <typename K, typename V>
void Write(const K& key, const V& value)
{
- CDataStream ssKey(SER_DISK, CLIENT_VERSION);
- ssKey.reserve(ssKey.GetSerializeSize(key));
+ ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
ssKey << key;
leveldb::Slice slKey(&ssKey[0], ssKey.size());
- CDataStream ssValue(SER_DISK, CLIENT_VERSION);
- ssValue.reserve(ssValue.GetSerializeSize(value));
+ ssValue.reserve(DBWRAPPER_PREALLOC_VALUE_SIZE);
ssValue << value;
ssValue.Xor(dbwrapper_private::GetObfuscateKey(parent));
leveldb::Slice slValue(&ssValue[0], ssValue.size());
batch.Put(slKey, slValue);
+ ssKey.clear();
+ ssValue.clear();
}
template <typename K>
void Erase(const K& key)
{
- CDataStream ssKey(SER_DISK, CLIENT_VERSION);
- ssKey.reserve(ssKey.GetSerializeSize(key));
+ ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
ssKey << key;
leveldb::Slice slKey(&ssKey[0], ssKey.size());
batch.Delete(slKey);
+ ssKey.clear();
}
};
@@ -107,7 +113,7 @@ public:
template<typename K> void Seek(const K& key) {
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
- ssKey.reserve(ssKey.GetSerializeSize(key));
+ ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
ssKey << key;
leveldb::Slice slKey(&ssKey[0], ssKey.size());
piter->Seek(slKey);
@@ -200,7 +206,7 @@ public:
bool Read(const K& key, V& value) const
{
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
- ssKey.reserve(ssKey.GetSerializeSize(key));
+ ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
ssKey << key;
leveldb::Slice slKey(&ssKey[0], ssKey.size());
@@ -234,7 +240,7 @@ public:
bool Exists(const K& key) const
{
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
- ssKey.reserve(ssKey.GetSerializeSize(key));
+ ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
ssKey << key;
leveldb::Slice slKey(&ssKey[0], ssKey.size());
diff --git a/src/hash.h b/src/hash.h
index db4e130ae7..94e7f5ea6c 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -132,15 +132,17 @@ class CHashWriter
private:
CHash256 ctx;
+ const int nType;
+ const int nVersion;
public:
- int nType;
- int nVersion;
CHashWriter(int nTypeIn, int nVersionIn) : nType(nTypeIn), nVersion(nVersionIn) {}
- CHashWriter& write(const char *pch, size_t size) {
+ int GetType() const { return nType; }
+ int GetVersion() const { return nVersion; }
+
+ void write(const char *pch, size_t size) {
ctx.Write((const unsigned char*)pch, size);
- return (*this);
}
// invalidates the object
@@ -153,7 +155,7 @@ public:
template<typename T>
CHashWriter& operator<<(const T& obj) {
// Serialize to this stream
- ::Serialize(*this, obj, nType, nVersion);
+ ::Serialize(*this, obj);
return (*this);
}
};
diff --git a/src/httprpc.cpp b/src/httprpc.cpp
index 6a6c5276cc..e35acb6cd9 100644
--- a/src/httprpc.cpp
+++ b/src/httprpc.cpp
@@ -127,7 +127,7 @@ static bool multiUserAuthorized(std::string strUserPass)
return false;
}
-static bool RPCAuthorized(const std::string& strAuth)
+static bool RPCAuthorized(const std::string& strAuth, std::string& strAuthUsernameOut)
{
if (strRPCUserColonPass.empty()) // Belt-and-suspenders measure if InitRPCAuthentication was not called
return false;
@@ -136,7 +136,10 @@ static bool RPCAuthorized(const std::string& strAuth)
std::string strUserPass64 = strAuth.substr(6);
boost::trim(strUserPass64);
std::string strUserPass = DecodeBase64(strUserPass64);
-
+
+ if (strUserPass.find(":") != std::string::npos)
+ strAuthUsernameOut = strUserPass.substr(0, strUserPass.find(":"));
+
//Check if authorized under single-user field
if (TimingResistantEqual(strUserPass, strRPCUserColonPass)) {
return true;
@@ -159,7 +162,8 @@ static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &)
return false;
}
- if (!RPCAuthorized(authHeader.second)) {
+ JSONRPCRequest jreq;
+ if (!RPCAuthorized(authHeader.second, jreq.authUser)) {
LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", req->GetPeer().ToString());
/* Deter brute-forcing
@@ -172,19 +176,21 @@ static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &)
return false;
}
- JSONRequest jreq;
try {
// Parse request
UniValue valRequest;
if (!valRequest.read(req->ReadBody()))
throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");
+ // Set the URI
+ jreq.URI = req->GetURI();
+
std::string strReply;
// singleton request
if (valRequest.isObject()) {
jreq.parse(valRequest);
- UniValue result = tableRPC.execute(jreq.strMethod, jreq.params);
+ UniValue result = tableRPC.execute(jreq);
// Send reply
strReply = JSONRPCReply(result, NullUniValue, jreq.id);
diff --git a/src/init.cpp b/src/init.cpp
index e9552da67d..e642729a99 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -19,10 +19,11 @@
#include "httpserver.h"
#include "httprpc.h"
#include "key.h"
-#include "main.h"
+#include "validation.h"
#include "miner.h"
#include "netbase.h"
#include "net.h"
+#include "net_processing.h"
#include "policy/policy.h"
#include "rpc/server.h"
#include "rpc/register.h"
@@ -40,6 +41,7 @@
#ifdef ENABLE_WALLET
#include "wallet/wallet.h"
#endif
+#include "warnings.h"
#include <stdint.h>
#include <stdio.h>
#include <memory>
@@ -72,6 +74,7 @@ static const bool DEFAULT_DISABLE_SAFEMODE = false;
static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false;
std::unique_ptr<CConnman> g_connman;
+std::unique_ptr<PeerLogicValidation> peerLogic;
#if ENABLE_ZMQ
static CZMQNotificationInterface* pzmqNotificationInterface = NULL;
@@ -200,11 +203,13 @@ void Shutdown()
pwalletMain->Flush(false);
#endif
MapPort(false);
- g_connman->Stop();
+ UnregisterValidationInterface(peerLogic.get());
+ peerLogic.reset();
g_connman.reset();
StopTorControl();
UnregisterNodeSignals(GetNodeSignals());
+ DumpMempool();
if (fFeeEstimatesInitialized)
{
@@ -325,7 +330,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME));
if (mode == HMM_BITCOIND)
{
-#ifndef WIN32
+#if HAVE_DECL_DAEMON
strUsage += HelpMessageOpt("-daemon", _("Run in the background as a daemon and accept commands"));
#endif
}
@@ -357,13 +362,13 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-banscore=<n>", strprintf(_("Threshold for disconnecting misbehaving peers (default: %u)"), DEFAULT_BANSCORE_THRESHOLD));
strUsage += HelpMessageOpt("-bantime=<n>", strprintf(_("Number of seconds to keep misbehaving peers from reconnecting (default: %u)"), DEFAULT_MISBEHAVING_BANTIME));
strUsage += HelpMessageOpt("-bind=<addr>", _("Bind to given address and always listen on it. Use [host]:port notation for IPv6"));
- strUsage += HelpMessageOpt("-connect=<ip>", _("Connect only to the specified node(s)"));
+ strUsage += HelpMessageOpt("-connect=<ip>", _("Connect only to the specified node(s); -noconnect or -connect=0 alone to disable automatic connections"));
strUsage += HelpMessageOpt("-discover", _("Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)"));
strUsage += HelpMessageOpt("-dns", _("Allow DNS lookups for -addnode, -seednode and -connect") + " " + strprintf(_("(default: %u)"), DEFAULT_NAME_LOOKUP));
- strUsage += HelpMessageOpt("-dnsseed", _("Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect)"));
+ strUsage += HelpMessageOpt("-dnsseed", _("Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect/-noconnect)"));
strUsage += HelpMessageOpt("-externalip=<ip>", _("Specify your own public address"));
strUsage += HelpMessageOpt("-forcednsseed", strprintf(_("Always query for peer addresses via DNS lookup (default: %u)"), DEFAULT_FORCEDNSSEED));
- strUsage += HelpMessageOpt("-listen", _("Accept connections from outside (default: 1 if no -proxy or -connect)"));
+ strUsage += HelpMessageOpt("-listen", _("Accept connections from outside (default: 1 if no -proxy or -connect/-noconnect)"));
strUsage += HelpMessageOpt("-listenonion", strprintf(_("Automatically create Tor hidden service (default: %d)"), DEFAULT_LISTEN_ONION));
strUsage += HelpMessageOpt("-maxconnections=<n>", strprintf(_("Maintain at most <n> connections to peers (default: %u)"), DEFAULT_MAX_PEER_CONNECTIONS));
strUsage += HelpMessageOpt("-maxreceivebuffer=<n>", strprintf(_("Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)"), DEFAULT_MAXRECEIVEBUFFER));
@@ -376,6 +381,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-port=<port>", strprintf(_("Listen for connections on <port> (default: %u or testnet: %u)"), Params(CBaseChainParams::MAIN).GetDefaultPort(), Params(CBaseChainParams::TESTNET).GetDefaultPort()));
strUsage += HelpMessageOpt("-proxy=<ip:port>", _("Connect through SOCKS5 proxy"));
strUsage += HelpMessageOpt("-proxyrandomize", strprintf(_("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)"), DEFAULT_PROXYRANDOMIZE));
+ strUsage += HelpMessageOpt("-rpcserialversion", strprintf(_("Sets the serialization of raw transaction or block hex returned in non-verbose mode, non-segwit(0) or segwit(1) (default: %d)"), DEFAULT_RPC_SERIALIZE_VERSION));
strUsage += HelpMessageOpt("-seednode=<ip>", _("Connect to a node to retrieve peer addresses, and disconnect"));
strUsage += HelpMessageOpt("-timeout=<n>", strprintf(_("Specify connection timeout in milliseconds (minimum: 1, default: %d)"), DEFAULT_CONNECT_TIMEOUT));
strUsage += HelpMessageOpt("-torcontrol=<ip>:<port>", strprintf(_("Tor control port to use if onion listening enabled (default: %s)"), DEFAULT_TOR_CONTROL));
@@ -388,7 +394,7 @@ std::string HelpMessage(HelpMessageMode mode)
#endif
#endif
strUsage += HelpMessageOpt("-whitebind=<addr>", _("Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6"));
- strUsage += HelpMessageOpt("-whitelist=<netmask>", _("Whitelist peers connecting from the given netmask or IP address. Can be specified multiple times.") +
+ strUsage += HelpMessageOpt("-whitelist=<IP address or network>", _("Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR notated network (e.g. 1.2.3.0/24). Can be specified multiple times.") +
" " + _("Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway"));
strUsage += HelpMessageOpt("-whitelistrelay", strprintf(_("Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d)"), DEFAULT_WHITELISTRELAY));
strUsage += HelpMessageOpt("-whitelistforcerelay", strprintf(_("Force relay of transactions from whitelisted peers even if they violate local relay policy (default: %d)"), DEFAULT_WHITELISTFORCERELAY));
@@ -424,7 +430,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-limitdescendantsize=<n>", strprintf("Do not accept transactions if any ancestor would have more than <n> kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT));
strUsage += HelpMessageOpt("-bip9params=deployment:start:end", "Use given start/end times for specified BIP9 deployment (regtest-only)");
}
- string debugCategories = "addrman, alert, bench, coindb, db, http, libevent, lock, mempool, mempoolrej, net, proxy, prune, rand, reindex, rpc, selectcoins, tor, zmq"; // Don't translate these and qt below
+ string debugCategories = "addrman, alert, bench, cmpctblock, coindb, db, http, libevent, lock, mempool, mempoolrej, net, proxy, prune, rand, reindex, rpc, selectcoins, tor, zmq"; // Don't translate these and qt below
if (mode == HMM_BITCOIN_QT)
debugCategories += ", qt";
strUsage += HelpMessageOpt("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " +
@@ -598,6 +604,8 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles)
{
const CChainParams& chainparams = Params();
RenameThread("bitcoin-loadblk");
+
+ {
CImportingNow imp;
// -reindex
@@ -657,6 +665,8 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles)
LogPrintf("Stopping after block import\n");
StartShutdown();
}
+ } // End scope of CImportingNow
+ LoadMempool();
}
/** Sanity checks
@@ -744,26 +754,10 @@ void InitParameterInteraction()
LogPrintf("%s: parameter interaction: -externalip set -> setting -discover=0\n", __func__);
}
- if (GetBoolArg("-salvagewallet", false)) {
- // Rewrite just private keys: rescan to find transactions
- if (SoftSetBoolArg("-rescan", true))
- LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting -rescan=1\n", __func__);
- }
-
- // -zapwallettx implies a rescan
- if (GetBoolArg("-zapwallettxes", false)) {
- if (SoftSetBoolArg("-rescan", true))
- LogPrintf("%s: parameter interaction: -zapwallettxes=<mode> -> setting -rescan=1\n", __func__);
- }
-
- // disable walletbroadcast and whitelistrelay in blocksonly mode
+ // disable whitelistrelay in blocksonly mode
if (GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)) {
if (SoftSetBoolArg("-whitelistrelay", false))
LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -whitelistrelay=0\n", __func__);
-#ifdef ENABLE_WALLET
- if (SoftSetBoolArg("-walletbroadcast", false))
- LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__);
-#endif
}
// Forcing relay from whitelisted hosts implies we will accept relays from them in the first place.
@@ -789,10 +783,17 @@ void InitLogging()
LogPrintf("Bitcoin version %s\n", FormatFullVersion());
}
-/** Initialize bitcoin.
- * @pre Parameters should be parsed and config file should be read.
- */
-bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
+namespace { // Variables internal to initialization process only
+
+ServiceFlags nRelevantServices = NODE_NETWORK;
+int nMaxConnections;
+int nUserMaxConnections;
+int nFD;
+ServiceFlags nLocalServices = NODE_NETWORK;
+
+}
+
+bool AppInitBasicSetup()
{
// ********************************************************* Step 1: setup
#ifdef _MSC_VER
@@ -822,12 +823,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
return InitError("Initializing networking failed");
#ifndef WIN32
- if (GetBoolArg("-sysperms", false)) {
-#ifdef ENABLE_WALLET
- if (!GetBoolArg("-disablewallet", false))
- return InitError("-sysperms is not allowed in combination with enabled wallet functionality");
-#endif
- } else {
+ if (!GetBoolArg("-sysperms", false)) {
umask(077);
}
@@ -849,31 +845,32 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
// Ignore SIGPIPE, otherwise it will bring the daemon down if the client closes unexpectedly
signal(SIGPIPE, SIG_IGN);
#endif
+ return true;
+}
- // ********************************************************* Step 2: parameter interactions
+bool AppInitParameterInteraction()
+{
const CChainParams& chainparams = Params();
+ // ********************************************************* Step 2: parameter interactions
// also see: InitParameterInteraction()
- // if using block pruning, then disable txindex
+ // if using block pruning, then disallow txindex
if (GetArg("-prune", 0)) {
if (GetBoolArg("-txindex", DEFAULT_TXINDEX))
return InitError(_("Prune mode is incompatible with -txindex."));
-#ifdef ENABLE_WALLET
- if (GetBoolArg("-rescan", false)) {
- return InitError(_("Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again."));
- }
-#endif
}
// Make sure enough file descriptors are available
- int nBind = std::max((int)mapArgs.count("-bind") + (int)mapArgs.count("-whitebind"), 1);
- int nUserMaxConnections = GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS);
- int nMaxConnections = std::max(nUserMaxConnections, 0);
+ int nBind = std::max(
+ (mapMultiArgs.count("-bind") ? mapMultiArgs.at("-bind").size() : 0) +
+ (mapMultiArgs.count("-whitebind") ? mapMultiArgs.at("-whitebind").size() : 0), size_t(1));
+ nUserMaxConnections = GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS);
+ nMaxConnections = std::max(nUserMaxConnections, 0);
// Trim requested connection counts, to fit into system limitations
nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS)), 0);
- int nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS);
+ nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS);
if (nFD < MIN_CORE_FILEDESCRIPTORS)
return InitError(_("Not enough file descriptors available."));
nMaxConnections = std::min(nFD - MIN_CORE_FILEDESCRIPTORS, nMaxConnections);
@@ -931,8 +928,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
else if (nScriptCheckThreads > MAX_SCRIPTCHECK_THREADS)
nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS;
- fServer = GetBoolArg("-server", false);
-
// block pruning; get the amount of disk space (in MiB) to allot for block & undo files
int64_t nSignedPruneTarget = GetArg("-prune", 0) * 1024 * 1024;
if (nSignedPruneTarget < 0) {
@@ -949,9 +944,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
RegisterAllCoreRPCCommands(tableRPC);
#ifdef ENABLE_WALLET
- bool fDisableWallet = GetBoolArg("-disablewallet", false);
- if (!fDisableWallet)
- RegisterWalletRPCCommands(tableRPC);
+ RegisterWalletRPCCommands(tableRPC);
#endif
nConnectTimeout = GetArg("-timeout", DEFAULT_CONNECT_TIMEOUT);
@@ -967,21 +960,21 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if (mapArgs.count("-minrelaytxfee"))
{
CAmount n = 0;
- if (ParseMoney(mapArgs["-minrelaytxfee"], n) && n > 0)
- ::minRelayTxFee = CFeeRate(n);
- else
+ if (!ParseMoney(mapArgs["-minrelaytxfee"], n) || 0 == n)
return InitError(AmountErrMsg("minrelaytxfee", mapArgs["-minrelaytxfee"]));
+ // High fee check is done afterward in CWallet::ParameterInteraction()
+ ::minRelayTxFee = CFeeRate(n);
}
- fRequireStandard = !GetBoolArg("-acceptnonstdtxn", !Params().RequireStandard());
- if (Params().RequireStandard() && !fRequireStandard)
+ fRequireStandard = !GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard());
+ if (chainparams.RequireStandard() && !fRequireStandard)
return InitError(strprintf("acceptnonstdtxn is not currently supported for %s chain", chainparams.NetworkIDString()));
nBytesPerSigOp = GetArg("-bytespersigop", nBytesPerSigOp);
#ifdef ENABLE_WALLET
if (!CWallet::ParameterInteraction())
return false;
-#endif // ENABLE_WALLET
+#endif
fIsBareMultisigStd = GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG);
fAcceptDatacarrier = GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER);
@@ -990,12 +983,15 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
// Option to startup with mocktime set (used for regression testing):
SetMockTime(GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op
- ServiceFlags nLocalServices = NODE_NETWORK;
- ServiceFlags nRelevantServices = NODE_NETWORK;
-
if (GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS))
nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM);
+ if (GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) < 0)
+ return InitError("rpcserialversion must be non-negative.");
+
+ if (GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) > 1)
+ return InitError("unknown rpcserialversion requested.");
+
nMaxTipAge = GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
fEnableReplacement = GetBoolArg("-mempoolreplacement", DEFAULT_ENABLE_REPLACEMENT);
@@ -1009,7 +1005,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if (!mapMultiArgs["-bip9params"].empty()) {
// Allow overriding BIP9 parameters for testing
- if (!Params().MineBlocksOnDemand()) {
+ if (!chainparams.MineBlocksOnDemand()) {
return InitError("BIP9 parameters may only be overridden on regtest.");
}
const vector<string>& deployments = mapMultiArgs["-bip9params"];
@@ -1041,17 +1037,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
}
}
}
+ return true;
+}
- // ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log
-
- // Initialize elliptic curve code
- ECC_Start();
- globalVerifyHandle.reset(new ECCVerifyHandle());
-
- // Sanity check
- if (!InitSanityCheck())
- return InitError(strprintf(_("Initialization sanity check failed. %s is shutting down."), _(PACKAGE_NAME)));
-
+static bool LockDataDirectory(bool probeOnly)
+{
std::string strDataDir = GetDataDir().string();
// Make sure only a single Bitcoin process is using the data directory.
@@ -1061,11 +1051,45 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
try {
static boost::interprocess::file_lock lock(pathLockFile.string().c_str());
- if (!lock.try_lock())
+ if (!lock.try_lock()) {
return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running."), strDataDir, _(PACKAGE_NAME)));
+ }
+ if (probeOnly) {
+ lock.unlock();
+ }
} catch(const boost::interprocess::interprocess_exception& e) {
return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running.") + " %s.", strDataDir, _(PACKAGE_NAME), e.what()));
}
+ return true;
+}
+
+bool AppInitSanityChecks()
+{
+ // ********************************************************* Step 4: sanity checks
+
+ // Initialize elliptic curve code
+ ECC_Start();
+ globalVerifyHandle.reset(new ECCVerifyHandle());
+
+ // Sanity check
+ if (!InitSanityCheck())
+ return InitError(strprintf(_("Initialization sanity check failed. %s is shutting down."), _(PACKAGE_NAME)));
+
+ // Probe the data directory lock to give an early error message, if possible
+ return LockDataDirectory(true);
+}
+
+bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
+{
+ const CChainParams& chainparams = Params();
+ // ********************************************************* Step 4a: application initialization
+ // After daemonization get the data directory lock again and hold on to it until exit
+ // This creates a slight window for a race condition to happen, however this condition is harmless: it
+ // will at most make us exit without printing a message to console.
+ if (!LockDataDirectory(false)) {
+ // Detailed error printed inside LockDataDirectory
+ return false;
+ }
#ifndef WIN32
CreatePidFile(GetPidFile(), getpid());
@@ -1079,10 +1103,12 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if (!fLogTimestamps)
LogPrintf("Startup time: %s\n", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime()));
LogPrintf("Default data directory %s\n", GetDefaultDataDir().string());
- LogPrintf("Using data directory %s\n", strDataDir);
- LogPrintf("Using config file %s\n", GetConfigFile().string());
+ LogPrintf("Using data directory %s\n", GetDataDir().string());
+ LogPrintf("Using config file %s\n", GetConfigFile(GetArg("-conf", BITCOIN_CONF_FILENAME)).string());
LogPrintf("Using at most %i connections (%i file descriptors available)\n", nMaxConnections, nFD);
+ InitSignatureCache();
+
LogPrintf("Using %u threads for script verification\n", nScriptCheckThreads);
if (nScriptCheckThreads) {
for (int i=0; i<nScriptCheckThreads-1; i++)
@@ -1098,7 +1124,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
* that the server is there and will be ready later). Warmup mode will
* be disabled when initialisation is finished.
*/
- if (fServer)
+ if (GetBoolArg("-server", false))
{
uiInterface.InitMessage.connect(SetRPCWarmupStatus);
if (!AppInitServers(threadGroup))
@@ -1109,17 +1135,21 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
// ********************************************************* Step 5: verify wallet database integrity
#ifdef ENABLE_WALLET
- if (!fDisableWallet) {
- if (!CWallet::Verify())
- return false;
- } // (!fDisableWallet)
-#endif // ENABLE_WALLET
+ if (!CWallet::Verify())
+ return false;
+#endif
// ********************************************************* Step 6: network initialization
+ // Note that we absolutely cannot open any actual connections
+ // until the very end ("start node") as the UTXO/block state
+ // is not yet setup and may end up being set up twice if we
+ // need to reindex later.
assert(!g_connman);
- g_connman = std::unique_ptr<CConnman>(new CConnman());
+ g_connman = std::unique_ptr<CConnman>(new CConnman(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max())));
CConnman& connman = *g_connman;
+ peerLogic.reset(new PeerLogicValidation(&connman));
+ RegisterValidationInterface(peerLogic.get());
RegisterNodeSignals(GetNodeSignals());
// sanitize comments per BIP-0014, format user agent and check total size
@@ -1250,8 +1280,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
RegisterValidationInterface(pzmqNotificationInterface);
}
#endif
+ uint64_t nMaxOutboundLimit = 0; //unlimited unless -maxuploadtarget is set
+ uint64_t nMaxOutboundTimeframe = MAX_UPLOAD_TIMEFRAME;
+
if (mapArgs.count("-maxuploadtarget")) {
- connman.SetMaxOutboundTarget(GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET)*1024*1024);
+ nMaxOutboundLimit = GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET)*1024*1024;
}
// ********************************************************* Step 7: load block chain
@@ -1330,7 +1363,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
CleanupBlockRevFiles();
}
- if (!LoadBlockIndex()) {
+ if (!LoadBlockIndex(chainparams)) {
strLoadError = _("Error loading block database");
break;
}
@@ -1438,17 +1471,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
// ********************************************************* Step 8: load wallet
#ifdef ENABLE_WALLET
- if (fDisableWallet) {
- pwalletMain = NULL;
- LogPrintf("Wallet disabled!\n");
- } else {
- CWallet::InitLoadWallet();
- if (!pwalletMain)
- return false;
- }
-#else // ENABLE_WALLET
+ if (!CWallet::InitLoadWallet())
+ return false;
+#else
LogPrintf("No wallet support compiled in!\n");
-#endif // !ENABLE_WALLET
+#endif
// ********************************************************* Step 9: data directory maintenance
@@ -1463,7 +1490,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
}
}
- if (Params().GetConsensus().vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout != 0) {
+ if (chainparams.GetConsensus().vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout != 0) {
// Only advertize witness capabilities if they have a reasonable start time.
// This allows us to have the code merged without a defined softfork, by setting its
// end time to 0.
@@ -1534,6 +1561,9 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
connOptions.nSendBufferMaxSize = 1000*GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER);
connOptions.nReceiveFloodSize = 1000*GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER);
+ connOptions.nMaxOutboundTimeframe = nMaxOutboundTimeframe;
+ connOptions.nMaxOutboundLimit = nMaxOutboundLimit;
+
if(!connman.Start(threadGroup, scheduler, strNodeError, connOptions))
return InitError(strNodeError);
@@ -1543,10 +1573,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
uiInterface.InitMessage(_("Done loading"));
#ifdef ENABLE_WALLET
- if (pwalletMain) {
- // Run a thread to flush wallet periodically
- threadGroup.create_thread(boost::bind(&ThreadFlushWalletDB, boost::ref(pwalletMain->strWalletFile)));
- }
+ if (pwalletMain)
+ pwalletMain->postInitProcess(threadGroup);
#endif
return !fRequestShutdown;
diff --git a/src/init.h b/src/init.h
index 63e07ccb3c..1b87e0ec52 100644
--- a/src/init.h
+++ b/src/init.h
@@ -25,7 +25,30 @@ void Shutdown();
void InitLogging();
//!Parameter interaction: change current parameters depending on various rules
void InitParameterInteraction();
-bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler);
+
+/** Initialize bitcoin core: Basic context setup.
+ * @note This can be done before daemonization.
+ * @pre Parameters should be parsed and config file should be read.
+ */
+bool AppInitBasicSetup();
+/**
+ * Initialization: parameter interaction.
+ * @note This can be done before daemonization.
+ * @pre Parameters should be parsed and config file should be read, AppInitBasicSetup should have been called.
+ */
+bool AppInitParameterInteraction();
+/**
+ * Initialization sanity checks: ecc init, sanity checks, dir lock.
+ * @note This can be done before daemonization.
+ * @pre Parameters should be parsed and config file should be read, AppInitParameterInteraction should have been called.
+ */
+bool AppInitSanityChecks();
+/**
+ * Bitcoin core main initialization.
+ * @note This should only be done after daemonization.
+ * @pre Parameters should be parsed and config file should be read, AppInitSanityChecks should have been called.
+ */
+bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler);
/** The help message mode determines what help message to show */
enum HelpMessageMode {
diff --git a/src/key.cpp b/src/key.cpp
index 79023566c3..b3ea98fb92 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -125,8 +125,8 @@ bool CKey::Check(const unsigned char *vch) {
void CKey::MakeNewKey(bool fCompressedIn) {
do {
- GetStrongRandBytes(vch, sizeof(vch));
- } while (!Check(vch));
+ GetStrongRandBytes(keydata.data(), keydata.size());
+ } while (!Check(keydata.data()));
fValid = true;
fCompressed = fCompressedIn;
}
@@ -224,41 +224,37 @@ bool CKey::Load(CPrivKey &privkey, CPubKey &vchPubKey, bool fSkipCheck=false) {
bool CKey::Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const {
assert(IsValid());
assert(IsCompressed());
- unsigned char out[64];
- LockObject(out);
+ std::vector<unsigned char, secure_allocator<unsigned char>> vout(64);
if ((nChild >> 31) == 0) {
CPubKey pubkey = GetPubKey();
assert(pubkey.begin() + 33 == pubkey.end());
- BIP32Hash(cc, nChild, *pubkey.begin(), pubkey.begin()+1, out);
+ BIP32Hash(cc, nChild, *pubkey.begin(), pubkey.begin()+1, vout.data());
} else {
assert(begin() + 32 == end());
- BIP32Hash(cc, nChild, 0, begin(), out);
+ BIP32Hash(cc, nChild, 0, begin(), vout.data());
}
- memcpy(ccChild.begin(), out+32, 32);
+ memcpy(ccChild.begin(), vout.data()+32, 32);
memcpy((unsigned char*)keyChild.begin(), begin(), 32);
- bool ret = secp256k1_ec_privkey_tweak_add(secp256k1_context_sign, (unsigned char*)keyChild.begin(), out);
- UnlockObject(out);
+ bool ret = secp256k1_ec_privkey_tweak_add(secp256k1_context_sign, (unsigned char*)keyChild.begin(), vout.data());
keyChild.fCompressed = true;
keyChild.fValid = ret;
return ret;
}
-bool CExtKey::Derive(CExtKey &out, unsigned int nChild) const {
+bool CExtKey::Derive(CExtKey &out, unsigned int _nChild) const {
out.nDepth = nDepth + 1;
CKeyID id = key.GetPubKey().GetID();
memcpy(&out.vchFingerprint[0], &id, 4);
- out.nChild = nChild;
- return key.Derive(out.key, out.chaincode, nChild, chaincode);
+ out.nChild = _nChild;
+ return key.Derive(out.key, out.chaincode, _nChild, chaincode);
}
void CExtKey::SetMaster(const unsigned char *seed, unsigned int nSeedLen) {
static const unsigned char hashkey[] = {'B','i','t','c','o','i','n',' ','s','e','e','d'};
- unsigned char out[64];
- LockObject(out);
- CHMAC_SHA512(hashkey, sizeof(hashkey)).Write(seed, nSeedLen).Finalize(out);
- key.Set(&out[0], &out[32], true);
- memcpy(chaincode.begin(), &out[32], 32);
- UnlockObject(out);
+ std::vector<unsigned char, secure_allocator<unsigned char>> vout(64);
+ CHMAC_SHA512(hashkey, sizeof(hashkey)).Write(seed, nSeedLen).Finalize(vout.data());
+ key.Set(&vout[0], &vout[32], true);
+ memcpy(chaincode.begin(), &vout[32], 32);
nDepth = 0;
nChild = 0;
memset(vchFingerprint, 0, sizeof(vchFingerprint));
@@ -308,12 +304,10 @@ void ECC_Start() {
{
// Pass in a random blinding seed to the secp256k1 context.
- unsigned char seed[32];
- LockObject(seed);
- GetRandBytes(seed, 32);
- bool ret = secp256k1_context_randomize(ctx, seed);
+ std::vector<unsigned char, secure_allocator<unsigned char>> vseed(32);
+ GetRandBytes(vseed.data(), 32);
+ bool ret = secp256k1_context_randomize(ctx, vseed.data());
assert(ret);
- UnlockObject(seed);
}
secp256k1_context_sign = ctx;
diff --git a/src/key.h b/src/key.h
index b589710bad..ff5252b7a0 100644
--- a/src/key.h
+++ b/src/key.h
@@ -43,9 +43,7 @@ private:
bool fCompressed;
//! The actual byte data
- unsigned char vch[32];
-
- static_assert(sizeof(vch) == 32, "vch must be 32 bytes in length to not break serialization");
+ std::vector<unsigned char, secure_allocator<unsigned char> > keydata;
//! Check whether the 32-byte array pointed to be vch is valid keydata.
bool static Check(const unsigned char* vch);
@@ -54,37 +52,30 @@ public:
//! Construct an invalid private key.
CKey() : fValid(false), fCompressed(false)
{
- LockObject(vch);
- }
-
- //! Copy constructor. This is necessary because of memlocking.
- CKey(const CKey& secret) : fValid(secret.fValid), fCompressed(secret.fCompressed)
- {
- LockObject(vch);
- memcpy(vch, secret.vch, sizeof(vch));
+ // Important: vch must be 32 bytes in length to not break serialization
+ keydata.resize(32);
}
//! Destructor (again necessary because of memlocking).
~CKey()
{
- UnlockObject(vch);
}
friend bool operator==(const CKey& a, const CKey& b)
{
return a.fCompressed == b.fCompressed &&
a.size() == b.size() &&
- memcmp(&a.vch[0], &b.vch[0], a.size()) == 0;
+ memcmp(a.keydata.data(), b.keydata.data(), a.size()) == 0;
}
//! Initialize using begin and end iterators to byte data.
template <typename T>
void Set(const T pbegin, const T pend, bool fCompressedIn)
{
- if (pend - pbegin != sizeof(vch)) {
+ if (size_t(pend - pbegin) != keydata.size()) {
fValid = false;
} else if (Check(&pbegin[0])) {
- memcpy(vch, (unsigned char*)&pbegin[0], sizeof(vch));
+ memcpy(keydata.data(), (unsigned char*)&pbegin[0], keydata.size());
fValid = true;
fCompressed = fCompressedIn;
} else {
@@ -93,9 +84,9 @@ public:
}
//! Simple read-only vector-like interface.
- unsigned int size() const { return (fValid ? sizeof(vch) : 0); }
- const unsigned char* begin() const { return vch; }
- const unsigned char* end() const { return vch + size(); }
+ unsigned int size() const { return (fValid ? keydata.size() : 0); }
+ const unsigned char* begin() const { return keydata.data(); }
+ const unsigned char* end() const { return keydata.data() + size(); }
//! Check whether this private key is valid.
bool IsValid() const { return fValid; }
@@ -171,7 +162,7 @@ struct CExtKey {
CExtPubKey Neuter() const;
void SetMaster(const unsigned char* seed, unsigned int nSeedLen);
template <typename Stream>
- void Serialize(Stream& s, int nType, int nVersion) const
+ void Serialize(Stream& s) const
{
unsigned int len = BIP32_EXTKEY_SIZE;
::WriteCompactSize(s, len);
@@ -180,7 +171,7 @@ struct CExtKey {
s.write((const char *)&code[0], len);
}
template <typename Stream>
- void Unserialize(Stream& s, int nType, int nVersion)
+ void Unserialize(Stream& s)
{
unsigned int len = ::ReadCompactSize(s);
unsigned char code[BIP32_EXTKEY_SIZE];
diff --git a/src/leveldb/.travis.yml b/src/leveldb/.travis.yml
new file mode 100644
index 0000000000..f5bd74c454
--- /dev/null
+++ b/src/leveldb/.travis.yml
@@ -0,0 +1,13 @@
+language: cpp
+compiler:
+- clang
+- gcc
+os:
+- linux
+- osx
+sudo: false
+before_install:
+- echo $LANG
+- echo $LC_ALL
+script:
+- make -j 4 check
diff --git a/src/leveldb/Makefile b/src/leveldb/Makefile
index 2bd2cadcdd..07a5a1ead6 100644
--- a/src/leveldb/Makefile
+++ b/src/leveldb/Makefile
@@ -20,208 +20,395 @@ $(shell CC="$(CC)" CXX="$(CXX)" TARGET_OS="$(TARGET_OS)" \
# this file is generated by the previous line to set build flags and sources
include build_config.mk
+TESTS = \
+ db/autocompact_test \
+ db/c_test \
+ db/corruption_test \
+ db/db_test \
+ db/dbformat_test \
+ db/fault_injection_test \
+ db/filename_test \
+ db/log_test \
+ db/recovery_test \
+ db/skiplist_test \
+ db/version_edit_test \
+ db/version_set_test \
+ db/write_batch_test \
+ helpers/memenv/memenv_test \
+ issues/issue178_test \
+ issues/issue200_test \
+ table/filter_block_test \
+ table/table_test \
+ util/arena_test \
+ util/bloom_test \
+ util/cache_test \
+ util/coding_test \
+ util/crc32c_test \
+ util/env_test \
+ util/hash_test
+
+UTILS = \
+ db/db_bench \
+ db/leveldbutil
+
+# Put the object files in a subdirectory, but the application at the top of the object dir.
+PROGNAMES := $(notdir $(TESTS) $(UTILS))
+
+# On Linux may need libkyotocabinet-dev for dependency.
+BENCHMARKS = \
+ doc/bench/db_bench_sqlite3 \
+ doc/bench/db_bench_tree_db
+
CFLAGS += -I. -I./include $(PLATFORM_CCFLAGS) $(OPT)
CXXFLAGS += -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT)
LDFLAGS += $(PLATFORM_LDFLAGS)
LIBS += $(PLATFORM_LIBS)
-LIBOBJECTS = $(SOURCES:.cc=.o)
-MEMENVOBJECTS = $(MEMENV_SOURCES:.cc=.o)
-
-TESTUTIL = ./util/testutil.o
-TESTHARNESS = ./util/testharness.o $(TESTUTIL)
+SIMULATOR_OUTDIR=out-ios-x86
+DEVICE_OUTDIR=out-ios-arm
-# Note: iOS should probably be using libtool, not ar.
ifeq ($(PLATFORM), IOS)
+# Note: iOS should probably be using libtool, not ar.
AR=xcrun ar
+SIMULATORSDK=$(shell xcrun -sdk iphonesimulator --show-sdk-path)
+DEVICESDK=$(shell xcrun -sdk iphoneos --show-sdk-path)
+DEVICE_CFLAGS = -isysroot "$(DEVICESDK)" -arch armv6 -arch armv7 -arch armv7s -arch arm64
+SIMULATOR_CFLAGS = -isysroot "$(SIMULATORSDK)" -arch i686 -arch x86_64
+STATIC_OUTDIR=out-ios-universal
+else
+STATIC_OUTDIR=out-static
+SHARED_OUTDIR=out-shared
+STATIC_PROGRAMS := $(addprefix $(STATIC_OUTDIR)/, $(PROGNAMES))
+SHARED_PROGRAMS := $(addprefix $(SHARED_OUTDIR)/, db_bench)
endif
-TESTS = \
- arena_test \
- autocompact_test \
- bloom_test \
- c_test \
- cache_test \
- coding_test \
- corruption_test \
- crc32c_test \
- db_test \
- dbformat_test \
- env_test \
- filename_test \
- filter_block_test \
- hash_test \
- issue178_test \
- issue200_test \
- log_test \
- memenv_test \
- skiplist_test \
- table_test \
- version_edit_test \
- version_set_test \
- write_batch_test
-
-PROGRAMS = db_bench leveldbutil $(TESTS)
-BENCHMARKS = db_bench_sqlite3 db_bench_tree_db
-
-LIBRARY = libleveldb.a
-MEMENVLIBRARY = libmemenv.a
+STATIC_LIBOBJECTS := $(addprefix $(STATIC_OUTDIR)/, $(SOURCES:.cc=.o))
+STATIC_MEMENVOBJECTS := $(addprefix $(STATIC_OUTDIR)/, $(MEMENV_SOURCES:.cc=.o))
+
+DEVICE_LIBOBJECTS := $(addprefix $(DEVICE_OUTDIR)/, $(SOURCES:.cc=.o))
+DEVICE_MEMENVOBJECTS := $(addprefix $(DEVICE_OUTDIR)/, $(MEMENV_SOURCES:.cc=.o))
+
+SIMULATOR_LIBOBJECTS := $(addprefix $(SIMULATOR_OUTDIR)/, $(SOURCES:.cc=.o))
+SIMULATOR_MEMENVOBJECTS := $(addprefix $(SIMULATOR_OUTDIR)/, $(MEMENV_SOURCES:.cc=.o))
+
+SHARED_LIBOBJECTS := $(addprefix $(SHARED_OUTDIR)/, $(SOURCES:.cc=.o))
+SHARED_MEMENVOBJECTS := $(addprefix $(SHARED_OUTDIR)/, $(MEMENV_SOURCES:.cc=.o))
+
+TESTUTIL := $(STATIC_OUTDIR)/util/testutil.o
+TESTHARNESS := $(STATIC_OUTDIR)/util/testharness.o $(TESTUTIL)
+
+STATIC_TESTOBJS := $(addprefix $(STATIC_OUTDIR)/, $(addsuffix .o, $(TESTS)))
+STATIC_UTILOBJS := $(addprefix $(STATIC_OUTDIR)/, $(addsuffix .o, $(UTILS)))
+STATIC_ALLOBJS := $(STATIC_LIBOBJECTS) $(STATIC_MEMENVOBJECTS) $(STATIC_TESTOBJS) $(STATIC_UTILOBJS) $(TESTHARNESS)
+DEVICE_ALLOBJS := $(DEVICE_LIBOBJECTS) $(DEVICE_MEMENVOBJECTS)
+SIMULATOR_ALLOBJS := $(SIMULATOR_LIBOBJECTS) $(SIMULATOR_MEMENVOBJECTS)
default: all
# Should we build shared libraries?
ifneq ($(PLATFORM_SHARED_EXT),)
+# Many leveldb test apps use non-exported API's. Only build a subset for testing.
+SHARED_ALLOBJS := $(SHARED_LIBOBJECTS) $(SHARED_MEMENVOBJECTS) $(TESTHARNESS)
+
ifneq ($(PLATFORM_SHARED_VERSIONED),true)
-SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT)
-SHARED2 = $(SHARED1)
-SHARED3 = $(SHARED1)
-SHARED = $(SHARED1)
+SHARED_LIB1 = libleveldb.$(PLATFORM_SHARED_EXT)
+SHARED_LIB2 = $(SHARED_LIB1)
+SHARED_LIB3 = $(SHARED_LIB1)
+SHARED_LIBS = $(SHARED_LIB1)
+SHARED_MEMENVLIB = $(SHARED_OUTDIR)/libmemenv.a
else
# Update db.h if you change these.
-SHARED_MAJOR = 1
-SHARED_MINOR = 18
-SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT)
-SHARED2 = $(SHARED1).$(SHARED_MAJOR)
-SHARED3 = $(SHARED1).$(SHARED_MAJOR).$(SHARED_MINOR)
-SHARED = $(SHARED1) $(SHARED2) $(SHARED3)
-$(SHARED1): $(SHARED3)
- ln -fs $(SHARED3) $(SHARED1)
-$(SHARED2): $(SHARED3)
- ln -fs $(SHARED3) $(SHARED2)
+SHARED_VERSION_MAJOR = 1
+SHARED_VERSION_MINOR = 19
+SHARED_LIB1 = libleveldb.$(PLATFORM_SHARED_EXT)
+SHARED_LIB2 = $(SHARED_LIB1).$(SHARED_VERSION_MAJOR)
+SHARED_LIB3 = $(SHARED_LIB1).$(SHARED_VERSION_MAJOR).$(SHARED_VERSION_MINOR)
+SHARED_LIBS = $(SHARED_OUTDIR)/$(SHARED_LIB1) $(SHARED_OUTDIR)/$(SHARED_LIB2) $(SHARED_OUTDIR)/$(SHARED_LIB3)
+$(SHARED_OUTDIR)/$(SHARED_LIB1): $(SHARED_OUTDIR)/$(SHARED_LIB3)
+ ln -fs $(SHARED_LIB3) $(SHARED_OUTDIR)/$(SHARED_LIB1)
+$(SHARED_OUTDIR)/$(SHARED_LIB2): $(SHARED_OUTDIR)/$(SHARED_LIB3)
+ ln -fs $(SHARED_LIB3) $(SHARED_OUTDIR)/$(SHARED_LIB2)
+SHARED_MEMENVLIB = $(SHARED_OUTDIR)/libmemenv.a
endif
-$(SHARED3):
- $(CXX) $(LDFLAGS) $(PLATFORM_SHARED_LDFLAGS)$(SHARED2) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SOURCES) -o $(SHARED3) $(LIBS)
+$(SHARED_OUTDIR)/$(SHARED_LIB3): $(SHARED_LIBOBJECTS)
+ $(CXX) $(LDFLAGS) $(PLATFORM_SHARED_LDFLAGS)$(SHARED_LIB2) $(SHARED_LIBOBJECTS) -o $(SHARED_OUTDIR)/$(SHARED_LIB3) $(LIBS)
endif # PLATFORM_SHARED_EXT
-all: $(SHARED) $(LIBRARY)
+all: $(SHARED_LIBS) $(SHARED_PROGRAMS) $(STATIC_OUTDIR)/libleveldb.a $(STATIC_OUTDIR)/libmemenv.a $(STATIC_PROGRAMS)
-check: all $(PROGRAMS) $(TESTS)
- for t in $(TESTS); do echo "***** Running $$t"; ./$$t || exit 1; done
+check: $(STATIC_PROGRAMS)
+ for t in $(notdir $(TESTS)); do echo "***** Running $$t"; $(STATIC_OUTDIR)/$$t || exit 1; done
clean:
- -rm -f $(PROGRAMS) $(BENCHMARKS) $(LIBRARY) $(SHARED) $(MEMENVLIBRARY) */*.o */*/*.o ios-x86/*/*.o ios-arm/*/*.o build_config.mk
- -rm -rf ios-x86/* ios-arm/*
+ -rm -rf out-static out-shared out-ios-x86 out-ios-arm out-ios-universal
+ -rm -f build_config.mk
+ -rm -rf ios-x86 ios-arm
-$(LIBRARY): $(LIBOBJECTS)
- rm -f $@
- $(AR) -rs $@ $(LIBOBJECTS)
+$(STATIC_OUTDIR):
+ mkdir $@
-db_bench: db/db_bench.o $(LIBOBJECTS) $(TESTUTIL)
- $(CXX) $(LDFLAGS) db/db_bench.o $(LIBOBJECTS) $(TESTUTIL) -o $@ $(LIBS)
+$(STATIC_OUTDIR)/db: | $(STATIC_OUTDIR)
+ mkdir $@
-db_bench_sqlite3: doc/bench/db_bench_sqlite3.o $(LIBOBJECTS) $(TESTUTIL)
- $(CXX) $(LDFLAGS) doc/bench/db_bench_sqlite3.o $(LIBOBJECTS) $(TESTUTIL) -o $@ -lsqlite3 $(LIBS)
+$(STATIC_OUTDIR)/helpers/memenv: | $(STATIC_OUTDIR)
+ mkdir -p $@
-db_bench_tree_db: doc/bench/db_bench_tree_db.o $(LIBOBJECTS) $(TESTUTIL)
- $(CXX) $(LDFLAGS) doc/bench/db_bench_tree_db.o $(LIBOBJECTS) $(TESTUTIL) -o $@ -lkyotocabinet $(LIBS)
+$(STATIC_OUTDIR)/port: | $(STATIC_OUTDIR)
+ mkdir $@
-leveldbutil: db/leveldb_main.o $(LIBOBJECTS)
- $(CXX) $(LDFLAGS) db/leveldb_main.o $(LIBOBJECTS) -o $@ $(LIBS)
+$(STATIC_OUTDIR)/table: | $(STATIC_OUTDIR)
+ mkdir $@
-arena_test: util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS)
- $(CXX) $(LDFLAGS) util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(STATIC_OUTDIR)/util: | $(STATIC_OUTDIR)
+ mkdir $@
-autocompact_test: db/autocompact_test.o $(LIBOBJECTS) $(TESTHARNESS)
- $(CXX) $(LDFLAGS) db/autocompact_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+.PHONY: STATIC_OBJDIRS
+STATIC_OBJDIRS: \
+ $(STATIC_OUTDIR)/db \
+ $(STATIC_OUTDIR)/port \
+ $(STATIC_OUTDIR)/table \
+ $(STATIC_OUTDIR)/util \
+ $(STATIC_OUTDIR)/helpers/memenv
-bloom_test: util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS)
- $(CXX) $(LDFLAGS) util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(SHARED_OUTDIR):
+ mkdir $@
-c_test: db/c_test.o $(LIBOBJECTS) $(TESTHARNESS)
- $(CXX) $(LDFLAGS) db/c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(SHARED_OUTDIR)/db: | $(SHARED_OUTDIR)
+ mkdir $@
-cache_test: util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS)
- $(CXX) $(LDFLAGS) util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(SHARED_OUTDIR)/helpers/memenv: | $(SHARED_OUTDIR)
+ mkdir -p $@
-coding_test: util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS)
- $(CXX) $(LDFLAGS) util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(SHARED_OUTDIR)/port: | $(SHARED_OUTDIR)
+ mkdir $@
-corruption_test: db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS)
- $(CXX) $(LDFLAGS) db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(SHARED_OUTDIR)/table: | $(SHARED_OUTDIR)
+ mkdir $@
-crc32c_test: util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS)
- $(CXX) $(LDFLAGS) util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(SHARED_OUTDIR)/util: | $(SHARED_OUTDIR)
+ mkdir $@
-db_test: db/db_test.o $(LIBOBJECTS) $(TESTHARNESS)
- $(CXX) $(LDFLAGS) db/db_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+.PHONY: SHARED_OBJDIRS
+SHARED_OBJDIRS: \
+ $(SHARED_OUTDIR)/db \
+ $(SHARED_OUTDIR)/port \
+ $(SHARED_OUTDIR)/table \
+ $(SHARED_OUTDIR)/util \
+ $(SHARED_OUTDIR)/helpers/memenv
-dbformat_test: db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS)
- $(CXX) $(LDFLAGS) db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(DEVICE_OUTDIR):
+ mkdir $@
-env_test: util/env_test.o $(LIBOBJECTS) $(TESTHARNESS)
- $(CXX) $(LDFLAGS) util/env_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(DEVICE_OUTDIR)/db: | $(DEVICE_OUTDIR)
+ mkdir $@
-filename_test: db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS)
- $(CXX) $(LDFLAGS) db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(DEVICE_OUTDIR)/helpers/memenv: | $(DEVICE_OUTDIR)
+ mkdir -p $@
-filter_block_test: table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS)
- $(CXX) $(LDFLAGS) table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(DEVICE_OUTDIR)/port: | $(DEVICE_OUTDIR)
+ mkdir $@
-hash_test: util/hash_test.o $(LIBOBJECTS) $(TESTHARNESS)
- $(CXX) $(LDFLAGS) util/hash_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(DEVICE_OUTDIR)/table: | $(DEVICE_OUTDIR)
+ mkdir $@
-issue178_test: issues/issue178_test.o $(LIBOBJECTS) $(TESTHARNESS)
- $(CXX) $(LDFLAGS) issues/issue178_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(DEVICE_OUTDIR)/util: | $(DEVICE_OUTDIR)
+ mkdir $@
-issue200_test: issues/issue200_test.o $(LIBOBJECTS) $(TESTHARNESS)
- $(CXX) $(LDFLAGS) issues/issue200_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+.PHONY: DEVICE_OBJDIRS
+DEVICE_OBJDIRS: \
+ $(DEVICE_OUTDIR)/db \
+ $(DEVICE_OUTDIR)/port \
+ $(DEVICE_OUTDIR)/table \
+ $(DEVICE_OUTDIR)/util \
+ $(DEVICE_OUTDIR)/helpers/memenv
-log_test: db/log_test.o $(LIBOBJECTS) $(TESTHARNESS)
- $(CXX) $(LDFLAGS) db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(SIMULATOR_OUTDIR):
+ mkdir $@
-table_test: table/table_test.o $(LIBOBJECTS) $(TESTHARNESS)
- $(CXX) $(LDFLAGS) table/table_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(SIMULATOR_OUTDIR)/db: | $(SIMULATOR_OUTDIR)
+ mkdir $@
-skiplist_test: db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS)
- $(CXX) $(LDFLAGS) db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(SIMULATOR_OUTDIR)/helpers/memenv: | $(SIMULATOR_OUTDIR)
+ mkdir -p $@
-version_edit_test: db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS)
- $(CXX) $(LDFLAGS) db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(SIMULATOR_OUTDIR)/port: | $(SIMULATOR_OUTDIR)
+ mkdir $@
-version_set_test: db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS)
- $(CXX) $(LDFLAGS) db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(SIMULATOR_OUTDIR)/table: | $(SIMULATOR_OUTDIR)
+ mkdir $@
-write_batch_test: db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS)
- $(CXX) $(LDFLAGS) db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(SIMULATOR_OUTDIR)/util: | $(SIMULATOR_OUTDIR)
+ mkdir $@
-$(MEMENVLIBRARY) : $(MEMENVOBJECTS)
- rm -f $@
- $(AR) -rs $@ $(MEMENVOBJECTS)
+.PHONY: SIMULATOR_OBJDIRS
+SIMULATOR_OBJDIRS: \
+ $(SIMULATOR_OUTDIR)/db \
+ $(SIMULATOR_OUTDIR)/port \
+ $(SIMULATOR_OUTDIR)/table \
+ $(SIMULATOR_OUTDIR)/util \
+ $(SIMULATOR_OUTDIR)/helpers/memenv
-memenv_test : helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS)
- $(CXX) $(LDFLAGS) helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS) -o $@ $(LIBS)
+$(STATIC_ALLOBJS): | STATIC_OBJDIRS
+$(DEVICE_ALLOBJS): | DEVICE_OBJDIRS
+$(SIMULATOR_ALLOBJS): | SIMULATOR_OBJDIRS
+$(SHARED_ALLOBJS): | SHARED_OBJDIRS
ifeq ($(PLATFORM), IOS)
-# For iOS, create universal object files to be used on both the simulator and
+$(DEVICE_OUTDIR)/libleveldb.a: $(DEVICE_LIBOBJECTS)
+ rm -f $@
+ $(AR) -rs $@ $(DEVICE_LIBOBJECTS)
+
+$(SIMULATOR_OUTDIR)/libleveldb.a: $(SIMULATOR_LIBOBJECTS)
+ rm -f $@
+ $(AR) -rs $@ $(SIMULATOR_LIBOBJECTS)
+
+$(DEVICE_OUTDIR)/libmemenv.a: $(DEVICE_MEMENVOBJECTS)
+ rm -f $@
+ $(AR) -rs $@ $(DEVICE_MEMENVOBJECTS)
+
+$(SIMULATOR_OUTDIR)/libmemenv.a: $(SIMULATOR_MEMENVOBJECTS)
+ rm -f $@
+ $(AR) -rs $@ $(SIMULATOR_MEMENVOBJECTS)
+
+# For iOS, create universal object libraries to be used on both the simulator and
# a device.
-PLATFORMSROOT=/Applications/Xcode.app/Contents/Developer/Platforms
-SIMULATORROOT=$(PLATFORMSROOT)/iPhoneSimulator.platform/Developer
-DEVICEROOT=$(PLATFORMSROOT)/iPhoneOS.platform/Developer
-IOSVERSION=$(shell defaults read $(PLATFORMSROOT)/iPhoneOS.platform/version CFBundleShortVersionString)
-IOSARCH=-arch armv6 -arch armv7 -arch armv7s -arch arm64
-
-.cc.o:
- mkdir -p ios-x86/$(dir $@)
- xcrun -sdk iphonesimulator $(CXX) $(CXXFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -arch x86_64 -c $< -o ios-x86/$@
- mkdir -p ios-arm/$(dir $@)
- xcrun -sdk iphoneos $(CXX) $(CXXFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk $(IOSARCH) -c $< -o ios-arm/$@
- xcrun lipo ios-x86/$@ ios-arm/$@ -create -output $@
-
-.c.o:
- mkdir -p ios-x86/$(dir $@)
- xcrun -sdk iphonesimulator $(CC) $(CFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -arch x86_64 -c $< -o ios-x86/$@
- mkdir -p ios-arm/$(dir $@)
- xcrun -sdk iphoneos $(CC) $(CFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk $(IOSARCH) -c $< -o ios-arm/$@
- xcrun lipo ios-x86/$@ ios-arm/$@ -create -output $@
+$(STATIC_OUTDIR)/libleveldb.a: $(STATIC_OUTDIR) $(DEVICE_OUTDIR)/libleveldb.a $(SIMULATOR_OUTDIR)/libleveldb.a
+ lipo -create $(DEVICE_OUTDIR)/libleveldb.a $(SIMULATOR_OUTDIR)/libleveldb.a -output $@
+$(STATIC_OUTDIR)/libmemenv.a: $(STATIC_OUTDIR) $(DEVICE_OUTDIR)/libmemenv.a $(SIMULATOR_OUTDIR)/libmemenv.a
+ lipo -create $(DEVICE_OUTDIR)/libmemenv.a $(SIMULATOR_OUTDIR)/libmemenv.a -output $@
else
-.cc.o:
+$(STATIC_OUTDIR)/libleveldb.a:$(STATIC_LIBOBJECTS)
+ rm -f $@
+ $(AR) -rs $@ $(STATIC_LIBOBJECTS)
+
+$(STATIC_OUTDIR)/libmemenv.a:$(STATIC_MEMENVOBJECTS)
+ rm -f $@
+ $(AR) -rs $@ $(STATIC_MEMENVOBJECTS)
+endif
+
+$(SHARED_MEMENVLIB):$(SHARED_MEMENVOBJECTS)
+ rm -f $@
+ $(AR) -rs $@ $(SHARED_MEMENVOBJECTS)
+
+$(STATIC_OUTDIR)/db_bench:db/db_bench.cc $(STATIC_LIBOBJECTS) $(TESTUTIL)
+ $(CXX) $(LDFLAGS) $(CXXFLAGS) db/db_bench.cc $(STATIC_LIBOBJECTS) $(TESTUTIL) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/db_bench_sqlite3:doc/bench/db_bench_sqlite3.cc $(STATIC_LIBOBJECTS) $(TESTUTIL)
+ $(CXX) $(LDFLAGS) $(CXXFLAGS) doc/bench/db_bench_sqlite3.cc $(STATIC_LIBOBJECTS) $(TESTUTIL) -o $@ -lsqlite3 $(LIBS)
+
+$(STATIC_OUTDIR)/db_bench_tree_db:doc/bench/db_bench_tree_db.cc $(STATIC_LIBOBJECTS) $(TESTUTIL)
+ $(CXX) $(LDFLAGS) $(CXXFLAGS) doc/bench/db_bench_tree_db.cc $(STATIC_LIBOBJECTS) $(TESTUTIL) -o $@ -lkyotocabinet $(LIBS)
+
+$(STATIC_OUTDIR)/leveldbutil:db/leveldbutil.cc $(STATIC_LIBOBJECTS)
+ $(CXX) $(LDFLAGS) $(CXXFLAGS) db/leveldbutil.cc $(STATIC_LIBOBJECTS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/arena_test:util/arena_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+ $(CXX) $(LDFLAGS) $(CXXFLAGS) util/arena_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/autocompact_test:db/autocompact_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+ $(CXX) $(LDFLAGS) $(CXXFLAGS) db/autocompact_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/bloom_test:util/bloom_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+ $(CXX) $(LDFLAGS) $(CXXFLAGS) util/bloom_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/c_test:$(STATIC_OUTDIR)/db/c_test.o $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+ $(CXX) $(LDFLAGS) $(STATIC_OUTDIR)/db/c_test.o $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/cache_test:util/cache_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+ $(CXX) $(LDFLAGS) $(CXXFLAGS) util/cache_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/coding_test:util/coding_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+ $(CXX) $(LDFLAGS) $(CXXFLAGS) util/coding_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/corruption_test:db/corruption_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+ $(CXX) $(LDFLAGS) $(CXXFLAGS) db/corruption_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/crc32c_test:util/crc32c_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+ $(CXX) $(LDFLAGS) $(CXXFLAGS) util/crc32c_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/db_test:db/db_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+ $(CXX) $(LDFLAGS) $(CXXFLAGS) db/db_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/dbformat_test:db/dbformat_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+ $(CXX) $(LDFLAGS) $(CXXFLAGS) db/dbformat_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/env_test:util/env_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+ $(CXX) $(LDFLAGS) $(CXXFLAGS) util/env_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/fault_injection_test:db/fault_injection_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+ $(CXX) $(LDFLAGS) $(CXXFLAGS) db/fault_injection_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/filename_test:db/filename_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+ $(CXX) $(LDFLAGS) $(CXXFLAGS) db/filename_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/filter_block_test:table/filter_block_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+ $(CXX) $(LDFLAGS) $(CXXFLAGS) table/filter_block_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/hash_test:util/hash_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+ $(CXX) $(LDFLAGS) $(CXXFLAGS) util/hash_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/issue178_test:issues/issue178_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+ $(CXX) $(LDFLAGS) $(CXXFLAGS) issues/issue178_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/issue200_test:issues/issue200_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+ $(CXX) $(LDFLAGS) $(CXXFLAGS) issues/issue200_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/log_test:db/log_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+ $(CXX) $(LDFLAGS) $(CXXFLAGS) db/log_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/recovery_test:db/recovery_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+ $(CXX) $(LDFLAGS) $(CXXFLAGS) db/recovery_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/table_test:table/table_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+ $(CXX) $(LDFLAGS) $(CXXFLAGS) table/table_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/skiplist_test:db/skiplist_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+ $(CXX) $(LDFLAGS) $(CXXFLAGS) db/skiplist_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/version_edit_test:db/version_edit_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+ $(CXX) $(LDFLAGS) $(CXXFLAGS) db/version_edit_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/version_set_test:db/version_set_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+ $(CXX) $(LDFLAGS) $(CXXFLAGS) db/version_set_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/write_batch_test:db/write_batch_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+ $(CXX) $(LDFLAGS) $(CXXFLAGS) db/write_batch_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/memenv_test:$(STATIC_OUTDIR)/helpers/memenv/memenv_test.o $(STATIC_OUTDIR)/libmemenv.a $(STATIC_OUTDIR)/libleveldb.a $(TESTHARNESS)
+ $(XCRUN) $(CXX) $(LDFLAGS) $(STATIC_OUTDIR)/helpers/memenv/memenv_test.o $(STATIC_OUTDIR)/libmemenv.a $(STATIC_OUTDIR)/libleveldb.a $(TESTHARNESS) -o $@ $(LIBS)
+
+$(SHARED_OUTDIR)/db_bench:$(SHARED_OUTDIR)/db/db_bench.o $(SHARED_LIBS) $(TESTUTIL)
+ $(XCRUN) $(CXX) $(LDFLAGS) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SHARED_OUTDIR)/db/db_bench.o $(TESTUTIL) $(SHARED_OUTDIR)/$(SHARED_LIB3) -o $@ $(LIBS)
+
+.PHONY: run-shared
+run-shared: $(SHARED_OUTDIR)/db_bench
+ LD_LIBRARY_PATH=$(SHARED_OUTDIR) $(SHARED_OUTDIR)/db_bench
+
+$(SIMULATOR_OUTDIR)/%.o: %.cc
+ xcrun -sdk iphonesimulator $(CXX) $(CXXFLAGS) $(SIMULATOR_CFLAGS) -c $< -o $@
+
+$(DEVICE_OUTDIR)/%.o: %.cc
+ xcrun -sdk iphoneos $(CXX) $(CXXFLAGS) $(DEVICE_CFLAGS) -c $< -o $@
+
+$(SIMULATOR_OUTDIR)/%.o: %.c
+ xcrun -sdk iphonesimulator $(CC) $(CFLAGS) $(SIMULATOR_CFLAGS) -c $< -o $@
+
+$(DEVICE_OUTDIR)/%.o: %.c
+ xcrun -sdk iphoneos $(CC) $(CFLAGS) $(DEVICE_CFLAGS) -c $< -o $@
+
+$(STATIC_OUTDIR)/%.o: %.cc
$(CXX) $(CXXFLAGS) -c $< -o $@
-.c.o:
+$(STATIC_OUTDIR)/%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
-endif
+
+$(SHARED_OUTDIR)/%.o: %.cc
+ $(CXX) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) -c $< -o $@
+
+$(SHARED_OUTDIR)/%.o: %.c
+ $(CC) $(CFLAGS) $(PLATFORM_SHARED_CFLAGS) -c $< -o $@
diff --git a/src/leveldb/README b/src/leveldb/README
deleted file mode 100644
index 3618adeeed..0000000000
--- a/src/leveldb/README
+++ /dev/null
@@ -1,51 +0,0 @@
-leveldb: A key-value store
-Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com)
-
-The code under this directory implements a system for maintaining a
-persistent key/value store.
-
-See doc/index.html for more explanation.
-See doc/impl.html for a brief overview of the implementation.
-
-The public interface is in include/*.h. Callers should not include or
-rely on the details of any other header files in this package. Those
-internal APIs may be changed without warning.
-
-Guide to header files:
-
-include/db.h
- Main interface to the DB: Start here
-
-include/options.h
- Control over the behavior of an entire database, and also
- control over the behavior of individual reads and writes.
-
-include/comparator.h
- Abstraction for user-specified comparison function. If you want
- just bytewise comparison of keys, you can use the default comparator,
- but clients can write their own comparator implementations if they
- want custom ordering (e.g. to handle different character
- encodings, etc.)
-
-include/iterator.h
- Interface for iterating over data. You can get an iterator
- from a DB object.
-
-include/write_batch.h
- Interface for atomically applying multiple updates to a database.
-
-include/slice.h
- A simple module for maintaining a pointer and a length into some
- other byte array.
-
-include/status.h
- Status is returned from many of the public interfaces and is used
- to report success and various kinds of errors.
-
-include/env.h
- Abstraction of the OS environment. A posix implementation of
- this interface is in util/env_posix.cc
-
-include/table.h
-include/table_builder.h
- Lower-level modules that most clients probably won't use directly
diff --git a/src/leveldb/README.md b/src/leveldb/README.md
index 480affb5ca..c75b185e0e 100644
--- a/src/leveldb/README.md
+++ b/src/leveldb/README.md
@@ -1,5 +1,7 @@
**LevelDB is a fast key-value storage library written at Google that provides an ordered mapping from string keys to string values.**
+[![Build Status](https://travis-ci.org/google/leveldb.svg?branch=master)](https://travis-ci.org/google/leveldb)
+
Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com)
# Features
@@ -10,9 +12,11 @@ Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com)
* Multiple changes can be made in one atomic batch.
* Users can create a transient snapshot to get a consistent view of data.
* Forward and backward iteration is supported over the data.
- * Data is automatically compressed using the [Snappy compression library](http://code.google.com/p/snappy).
+ * Data is automatically compressed using the [Snappy compression library](http://google.github.io/snappy/).
* External activity (file system operations etc.) is relayed through a virtual interface so users can customize the operating system interactions.
- * [Detailed documentation](http://htmlpreview.github.io/?https://github.com/google/leveldb/blob/master/doc/index.html) about how to use the library is included with the source code.
+
+# Documentation
+ [LevelDB library documentation](https://rawgit.com/google/leveldb/master/doc/index.html) is online and bundled with the source code.
# Limitations
@@ -20,6 +24,37 @@ Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com)
* Only a single process (possibly multi-threaded) can access a particular database at a time.
* There is no client-server support builtin to the library. An application that needs such support will have to wrap their own server around the library.
+# Contributing to the leveldb Project
+The leveldb project welcomes contributions. leveldb's primary goal is to be
+a reliable and fast key/value store. Changes that are in line with the
+features/limitations outlined above, and meet the requirements below,
+will be considered.
+
+Contribution requirements:
+
+1. **POSIX only**. We _generally_ will only accept changes that are both
+ compiled, and tested on a POSIX platform - usually Linux. Very small
+ changes will sometimes be accepted, but consider that more of an
+ exception than the rule.
+
+2. **Stable API**. We strive very hard to maintain a stable API. Changes that
+ require changes for projects using leveldb _might_ be rejected without
+ sufficient benefit to the project.
+
+3. **Tests**: All changes must be accompanied by a new (or changed) test, or
+ a sufficient explanation as to why a new (or changed) test is not required.
+
+## Submitting a Pull Request
+Before any pull request will be accepted the author must first sign a
+Contributor License Agreement (CLA) at https://cla.developers.google.com/.
+
+In order to keep the commit timeline linear
+[squash](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Squashing-Commits)
+your changes down to a single commit and [rebase](https://git-scm.com/docs/git-rebase)
+on google/leveldb/master. This keeps the commit timeline linear and more easily sync'ed
+with the internal repository at Google. More information at GitHub's
+[About Git rebase](https://help.github.com/articles/about-git-rebase/) page.
+
# Performance
Here is a performance report (with explanations) from the run of the
diff --git a/src/leveldb/build_detect_platform b/src/leveldb/build_detect_platform
index a1101c1bda..d7edab1d87 100755
--- a/src/leveldb/build_detect_platform
+++ b/src/leveldb/build_detect_platform
@@ -175,7 +175,7 @@ DIRS="$PREFIX/db $PREFIX/util $PREFIX/table"
set -f # temporarily disable globbing so that our patterns aren't expanded
PRUNE_TEST="-name *test*.cc -prune"
PRUNE_BENCH="-name *_bench.cc -prune"
-PRUNE_TOOL="-name leveldb_main.cc -prune"
+PRUNE_TOOL="-name leveldbutil.cc -prune"
PORTABLE_FILES=`find $DIRS $PRUNE_TEST -o $PRUNE_BENCH -o $PRUNE_TOOL -o -name '*.cc' -print | sort | sed "s,^$PREFIX/,," | tr "\n" " "`
set +f # re-enable globbing
diff --git a/src/leveldb/db/corruption_test.cc b/src/leveldb/db/corruption_test.cc
index 96afc68913..37a484d25f 100644
--- a/src/leveldb/db/corruption_test.cc
+++ b/src/leveldb/db/corruption_test.cc
@@ -36,7 +36,7 @@ class CorruptionTest {
tiny_cache_ = NewLRUCache(100);
options_.env = &env_;
options_.block_cache = tiny_cache_;
- dbname_ = test::TmpDir() + "/db_test";
+ dbname_ = test::TmpDir() + "/corruption_test";
DestroyDB(dbname_, options_);
db_ = NULL;
diff --git a/src/leveldb/db/db_bench.cc b/src/leveldb/db/db_bench.cc
index 705a170aae..7a0f5e08cd 100644
--- a/src/leveldb/db/db_bench.cc
+++ b/src/leveldb/db/db_bench.cc
@@ -33,6 +33,7 @@
// readmissing -- read N missing keys in random order
// readhot -- read N times in random order from 1% section of DB
// seekrandom -- N random seeks
+// open -- cost of opening a DB
// crc32c -- repeated crc32c of 4K of data
// acquireload -- load N*1000 times
// Meta operations:
@@ -99,6 +100,9 @@ static int FLAGS_bloom_bits = -1;
// benchmark will fail.
static bool FLAGS_use_existing_db = false;
+// If true, reuse existing log/MANIFEST files when re-opening a database.
+static bool FLAGS_reuse_logs = false;
+
// Use the db with the following name.
static const char* FLAGS_db = NULL;
@@ -138,6 +142,7 @@ class RandomGenerator {
}
};
+#if defined(__linux)
static Slice TrimSpace(Slice s) {
size_t start = 0;
while (start < s.size() && isspace(s[start])) {
@@ -149,6 +154,7 @@ static Slice TrimSpace(Slice s) {
}
return Slice(s.data() + start, limit - start);
}
+#endif
static void AppendWithSpace(std::string* str, Slice msg) {
if (msg.empty()) return;
@@ -442,7 +448,11 @@ class Benchmark {
bool fresh_db = false;
int num_threads = FLAGS_threads;
- if (name == Slice("fillseq")) {
+ if (name == Slice("open")) {
+ method = &Benchmark::OpenBench;
+ num_ /= 10000;
+ if (num_ < 1) num_ = 1;
+ } else if (name == Slice("fillseq")) {
fresh_db = true;
method = &Benchmark::WriteSeq;
} else if (name == Slice("fillbatch")) {
@@ -695,6 +705,7 @@ class Benchmark {
options.write_buffer_size = FLAGS_write_buffer_size;
options.max_open_files = FLAGS_open_files;
options.filter_policy = filter_policy_;
+ options.reuse_logs = FLAGS_reuse_logs;
Status s = DB::Open(options, FLAGS_db, &db_);
if (!s.ok()) {
fprintf(stderr, "open error: %s\n", s.ToString().c_str());
@@ -702,6 +713,14 @@ class Benchmark {
}
}
+ void OpenBench(ThreadState* thread) {
+ for (int i = 0; i < num_; i++) {
+ delete db_;
+ Open();
+ thread->stats.FinishedSingleOp();
+ }
+ }
+
void WriteSeq(ThreadState* thread) {
DoWrite(thread, true);
}
@@ -941,6 +960,9 @@ int main(int argc, char** argv) {
} else if (sscanf(argv[i], "--use_existing_db=%d%c", &n, &junk) == 1 &&
(n == 0 || n == 1)) {
FLAGS_use_existing_db = n;
+ } else if (sscanf(argv[i], "--reuse_logs=%d%c", &n, &junk) == 1 &&
+ (n == 0 || n == 1)) {
+ FLAGS_reuse_logs = n;
} else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) {
FLAGS_num = n;
} else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) {
diff --git a/src/leveldb/db/db_impl.cc b/src/leveldb/db/db_impl.cc
index 49b95953b4..60f4e66e55 100644
--- a/src/leveldb/db/db_impl.cc
+++ b/src/leveldb/db/db_impl.cc
@@ -125,7 +125,7 @@ DBImpl::DBImpl(const Options& raw_options, const std::string& dbname)
db_lock_(NULL),
shutting_down_(NULL),
bg_cv_(&mutex_),
- mem_(new MemTable(internal_comparator_)),
+ mem_(NULL),
imm_(NULL),
logfile_(NULL),
logfile_number_(0),
@@ -134,7 +134,6 @@ DBImpl::DBImpl(const Options& raw_options, const std::string& dbname)
tmp_batch_(new WriteBatch),
bg_compaction_scheduled_(false),
manual_compaction_(NULL) {
- mem_->Ref();
has_imm_.Release_Store(NULL);
// Reserve ten files or so for other uses and give the rest to TableCache.
@@ -271,7 +270,7 @@ void DBImpl::DeleteObsoleteFiles() {
}
}
-Status DBImpl::Recover(VersionEdit* edit) {
+Status DBImpl::Recover(VersionEdit* edit, bool *save_manifest) {
mutex_.AssertHeld();
// Ignore error from CreateDir since the creation of the DB is
@@ -301,66 +300,69 @@ Status DBImpl::Recover(VersionEdit* edit) {
}
}
- s = versions_->Recover();
- if (s.ok()) {
- SequenceNumber max_sequence(0);
-
- // Recover from all newer log files than the ones named in the
- // descriptor (new log files may have been added by the previous
- // incarnation without registering them in the descriptor).
- //
- // Note that PrevLogNumber() is no longer used, but we pay
- // attention to it in case we are recovering a database
- // produced by an older version of leveldb.
- const uint64_t min_log = versions_->LogNumber();
- const uint64_t prev_log = versions_->PrevLogNumber();
- std::vector<std::string> filenames;
- s = env_->GetChildren(dbname_, &filenames);
+ s = versions_->Recover(save_manifest);
+ if (!s.ok()) {
+ return s;
+ }
+ SequenceNumber max_sequence(0);
+
+ // Recover from all newer log files than the ones named in the
+ // descriptor (new log files may have been added by the previous
+ // incarnation without registering them in the descriptor).
+ //
+ // Note that PrevLogNumber() is no longer used, but we pay
+ // attention to it in case we are recovering a database
+ // produced by an older version of leveldb.
+ const uint64_t min_log = versions_->LogNumber();
+ const uint64_t prev_log = versions_->PrevLogNumber();
+ std::vector<std::string> filenames;
+ s = env_->GetChildren(dbname_, &filenames);
+ if (!s.ok()) {
+ return s;
+ }
+ std::set<uint64_t> expected;
+ versions_->AddLiveFiles(&expected);
+ uint64_t number;
+ FileType type;
+ std::vector<uint64_t> logs;
+ for (size_t i = 0; i < filenames.size(); i++) {
+ if (ParseFileName(filenames[i], &number, &type)) {
+ expected.erase(number);
+ if (type == kLogFile && ((number >= min_log) || (number == prev_log)))
+ logs.push_back(number);
+ }
+ }
+ if (!expected.empty()) {
+ char buf[50];
+ snprintf(buf, sizeof(buf), "%d missing files; e.g.",
+ static_cast<int>(expected.size()));
+ return Status::Corruption(buf, TableFileName(dbname_, *(expected.begin())));
+ }
+
+ // Recover in the order in which the logs were generated
+ std::sort(logs.begin(), logs.end());
+ for (size_t i = 0; i < logs.size(); i++) {
+ s = RecoverLogFile(logs[i], (i == logs.size() - 1), save_manifest, edit,
+ &max_sequence);
if (!s.ok()) {
return s;
}
- std::set<uint64_t> expected;
- versions_->AddLiveFiles(&expected);
- uint64_t number;
- FileType type;
- std::vector<uint64_t> logs;
- for (size_t i = 0; i < filenames.size(); i++) {
- if (ParseFileName(filenames[i], &number, &type)) {
- expected.erase(number);
- if (type == kLogFile && ((number >= min_log) || (number == prev_log)))
- logs.push_back(number);
- }
- }
- if (!expected.empty()) {
- char buf[50];
- snprintf(buf, sizeof(buf), "%d missing files; e.g.",
- static_cast<int>(expected.size()));
- return Status::Corruption(buf, TableFileName(dbname_, *(expected.begin())));
- }
-
- // Recover in the order in which the logs were generated
- std::sort(logs.begin(), logs.end());
- for (size_t i = 0; i < logs.size(); i++) {
- s = RecoverLogFile(logs[i], edit, &max_sequence);
- // The previous incarnation may not have written any MANIFEST
- // records after allocating this log number. So we manually
- // update the file number allocation counter in VersionSet.
- versions_->MarkFileNumberUsed(logs[i]);
- }
+ // The previous incarnation may not have written any MANIFEST
+ // records after allocating this log number. So we manually
+ // update the file number allocation counter in VersionSet.
+ versions_->MarkFileNumberUsed(logs[i]);
+ }
- if (s.ok()) {
- if (versions_->LastSequence() < max_sequence) {
- versions_->SetLastSequence(max_sequence);
- }
- }
+ if (versions_->LastSequence() < max_sequence) {
+ versions_->SetLastSequence(max_sequence);
}
- return s;
+ return Status::OK();
}
-Status DBImpl::RecoverLogFile(uint64_t log_number,
- VersionEdit* edit,
+Status DBImpl::RecoverLogFile(uint64_t log_number, bool last_log,
+ bool* save_manifest, VersionEdit* edit,
SequenceNumber* max_sequence) {
struct LogReporter : public log::Reader::Reporter {
Env* env;
@@ -405,6 +407,7 @@ Status DBImpl::RecoverLogFile(uint64_t log_number,
std::string scratch;
Slice record;
WriteBatch batch;
+ int compactions = 0;
MemTable* mem = NULL;
while (reader.ReadRecord(&record, &scratch) &&
status.ok()) {
@@ -432,25 +435,52 @@ Status DBImpl::RecoverLogFile(uint64_t log_number,
}
if (mem->ApproximateMemoryUsage() > options_.write_buffer_size) {
+ compactions++;
+ *save_manifest = true;
status = WriteLevel0Table(mem, edit, NULL);
+ mem->Unref();
+ mem = NULL;
if (!status.ok()) {
// Reflect errors immediately so that conditions like full
// file-systems cause the DB::Open() to fail.
break;
}
- mem->Unref();
- mem = NULL;
}
}
- if (status.ok() && mem != NULL) {
- status = WriteLevel0Table(mem, edit, NULL);
- // Reflect errors immediately so that conditions like full
- // file-systems cause the DB::Open() to fail.
+ delete file;
+
+ // See if we should keep reusing the last log file.
+ if (status.ok() && options_.reuse_logs && last_log && compactions == 0) {
+ assert(logfile_ == NULL);
+ assert(log_ == NULL);
+ assert(mem_ == NULL);
+ uint64_t lfile_size;
+ if (env_->GetFileSize(fname, &lfile_size).ok() &&
+ env_->NewAppendableFile(fname, &logfile_).ok()) {
+ Log(options_.info_log, "Reusing old log %s \n", fname.c_str());
+ log_ = new log::Writer(logfile_, lfile_size);
+ logfile_number_ = log_number;
+ if (mem != NULL) {
+ mem_ = mem;
+ mem = NULL;
+ } else {
+ // mem can be NULL if lognum exists but was empty.
+ mem_ = new MemTable(internal_comparator_);
+ mem_->Ref();
+ }
+ }
+ }
+
+ if (mem != NULL) {
+ // mem did not get reused; compact it.
+ if (status.ok()) {
+ *save_manifest = true;
+ status = WriteLevel0Table(mem, edit, NULL);
+ }
+ mem->Unref();
}
- if (mem != NULL) mem->Unref();
- delete file;
return status;
}
@@ -821,8 +851,9 @@ Status DBImpl::FinishCompactionOutputFile(CompactionState* compact,
delete iter;
if (s.ok()) {
Log(options_.info_log,
- "Generated table #%llu: %lld keys, %lld bytes",
+ "Generated table #%llu@%d: %lld keys, %lld bytes",
(unsigned long long) output_number,
+ compact->compaction->level(),
(unsigned long long) current_entries,
(unsigned long long) current_bytes);
}
@@ -1395,6 +1426,19 @@ bool DBImpl::GetProperty(const Slice& property, std::string* value) {
} else if (in == "sstables") {
*value = versions_->current()->DebugString();
return true;
+ } else if (in == "approximate-memory-usage") {
+ size_t total_usage = options_.block_cache->TotalCharge();
+ if (mem_) {
+ total_usage += mem_->ApproximateMemoryUsage();
+ }
+ if (imm_) {
+ total_usage += imm_->ApproximateMemoryUsage();
+ }
+ char buf[50];
+ snprintf(buf, sizeof(buf), "%llu",
+ static_cast<unsigned long long>(total_usage));
+ value->append(buf);
+ return true;
}
return false;
@@ -1449,8 +1493,11 @@ Status DB::Open(const Options& options, const std::string& dbname,
DBImpl* impl = new DBImpl(options, dbname);
impl->mutex_.Lock();
VersionEdit edit;
- Status s = impl->Recover(&edit); // Handles create_if_missing, error_if_exists
- if (s.ok()) {
+ // Recover handles create_if_missing, error_if_exists
+ bool save_manifest = false;
+ Status s = impl->Recover(&edit, &save_manifest);
+ if (s.ok() && impl->mem_ == NULL) {
+ // Create new log and a corresponding memtable.
uint64_t new_log_number = impl->versions_->NewFileNumber();
WritableFile* lfile;
s = options.env->NewWritableFile(LogFileName(dbname, new_log_number),
@@ -1460,15 +1507,22 @@ Status DB::Open(const Options& options, const std::string& dbname,
impl->logfile_ = lfile;
impl->logfile_number_ = new_log_number;
impl->log_ = new log::Writer(lfile);
- s = impl->versions_->LogAndApply(&edit, &impl->mutex_);
- }
- if (s.ok()) {
- impl->DeleteObsoleteFiles();
- impl->MaybeScheduleCompaction();
+ impl->mem_ = new MemTable(impl->internal_comparator_);
+ impl->mem_->Ref();
}
}
+ if (s.ok() && save_manifest) {
+ edit.SetPrevLogNumber(0); // No older logs needed after recovery.
+ edit.SetLogNumber(impl->logfile_number_);
+ s = impl->versions_->LogAndApply(&edit, &impl->mutex_);
+ }
+ if (s.ok()) {
+ impl->DeleteObsoleteFiles();
+ impl->MaybeScheduleCompaction();
+ }
impl->mutex_.Unlock();
if (s.ok()) {
+ assert(impl->mem_ != NULL);
*dbptr = impl;
} else {
delete impl;
diff --git a/src/leveldb/db/db_impl.h b/src/leveldb/db/db_impl.h
index cfc998164a..8ff323e728 100644
--- a/src/leveldb/db/db_impl.h
+++ b/src/leveldb/db/db_impl.h
@@ -78,7 +78,8 @@ class DBImpl : public DB {
// Recover the descriptor from persistent storage. May do a significant
// amount of work to recover recently logged updates. Any changes to
// be made to the descriptor are added to *edit.
- Status Recover(VersionEdit* edit) EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+ Status Recover(VersionEdit* edit, bool* save_manifest)
+ EXCLUSIVE_LOCKS_REQUIRED(mutex_);
void MaybeIgnoreError(Status* s) const;
@@ -90,9 +91,8 @@ class DBImpl : public DB {
// Errors are recorded in bg_error_.
void CompactMemTable() EXCLUSIVE_LOCKS_REQUIRED(mutex_);
- Status RecoverLogFile(uint64_t log_number,
- VersionEdit* edit,
- SequenceNumber* max_sequence)
+ Status RecoverLogFile(uint64_t log_number, bool last_log, bool* save_manifest,
+ VersionEdit* edit, SequenceNumber* max_sequence)
EXCLUSIVE_LOCKS_REQUIRED(mutex_);
Status WriteLevel0Table(MemTable* mem, VersionEdit* edit, Version* base)
diff --git a/src/leveldb/db/db_test.cc b/src/leveldb/db/db_test.cc
index 0fed9137d5..a0b08bc19c 100644
--- a/src/leveldb/db/db_test.cc
+++ b/src/leveldb/db/db_test.cc
@@ -193,6 +193,7 @@ class DBTest {
// Sequence of option configurations to try
enum OptionConfig {
kDefault,
+ kReuse,
kFilter,
kUncompressed,
kEnd
@@ -237,7 +238,11 @@ class DBTest {
// Return the current option configuration.
Options CurrentOptions() {
Options options;
+ options.reuse_logs = false;
switch (option_config_) {
+ case kReuse:
+ options.reuse_logs = true;
+ break;
case kFilter:
options.filter_policy = filter_policy_;
break;
@@ -558,6 +563,17 @@ TEST(DBTest, GetFromVersions) {
} while (ChangeOptions());
}
+TEST(DBTest, GetMemUsage) {
+ do {
+ ASSERT_OK(Put("foo", "v1"));
+ std::string val;
+ ASSERT_TRUE(db_->GetProperty("leveldb.approximate-memory-usage", &val));
+ int mem_usage = atoi(val.c_str());
+ ASSERT_GT(mem_usage, 0);
+ ASSERT_LT(mem_usage, 5*1024*1024);
+ } while (ChangeOptions());
+}
+
TEST(DBTest, GetSnapshot) {
do {
// Try with both a short key and a long key
@@ -1080,6 +1096,14 @@ TEST(DBTest, ApproximateSizes) {
// 0 because GetApproximateSizes() does not account for memtable space
ASSERT_TRUE(Between(Size("", Key(50)), 0, 0));
+ if (options.reuse_logs) {
+ // Recovery will reuse memtable, and GetApproximateSizes() does not
+ // account for memtable usage;
+ Reopen(&options);
+ ASSERT_TRUE(Between(Size("", Key(50)), 0, 0));
+ continue;
+ }
+
// Check sizes across recovery by reopening a few times
for (int run = 0; run < 3; run++) {
Reopen(&options);
@@ -1123,6 +1147,11 @@ TEST(DBTest, ApproximateSizes_MixOfSmallAndLarge) {
ASSERT_OK(Put(Key(6), RandomString(&rnd, 300000)));
ASSERT_OK(Put(Key(7), RandomString(&rnd, 10000)));
+ if (options.reuse_logs) {
+ // Need to force a memtable compaction since recovery does not do so.
+ ASSERT_OK(dbfull()->TEST_CompactMemTable());
+ }
+
// Check sizes across recovery by reopening a few times
for (int run = 0; run < 3; run++) {
Reopen(&options);
@@ -2084,7 +2113,8 @@ void BM_LogAndApply(int iters, int num_base_files) {
InternalKeyComparator cmp(BytewiseComparator());
Options options;
VersionSet vset(dbname, &options, NULL, &cmp);
- ASSERT_OK(vset.Recover());
+ bool save_manifest;
+ ASSERT_OK(vset.Recover(&save_manifest));
VersionEdit vbase;
uint64_t fnum = 1;
for (int i = 0; i < num_base_files; i++) {
diff --git a/src/leveldb/db/fault_injection_test.cc b/src/leveldb/db/fault_injection_test.cc
new file mode 100644
index 0000000000..875dfe81ee
--- /dev/null
+++ b/src/leveldb/db/fault_injection_test.cc
@@ -0,0 +1,554 @@
+// Copyright 2014 The LevelDB Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file. See the AUTHORS file for names of contributors.
+
+// This test uses a custom Env to keep track of the state of a filesystem as of
+// the last "sync". It then checks for data loss errors by purposely dropping
+// file data (or entire files) not protected by a "sync".
+
+#include "leveldb/db.h"
+
+#include <map>
+#include <set>
+#include "db/db_impl.h"
+#include "db/filename.h"
+#include "db/log_format.h"
+#include "db/version_set.h"
+#include "leveldb/cache.h"
+#include "leveldb/env.h"
+#include "leveldb/table.h"
+#include "leveldb/write_batch.h"
+#include "util/logging.h"
+#include "util/mutexlock.h"
+#include "util/testharness.h"
+#include "util/testutil.h"
+
+namespace leveldb {
+
+static const int kValueSize = 1000;
+static const int kMaxNumValues = 2000;
+static const size_t kNumIterations = 3;
+
+class FaultInjectionTestEnv;
+
+namespace {
+
+// Assume a filename, and not a directory name like "/foo/bar/"
+static std::string GetDirName(const std::string filename) {
+ size_t found = filename.find_last_of("/\\");
+ if (found == std::string::npos) {
+ return "";
+ } else {
+ return filename.substr(0, found);
+ }
+}
+
+Status SyncDir(const std::string& dir) {
+ // As this is a test it isn't required to *actually* sync this directory.
+ return Status::OK();
+}
+
+// A basic file truncation function suitable for this test.
+Status Truncate(const std::string& filename, uint64_t length) {
+ leveldb::Env* env = leveldb::Env::Default();
+
+ SequentialFile* orig_file;
+ Status s = env->NewSequentialFile(filename, &orig_file);
+ if (!s.ok())
+ return s;
+
+ char* scratch = new char[length];
+ leveldb::Slice result;
+ s = orig_file->Read(length, &result, scratch);
+ delete orig_file;
+ if (s.ok()) {
+ std::string tmp_name = GetDirName(filename) + "/truncate.tmp";
+ WritableFile* tmp_file;
+ s = env->NewWritableFile(tmp_name, &tmp_file);
+ if (s.ok()) {
+ s = tmp_file->Append(result);
+ delete tmp_file;
+ if (s.ok()) {
+ s = env->RenameFile(tmp_name, filename);
+ } else {
+ env->DeleteFile(tmp_name);
+ }
+ }
+ }
+
+ delete[] scratch;
+
+ return s;
+}
+
+struct FileState {
+ std::string filename_;
+ ssize_t pos_;
+ ssize_t pos_at_last_sync_;
+ ssize_t pos_at_last_flush_;
+
+ FileState(const std::string& filename)
+ : filename_(filename),
+ pos_(-1),
+ pos_at_last_sync_(-1),
+ pos_at_last_flush_(-1) { }
+
+ FileState() : pos_(-1), pos_at_last_sync_(-1), pos_at_last_flush_(-1) {}
+
+ bool IsFullySynced() const { return pos_ <= 0 || pos_ == pos_at_last_sync_; }
+
+ Status DropUnsyncedData() const;
+};
+
+} // anonymous namespace
+
+// A wrapper around WritableFile which informs another Env whenever this file
+// is written to or sync'ed.
+class TestWritableFile : public WritableFile {
+ public:
+ TestWritableFile(const FileState& state,
+ WritableFile* f,
+ FaultInjectionTestEnv* env);
+ virtual ~TestWritableFile();
+ virtual Status Append(const Slice& data);
+ virtual Status Close();
+ virtual Status Flush();
+ virtual Status Sync();
+
+ private:
+ FileState state_;
+ WritableFile* target_;
+ bool writable_file_opened_;
+ FaultInjectionTestEnv* env_;
+
+ Status SyncParent();
+};
+
+class FaultInjectionTestEnv : public EnvWrapper {
+ public:
+ FaultInjectionTestEnv() : EnvWrapper(Env::Default()), filesystem_active_(true) {}
+ virtual ~FaultInjectionTestEnv() { }
+ virtual Status NewWritableFile(const std::string& fname,
+ WritableFile** result);
+ virtual Status NewAppendableFile(const std::string& fname,
+ WritableFile** result);
+ virtual Status DeleteFile(const std::string& f);
+ virtual Status RenameFile(const std::string& s, const std::string& t);
+
+ void WritableFileClosed(const FileState& state);
+ Status DropUnsyncedFileData();
+ Status DeleteFilesCreatedAfterLastDirSync();
+ void DirWasSynced();
+ bool IsFileCreatedSinceLastDirSync(const std::string& filename);
+ void ResetState();
+ void UntrackFile(const std::string& f);
+ // Setting the filesystem to inactive is the test equivalent to simulating a
+ // system reset. Setting to inactive will freeze our saved filesystem state so
+ // that it will stop being recorded. It can then be reset back to the state at
+ // the time of the reset.
+ bool IsFilesystemActive() const { return filesystem_active_; }
+ void SetFilesystemActive(bool active) { filesystem_active_ = active; }
+
+ private:
+ port::Mutex mutex_;
+ std::map<std::string, FileState> db_file_state_;
+ std::set<std::string> new_files_since_last_dir_sync_;
+ bool filesystem_active_; // Record flushes, syncs, writes
+};
+
+TestWritableFile::TestWritableFile(const FileState& state,
+ WritableFile* f,
+ FaultInjectionTestEnv* env)
+ : state_(state),
+ target_(f),
+ writable_file_opened_(true),
+ env_(env) {
+ assert(f != NULL);
+}
+
+TestWritableFile::~TestWritableFile() {
+ if (writable_file_opened_) {
+ Close();
+ }
+ delete target_;
+}
+
+Status TestWritableFile::Append(const Slice& data) {
+ Status s = target_->Append(data);
+ if (s.ok() && env_->IsFilesystemActive()) {
+ state_.pos_ += data.size();
+ }
+ return s;
+}
+
+Status TestWritableFile::Close() {
+ writable_file_opened_ = false;
+ Status s = target_->Close();
+ if (s.ok()) {
+ env_->WritableFileClosed(state_);
+ }
+ return s;
+}
+
+Status TestWritableFile::Flush() {
+ Status s = target_->Flush();
+ if (s.ok() && env_->IsFilesystemActive()) {
+ state_.pos_at_last_flush_ = state_.pos_;
+ }
+ return s;
+}
+
+Status TestWritableFile::SyncParent() {
+ Status s = SyncDir(GetDirName(state_.filename_));
+ if (s.ok()) {
+ env_->DirWasSynced();
+ }
+ return s;
+}
+
+Status TestWritableFile::Sync() {
+ if (!env_->IsFilesystemActive()) {
+ return Status::OK();
+ }
+ // Ensure new files referred to by the manifest are in the filesystem.
+ Status s = target_->Sync();
+ if (s.ok()) {
+ state_.pos_at_last_sync_ = state_.pos_;
+ }
+ if (env_->IsFileCreatedSinceLastDirSync(state_.filename_)) {
+ Status ps = SyncParent();
+ if (s.ok() && !ps.ok()) {
+ s = ps;
+ }
+ }
+ return s;
+}
+
+Status FaultInjectionTestEnv::NewWritableFile(const std::string& fname,
+ WritableFile** result) {
+ WritableFile* actual_writable_file;
+ Status s = target()->NewWritableFile(fname, &actual_writable_file);
+ if (s.ok()) {
+ FileState state(fname);
+ state.pos_ = 0;
+ *result = new TestWritableFile(state, actual_writable_file, this);
+ // NewWritableFile doesn't append to files, so if the same file is
+ // opened again then it will be truncated - so forget our saved
+ // state.
+ UntrackFile(fname);
+ MutexLock l(&mutex_);
+ new_files_since_last_dir_sync_.insert(fname);
+ }
+ return s;
+}
+
+Status FaultInjectionTestEnv::NewAppendableFile(const std::string& fname,
+ WritableFile** result) {
+ WritableFile* actual_writable_file;
+ Status s = target()->NewAppendableFile(fname, &actual_writable_file);
+ if (s.ok()) {
+ FileState state(fname);
+ state.pos_ = 0;
+ {
+ MutexLock l(&mutex_);
+ if (db_file_state_.count(fname) == 0) {
+ new_files_since_last_dir_sync_.insert(fname);
+ } else {
+ state = db_file_state_[fname];
+ }
+ }
+ *result = new TestWritableFile(state, actual_writable_file, this);
+ }
+ return s;
+}
+
+Status FaultInjectionTestEnv::DropUnsyncedFileData() {
+ Status s;
+ MutexLock l(&mutex_);
+ for (std::map<std::string, FileState>::const_iterator it =
+ db_file_state_.begin();
+ s.ok() && it != db_file_state_.end(); ++it) {
+ const FileState& state = it->second;
+ if (!state.IsFullySynced()) {
+ s = state.DropUnsyncedData();
+ }
+ }
+ return s;
+}
+
+void FaultInjectionTestEnv::DirWasSynced() {
+ MutexLock l(&mutex_);
+ new_files_since_last_dir_sync_.clear();
+}
+
+bool FaultInjectionTestEnv::IsFileCreatedSinceLastDirSync(
+ const std::string& filename) {
+ MutexLock l(&mutex_);
+ return new_files_since_last_dir_sync_.find(filename) !=
+ new_files_since_last_dir_sync_.end();
+}
+
+void FaultInjectionTestEnv::UntrackFile(const std::string& f) {
+ MutexLock l(&mutex_);
+ db_file_state_.erase(f);
+ new_files_since_last_dir_sync_.erase(f);
+}
+
+Status FaultInjectionTestEnv::DeleteFile(const std::string& f) {
+ Status s = EnvWrapper::DeleteFile(f);
+ ASSERT_OK(s);
+ if (s.ok()) {
+ UntrackFile(f);
+ }
+ return s;
+}
+
+Status FaultInjectionTestEnv::RenameFile(const std::string& s,
+ const std::string& t) {
+ Status ret = EnvWrapper::RenameFile(s, t);
+
+ if (ret.ok()) {
+ MutexLock l(&mutex_);
+ if (db_file_state_.find(s) != db_file_state_.end()) {
+ db_file_state_[t] = db_file_state_[s];
+ db_file_state_.erase(s);
+ }
+
+ if (new_files_since_last_dir_sync_.erase(s) != 0) {
+ assert(new_files_since_last_dir_sync_.find(t) ==
+ new_files_since_last_dir_sync_.end());
+ new_files_since_last_dir_sync_.insert(t);
+ }
+ }
+
+ return ret;
+}
+
+void FaultInjectionTestEnv::ResetState() {
+ // Since we are not destroying the database, the existing files
+ // should keep their recorded synced/flushed state. Therefore
+ // we do not reset db_file_state_ and new_files_since_last_dir_sync_.
+ MutexLock l(&mutex_);
+ SetFilesystemActive(true);
+}
+
+Status FaultInjectionTestEnv::DeleteFilesCreatedAfterLastDirSync() {
+ // Because DeleteFile access this container make a copy to avoid deadlock
+ mutex_.Lock();
+ std::set<std::string> new_files(new_files_since_last_dir_sync_.begin(),
+ new_files_since_last_dir_sync_.end());
+ mutex_.Unlock();
+ Status s;
+ std::set<std::string>::const_iterator it;
+ for (it = new_files.begin(); s.ok() && it != new_files.end(); ++it) {
+ s = DeleteFile(*it);
+ }
+ return s;
+}
+
+void FaultInjectionTestEnv::WritableFileClosed(const FileState& state) {
+ MutexLock l(&mutex_);
+ db_file_state_[state.filename_] = state;
+}
+
+Status FileState::DropUnsyncedData() const {
+ ssize_t sync_pos = pos_at_last_sync_ == -1 ? 0 : pos_at_last_sync_;
+ return Truncate(filename_, sync_pos);
+}
+
+class FaultInjectionTest {
+ public:
+ enum ExpectedVerifResult { VAL_EXPECT_NO_ERROR, VAL_EXPECT_ERROR };
+ enum ResetMethod { RESET_DROP_UNSYNCED_DATA, RESET_DELETE_UNSYNCED_FILES };
+
+ FaultInjectionTestEnv* env_;
+ std::string dbname_;
+ Cache* tiny_cache_;
+ Options options_;
+ DB* db_;
+
+ FaultInjectionTest()
+ : env_(new FaultInjectionTestEnv),
+ tiny_cache_(NewLRUCache(100)),
+ db_(NULL) {
+ dbname_ = test::TmpDir() + "/fault_test";
+ DestroyDB(dbname_, Options()); // Destroy any db from earlier run
+ options_.reuse_logs = true;
+ options_.env = env_;
+ options_.paranoid_checks = true;
+ options_.block_cache = tiny_cache_;
+ options_.create_if_missing = true;
+ }
+
+ ~FaultInjectionTest() {
+ CloseDB();
+ DestroyDB(dbname_, Options());
+ delete tiny_cache_;
+ delete env_;
+ }
+
+ void ReuseLogs(bool reuse) {
+ options_.reuse_logs = reuse;
+ }
+
+ void Build(int start_idx, int num_vals) {
+ std::string key_space, value_space;
+ WriteBatch batch;
+ for (int i = start_idx; i < start_idx + num_vals; i++) {
+ Slice key = Key(i, &key_space);
+ batch.Clear();
+ batch.Put(key, Value(i, &value_space));
+ WriteOptions options;
+ ASSERT_OK(db_->Write(options, &batch));
+ }
+ }
+
+ Status ReadValue(int i, std::string* val) const {
+ std::string key_space, value_space;
+ Slice key = Key(i, &key_space);
+ Value(i, &value_space);
+ ReadOptions options;
+ return db_->Get(options, key, val);
+ }
+
+ Status Verify(int start_idx, int num_vals,
+ ExpectedVerifResult expected) const {
+ std::string val;
+ std::string value_space;
+ Status s;
+ for (int i = start_idx; i < start_idx + num_vals && s.ok(); i++) {
+ Value(i, &value_space);
+ s = ReadValue(i, &val);
+ if (expected == VAL_EXPECT_NO_ERROR) {
+ if (s.ok()) {
+ ASSERT_EQ(value_space, val);
+ }
+ } else if (s.ok()) {
+ fprintf(stderr, "Expected an error at %d, but was OK\n", i);
+ s = Status::IOError(dbname_, "Expected value error:");
+ } else {
+ s = Status::OK(); // An expected error
+ }
+ }
+ return s;
+ }
+
+ // Return the ith key
+ Slice Key(int i, std::string* storage) const {
+ char buf[100];
+ snprintf(buf, sizeof(buf), "%016d", i);
+ storage->assign(buf, strlen(buf));
+ return Slice(*storage);
+ }
+
+ // Return the value to associate with the specified key
+ Slice Value(int k, std::string* storage) const {
+ Random r(k);
+ return test::RandomString(&r, kValueSize, storage);
+ }
+
+ Status OpenDB() {
+ delete db_;
+ db_ = NULL;
+ env_->ResetState();
+ return DB::Open(options_, dbname_, &db_);
+ }
+
+ void CloseDB() {
+ delete db_;
+ db_ = NULL;
+ }
+
+ void DeleteAllData() {
+ Iterator* iter = db_->NewIterator(ReadOptions());
+ WriteOptions options;
+ for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
+ ASSERT_OK(db_->Delete(WriteOptions(), iter->key()));
+ }
+
+ delete iter;
+ }
+
+ void ResetDBState(ResetMethod reset_method) {
+ switch (reset_method) {
+ case RESET_DROP_UNSYNCED_DATA:
+ ASSERT_OK(env_->DropUnsyncedFileData());
+ break;
+ case RESET_DELETE_UNSYNCED_FILES:
+ ASSERT_OK(env_->DeleteFilesCreatedAfterLastDirSync());
+ break;
+ default:
+ assert(false);
+ }
+ }
+
+ void PartialCompactTestPreFault(int num_pre_sync, int num_post_sync) {
+ DeleteAllData();
+ Build(0, num_pre_sync);
+ db_->CompactRange(NULL, NULL);
+ Build(num_pre_sync, num_post_sync);
+ }
+
+ void PartialCompactTestReopenWithFault(ResetMethod reset_method,
+ int num_pre_sync,
+ int num_post_sync) {
+ env_->SetFilesystemActive(false);
+ CloseDB();
+ ResetDBState(reset_method);
+ ASSERT_OK(OpenDB());
+ ASSERT_OK(Verify(0, num_pre_sync, FaultInjectionTest::VAL_EXPECT_NO_ERROR));
+ ASSERT_OK(Verify(num_pre_sync, num_post_sync, FaultInjectionTest::VAL_EXPECT_ERROR));
+ }
+
+ void NoWriteTestPreFault() {
+ }
+
+ void NoWriteTestReopenWithFault(ResetMethod reset_method) {
+ CloseDB();
+ ResetDBState(reset_method);
+ ASSERT_OK(OpenDB());
+ }
+
+ void DoTest() {
+ Random rnd(0);
+ ASSERT_OK(OpenDB());
+ for (size_t idx = 0; idx < kNumIterations; idx++) {
+ int num_pre_sync = rnd.Uniform(kMaxNumValues);
+ int num_post_sync = rnd.Uniform(kMaxNumValues);
+
+ PartialCompactTestPreFault(num_pre_sync, num_post_sync);
+ PartialCompactTestReopenWithFault(RESET_DROP_UNSYNCED_DATA,
+ num_pre_sync,
+ num_post_sync);
+
+ NoWriteTestPreFault();
+ NoWriteTestReopenWithFault(RESET_DROP_UNSYNCED_DATA);
+
+ PartialCompactTestPreFault(num_pre_sync, num_post_sync);
+ // No new files created so we expect all values since no files will be
+ // dropped.
+ PartialCompactTestReopenWithFault(RESET_DELETE_UNSYNCED_FILES,
+ num_pre_sync + num_post_sync,
+ 0);
+
+ NoWriteTestPreFault();
+ NoWriteTestReopenWithFault(RESET_DELETE_UNSYNCED_FILES);
+ }
+ }
+};
+
+TEST(FaultInjectionTest, FaultTestNoLogReuse) {
+ ReuseLogs(false);
+ DoTest();
+}
+
+TEST(FaultInjectionTest, FaultTestWithLogReuse) {
+ ReuseLogs(true);
+ DoTest();
+}
+
+} // namespace leveldb
+
+int main(int argc, char** argv) {
+ return leveldb::test::RunAllTests();
+}
diff --git a/src/leveldb/db/leveldb_main.cc b/src/leveldb/db/leveldbutil.cc
index 9f4b7dd70c..9f4b7dd70c 100644
--- a/src/leveldb/db/leveldb_main.cc
+++ b/src/leveldb/db/leveldbutil.cc
diff --git a/src/leveldb/db/log_reader.cc b/src/leveldb/db/log_reader.cc
index e44b66c85b..a6d304545d 100644
--- a/src/leveldb/db/log_reader.cc
+++ b/src/leveldb/db/log_reader.cc
@@ -25,7 +25,8 @@ Reader::Reader(SequentialFile* file, Reporter* reporter, bool checksum,
eof_(false),
last_record_offset_(0),
end_of_buffer_offset_(0),
- initial_offset_(initial_offset) {
+ initial_offset_(initial_offset),
+ resyncing_(initial_offset > 0) {
}
Reader::~Reader() {
@@ -72,8 +73,25 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch) {
Slice fragment;
while (true) {
- uint64_t physical_record_offset = end_of_buffer_offset_ - buffer_.size();
const unsigned int record_type = ReadPhysicalRecord(&fragment);
+
+ // ReadPhysicalRecord may have only had an empty trailer remaining in its
+ // internal buffer. Calculate the offset of the next physical record now
+ // that it has returned, properly accounting for its header size.
+ uint64_t physical_record_offset =
+ end_of_buffer_offset_ - buffer_.size() - kHeaderSize - fragment.size();
+
+ if (resyncing_) {
+ if (record_type == kMiddleType) {
+ continue;
+ } else if (record_type == kLastType) {
+ resyncing_ = false;
+ continue;
+ } else {
+ resyncing_ = false;
+ }
+ }
+
switch (record_type) {
case kFullType:
if (in_fragmented_record) {
diff --git a/src/leveldb/db/log_reader.h b/src/leveldb/db/log_reader.h
index 6aff791716..8389d61f8f 100644
--- a/src/leveldb/db/log_reader.h
+++ b/src/leveldb/db/log_reader.h
@@ -73,6 +73,11 @@ class Reader {
// Offset at which to start looking for the first record to return
uint64_t const initial_offset_;
+ // True if we are resynchronizing after a seek (initial_offset_ > 0). In
+ // particular, a run of kMiddleType and kLastType records can be silently
+ // skipped in this mode
+ bool resyncing_;
+
// Extend record types with the following special values
enum {
kEof = kMaxRecordType + 1,
diff --git a/src/leveldb/db/log_test.cc b/src/leveldb/db/log_test.cc
index dcf0562652..48a5928657 100644
--- a/src/leveldb/db/log_test.cc
+++ b/src/leveldb/db/log_test.cc
@@ -79,7 +79,7 @@ class LogTest {
virtual Status Skip(uint64_t n) {
if (n > contents_.size()) {
contents_.clear();
- return Status::NotFound("in-memory file skipepd past end");
+ return Status::NotFound("in-memory file skipped past end");
}
contents_.remove_prefix(n);
@@ -104,23 +104,34 @@ class LogTest {
StringSource source_;
ReportCollector report_;
bool reading_;
- Writer writer_;
- Reader reader_;
+ Writer* writer_;
+ Reader* reader_;
// Record metadata for testing initial offset functionality
static size_t initial_offset_record_sizes_[];
static uint64_t initial_offset_last_record_offsets_[];
+ static int num_initial_offset_records_;
public:
LogTest() : reading_(false),
- writer_(&dest_),
- reader_(&source_, &report_, true/*checksum*/,
- 0/*initial_offset*/) {
+ writer_(new Writer(&dest_)),
+ reader_(new Reader(&source_, &report_, true/*checksum*/,
+ 0/*initial_offset*/)) {
+ }
+
+ ~LogTest() {
+ delete writer_;
+ delete reader_;
+ }
+
+ void ReopenForAppend() {
+ delete writer_;
+ writer_ = new Writer(&dest_, dest_.contents_.size());
}
void Write(const std::string& msg) {
ASSERT_TRUE(!reading_) << "Write() after starting to read";
- writer_.AddRecord(Slice(msg));
+ writer_->AddRecord(Slice(msg));
}
size_t WrittenBytes() const {
@@ -134,7 +145,7 @@ class LogTest {
}
std::string scratch;
Slice record;
- if (reader_.ReadRecord(&record, &scratch)) {
+ if (reader_->ReadRecord(&record, &scratch)) {
return record.ToString();
} else {
return "EOF";
@@ -182,13 +193,18 @@ class LogTest {
}
void WriteInitialOffsetLog() {
- for (int i = 0; i < 4; i++) {
+ for (int i = 0; i < num_initial_offset_records_; i++) {
std::string record(initial_offset_record_sizes_[i],
static_cast<char>('a' + i));
Write(record);
}
}
+ void StartReadingAt(uint64_t initial_offset) {
+ delete reader_;
+ reader_ = new Reader(&source_, &report_, true/*checksum*/, initial_offset);
+ }
+
void CheckOffsetPastEndReturnsNoRecords(uint64_t offset_past_end) {
WriteInitialOffsetLog();
reading_ = true;
@@ -208,32 +224,48 @@ class LogTest {
source_.contents_ = Slice(dest_.contents_);
Reader* offset_reader = new Reader(&source_, &report_, true/*checksum*/,
initial_offset);
- Slice record;
- std::string scratch;
- ASSERT_TRUE(offset_reader->ReadRecord(&record, &scratch));
- ASSERT_EQ(initial_offset_record_sizes_[expected_record_offset],
- record.size());
- ASSERT_EQ(initial_offset_last_record_offsets_[expected_record_offset],
- offset_reader->LastRecordOffset());
- ASSERT_EQ((char)('a' + expected_record_offset), record.data()[0]);
+
+ // Read all records from expected_record_offset through the last one.
+ ASSERT_LT(expected_record_offset, num_initial_offset_records_);
+ for (; expected_record_offset < num_initial_offset_records_;
+ ++expected_record_offset) {
+ Slice record;
+ std::string scratch;
+ ASSERT_TRUE(offset_reader->ReadRecord(&record, &scratch));
+ ASSERT_EQ(initial_offset_record_sizes_[expected_record_offset],
+ record.size());
+ ASSERT_EQ(initial_offset_last_record_offsets_[expected_record_offset],
+ offset_reader->LastRecordOffset());
+ ASSERT_EQ((char)('a' + expected_record_offset), record.data()[0]);
+ }
delete offset_reader;
}
-
};
size_t LogTest::initial_offset_record_sizes_[] =
{10000, // Two sizable records in first block
10000,
2 * log::kBlockSize - 1000, // Span three blocks
- 1};
+ 1,
+ 13716, // Consume all but two bytes of block 3.
+ log::kBlockSize - kHeaderSize, // Consume the entirety of block 4.
+ };
uint64_t LogTest::initial_offset_last_record_offsets_[] =
{0,
kHeaderSize + 10000,
2 * (kHeaderSize + 10000),
2 * (kHeaderSize + 10000) +
- (2 * log::kBlockSize - 1000) + 3 * kHeaderSize};
+ (2 * log::kBlockSize - 1000) + 3 * kHeaderSize,
+ 2 * (kHeaderSize + 10000) +
+ (2 * log::kBlockSize - 1000) + 3 * kHeaderSize
+ + kHeaderSize + 1,
+ 3 * log::kBlockSize,
+ };
+// LogTest::initial_offset_last_record_offsets_ must be defined before this.
+int LogTest::num_initial_offset_records_ =
+ sizeof(LogTest::initial_offset_last_record_offsets_)/sizeof(uint64_t);
TEST(LogTest, Empty) {
ASSERT_EQ("EOF", Read());
@@ -318,6 +350,15 @@ TEST(LogTest, AlignedEof) {
ASSERT_EQ("EOF", Read());
}
+TEST(LogTest, OpenForAppend) {
+ Write("hello");
+ ReopenForAppend();
+ Write("world");
+ ASSERT_EQ("hello", Read());
+ ASSERT_EQ("world", Read());
+ ASSERT_EQ("EOF", Read());
+}
+
TEST(LogTest, RandomRead) {
const int N = 500;
Random write_rnd(301);
@@ -445,6 +486,22 @@ TEST(LogTest, PartialLastIsIgnored) {
ASSERT_EQ(0, DroppedBytes());
}
+TEST(LogTest, SkipIntoMultiRecord) {
+ // Consider a fragmented record:
+ // first(R1), middle(R1), last(R1), first(R2)
+ // If initial_offset points to a record after first(R1) but before first(R2)
+ // incomplete fragment errors are not actual errors, and must be suppressed
+ // until a new first or full record is encountered.
+ Write(BigString("foo", 3*kBlockSize));
+ Write("correct");
+ StartReadingAt(kBlockSize);
+
+ ASSERT_EQ("correct", Read());
+ ASSERT_EQ("", ReportMessage());
+ ASSERT_EQ(0, DroppedBytes());
+ ASSERT_EQ("EOF", Read());
+}
+
TEST(LogTest, ErrorJoinsRecords) {
// Consider two fragmented records:
// first(R1) last(R1) first(R2) last(R2)
@@ -514,6 +571,10 @@ TEST(LogTest, ReadFourthStart) {
3);
}
+TEST(LogTest, ReadInitialOffsetIntoBlockPadding) {
+ CheckInitialOffsetRecord(3 * log::kBlockSize - 3, 5);
+}
+
TEST(LogTest, ReadEnd) {
CheckOffsetPastEndReturnsNoRecords(0);
}
diff --git a/src/leveldb/db/log_writer.cc b/src/leveldb/db/log_writer.cc
index 2da99ac088..74a03270da 100644
--- a/src/leveldb/db/log_writer.cc
+++ b/src/leveldb/db/log_writer.cc
@@ -12,15 +12,24 @@
namespace leveldb {
namespace log {
-Writer::Writer(WritableFile* dest)
- : dest_(dest),
- block_offset_(0) {
+static void InitTypeCrc(uint32_t* type_crc) {
for (int i = 0; i <= kMaxRecordType; i++) {
char t = static_cast<char>(i);
- type_crc_[i] = crc32c::Value(&t, 1);
+ type_crc[i] = crc32c::Value(&t, 1);
}
}
+Writer::Writer(WritableFile* dest)
+ : dest_(dest),
+ block_offset_(0) {
+ InitTypeCrc(type_crc_);
+}
+
+Writer::Writer(WritableFile* dest, uint64_t dest_length)
+ : dest_(dest), block_offset_(dest_length % kBlockSize) {
+ InitTypeCrc(type_crc_);
+}
+
Writer::~Writer() {
}
diff --git a/src/leveldb/db/log_writer.h b/src/leveldb/db/log_writer.h
index a3a954d967..9e7cc4705b 100644
--- a/src/leveldb/db/log_writer.h
+++ b/src/leveldb/db/log_writer.h
@@ -22,6 +22,12 @@ class Writer {
// "*dest" must be initially empty.
// "*dest" must remain live while this Writer is in use.
explicit Writer(WritableFile* dest);
+
+ // Create a writer that will append data to "*dest".
+ // "*dest" must have initial length "dest_length".
+ // "*dest" must remain live while this Writer is in use.
+ Writer(WritableFile* dest, uint64_t dest_length);
+
~Writer();
Status AddRecord(const Slice& slice);
diff --git a/src/leveldb/db/memtable.h b/src/leveldb/db/memtable.h
index 92e90bb099..9f41567cde 100644
--- a/src/leveldb/db/memtable.h
+++ b/src/leveldb/db/memtable.h
@@ -36,10 +36,7 @@ class MemTable {
}
// Returns an estimate of the number of bytes of data in use by this
- // data structure.
- //
- // REQUIRES: external synchronization to prevent simultaneous
- // operations on the same MemTable.
+ // data structure. It is safe to call when MemTable is being modified.
size_t ApproximateMemoryUsage();
// Return an iterator that yields the contents of the memtable.
diff --git a/src/leveldb/db/recovery_test.cc b/src/leveldb/db/recovery_test.cc
new file mode 100644
index 0000000000..9596f4288a
--- /dev/null
+++ b/src/leveldb/db/recovery_test.cc
@@ -0,0 +1,324 @@
+// Copyright (c) 2014 The LevelDB Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file. See the AUTHORS file for names of contributors.
+
+#include "db/db_impl.h"
+#include "db/filename.h"
+#include "db/version_set.h"
+#include "db/write_batch_internal.h"
+#include "leveldb/db.h"
+#include "leveldb/env.h"
+#include "leveldb/write_batch.h"
+#include "util/logging.h"
+#include "util/testharness.h"
+#include "util/testutil.h"
+
+namespace leveldb {
+
+class RecoveryTest {
+ public:
+ RecoveryTest() : env_(Env::Default()), db_(NULL) {
+ dbname_ = test::TmpDir() + "/recovery_test";
+ DestroyDB(dbname_, Options());
+ Open();
+ }
+
+ ~RecoveryTest() {
+ Close();
+ DestroyDB(dbname_, Options());
+ }
+
+ DBImpl* dbfull() const { return reinterpret_cast<DBImpl*>(db_); }
+ Env* env() const { return env_; }
+
+ bool CanAppend() {
+ WritableFile* tmp;
+ Status s = env_->NewAppendableFile(CurrentFileName(dbname_), &tmp);
+ delete tmp;
+ if (s.IsNotSupportedError()) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ void Close() {
+ delete db_;
+ db_ = NULL;
+ }
+
+ void Open(Options* options = NULL) {
+ Close();
+ Options opts;
+ if (options != NULL) {
+ opts = *options;
+ } else {
+ opts.reuse_logs = true; // TODO(sanjay): test both ways
+ opts.create_if_missing = true;
+ }
+ if (opts.env == NULL) {
+ opts.env = env_;
+ }
+ ASSERT_OK(DB::Open(opts, dbname_, &db_));
+ ASSERT_EQ(1, NumLogs());
+ }
+
+ Status Put(const std::string& k, const std::string& v) {
+ return db_->Put(WriteOptions(), k, v);
+ }
+
+ std::string Get(const std::string& k, const Snapshot* snapshot = NULL) {
+ std::string result;
+ Status s = db_->Get(ReadOptions(), k, &result);
+ if (s.IsNotFound()) {
+ result = "NOT_FOUND";
+ } else if (!s.ok()) {
+ result = s.ToString();
+ }
+ return result;
+ }
+
+ std::string ManifestFileName() {
+ std::string current;
+ ASSERT_OK(ReadFileToString(env_, CurrentFileName(dbname_), &current));
+ size_t len = current.size();
+ if (len > 0 && current[len-1] == '\n') {
+ current.resize(len - 1);
+ }
+ return dbname_ + "/" + current;
+ }
+
+ std::string LogName(uint64_t number) {
+ return LogFileName(dbname_, number);
+ }
+
+ size_t DeleteLogFiles() {
+ std::vector<uint64_t> logs = GetFiles(kLogFile);
+ for (size_t i = 0; i < logs.size(); i++) {
+ ASSERT_OK(env_->DeleteFile(LogName(logs[i]))) << LogName(logs[i]);
+ }
+ return logs.size();
+ }
+
+ uint64_t FirstLogFile() {
+ return GetFiles(kLogFile)[0];
+ }
+
+ std::vector<uint64_t> GetFiles(FileType t) {
+ std::vector<std::string> filenames;
+ ASSERT_OK(env_->GetChildren(dbname_, &filenames));
+ std::vector<uint64_t> result;
+ for (size_t i = 0; i < filenames.size(); i++) {
+ uint64_t number;
+ FileType type;
+ if (ParseFileName(filenames[i], &number, &type) && type == t) {
+ result.push_back(number);
+ }
+ }
+ return result;
+ }
+
+ int NumLogs() {
+ return GetFiles(kLogFile).size();
+ }
+
+ int NumTables() {
+ return GetFiles(kTableFile).size();
+ }
+
+ uint64_t FileSize(const std::string& fname) {
+ uint64_t result;
+ ASSERT_OK(env_->GetFileSize(fname, &result)) << fname;
+ return result;
+ }
+
+ void CompactMemTable() {
+ dbfull()->TEST_CompactMemTable();
+ }
+
+ // Directly construct a log file that sets key to val.
+ void MakeLogFile(uint64_t lognum, SequenceNumber seq, Slice key, Slice val) {
+ std::string fname = LogFileName(dbname_, lognum);
+ WritableFile* file;
+ ASSERT_OK(env_->NewWritableFile(fname, &file));
+ log::Writer writer(file);
+ WriteBatch batch;
+ batch.Put(key, val);
+ WriteBatchInternal::SetSequence(&batch, seq);
+ ASSERT_OK(writer.AddRecord(WriteBatchInternal::Contents(&batch)));
+ ASSERT_OK(file->Flush());
+ delete file;
+ }
+
+ private:
+ std::string dbname_;
+ Env* env_;
+ DB* db_;
+};
+
+TEST(RecoveryTest, ManifestReused) {
+ if (!CanAppend()) {
+ fprintf(stderr, "skipping test because env does not support appending\n");
+ return;
+ }
+ ASSERT_OK(Put("foo", "bar"));
+ Close();
+ std::string old_manifest = ManifestFileName();
+ Open();
+ ASSERT_EQ(old_manifest, ManifestFileName());
+ ASSERT_EQ("bar", Get("foo"));
+ Open();
+ ASSERT_EQ(old_manifest, ManifestFileName());
+ ASSERT_EQ("bar", Get("foo"));
+}
+
+TEST(RecoveryTest, LargeManifestCompacted) {
+ if (!CanAppend()) {
+ fprintf(stderr, "skipping test because env does not support appending\n");
+ return;
+ }
+ ASSERT_OK(Put("foo", "bar"));
+ Close();
+ std::string old_manifest = ManifestFileName();
+
+ // Pad with zeroes to make manifest file very big.
+ {
+ uint64_t len = FileSize(old_manifest);
+ WritableFile* file;
+ ASSERT_OK(env()->NewAppendableFile(old_manifest, &file));
+ std::string zeroes(3*1048576 - static_cast<size_t>(len), 0);
+ ASSERT_OK(file->Append(zeroes));
+ ASSERT_OK(file->Flush());
+ delete file;
+ }
+
+ Open();
+ std::string new_manifest = ManifestFileName();
+ ASSERT_NE(old_manifest, new_manifest);
+ ASSERT_GT(10000, FileSize(new_manifest));
+ ASSERT_EQ("bar", Get("foo"));
+
+ Open();
+ ASSERT_EQ(new_manifest, ManifestFileName());
+ ASSERT_EQ("bar", Get("foo"));
+}
+
+TEST(RecoveryTest, NoLogFiles) {
+ ASSERT_OK(Put("foo", "bar"));
+ ASSERT_EQ(1, DeleteLogFiles());
+ Open();
+ ASSERT_EQ("NOT_FOUND", Get("foo"));
+ Open();
+ ASSERT_EQ("NOT_FOUND", Get("foo"));
+}
+
+TEST(RecoveryTest, LogFileReuse) {
+ if (!CanAppend()) {
+ fprintf(stderr, "skipping test because env does not support appending\n");
+ return;
+ }
+ for (int i = 0; i < 2; i++) {
+ ASSERT_OK(Put("foo", "bar"));
+ if (i == 0) {
+ // Compact to ensure current log is empty
+ CompactMemTable();
+ }
+ Close();
+ ASSERT_EQ(1, NumLogs());
+ uint64_t number = FirstLogFile();
+ if (i == 0) {
+ ASSERT_EQ(0, FileSize(LogName(number)));
+ } else {
+ ASSERT_LT(0, FileSize(LogName(number)));
+ }
+ Open();
+ ASSERT_EQ(1, NumLogs());
+ ASSERT_EQ(number, FirstLogFile()) << "did not reuse log file";
+ ASSERT_EQ("bar", Get("foo"));
+ Open();
+ ASSERT_EQ(1, NumLogs());
+ ASSERT_EQ(number, FirstLogFile()) << "did not reuse log file";
+ ASSERT_EQ("bar", Get("foo"));
+ }
+}
+
+TEST(RecoveryTest, MultipleMemTables) {
+ // Make a large log.
+ const int kNum = 1000;
+ for (int i = 0; i < kNum; i++) {
+ char buf[100];
+ snprintf(buf, sizeof(buf), "%050d", i);
+ ASSERT_OK(Put(buf, buf));
+ }
+ ASSERT_EQ(0, NumTables());
+ Close();
+ ASSERT_EQ(0, NumTables());
+ ASSERT_EQ(1, NumLogs());
+ uint64_t old_log_file = FirstLogFile();
+
+ // Force creation of multiple memtables by reducing the write buffer size.
+ Options opt;
+ opt.reuse_logs = true;
+ opt.write_buffer_size = (kNum*100) / 2;
+ Open(&opt);
+ ASSERT_LE(2, NumTables());
+ ASSERT_EQ(1, NumLogs());
+ ASSERT_NE(old_log_file, FirstLogFile()) << "must not reuse log";
+ for (int i = 0; i < kNum; i++) {
+ char buf[100];
+ snprintf(buf, sizeof(buf), "%050d", i);
+ ASSERT_EQ(buf, Get(buf));
+ }
+}
+
+TEST(RecoveryTest, MultipleLogFiles) {
+ ASSERT_OK(Put("foo", "bar"));
+ Close();
+ ASSERT_EQ(1, NumLogs());
+
+ // Make a bunch of uncompacted log files.
+ uint64_t old_log = FirstLogFile();
+ MakeLogFile(old_log+1, 1000, "hello", "world");
+ MakeLogFile(old_log+2, 1001, "hi", "there");
+ MakeLogFile(old_log+3, 1002, "foo", "bar2");
+
+ // Recover and check that all log files were processed.
+ Open();
+ ASSERT_LE(1, NumTables());
+ ASSERT_EQ(1, NumLogs());
+ uint64_t new_log = FirstLogFile();
+ ASSERT_LE(old_log+3, new_log);
+ ASSERT_EQ("bar2", Get("foo"));
+ ASSERT_EQ("world", Get("hello"));
+ ASSERT_EQ("there", Get("hi"));
+
+ // Test that previous recovery produced recoverable state.
+ Open();
+ ASSERT_LE(1, NumTables());
+ ASSERT_EQ(1, NumLogs());
+ if (CanAppend()) {
+ ASSERT_EQ(new_log, FirstLogFile());
+ }
+ ASSERT_EQ("bar2", Get("foo"));
+ ASSERT_EQ("world", Get("hello"));
+ ASSERT_EQ("there", Get("hi"));
+
+ // Check that introducing an older log file does not cause it to be re-read.
+ Close();
+ MakeLogFile(old_log+1, 2000, "hello", "stale write");
+ Open();
+ ASSERT_LE(1, NumTables());
+ ASSERT_EQ(1, NumLogs());
+ if (CanAppend()) {
+ ASSERT_EQ(new_log, FirstLogFile());
+ }
+ ASSERT_EQ("bar2", Get("foo"));
+ ASSERT_EQ("world", Get("hello"));
+ ASSERT_EQ("there", Get("hi"));
+}
+
+} // namespace leveldb
+
+int main(int argc, char** argv) {
+ return leveldb::test::RunAllTests();
+}
diff --git a/src/leveldb/db/skiplist.h b/src/leveldb/db/skiplist.h
index ed8b092203..8bd77764d8 100644
--- a/src/leveldb/db/skiplist.h
+++ b/src/leveldb/db/skiplist.h
@@ -1,10 +1,10 @@
-#ifndef STORAGE_LEVELDB_DB_SKIPLIST_H_
-#define STORAGE_LEVELDB_DB_SKIPLIST_H_
-
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.
-//
+
+#ifndef STORAGE_LEVELDB_DB_SKIPLIST_H_
+#define STORAGE_LEVELDB_DB_SKIPLIST_H_
+
// Thread safety
// -------------
//
diff --git a/src/leveldb/db/skiplist_test.cc b/src/leveldb/db/skiplist_test.cc
index c78f4b4fb1..aee1461e1b 100644
--- a/src/leveldb/db/skiplist_test.cc
+++ b/src/leveldb/db/skiplist_test.cc
@@ -250,7 +250,7 @@ class ConcurrentTest {
// Note that generation 0 is never inserted, so it is ok if
// <*,0,*> is missing.
ASSERT_TRUE((gen(pos) == 0) ||
- (gen(pos) > initial_state.Get(key(pos)))
+ (gen(pos) > static_cast<Key>(initial_state.Get(key(pos))))
) << "key: " << key(pos)
<< "; gen: " << gen(pos)
<< "; initgen: "
diff --git a/src/leveldb/db/snapshot.h b/src/leveldb/db/snapshot.h
index e7f8fd2c37..6ed413c42d 100644
--- a/src/leveldb/db/snapshot.h
+++ b/src/leveldb/db/snapshot.h
@@ -5,6 +5,7 @@
#ifndef STORAGE_LEVELDB_DB_SNAPSHOT_H_
#define STORAGE_LEVELDB_DB_SNAPSHOT_H_
+#include "db/dbformat.h"
#include "leveldb/db.h"
namespace leveldb {
diff --git a/src/leveldb/db/version_set.cc b/src/leveldb/db/version_set.cc
index aa83df55e4..a5e0f77a6a 100644
--- a/src/leveldb/db/version_set.cc
+++ b/src/leveldb/db/version_set.cc
@@ -893,7 +893,7 @@ Status VersionSet::LogAndApply(VersionEdit* edit, port::Mutex* mu) {
return s;
}
-Status VersionSet::Recover() {
+Status VersionSet::Recover(bool *save_manifest) {
struct LogReporter : public log::Reader::Reporter {
Status* status;
virtual void Corruption(size_t bytes, const Status& s) {
@@ -1003,11 +1003,49 @@ Status VersionSet::Recover() {
last_sequence_ = last_sequence;
log_number_ = log_number;
prev_log_number_ = prev_log_number;
+
+ // See if we can reuse the existing MANIFEST file.
+ if (ReuseManifest(dscname, current)) {
+ // No need to save new manifest
+ } else {
+ *save_manifest = true;
+ }
}
return s;
}
+bool VersionSet::ReuseManifest(const std::string& dscname,
+ const std::string& dscbase) {
+ if (!options_->reuse_logs) {
+ return false;
+ }
+ FileType manifest_type;
+ uint64_t manifest_number;
+ uint64_t manifest_size;
+ if (!ParseFileName(dscbase, &manifest_number, &manifest_type) ||
+ manifest_type != kDescriptorFile ||
+ !env_->GetFileSize(dscname, &manifest_size).ok() ||
+ // Make new compacted MANIFEST if old one is too big
+ manifest_size >= kTargetFileSize) {
+ return false;
+ }
+
+ assert(descriptor_file_ == NULL);
+ assert(descriptor_log_ == NULL);
+ Status r = env_->NewAppendableFile(dscname, &descriptor_file_);
+ if (!r.ok()) {
+ Log(options_->info_log, "Reuse MANIFEST: %s\n", r.ToString().c_str());
+ assert(descriptor_file_ == NULL);
+ return false;
+ }
+
+ Log(options_->info_log, "Reusing MANIFEST %s\n", dscname.c_str());
+ descriptor_log_ = new log::Writer(descriptor_file_, manifest_size);
+ manifest_file_number_ = manifest_number;
+ return true;
+}
+
void VersionSet::MarkFileNumberUsed(uint64_t number) {
if (next_file_number_ <= number) {
next_file_number_ = number + 1;
diff --git a/src/leveldb/db/version_set.h b/src/leveldb/db/version_set.h
index 8dc14b8e01..1dec745673 100644
--- a/src/leveldb/db/version_set.h
+++ b/src/leveldb/db/version_set.h
@@ -179,7 +179,7 @@ class VersionSet {
EXCLUSIVE_LOCKS_REQUIRED(mu);
// Recover the last saved descriptor from persistent storage.
- Status Recover();
+ Status Recover(bool *save_manifest);
// Return the current version.
Version* current() const { return current_; }
@@ -274,6 +274,8 @@ class VersionSet {
friend class Compaction;
friend class Version;
+ bool ReuseManifest(const std::string& dscname, const std::string& dscbase);
+
void Finalize(Version* v);
void GetRange(const std::vector<FileMetaData*>& inputs,
diff --git a/src/leveldb/db/write_batch_internal.h b/src/leveldb/db/write_batch_internal.h
index 310a3c8912..9448ef7b21 100644
--- a/src/leveldb/db/write_batch_internal.h
+++ b/src/leveldb/db/write_batch_internal.h
@@ -5,6 +5,7 @@
#ifndef STORAGE_LEVELDB_DB_WRITE_BATCH_INTERNAL_H_
#define STORAGE_LEVELDB_DB_WRITE_BATCH_INTERNAL_H_
+#include "db/dbformat.h"
#include "leveldb/write_batch.h"
namespace leveldb {
diff --git a/src/leveldb/doc/index.html b/src/leveldb/doc/index.html
index 3ed0ed9d9e..2155192581 100644
--- a/src/leveldb/doc/index.html
+++ b/src/leveldb/doc/index.html
@@ -22,7 +22,7 @@ directory. The following example shows how to open a database,
creating it if necessary:
<p>
<pre>
- #include &lt;assert&gt;
+ #include &lt;cassert&gt;
#include "leveldb/db.h"
leveldb::DB* db;
diff --git a/src/leveldb/helpers/memenv/memenv.cc b/src/leveldb/helpers/memenv/memenv.cc
index 43ef2e0729..9a98884daf 100644
--- a/src/leveldb/helpers/memenv/memenv.cc
+++ b/src/leveldb/helpers/memenv/memenv.cc
@@ -277,6 +277,19 @@ class InMemoryEnv : public EnvWrapper {
return Status::OK();
}
+ virtual Status NewAppendableFile(const std::string& fname,
+ WritableFile** result) {
+ MutexLock lock(&mutex_);
+ FileState** sptr = &file_map_[fname];
+ FileState* file = *sptr;
+ if (file == NULL) {
+ file = new FileState();
+ file->Ref();
+ }
+ *result = new WritableFileImpl(file);
+ return Status::OK();
+ }
+
virtual bool FileExists(const std::string& fname) {
MutexLock lock(&mutex_);
return file_map_.find(fname) != file_map_.end();
diff --git a/src/leveldb/helpers/memenv/memenv_test.cc b/src/leveldb/helpers/memenv/memenv_test.cc
index a44310fed8..5cff77613f 100644
--- a/src/leveldb/helpers/memenv/memenv_test.cc
+++ b/src/leveldb/helpers/memenv/memenv_test.cc
@@ -40,6 +40,8 @@ TEST(MemEnvTest, Basics) {
// Create a file.
ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file));
+ ASSERT_OK(env_->GetFileSize("/dir/f", &file_size));
+ ASSERT_EQ(0, file_size);
delete writable_file;
// Check that the file exists.
@@ -55,9 +57,16 @@ TEST(MemEnvTest, Basics) {
ASSERT_OK(writable_file->Append("abc"));
delete writable_file;
- // Check for expected size.
+ // Check that append works.
+ ASSERT_OK(env_->NewAppendableFile("/dir/f", &writable_file));
ASSERT_OK(env_->GetFileSize("/dir/f", &file_size));
ASSERT_EQ(3, file_size);
+ ASSERT_OK(writable_file->Append("hello"));
+ delete writable_file;
+
+ // Check for expected size.
+ ASSERT_OK(env_->GetFileSize("/dir/f", &file_size));
+ ASSERT_EQ(8, file_size);
// Check that renaming works.
ASSERT_TRUE(!env_->RenameFile("/dir/non_existent", "/dir/g").ok());
@@ -65,7 +74,7 @@ TEST(MemEnvTest, Basics) {
ASSERT_TRUE(!env_->FileExists("/dir/f"));
ASSERT_TRUE(env_->FileExists("/dir/g"));
ASSERT_OK(env_->GetFileSize("/dir/g", &file_size));
- ASSERT_EQ(3, file_size);
+ ASSERT_EQ(8, file_size);
// Check that opening non-existent file fails.
SequentialFile* seq_file;
diff --git a/src/leveldb/include/leveldb/cache.h b/src/leveldb/include/leveldb/cache.h
index 1a201e5e0a..6819d5bc49 100644
--- a/src/leveldb/include/leveldb/cache.h
+++ b/src/leveldb/include/leveldb/cache.h
@@ -81,6 +81,17 @@ class Cache {
// its cache keys.
virtual uint64_t NewId() = 0;
+ // Remove all cache entries that are not actively in use. Memory-constrained
+ // applications may wish to call this method to reduce memory usage.
+ // Default implementation of Prune() does nothing. Subclasses are strongly
+ // encouraged to override the default implementation. A future release of
+ // leveldb may change Prune() to a pure abstract method.
+ virtual void Prune() {}
+
+ // Return an estimate of the combined charges of all elements stored in the
+ // cache.
+ virtual size_t TotalCharge() const = 0;
+
private:
void LRU_Remove(Handle* e);
void LRU_Append(Handle* e);
diff --git a/src/leveldb/include/leveldb/db.h b/src/leveldb/include/leveldb/db.h
index 4c169bf22e..9752cbad51 100644
--- a/src/leveldb/include/leveldb/db.h
+++ b/src/leveldb/include/leveldb/db.h
@@ -14,7 +14,7 @@ namespace leveldb {
// Update Makefile if you change these
static const int kMajorVersion = 1;
-static const int kMinorVersion = 18;
+static const int kMinorVersion = 19;
struct Options;
struct ReadOptions;
@@ -115,6 +115,8 @@ class DB {
// about the internal operation of the DB.
// "leveldb.sstables" - returns a multi-line string that describes all
// of the sstables that make up the db contents.
+ // "leveldb.approximate-memory-usage" - returns the approximate number of
+ // bytes of memory in use by the DB.
virtual bool GetProperty(const Slice& property, std::string* value) = 0;
// For each i in [0,n-1], store in "sizes[i]", the approximate
diff --git a/src/leveldb/include/leveldb/env.h b/src/leveldb/include/leveldb/env.h
index f709514da6..99b6c21414 100644
--- a/src/leveldb/include/leveldb/env.h
+++ b/src/leveldb/include/leveldb/env.h
@@ -69,6 +69,21 @@ class Env {
virtual Status NewWritableFile(const std::string& fname,
WritableFile** result) = 0;
+ // Create an object that either appends to an existing file, or
+ // writes to a new file (if the file does not exist to begin with).
+ // On success, stores a pointer to the new file in *result and
+ // returns OK. On failure stores NULL in *result and returns
+ // non-OK.
+ //
+ // The returned file will only be accessed by one thread at a time.
+ //
+ // May return an IsNotSupportedError error if this Env does
+ // not allow appending to an existing file. Users of Env (including
+ // the leveldb implementation) must be prepared to deal with
+ // an Env that does not support appending.
+ virtual Status NewAppendableFile(const std::string& fname,
+ WritableFile** result);
+
// Returns true iff the named file exists.
virtual bool FileExists(const std::string& fname) = 0;
@@ -289,6 +304,9 @@ class EnvWrapper : public Env {
Status NewWritableFile(const std::string& f, WritableFile** r) {
return target_->NewWritableFile(f, r);
}
+ Status NewAppendableFile(const std::string& f, WritableFile** r) {
+ return target_->NewAppendableFile(f, r);
+ }
bool FileExists(const std::string& f) { return target_->FileExists(f); }
Status GetChildren(const std::string& dir, std::vector<std::string>* r) {
return target_->GetChildren(dir, r);
diff --git a/src/leveldb/include/leveldb/iterator.h b/src/leveldb/include/leveldb/iterator.h
index 76aced04bd..da631ed9d8 100644
--- a/src/leveldb/include/leveldb/iterator.h
+++ b/src/leveldb/include/leveldb/iterator.h
@@ -37,7 +37,7 @@ class Iterator {
// Valid() after this call iff the source is not empty.
virtual void SeekToLast() = 0;
- // Position at the first key in the source that at or past target
+ // Position at the first key in the source that is at or past target.
// The iterator is Valid() after this call iff the source contains
// an entry that comes at or past target.
virtual void Seek(const Slice& target) = 0;
diff --git a/src/leveldb/include/leveldb/options.h b/src/leveldb/include/leveldb/options.h
index 7c9b973454..83a1ef39a4 100644
--- a/src/leveldb/include/leveldb/options.h
+++ b/src/leveldb/include/leveldb/options.h
@@ -128,6 +128,12 @@ struct Options {
// efficiently detect that and will switch to uncompressed mode.
CompressionType compression;
+ // EXPERIMENTAL: If true, append to existing MANIFEST and log files
+ // when a database is opened. This can significantly speed up open.
+ //
+ // Default: currently false, but may become true later.
+ bool reuse_logs;
+
// If non-NULL, use the specified filter policy to reduce disk reads.
// Many applications will benefit from passing the result of
// NewBloomFilterPolicy() here.
diff --git a/src/leveldb/include/leveldb/status.h b/src/leveldb/include/leveldb/status.h
index 11dbd4b47e..d9575f9753 100644
--- a/src/leveldb/include/leveldb/status.h
+++ b/src/leveldb/include/leveldb/status.h
@@ -60,6 +60,12 @@ class Status {
// Returns true iff the status indicates an IOError.
bool IsIOError() const { return code() == kIOError; }
+ // Returns true iff the status indicates a NotSupportedError.
+ bool IsNotSupportedError() const { return code() == kNotSupported; }
+
+ // Returns true iff the status indicates an InvalidArgument.
+ bool IsInvalidArgument() const { return code() == kInvalidArgument; }
+
// Return a string representation of this status suitable for printing.
// Returns the string "OK" for success.
std::string ToString() const;
diff --git a/src/leveldb/port/atomic_pointer.h b/src/leveldb/port/atomic_pointer.h
index 9bf091f757..1c4c7aafc6 100644
--- a/src/leveldb/port/atomic_pointer.h
+++ b/src/leveldb/port/atomic_pointer.h
@@ -35,8 +35,12 @@
#define ARCH_CPU_X86_FAMILY 1
#elif defined(__ARMEL__)
#define ARCH_CPU_ARM_FAMILY 1
+#elif defined(__aarch64__)
+#define ARCH_CPU_ARM64_FAMILY 1
#elif defined(__ppc__) || defined(__powerpc__) || defined(__powerpc64__)
#define ARCH_CPU_PPC_FAMILY 1
+#elif defined(__mips__)
+#define ARCH_CPU_MIPS_FAMILY 1
#endif
namespace leveldb {
@@ -92,6 +96,13 @@ inline void MemoryBarrier() {
}
#define LEVELDB_HAVE_MEMORY_BARRIER
+// ARM64
+#elif defined(ARCH_CPU_ARM64_FAMILY)
+inline void MemoryBarrier() {
+ asm volatile("dmb sy" : : : "memory");
+}
+#define LEVELDB_HAVE_MEMORY_BARRIER
+
// PPC
#elif defined(ARCH_CPU_PPC_FAMILY) && defined(__GNUC__)
inline void MemoryBarrier() {
@@ -101,6 +112,13 @@ inline void MemoryBarrier() {
}
#define LEVELDB_HAVE_MEMORY_BARRIER
+// MIPS
+#elif defined(ARCH_CPU_MIPS_FAMILY) && defined(__GNUC__)
+inline void MemoryBarrier() {
+ __asm__ __volatile__("sync" : : : "memory");
+}
+#define LEVELDB_HAVE_MEMORY_BARRIER
+
#endif
// AtomicPointer built using platform-specific MemoryBarrier()
@@ -215,6 +233,7 @@ class AtomicPointer {
#undef LEVELDB_HAVE_MEMORY_BARRIER
#undef ARCH_CPU_X86_FAMILY
#undef ARCH_CPU_ARM_FAMILY
+#undef ARCH_CPU_ARM64_FAMILY
#undef ARCH_CPU_PPC_FAMILY
} // namespace port
diff --git a/src/leveldb/port/port_posix.cc b/src/leveldb/port/port_posix.cc
index 5ba127a5b9..30e8007ae3 100644
--- a/src/leveldb/port/port_posix.cc
+++ b/src/leveldb/port/port_posix.cc
@@ -7,7 +7,6 @@
#include <cstdlib>
#include <stdio.h>
#include <string.h>
-#include "util/logging.h"
namespace leveldb {
namespace port {
diff --git a/src/leveldb/table/filter_block.cc b/src/leveldb/table/filter_block.cc
index 203e15c8bc..4e78b954f8 100644
--- a/src/leveldb/table/filter_block.cc
+++ b/src/leveldb/table/filter_block.cc
@@ -68,7 +68,7 @@ void FilterBlockBuilder::GenerateFilter() {
// Generate filter for current set of keys and append to result_.
filter_offsets_.push_back(result_.size());
- policy_->CreateFilter(&tmp_keys_[0], num_keys, &result_);
+ policy_->CreateFilter(&tmp_keys_[0], static_cast<int>(num_keys), &result_);
tmp_keys_.clear();
keys_.clear();
@@ -97,7 +97,7 @@ bool FilterBlockReader::KeyMayMatch(uint64_t block_offset, const Slice& key) {
if (index < num_) {
uint32_t start = DecodeFixed32(offset_ + index*4);
uint32_t limit = DecodeFixed32(offset_ + index*4 + 4);
- if (start <= limit && limit <= (offset_ - data_)) {
+ if (start <= limit && limit <= static_cast<size_t>(offset_ - data_)) {
Slice filter = Slice(data_ + start, limit - start);
return policy_->KeyMayMatch(key, filter);
} else if (start == limit) {
diff --git a/src/leveldb/table/format.cc b/src/leveldb/table/format.cc
index aa63144c9e..24e4e02445 100644
--- a/src/leveldb/table/format.cc
+++ b/src/leveldb/table/format.cc
@@ -30,15 +30,14 @@ Status BlockHandle::DecodeFrom(Slice* input) {
}
void Footer::EncodeTo(std::string* dst) const {
-#ifndef NDEBUG
const size_t original_size = dst->size();
-#endif
metaindex_handle_.EncodeTo(dst);
index_handle_.EncodeTo(dst);
dst->resize(2 * BlockHandle::kMaxEncodedLength); // Padding
PutFixed32(dst, static_cast<uint32_t>(kTableMagicNumber & 0xffffffffu));
PutFixed32(dst, static_cast<uint32_t>(kTableMagicNumber >> 32));
assert(dst->size() == original_size + kEncodedLength);
+ (void)original_size; // Disable unused variable warning.
}
Status Footer::DecodeFrom(Slice* input) {
diff --git a/src/leveldb/table/iterator_wrapper.h b/src/leveldb/table/iterator_wrapper.h
index 9e16b3dbed..f410c3fabe 100644
--- a/src/leveldb/table/iterator_wrapper.h
+++ b/src/leveldb/table/iterator_wrapper.h
@@ -5,6 +5,9 @@
#ifndef STORAGE_LEVELDB_TABLE_ITERATOR_WRAPPER_H_
#define STORAGE_LEVELDB_TABLE_ITERATOR_WRAPPER_H_
+#include "leveldb/iterator.h"
+#include "leveldb/slice.h"
+
namespace leveldb {
// A internal wrapper class with an interface similar to Iterator that
diff --git a/src/leveldb/table/table.cc b/src/leveldb/table/table.cc
index dff8a82590..decf8082cc 100644
--- a/src/leveldb/table/table.cc
+++ b/src/leveldb/table/table.cc
@@ -82,7 +82,7 @@ Status Table::Open(const Options& options,
*table = new Table(rep);
(*table)->ReadMeta(footer);
} else {
- if (index_block) delete index_block;
+ delete index_block;
}
return s;
diff --git a/src/leveldb/table/table_test.cc b/src/leveldb/table/table_test.cc
index c723bf84cf..abf6e246ff 100644
--- a/src/leveldb/table/table_test.cc
+++ b/src/leveldb/table/table_test.cc
@@ -853,12 +853,20 @@ TEST(TableTest, ApproximateOffsetOfCompressed) {
options.compression = kSnappyCompression;
c.Finish(options, &keys, &kvmap);
- ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, 0));
- ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, 0));
- ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, 0));
- ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), 2000, 3000));
- ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), 2000, 3000));
- ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 4000, 6000));
+ // Expected upper and lower bounds of space used by compressible strings.
+ static const int kSlop = 1000; // Compressor effectiveness varies.
+ const int expected = 2500; // 10000 * compression ratio (0.25)
+ const int min_z = expected - kSlop;
+ const int max_z = expected + kSlop;
+
+ ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, kSlop));
+ ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, kSlop));
+ ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, kSlop));
+ // Have now emitted a large compressible string, so adjust expected offset.
+ ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), min_z, max_z));
+ ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), min_z, max_z));
+ // Have now emitted two large compressible strings, so adjust expected offset.
+ ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 2 * min_z, 2 * max_z));
}
} // namespace leveldb
diff --git a/src/leveldb/util/arena.cc b/src/leveldb/util/arena.cc
index 9367f71492..74078213ee 100644
--- a/src/leveldb/util/arena.cc
+++ b/src/leveldb/util/arena.cc
@@ -9,8 +9,7 @@ namespace leveldb {
static const int kBlockSize = 4096;
-Arena::Arena() {
- blocks_memory_ = 0;
+Arena::Arena() : memory_usage_(0) {
alloc_ptr_ = NULL; // First allocation will allocate a block
alloc_bytes_remaining_ = 0;
}
@@ -60,8 +59,9 @@ char* Arena::AllocateAligned(size_t bytes) {
char* Arena::AllocateNewBlock(size_t block_bytes) {
char* result = new char[block_bytes];
- blocks_memory_ += block_bytes;
blocks_.push_back(result);
+ memory_usage_.NoBarrier_Store(
+ reinterpret_cast<void*>(MemoryUsage() + block_bytes + sizeof(char*)));
return result;
}
diff --git a/src/leveldb/util/arena.h b/src/leveldb/util/arena.h
index 73bbf1cb9b..48bab33741 100644
--- a/src/leveldb/util/arena.h
+++ b/src/leveldb/util/arena.h
@@ -9,6 +9,7 @@
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
+#include "port/port.h"
namespace leveldb {
@@ -24,10 +25,9 @@ class Arena {
char* AllocateAligned(size_t bytes);
// Returns an estimate of the total memory usage of data allocated
- // by the arena (including space allocated but not yet used for user
- // allocations).
+ // by the arena.
size_t MemoryUsage() const {
- return blocks_memory_ + blocks_.capacity() * sizeof(char*);
+ return reinterpret_cast<uintptr_t>(memory_usage_.NoBarrier_Load());
}
private:
@@ -41,8 +41,8 @@ class Arena {
// Array of new[] allocated memory blocks
std::vector<char*> blocks_;
- // Bytes of memory in blocks allocated so far
- size_t blocks_memory_;
+ // Total memory usage of the arena.
+ port::AtomicPointer memory_usage_;
// No copying allowed
Arena(const Arena&);
diff --git a/src/leveldb/util/bloom.cc b/src/leveldb/util/bloom.cc
index a27a2ace28..bf3e4ca6e9 100644
--- a/src/leveldb/util/bloom.cc
+++ b/src/leveldb/util/bloom.cc
@@ -47,7 +47,7 @@ class BloomFilterPolicy : public FilterPolicy {
dst->resize(init_size + bytes, 0);
dst->push_back(static_cast<char>(k_)); // Remember # of probes in filter
char* array = &(*dst)[init_size];
- for (size_t i = 0; i < n; i++) {
+ for (int i = 0; i < n; i++) {
// Use double-hashing to generate a sequence of hash values.
// See analysis in [Kirsch,Mitzenmacher 2006].
uint32_t h = BloomHash(keys[i]);
diff --git a/src/leveldb/util/bloom_test.cc b/src/leveldb/util/bloom_test.cc
index 77fb1b3159..1b87a2be3f 100644
--- a/src/leveldb/util/bloom_test.cc
+++ b/src/leveldb/util/bloom_test.cc
@@ -46,7 +46,8 @@ class BloomTest {
key_slices.push_back(Slice(keys_[i]));
}
filter_.clear();
- policy_->CreateFilter(&key_slices[0], key_slices.size(), &filter_);
+ policy_->CreateFilter(&key_slices[0], static_cast<int>(key_slices.size()),
+ &filter_);
keys_.clear();
if (kVerbose >= 2) DumpFilter();
}
diff --git a/src/leveldb/util/cache.cc b/src/leveldb/util/cache.cc
index 8b197bc02a..ce46886171 100644
--- a/src/leveldb/util/cache.cc
+++ b/src/leveldb/util/cache.cc
@@ -19,6 +19,23 @@ Cache::~Cache() {
namespace {
// LRU cache implementation
+//
+// Cache entries have an "in_cache" boolean indicating whether the cache has a
+// reference on the entry. The only ways that this can become false without the
+// entry being passed to its "deleter" are via Erase(), via Insert() when
+// an element with a duplicate key is inserted, or on destruction of the cache.
+//
+// The cache keeps two linked lists of items in the cache. All items in the
+// cache are in one list or the other, and never both. Items still referenced
+// by clients but erased from the cache are in neither list. The lists are:
+// - in-use: contains the items currently referenced by clients, in no
+// particular order. (This list is used for invariant checking. If we
+// removed the check, elements that would otherwise be on this list could be
+// left as disconnected singleton lists.)
+// - LRU: contains the items not currently referenced by clients, in LRU order
+// Elements are moved between these lists by the Ref() and Unref() methods,
+// when they detect an element in the cache acquiring or losing its only
+// external reference.
// An entry is a variable length heap-allocated structure. Entries
// are kept in a circular doubly linked list ordered by access time.
@@ -30,7 +47,8 @@ struct LRUHandle {
LRUHandle* prev;
size_t charge; // TODO(opt): Only allow uint32_t?
size_t key_length;
- uint32_t refs;
+ bool in_cache; // Whether entry is in the cache.
+ uint32_t refs; // References, including cache reference, if present.
uint32_t hash; // Hash of key(); used for fast sharding and comparisons
char key_data[1]; // Beginning of key
@@ -147,49 +165,77 @@ class LRUCache {
Cache::Handle* Lookup(const Slice& key, uint32_t hash);
void Release(Cache::Handle* handle);
void Erase(const Slice& key, uint32_t hash);
+ void Prune();
+ size_t TotalCharge() const {
+ MutexLock l(&mutex_);
+ return usage_;
+ }
private:
void LRU_Remove(LRUHandle* e);
- void LRU_Append(LRUHandle* e);
+ void LRU_Append(LRUHandle*list, LRUHandle* e);
+ void Ref(LRUHandle* e);
void Unref(LRUHandle* e);
+ bool FinishErase(LRUHandle* e);
// Initialized before use.
size_t capacity_;
// mutex_ protects the following state.
- port::Mutex mutex_;
+ mutable port::Mutex mutex_;
size_t usage_;
// Dummy head of LRU list.
// lru.prev is newest entry, lru.next is oldest entry.
+ // Entries have refs==1 and in_cache==true.
LRUHandle lru_;
+ // Dummy head of in-use list.
+ // Entries are in use by clients, and have refs >= 2 and in_cache==true.
+ LRUHandle in_use_;
+
HandleTable table_;
};
LRUCache::LRUCache()
: usage_(0) {
- // Make empty circular linked list
+ // Make empty circular linked lists.
lru_.next = &lru_;
lru_.prev = &lru_;
+ in_use_.next = &in_use_;
+ in_use_.prev = &in_use_;
}
LRUCache::~LRUCache() {
+ assert(in_use_.next == &in_use_); // Error if caller has an unreleased handle
for (LRUHandle* e = lru_.next; e != &lru_; ) {
LRUHandle* next = e->next;
- assert(e->refs == 1); // Error if caller has an unreleased handle
+ assert(e->in_cache);
+ e->in_cache = false;
+ assert(e->refs == 1); // Invariant of lru_ list.
Unref(e);
e = next;
}
}
+void LRUCache::Ref(LRUHandle* e) {
+ if (e->refs == 1 && e->in_cache) { // If on lru_ list, move to in_use_ list.
+ LRU_Remove(e);
+ LRU_Append(&in_use_, e);
+ }
+ e->refs++;
+}
+
void LRUCache::Unref(LRUHandle* e) {
assert(e->refs > 0);
e->refs--;
- if (e->refs <= 0) {
- usage_ -= e->charge;
+ if (e->refs == 0) { // Deallocate.
+ assert(!e->in_cache);
(*e->deleter)(e->key(), e->value);
free(e);
+ } else if (e->in_cache && e->refs == 1) { // No longer in use; move to lru_ list.
+ LRU_Remove(e);
+ LRU_Append(&lru_, e);
}
}
@@ -198,10 +244,10 @@ void LRUCache::LRU_Remove(LRUHandle* e) {
e->prev->next = e->next;
}
-void LRUCache::LRU_Append(LRUHandle* e) {
- // Make "e" newest entry by inserting just before lru_
- e->next = &lru_;
- e->prev = lru_.prev;
+void LRUCache::LRU_Append(LRUHandle* list, LRUHandle* e) {
+ // Make "e" newest entry by inserting just before *list
+ e->next = list;
+ e->prev = list->prev;
e->prev->next = e;
e->next->prev = e;
}
@@ -210,9 +256,7 @@ Cache::Handle* LRUCache::Lookup(const Slice& key, uint32_t hash) {
MutexLock l(&mutex_);
LRUHandle* e = table_.Lookup(key, hash);
if (e != NULL) {
- e->refs++;
- LRU_Remove(e);
- LRU_Append(e);
+ Ref(e);
}
return reinterpret_cast<Cache::Handle*>(e);
}
@@ -234,34 +278,58 @@ Cache::Handle* LRUCache::Insert(
e->charge = charge;
e->key_length = key.size();
e->hash = hash;
- e->refs = 2; // One from LRUCache, one for the returned handle
+ e->in_cache = false;
+ e->refs = 1; // for the returned handle.
memcpy(e->key_data, key.data(), key.size());
- LRU_Append(e);
- usage_ += charge;
- LRUHandle* old = table_.Insert(e);
- if (old != NULL) {
- LRU_Remove(old);
- Unref(old);
- }
+ if (capacity_ > 0) {
+ e->refs++; // for the cache's reference.
+ e->in_cache = true;
+ LRU_Append(&in_use_, e);
+ usage_ += charge;
+ FinishErase(table_.Insert(e));
+ } // else don't cache. (Tests use capacity_==0 to turn off caching.)
while (usage_ > capacity_ && lru_.next != &lru_) {
LRUHandle* old = lru_.next;
- LRU_Remove(old);
- table_.Remove(old->key(), old->hash);
- Unref(old);
+ assert(old->refs == 1);
+ bool erased = FinishErase(table_.Remove(old->key(), old->hash));
+ if (!erased) { // to avoid unused variable when compiled NDEBUG
+ assert(erased);
+ }
}
return reinterpret_cast<Cache::Handle*>(e);
}
-void LRUCache::Erase(const Slice& key, uint32_t hash) {
- MutexLock l(&mutex_);
- LRUHandle* e = table_.Remove(key, hash);
+// If e != NULL, finish removing *e from the cache; it has already been removed
+// from the hash table. Return whether e != NULL. Requires mutex_ held.
+bool LRUCache::FinishErase(LRUHandle* e) {
if (e != NULL) {
+ assert(e->in_cache);
LRU_Remove(e);
+ e->in_cache = false;
+ usage_ -= e->charge;
Unref(e);
}
+ return e != NULL;
+}
+
+void LRUCache::Erase(const Slice& key, uint32_t hash) {
+ MutexLock l(&mutex_);
+ FinishErase(table_.Remove(key, hash));
+}
+
+void LRUCache::Prune() {
+ MutexLock l(&mutex_);
+ while (lru_.next != &lru_) {
+ LRUHandle* e = lru_.next;
+ assert(e->refs == 1);
+ bool erased = FinishErase(table_.Remove(e->key(), e->hash));
+ if (!erased) { // to avoid unused variable when compiled NDEBUG
+ assert(erased);
+ }
+ }
}
static const int kNumShardBits = 4;
@@ -314,6 +382,18 @@ class ShardedLRUCache : public Cache {
MutexLock l(&id_mutex_);
return ++(last_id_);
}
+ virtual void Prune() {
+ for (int s = 0; s < kNumShards; s++) {
+ shard_[s].Prune();
+ }
+ }
+ virtual size_t TotalCharge() const {
+ size_t total = 0;
+ for (int s = 0; s < kNumShards; s++) {
+ total += shard_[s].TotalCharge();
+ }
+ return total;
+ }
};
} // end anonymous namespace
diff --git a/src/leveldb/util/cache_test.cc b/src/leveldb/util/cache_test.cc
index 43716715a8..468f7a6425 100644
--- a/src/leveldb/util/cache_test.cc
+++ b/src/leveldb/util/cache_test.cc
@@ -59,6 +59,11 @@ class CacheTest {
&CacheTest::Deleter));
}
+ Cache::Handle* InsertAndReturnHandle(int key, int value, int charge = 1) {
+ return cache_->Insert(EncodeKey(key), EncodeValue(value), charge,
+ &CacheTest::Deleter);
+ }
+
void Erase(int key) {
cache_->Erase(EncodeKey(key));
}
@@ -135,8 +140,11 @@ TEST(CacheTest, EntriesArePinned) {
TEST(CacheTest, EvictionPolicy) {
Insert(100, 101);
Insert(200, 201);
+ Insert(300, 301);
+ Cache::Handle* h = cache_->Lookup(EncodeKey(300));
- // Frequently used entry must be kept around
+ // Frequently used entry must be kept around,
+ // as must things that are still in use.
for (int i = 0; i < kCacheSize + 100; i++) {
Insert(1000+i, 2000+i);
ASSERT_EQ(2000+i, Lookup(1000+i));
@@ -144,6 +152,25 @@ TEST(CacheTest, EvictionPolicy) {
}
ASSERT_EQ(101, Lookup(100));
ASSERT_EQ(-1, Lookup(200));
+ ASSERT_EQ(301, Lookup(300));
+ cache_->Release(h);
+}
+
+TEST(CacheTest, UseExceedsCacheSize) {
+ // Overfill the cache, keeping handles on all inserted entries.
+ std::vector<Cache::Handle*> h;
+ for (int i = 0; i < kCacheSize + 100; i++) {
+ h.push_back(InsertAndReturnHandle(1000+i, 2000+i));
+ }
+
+ // Check that all the entries can be found in the cache.
+ for (int i = 0; i < h.size(); i++) {
+ ASSERT_EQ(2000+i, Lookup(1000+i));
+ }
+
+ for (int i = 0; i < h.size(); i++) {
+ cache_->Release(h[i]);
+ }
}
TEST(CacheTest, HeavyEntries) {
@@ -179,6 +206,19 @@ TEST(CacheTest, NewId) {
ASSERT_NE(a, b);
}
+TEST(CacheTest, Prune) {
+ Insert(1, 100);
+ Insert(2, 200);
+
+ Cache::Handle* handle = cache_->Lookup(EncodeKey(1));
+ ASSERT_TRUE(handle);
+ cache_->Prune();
+ cache_->Release(handle);
+
+ ASSERT_EQ(100, Lookup(1));
+ ASSERT_EQ(-1, Lookup(2));
+}
+
} // namespace leveldb
int main(int argc, char** argv) {
diff --git a/src/leveldb/util/env.cc b/src/leveldb/util/env.cc
index c2600e964a..c58a0821ef 100644
--- a/src/leveldb/util/env.cc
+++ b/src/leveldb/util/env.cc
@@ -9,6 +9,10 @@ namespace leveldb {
Env::~Env() {
}
+Status Env::NewAppendableFile(const std::string& fname, WritableFile** result) {
+ return Status::NotSupported("NewAppendableFile", fname);
+}
+
SequentialFile::~SequentialFile() {
}
diff --git a/src/leveldb/util/env_posix.cc b/src/leveldb/util/env_posix.cc
index ba2667864a..e0fca52f46 100644
--- a/src/leveldb/util/env_posix.cc
+++ b/src/leveldb/util/env_posix.cc
@@ -351,6 +351,19 @@ class PosixEnv : public Env {
return s;
}
+ virtual Status NewAppendableFile(const std::string& fname,
+ WritableFile** result) {
+ Status s;
+ FILE* f = fopen(fname.c_str(), "a");
+ if (f == NULL) {
+ *result = NULL;
+ s = IOError(fname, errno);
+ } else {
+ *result = new PosixWritableFile(fname, f);
+ }
+ return s;
+ }
+
virtual bool FileExists(const std::string& fname) {
return access(fname.c_str(), F_OK) == 0;
}
diff --git a/src/leveldb/util/env_win.cc b/src/leveldb/util/env_win.cc
index e11a96b791..b074b7579e 100644
--- a/src/leveldb/util/env_win.cc
+++ b/src/leveldb/util/env_win.cc
@@ -106,7 +106,7 @@ private:
class Win32WritableFile : public WritableFile
{
public:
- Win32WritableFile(const std::string& fname);
+ Win32WritableFile(const std::string& fname, bool append);
~Win32WritableFile();
virtual Status Append(const Slice& data);
@@ -158,6 +158,8 @@ public:
RandomAccessFile** result);
virtual Status NewWritableFile(const std::string& fname,
WritableFile** result);
+ virtual Status NewAppendableFile(const std::string& fname,
+ WritableFile** result);
virtual bool FileExists(const std::string& fname);
@@ -423,17 +425,23 @@ void Win32RandomAccessFile::_CleanUp()
}
}
-Win32WritableFile::Win32WritableFile(const std::string& fname)
+Win32WritableFile::Win32WritableFile(const std::string& fname, bool append)
: filename_(fname)
{
std::wstring path;
ToWidePath(fname, path);
- DWORD Flag = PathFileExistsW(path.c_str()) ? OPEN_EXISTING : CREATE_ALWAYS;
+ // NewAppendableFile: append to an existing file, or create a new one
+ // if none exists - this is OPEN_ALWAYS behavior, with
+ // FILE_APPEND_DATA to avoid having to manually position the file
+ // pointer at the end of the file.
+ // NewWritableFile: create a new file, delete if it exists - this is
+ // CREATE_ALWAYS behavior. This file is used for writing only so
+ // use GENERIC_WRITE.
_hFile = CreateFileW(path.c_str(),
- GENERIC_READ | GENERIC_WRITE,
+ append ? FILE_APPEND_DATA : GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE,
NULL,
- Flag,
+ append ? OPEN_ALWAYS : CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
// CreateFileW returns INVALID_HANDLE_VALUE in case of error, always check isEnable() before use
@@ -823,7 +831,9 @@ Status Win32Env::NewLogger( const std::string& fname, Logger** result )
{
Status sRet;
std::string path = fname;
- Win32WritableFile* pMapFile = new Win32WritableFile(ModifyPath(path));
+ // Logs are opened with write semantics, not with append semantics
+ // (see PosixEnv::NewLogger)
+ Win32WritableFile* pMapFile = new Win32WritableFile(ModifyPath(path), false);
if(!pMapFile->isEnable()){
delete pMapFile;
*result = NULL;
@@ -837,7 +847,20 @@ Status Win32Env::NewWritableFile( const std::string& fname, WritableFile** resul
{
Status sRet;
std::string path = fname;
- Win32WritableFile* pFile = new Win32WritableFile(ModifyPath(path));
+ Win32WritableFile* pFile = new Win32WritableFile(ModifyPath(path), false);
+ if(!pFile->isEnable()){
+ *result = NULL;
+ sRet = Status::IOError(fname,Win32::GetLastErrSz());
+ }else
+ *result = pFile;
+ return sRet;
+}
+
+Status Win32Env::NewAppendableFile( const std::string& fname, WritableFile** result )
+{
+ Status sRet;
+ std::string path = fname;
+ Win32WritableFile* pFile = new Win32WritableFile(ModifyPath(path), true);
if(!pFile->isEnable()){
*result = NULL;
sRet = Status::IOError(fname,Win32::GetLastErrSz());
diff --git a/src/leveldb/util/options.cc b/src/leveldb/util/options.cc
index 76af5b9302..8b618fb1ae 100644
--- a/src/leveldb/util/options.cc
+++ b/src/leveldb/util/options.cc
@@ -22,8 +22,8 @@ Options::Options()
block_size(4096),
block_restart_interval(16),
compression(kSnappyCompression),
+ reuse_logs(false),
filter_policy(NULL) {
}
-
} // namespace leveldb
diff --git a/src/leveldb/util/testutil.h b/src/leveldb/util/testutil.h
index adad3fc1ea..d7e4583702 100644
--- a/src/leveldb/util/testutil.h
+++ b/src/leveldb/util/testutil.h
@@ -45,6 +45,16 @@ class ErrorEnv : public EnvWrapper {
}
return target()->NewWritableFile(fname, result);
}
+
+ virtual Status NewAppendableFile(const std::string& fname,
+ WritableFile** result) {
+ if (writable_file_error_) {
+ ++num_writable_file_errors_;
+ *result = NULL;
+ return Status::IOError(fname, "fake error");
+ }
+ return target()->NewAppendableFile(fname, result);
+ }
};
} // namespace test
diff --git a/src/memusage.h b/src/memusage.h
index 3810bfad07..2e3c5a9b92 100644
--- a/src/memusage.h
+++ b/src/memusage.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2015 The Bitcoin developers
+// Copyright (c) 2015 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/merkleblock.cpp b/src/merkleblock.cpp
index 31332526a9..882717ac56 100644
--- a/src/merkleblock.cpp
+++ b/src/merkleblock.cpp
@@ -23,8 +23,8 @@ CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter& filter)
for (unsigned int i = 0; i < block.vtx.size(); i++)
{
- const uint256& hash = block.vtx[i].GetHash();
- if (filter.IsRelevantAndUpdate(block.vtx[i]))
+ const uint256& hash = block.vtx[i]->GetHash();
+ if (filter.IsRelevantAndUpdate(*block.vtx[i]))
{
vMatch.push_back(true);
vMatchedTxn.push_back(make_pair(i, hash));
@@ -49,7 +49,7 @@ CMerkleBlock::CMerkleBlock(const CBlock& block, const std::set<uint256>& txids)
for (unsigned int i = 0; i < block.vtx.size(); i++)
{
- const uint256& hash = block.vtx[i].GetHash();
+ const uint256& hash = block.vtx[i]->GetHash();
if (txids.count(hash))
vMatch.push_back(true);
else
diff --git a/src/merkleblock.h b/src/merkleblock.h
index 835cbcce55..17c33194a9 100644
--- a/src/merkleblock.h
+++ b/src/merkleblock.h
@@ -85,7 +85,7 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(nTransactions);
READWRITE(vHash);
std::vector<unsigned char> vBytes;
@@ -148,7 +148,7 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(header);
READWRITE(txn);
}
diff --git a/src/miner.cpp b/src/miner.cpp
index 9575858840..e80e8a2656 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -13,7 +13,7 @@
#include "consensus/merkle.h"
#include "consensus/validation.h"
#include "hash.h"
-#include "main.h"
+#include "validation.h"
#include "net.h"
#include "policy/policy.h"
#include "pow.h"
@@ -29,6 +29,7 @@
#include <boost/thread.hpp>
#include <boost/tuple/tuple.hpp>
#include <queue>
+#include <utility>
using namespace std;
@@ -122,18 +123,18 @@ void BlockAssembler::resetBlock()
blockFinished = false;
}
-CBlockTemplate* BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn)
+std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn)
{
resetBlock();
pblocktemplate.reset(new CBlockTemplate());
if(!pblocktemplate.get())
- return NULL;
+ return nullptr;
pblock = &pblocktemplate->block; // pointer for convenience
// Add dummy coinbase tx as first transaction
- pblock->vtx.push_back(CTransaction());
+ pblock->vtx.emplace_back();
pblocktemplate->vTxFees.push_back(-1); // updated at end
pblocktemplate->vTxSigOpsCost.push_back(-1); // updated at end
@@ -168,7 +169,6 @@ CBlockTemplate* BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn)
nLastBlockTx = nBlockTx;
nLastBlockSize = nBlockSize;
nLastBlockWeight = nBlockWeight;
- LogPrintf("CreateNewBlock(): total size %u txs: %u fees: %ld sigops %d\n", nBlockSize, nBlockTx, nFees, nBlockSigOpsCost);
// Create coinbase transaction.
CMutableTransaction coinbaseTx;
@@ -178,23 +178,26 @@ CBlockTemplate* BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn)
coinbaseTx.vout[0].scriptPubKey = scriptPubKeyIn;
coinbaseTx.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus());
coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0;
- pblock->vtx[0] = coinbaseTx;
+ pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx));
pblocktemplate->vchCoinbaseCommitment = GenerateCoinbaseCommitment(*pblock, pindexPrev, chainparams.GetConsensus());
pblocktemplate->vTxFees[0] = -nFees;
+ uint64_t nSerializeSize = GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION);
+ LogPrintf("CreateNewBlock(): total size: %u block weight: %u txs: %u fees: %ld sigops %d\n", nSerializeSize, GetBlockWeight(*pblock), nBlockTx, nFees, nBlockSigOpsCost);
+
// Fill in header
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus());
pblock->nNonce = 0;
- pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(pblock->vtx[0]);
+ pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]);
CValidationState state;
if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false)) {
throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, FormatStateMessage(state)));
}
- return pblocktemplate.release();
+ return std::move(pblocktemplate);
}
bool BlockAssembler::isStillDependent(CTxMemPool::txiter iter)
@@ -309,7 +312,7 @@ bool BlockAssembler::TestForBlock(CTxMemPool::txiter iter)
void BlockAssembler::AddToBlock(CTxMemPool::txiter iter)
{
- pblock->vtx.push_back(iter->GetTx());
+ pblock->vtx.emplace_back(iter->GetSharedTx());
pblocktemplate->vTxFees.push_back(iter->GetFee());
pblocktemplate->vTxSigOpsCost.push_back(iter->GetSigOpCost());
if (fNeedSizeAccounting) {
@@ -598,10 +601,10 @@ void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned
}
++nExtraNonce;
unsigned int nHeight = pindexPrev->nHeight+1; // Height first in coinbase required for block.version=2
- CMutableTransaction txCoinbase(pblock->vtx[0]);
+ CMutableTransaction txCoinbase(*pblock->vtx[0]);
txCoinbase.vin[0].scriptSig = (CScript() << nHeight << CScriptNum(nExtraNonce)) + COINBASE_FLAGS;
assert(txCoinbase.vin[0].scriptSig.size() <= 100);
- pblock->vtx[0] = txCoinbase;
+ pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
}
diff --git a/src/miner.h b/src/miner.h
index 11753f5e43..bad443b82a 100644
--- a/src/miner.h
+++ b/src/miner.h
@@ -164,7 +164,7 @@ private:
public:
BlockAssembler(const CChainParams& chainparams);
/** Construct a new block template with coinbase to scriptPubKeyIn */
- CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn);
+ std::unique_ptr<CBlockTemplate> CreateNewBlock(const CScript& scriptPubKeyIn);
private:
// utility functions
diff --git a/src/net.cpp b/src/net.cpp
index b39ef9f54a..27b200b3f0 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -63,6 +63,8 @@
const static std::string NET_MESSAGE_COMMAND_OTHER = "*other*";
+static const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL; // SHA256("netgroup")[0:8]
+static const uint64_t RANDOMIZER_ID_LOCALHOSTNONCE = 0xd93e69e2bbfa5735ULL; // SHA256("localhostnonce")[0:8]
//
// Global state variables
//
@@ -72,7 +74,6 @@ bool fRelayTxes = true;
CCriticalSection cs_mapLocalHost;
std::map<CNetAddr, LocalServiceInfo> mapLocalHost;
static bool vfLimited[NET_MAX] = {};
-static CNode* pnodeLocalHost = NULL;
std::string strSubVersion;
limitedmap<uint256, int64_t> mapAlreadyAskedFor(MAX_INV_SZ);
@@ -186,7 +187,8 @@ void AdvertiseLocal(CNode *pnode)
if (addrLocal.IsRoutable())
{
LogPrint("net", "AdvertiseLocal: advertising address %s\n", addrLocal.ToString());
- pnode->PushAddress(addrLocal);
+ FastRandomContext insecure_rand;
+ pnode->PushAddress(addrLocal, insecure_rand);
}
}
}
@@ -387,18 +389,18 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
addrman.Attempt(addrConnect, fCountFailure);
// Add node
- CNode* pnode = new CNode(GetNewNodeId(), nLocalServices, GetBestHeight(), hSocket, addrConnect, pszDest ? pszDest : "", false);
- GetNodeSignals().InitializeNode(pnode->GetId(), pnode);
+ NodeId id = GetNewNodeId();
+ uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
+ CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, pszDest ? pszDest : "", false);
+ pnode->nServicesExpected = ServiceFlags(addrConnect.nServices & nRelevantServices);
+ pnode->nTimeConnected = GetTime();
pnode->AddRef();
-
+ GetNodeSignals().InitializeNode(pnode, *this);
{
LOCK(cs_vNodes);
vNodes.push_back(pnode);
}
- pnode->nServicesExpected = ServiceFlags(addrConnect.nServices & nRelevantServices);
- pnode->nTimeConnected = GetTime();
-
return pnode;
} else if (!proxyConnectionFailed) {
// If connecting to the node failed, and failure is not caused by a problem connecting to
@@ -444,23 +446,6 @@ void CNode::CloseSocketDisconnect()
vRecvMsg.clear();
}
-void CNode::PushVersion()
-{
- int64_t nTime = (fInbound ? GetAdjustedTime() : GetTime());
- CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService(), addr.nServices));
- CAddress addrMe = GetLocalAddress(&addr, nLocalServices);
- if (fLogIPs)
- LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nMyStartingHeight, addrMe.ToString(), addrYou.ToString(), id);
- else
- LogPrint("net", "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nMyStartingHeight, addrMe.ToString(), id);
- PushMessage(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalServices, nTime, addrYou, addrMe,
- nLocalHostNonce, strSubVersion, nMyStartingHeight, ::fRelayTxes);
-}
-
-
-
-
-
void CConnman::ClearBanned()
{
{
@@ -628,6 +613,7 @@ void CNode::copyStats(CNodeStats &stats)
{
stats.nodeid = this->GetId();
X(nServices);
+ X(addr);
X(fRelayTxes);
X(nLastSend);
X(nLastRecv);
@@ -657,7 +643,7 @@ void CNode::copyStats(CNodeStats &stats)
// Raw ping time is in microseconds, but show it to user as whole seconds (Bitcoin users should be well used to small numbers with many decimal places by now :)
stats.dPingTime = (((double)nPingUsecTime) / 1e6);
- stats.dPingMin = (((double)nMinPingUsecTime) / 1e6);
+ stats.dMinPing = (((double)nMinPingUsecTime) / 1e6);
stats.dPingWait = (((double)nPingUsecWait) / 1e6);
// Leave string empty if addrLocal invalid (not filled in yet)
@@ -755,12 +741,21 @@ int CNetMessage::readData(const char *pch, unsigned int nBytes)
vRecv.resize(std::min(hdr.nMessageSize, nDataPos + nCopy + 256 * 1024));
}
+ hasher.Write((const unsigned char*)pch, nCopy);
memcpy(&vRecv[nDataPos], pch, nCopy);
nDataPos += nCopy;
return nCopy;
}
+const uint256& CNetMessage::GetMessageHash() const
+{
+ assert(complete());
+ if (data_hash.IsNull())
+ hasher.Finalize(data_hash.begin());
+ return data_hash;
+}
+
@@ -772,13 +767,13 @@ int CNetMessage::readData(const char *pch, unsigned int nBytes)
// requires LOCK(cs_vSend)
size_t SocketSendData(CNode *pnode)
{
- std::deque<CSerializeData>::iterator it = pnode->vSendMsg.begin();
+ auto it = pnode->vSendMsg.begin();
size_t nSentSize = 0;
while (it != pnode->vSendMsg.end()) {
- const CSerializeData &data = *it;
+ const auto &data = *it;
assert(data.size() > pnode->nSendOffset);
- int nBytes = send(pnode->hSocket, &data[pnode->nSendOffset], data.size() - pnode->nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT);
+ int nBytes = send(pnode->hSocket, reinterpret_cast<const char*>(data.data()) + pnode->nSendOffset, data.size() - pnode->nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT);
if (nBytes > 0) {
pnode->nLastSend = GetTime();
pnode->nSendBytes += nBytes;
@@ -822,7 +817,7 @@ struct NodeEvictionCandidate
int64_t nMinPingUsecTime;
int64_t nLastBlockTime;
int64_t nLastTXTime;
- bool fNetworkNode;
+ bool fRelevantServices;
bool fRelayTxes;
bool fBloomFilter;
CAddress addr;
@@ -847,7 +842,7 @@ static bool CompareNodeBlockTime(const NodeEvictionCandidate &a, const NodeEvict
{
// There is a fall-through here because it is common for a node to have many peers which have not yet relayed a block.
if (a.nLastBlockTime != b.nLastBlockTime) return a.nLastBlockTime < b.nLastBlockTime;
- if (a.fNetworkNode != b.fNetworkNode) return b.fNetworkNode;
+ if (a.fRelevantServices != b.fRelevantServices) return b.fRelevantServices;
return a.nTimeConnected > b.nTimeConnected;
}
@@ -882,7 +877,8 @@ bool CConnman::AttemptToEvictConnection()
if (node->fDisconnect)
continue;
NodeEvictionCandidate candidate = {node->id, node->nTimeConnected, node->nMinPingUsecTime,
- node->nLastBlockTime, node->nLastTXTime, node->fNetworkNode,
+ node->nLastBlockTime, node->nLastTXTime,
+ (node->nServices & nRelevantServices) == nRelevantServices,
node->fRelayTxes, node->pfilter != NULL, node->addr, node->nKeyedNetGroup};
vEvictionCandidates.push_back(candidate);
}
@@ -967,7 +963,6 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
CAddress addr;
int nInbound = 0;
int nMaxInbound = nMaxConnections - (nMaxOutbound + nMaxFeeler);
- assert(nMaxInbound > 0);
if (hSocket != INVALID_SOCKET)
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr))
@@ -989,6 +984,12 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
return;
}
+ if (!fNetworkActive) {
+ LogPrintf("connection from %s dropped: not accepting new connections\n", addr.ToString());
+ CloseSocket(hSocket);
+ return;
+ }
+
if (!IsSelectableSocket(hSocket))
{
LogPrintf("connection from %s dropped: non-selectable socket\n", addr.ToString());
@@ -1022,10 +1023,13 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
}
}
- CNode* pnode = new CNode(GetNewNodeId(), nLocalServices, GetBestHeight(), hSocket, addr, "", true);
- GetNodeSignals().InitializeNode(pnode->GetId(), pnode);
+ NodeId id = GetNewNodeId();
+ uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
+
+ CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addr, CalculateKeyedNetGroup(addr), nonce, "", true);
pnode->AddRef();
pnode->fWhitelisted = whitelisted;
+ GetNodeSignals().InitializeNode(pnode, *this);
LogPrint("net", "connection from %s accepted\n", addr.ToString());
@@ -1050,7 +1054,7 @@ void CConnman::ThreadSocketHandler()
BOOST_FOREACH(CNode* pnode, vNodesCopy)
{
if (pnode->fDisconnect ||
- (pnode->GetRefCount() <= 0 && pnode->vRecvMsg.empty() && pnode->nSendSize == 0 && pnode->ssSend.empty()))
+ (pnode->GetRefCount() <= 0 && pnode->vRecvMsg.empty() && pnode->nSendSize == 0))
{
// remove from vNodes
vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end());
@@ -1062,8 +1066,7 @@ void CConnman::ThreadSocketHandler()
pnode->CloseSocketDisconnect();
// hold in disconnected pool until all refs are released
- if (pnode->fNetworkNode || pnode->fInbound)
- pnode->Release();
+ pnode->Release();
vNodesDisconnected.push_back(pnode);
}
}
@@ -1098,8 +1101,13 @@ void CConnman::ThreadSocketHandler()
}
}
}
- if(vNodes.size() != nPrevNodeCount) {
- nPrevNodeCount = vNodes.size();
+ size_t vNodesSize;
+ {
+ LOCK(cs_vNodes);
+ vNodesSize = vNodes.size();
+ }
+ if(vNodesSize != nPrevNodeCount) {
+ nPrevNodeCount = vNodesSize;
if(clientInterface)
clientInterface->NotifyNumConnectionsChanged(nPrevNodeCount);
}
@@ -1154,10 +1162,6 @@ void CConnman::ThreadSocketHandler()
{
TRY_LOCK(pnode->cs_vSend, lockSend);
if (lockSend) {
- if (pnode->nOptimisticBytesWritten) {
- RecordBytesSent(pnode->nOptimisticBytesWritten);
- pnode->nOptimisticBytesWritten = 0;
- }
if (!pnode->vSendMsg.empty()) {
FD_SET(pnode->hSocket, &fdsetSend);
continue;
@@ -1452,6 +1456,7 @@ static std::string GetDNSHost(const CDNSSeedData& data, ServiceFlags* requiredSe
return data.host;
}
+ // See chainparams.cpp, most dnsseeds only support one or two possible servicebits hostnames
return strprintf("x%x.%s", *requiredServiceBits, data.host);
}
@@ -1459,12 +1464,19 @@ static std::string GetDNSHost(const CDNSSeedData& data, ServiceFlags* requiredSe
void CConnman::ThreadDNSAddressSeed()
{
// goal: only query DNS seeds if address need is acute
+ // Avoiding DNS seeds when we don't need them improves user privacy by
+ // creating fewer identifying DNS requests, reduces trust by giving seeds
+ // less influence on the network topology, and reduces traffic to the seeds.
if ((addrman.size() > 0) &&
(!GetBoolArg("-forcednsseed", DEFAULT_FORCEDNSSEED))) {
MilliSleep(11 * 1000);
LOCK(cs_vNodes);
- if (vNodes.size() >= 2) {
+ int nRelevant = 0;
+ for (auto pnode : vNodes) {
+ nRelevant += pnode->fSuccessfullyConnected && ((pnode->nServices & nRelevantServices) == nRelevantServices);
+ }
+ if (nRelevant >= 2) {
LogPrintf("P2P peers available. Skipped DNS seeding.\n");
return;
}
@@ -1619,7 +1631,6 @@ void CConnman::ThreadOpenConnections()
}
}
}
- assert(nOutbound <= (nMaxOutbound + nMaxFeeler));
// Feeler Connections
//
@@ -1672,8 +1683,8 @@ void CConnman::ThreadOpenConnections()
if (nANow - addr.nLastTry < 600 && nTries < 30)
continue;
- // only consider nodes missing relevant services after 40 failed attempts
- if ((addr.nServices & nRelevantServices) != nRelevantServices && nTries < 40)
+ // only consider nodes missing relevant services after 40 failed attempts and only if less than half the outbound are up.
+ if ((addr.nServices & nRelevantServices) != nRelevantServices && (nTries < 40 || nOutbound >= (nMaxOutbound >> 1)))
continue;
// do not allow non-default ports, unless after 50 invalid addresses selected already
@@ -1782,6 +1793,9 @@ bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
// Initiate outbound network connection
//
boost::this_thread::interruption_point();
+ if (!fNetworkActive) {
+ return false;
+ }
if (!pszDest) {
if (IsLocal(addrConnect) ||
FindNode((CNetAddr)addrConnect) || IsBanned(addrConnect) ||
@@ -1797,7 +1811,6 @@ bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
return false;
if (grantOutbound)
grantOutbound->MoveTo(pnode->grantOutbound);
- pnode->fNetworkNode = true;
if (fOneShot)
pnode->fOneShot = true;
if (fFeeler)
@@ -2023,8 +2036,30 @@ void Discover(boost::thread_group& threadGroup)
#endif
}
-CConnman::CConnman()
+void CConnman::SetNetworkActive(bool active)
{
+ if (fDebug) {
+ LogPrint("net", "SetNetworkActive: %s\n", active);
+ }
+
+ if (!active) {
+ fNetworkActive = false;
+
+ LOCK(cs_vNodes);
+ // Close sockets to all nodes
+ BOOST_FOREACH(CNode* pnode, vNodes) {
+ pnode->CloseSocketDisconnect();
+ }
+ } else {
+ fNetworkActive = true;
+ }
+
+ uiInterface.NotifyNetworkActiveChanged(fNetworkActive);
+}
+
+CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In) : nSeed0(nSeed0In), nSeed1(nSeed1In)
+{
+ fNetworkActive = true;
setBannedIsDirty = false;
fAddressesInitialized = false;
nLastNodeId = 0;
@@ -2046,9 +2081,7 @@ bool CConnman::Start(boost::thread_group& threadGroup, CScheduler& scheduler, st
{
nTotalBytesRecv = 0;
nTotalBytesSent = 0;
- nMaxOutboundLimit = 0;
nMaxOutboundTotalBytesSentInCycle = 0;
- nMaxOutboundTimeframe = 60*60*24; //1 day
nMaxOutboundCycleStartTime = 0;
nRelevantServices = connOptions.nRelevantServices;
@@ -2060,6 +2093,9 @@ bool CConnman::Start(boost::thread_group& threadGroup, CScheduler& scheduler, st
nSendBufferMaxSize = connOptions.nSendBufferMaxSize;
nReceiveFloodSize = connOptions.nSendBufferMaxSize;
+ nMaxOutboundLimit = connOptions.nMaxOutboundLimit;
+ nMaxOutboundTimeframe = connOptions.nMaxOutboundTimeframe;
+
SetBestHeight(connOptions.nBestHeight);
clientInterface = connOptions.uiInterface;
@@ -2105,13 +2141,6 @@ bool CConnman::Start(boost::thread_group& threadGroup, CScheduler& scheduler, st
semOutbound = new CSemaphore(std::min((nMaxOutbound + nMaxFeeler), nMaxConnections));
}
- if (pnodeLocalHost == NULL) {
- CNetAddr local;
- LookupHost("127.0.0.1", local, false);
- pnodeLocalHost = new CNode(GetNewNodeId(), nLocalServices, GetBestHeight(), INVALID_SOCKET, CAddress(CService(local, 0), nLocalServices));
- GetNodeSignals().InitializeNode(pnodeLocalHost->GetId(), pnodeLocalHost);
- }
-
//
// Start threads
//
@@ -2127,8 +2156,9 @@ bool CConnman::Start(boost::thread_group& threadGroup, CScheduler& scheduler, st
// Initiate outbound connections from -addnode
threadGroup.create_thread(boost::bind(&TraceThread<boost::function<void()> >, "addcon", boost::function<void()>(boost::bind(&CConnman::ThreadOpenAddedConnections, this))));
- // Initiate outbound connections
- threadGroup.create_thread(boost::bind(&TraceThread<boost::function<void()> >, "opencon", boost::function<void()>(boost::bind(&CConnman::ThreadOpenConnections, this))));
+ // Initiate outbound connections unless connect=0
+ if (!mapArgs.count("-connect") || mapMultiArgs["-connect"].size() != 1 || mapMultiArgs["-connect"][0] != "0")
+ threadGroup.create_thread(boost::bind(&TraceThread<boost::function<void()> >, "opencon", boost::function<void()>(boost::bind(&CConnman::ThreadOpenConnections, this))));
// Process messages
threadGroup.create_thread(boost::bind(&TraceThread<boost::function<void()> >, "msghand", boost::function<void()>(boost::bind(&CConnman::ThreadMessageHandler, this))));
@@ -2188,9 +2218,6 @@ void CConnman::Stop()
vhListenSocket.clear();
delete semOutbound;
semOutbound = NULL;
- if(pnodeLocalHost)
- DeleteNode(pnodeLocalHost);
- pnodeLocalHost = NULL;
}
void CConnman::DeleteNode(CNode* pnode)
@@ -2205,6 +2232,7 @@ void CConnman::DeleteNode(CNode* pnode)
CConnman::~CConnman()
{
+ Stop();
}
size_t CConnman::GetAddressCount() const
@@ -2362,11 +2390,7 @@ void CConnman::RecordBytesSent(uint64_t bytes)
void CConnman::SetMaxOutboundTarget(uint64_t limit)
{
LOCK(cs_totalBytesSent);
- uint64_t recommendedMinimum = (nMaxOutboundTimeframe / 600) * MAX_BLOCK_SERIALIZED_SIZE;
nMaxOutboundLimit = limit;
-
- if (limit > 0 && limit < recommendedMinimum)
- LogPrintf("Max outbound target is very small (%s bytes) and will be overshot. Recommended minimum is %s bytes.\n", nMaxOutboundLimit, recommendedMinimum);
}
uint64_t CConnman::GetMaxOutboundTarget()
@@ -2463,50 +2487,20 @@ int CConnman::GetBestHeight() const
return nBestHeight.load(std::memory_order_acquire);
}
-void CNode::Fuzz(int nChance)
-{
- if (!fSuccessfullyConnected) return; // Don't fuzz initial handshake
- if (GetRand(nChance) != 0) return; // Fuzz 1 of every nChance messages
-
- switch (GetRand(3))
- {
- case 0:
- // xor a random byte with a random value:
- if (!ssSend.empty()) {
- CDataStream::size_type pos = GetRand(ssSend.size());
- ssSend[pos] ^= (unsigned char)(GetRand(256));
- }
- break;
- case 1:
- // delete a random byte:
- if (!ssSend.empty()) {
- CDataStream::size_type pos = GetRand(ssSend.size());
- ssSend.erase(ssSend.begin()+pos);
- }
- break;
- case 2:
- // insert a random byte at a random position
- {
- CDataStream::size_type pos = GetRand(ssSend.size());
- char ch = (char)GetRand(256);
- ssSend.insert(ssSend.begin()+pos, ch);
- }
- break;
- }
- // Chance of more than one change half the time:
- // (more changes exponentially less likely):
- Fuzz(2);
-}
-
unsigned int CConnman::GetReceiveFloodSize() const { return nReceiveFloodSize; }
unsigned int CConnman::GetSendBufferSize() const{ return nSendBufferMaxSize; }
-CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNameIn, bool fInboundIn) :
- ssSend(SER_NETWORK, INIT_PROTO_VERSION),
+CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress& addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const std::string& addrNameIn, bool fInboundIn) :
addr(addrIn),
- nKeyedNetGroup(CalculateKeyedNetGroup(addrIn)),
+ fInbound(fInboundIn),
+ id(idIn),
+ nKeyedNetGroup(nKeyedNetGroupIn),
addrKnown(5000, 0.001),
- filterInventoryKnown(50000, 0.000001)
+ filterInventoryKnown(50000, 0.000001),
+ nLocalHostNonce(nLocalHostNonceIn),
+ nLocalServices(nLocalServicesIn),
+ nMyStartingHeight(nMyStartingHeightIn),
+ nSendVersion(0)
{
nServices = NODE_NONE;
nServicesExpected = NODE_NONE;
@@ -2525,8 +2519,6 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
fOneShot = false;
fClient = false; // set by version message
fFeeler = false;
- fInbound = fInboundIn;
- fNetworkNode = false;
fSuccessfullyConnected = false;
fDisconnect = false;
nRefCount = 0;
@@ -2554,12 +2546,6 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
minFeeFilter = 0;
lastSentFeeFilter = 0;
nextSendTimeFeeFilter = 0;
- id = idIn;
- nOptimisticBytesWritten = 0;
- nLocalServices = nLocalServicesIn;
-
- GetRandBytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce));
- nMyStartingHeight = nMyStartingHeightIn;
BOOST_FOREACH(const std::string &msg, getAllNetMessageTypes())
mapRecvBytesPerMsgCmd[msg] = 0;
@@ -2569,10 +2555,6 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
LogPrint("net", "Added connection to %s peer=%d\n", addrName, id);
else
LogPrint("net", "Added connection peer=%d\n", id);
-
- // Be shy and don't send version until we hear
- if (hSocket != INVALID_SOCKET && !fInbound)
- PushVersion();
}
CNode::~CNode()
@@ -2617,67 +2599,42 @@ void CNode::AskFor(const CInv& inv)
mapAskFor.insert(std::make_pair(nRequestTime, inv));
}
-void CNode::BeginMessage(const char* pszCommand) EXCLUSIVE_LOCK_FUNCTION(cs_vSend)
+void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg)
{
- ENTER_CRITICAL_SECTION(cs_vSend);
- assert(ssSend.size() == 0);
- ssSend << CMessageHeader(Params().MessageStart(), pszCommand, 0);
- LogPrint("net", "sending: %s ", SanitizeString(pszCommand));
-}
-
-void CNode::AbortMessage() UNLOCK_FUNCTION(cs_vSend)
-{
- ssSend.clear();
+ size_t nMessageSize = msg.data.size();
+ size_t nTotalSize = nMessageSize + CMessageHeader::HEADER_SIZE;
+ LogPrint("net", "sending %s (%d bytes) peer=%d\n", SanitizeString(msg.command.c_str()), nMessageSize, pnode->id);
- LEAVE_CRITICAL_SECTION(cs_vSend);
-
- LogPrint("net", "(aborted)\n");
-}
+ std::vector<unsigned char> serializedHeader;
+ serializedHeader.reserve(CMessageHeader::HEADER_SIZE);
+ uint256 hash = Hash(msg.data.data(), msg.data.data() + nMessageSize);
+ CMessageHeader hdr(Params().MessageStart(), msg.command.c_str(), nMessageSize);
+ memcpy(hdr.pchChecksum, hash.begin(), CMessageHeader::CHECKSUM_SIZE);
-void CNode::EndMessage(const char* pszCommand) UNLOCK_FUNCTION(cs_vSend)
-{
- // The -*messagestest options are intentionally not documented in the help message,
- // since they are only used during development to debug the networking code and are
- // not intended for end-users.
- if (mapArgs.count("-dropmessagestest") && GetRand(GetArg("-dropmessagestest", 2)) == 0)
- {
- LogPrint("net", "dropmessages DROPPING SEND MESSAGE\n");
- AbortMessage();
- return;
- }
- if (mapArgs.count("-fuzzmessagestest"))
- Fuzz(GetArg("-fuzzmessagestest", 10));
+ CVectorWriter{SER_NETWORK, INIT_PROTO_VERSION, serializedHeader, 0, hdr};
- if (ssSend.size() == 0)
+ size_t nBytesSent = 0;
{
- LEAVE_CRITICAL_SECTION(cs_vSend);
- return;
- }
- // Set the size
- unsigned int nSize = ssSend.size() - CMessageHeader::HEADER_SIZE;
- WriteLE32((uint8_t*)&ssSend[CMessageHeader::MESSAGE_SIZE_OFFSET], nSize);
-
- //log total amount of bytes per command
- mapSendBytesPerMsgCmd[std::string(pszCommand)] += nSize + CMessageHeader::HEADER_SIZE;
-
- // Set the checksum
- uint256 hash = Hash(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));
-
- LogPrint("net", "(%d bytes) peer=%d\n", nSize, id);
+ LOCK(pnode->cs_vSend);
+ if(pnode->hSocket == INVALID_SOCKET) {
+ return;
+ }
+ bool optimisticSend(pnode->vSendMsg.empty());
- std::deque<CSerializeData>::iterator it = vSendMsg.insert(vSendMsg.end(), CSerializeData());
- ssSend.GetAndClear(*it);
- nSendSize += (*it).size();
+ //log total amount of bytes per command
+ pnode->mapSendBytesPerMsgCmd[msg.command] += nTotalSize;
+ pnode->nSendSize += nTotalSize;
- // If write queue empty, attempt "optimistic write"
- if (it == vSendMsg.begin())
- nOptimisticBytesWritten += SocketSendData(this);
+ pnode->vSendMsg.push_back(std::move(serializedHeader));
+ if (nMessageSize)
+ pnode->vSendMsg.push_back(std::move(msg.data));
- LEAVE_CRITICAL_SECTION(cs_vSend);
+ // If write queue empty, attempt "optimistic write"
+ if (optimisticSend == true)
+ nBytesSent = SocketSendData(pnode);
+ }
+ if (nBytesSent)
+ RecordBytesSent(nBytesSent);
}
bool CConnman::ForNode(NodeId id, std::function<bool(CNode* pnode)> func)
@@ -2697,12 +2654,14 @@ int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds) {
return nNow + (int64_t)(log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */) * average_interval_seconds * -1000000.0 + 0.5);
}
-/* static */ uint64_t CNode::CalculateKeyedNetGroup(const CAddress& ad)
+CSipHasher CConnman::GetDeterministicRandomizer(uint64_t id)
{
- static const uint64_t k0 = GetRand(std::numeric_limits<uint64_t>::max());
- static const uint64_t k1 = GetRand(std::numeric_limits<uint64_t>::max());
+ return CSipHasher(nSeed0, nSeed1).Write(id);
+}
+uint64_t CConnman::CalculateKeyedNetGroup(const CAddress& ad)
+{
std::vector<unsigned char> vchNetGroup(ad.GetGroup());
- return CSipHasher(k0, k1).Write(&vchNetGroup[0], vchNetGroup.size()).Finalize();
+ return GetDeterministicRandomizer(RANDOMIZER_ID_NETGROUP).Write(&vchNetGroup[0], vchNetGroup.size()).Finalize();
}
diff --git a/src/net.h b/src/net.h
index a48ee02c44..a7c0ecf324 100644
--- a/src/net.h
+++ b/src/net.h
@@ -11,6 +11,7 @@
#include "amount.h"
#include "bloom.h"
#include "compat.h"
+#include "hash.h"
#include "limitedmap.h"
#include "netaddress.h"
#include "protocol.h"
@@ -72,6 +73,8 @@ static const size_t SETASKFOR_MAX_SZ = 2 * MAX_INV_SZ;
static const unsigned int DEFAULT_MAX_PEER_CONNECTIONS = 125;
/** The default for -maxuploadtarget. 0 = Unlimited */
static const uint64_t DEFAULT_MAX_UPLOAD_TARGET = 0;
+/** The default timeframe for -maxuploadtarget. 1 day. */
+static const uint64_t MAX_UPLOAD_TIMEFRAME = 60 * 60 * 24;
/** Default for blocks only*/
static const bool DEFAULT_BLOCKSONLY = false;
@@ -98,6 +101,20 @@ class CTransaction;
class CNodeStats;
class CClientUIInterface;
+struct CSerializedNetMsg
+{
+ CSerializedNetMsg() = default;
+ CSerializedNetMsg(CSerializedNetMsg&&) = default;
+ CSerializedNetMsg& operator=(CSerializedNetMsg&&) = default;
+ // No copying, only moves.
+ CSerializedNetMsg(const CSerializedNetMsg& msg) = delete;
+ CSerializedNetMsg& operator=(const CSerializedNetMsg&) = delete;
+
+ std::vector<unsigned char> data;
+ std::string command;
+};
+
+
class CConnman
{
public:
@@ -120,17 +137,23 @@ public:
CClientUIInterface* uiInterface = nullptr;
unsigned int nSendBufferMaxSize = 0;
unsigned int nReceiveFloodSize = 0;
+ uint64_t nMaxOutboundTimeframe = 0;
+ uint64_t nMaxOutboundLimit = 0;
};
- CConnman();
+ CConnman(uint64_t seed0, uint64_t seed1);
~CConnman();
bool Start(boost::thread_group& threadGroup, CScheduler& scheduler, std::string& strNodeError, Options options);
void Stop();
bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false);
+ bool GetNetworkActive() const { return fNetworkActive; };
+ void SetNetworkActive(bool active);
bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false, bool fFeeler = false);
bool CheckIncomingNonce(uint64_t nonce);
bool ForNode(NodeId id, std::function<bool(CNode* pnode)> func);
+ void PushMessage(CNode* pnode, CSerializedNetMsg&& msg);
+
template<typename Callable>
bool ForEachNodeContinueIf(Callable&& func)
{
@@ -294,6 +317,8 @@ public:
void SetBestHeight(int height);
int GetBestHeight() const;
+ /** Get a unique deterministic randomizer. */
+ CSipHasher GetDeterministicRandomizer(uint64_t id);
private:
struct ListenSocket {
@@ -311,6 +336,8 @@ private:
void ThreadSocketHandler();
void ThreadDNSAddressSeed();
+ uint64_t CalculateKeyedNetGroup(const CAddress& ad);
+
CNode* FindNode(const CNetAddr& ip);
CNode* FindNode(const CSubNet& subNet);
CNode* FindNode(const std::string& addrName);
@@ -361,6 +388,7 @@ private:
unsigned int nReceiveFloodSize;
std::vector<ListenSocket> vhListenSocket;
+ std::atomic<bool> fNetworkActive;
banmap_t setBanned;
CCriticalSection cs_setBanned;
bool setBannedIsDirty;
@@ -388,6 +416,9 @@ private:
int nMaxFeeler;
std::atomic<int> nBestHeight;
CClientUIInterface* clientInterface;
+
+ /** SipHasher seeds for deterministic randomness */
+ const uint64_t nSeed0, nSeed1;
};
extern std::unique_ptr<CConnman> g_connman;
void Discover(boost::thread_group& threadGroup);
@@ -416,7 +447,7 @@ struct CNodeSignals
{
boost::signals2::signal<bool (CNode*, CConnman&), CombinerAll> ProcessMessages;
boost::signals2::signal<bool (CNode*, CConnman&), CombinerAll> SendMessages;
- boost::signals2::signal<void (NodeId, const CNode*)> InitializeNode;
+ boost::signals2::signal<void (CNode*, CConnman&)> InitializeNode;
boost::signals2::signal<void (NodeId, bool&)> FinalizeNode;
};
@@ -491,14 +522,18 @@ public:
bool fWhitelisted;
double dPingTime;
double dPingWait;
- double dPingMin;
+ double dMinPing;
std::string addrLocal;
+ CAddress addr;
};
class CNetMessage {
+private:
+ mutable CHash256 hasher;
+ mutable uint256 data_hash;
public:
bool in_data; // parsing header (false) or data (true)
@@ -526,6 +561,8 @@ public:
return (hdr.nMessageSize == nDataPos);
}
+ const uint256& GetMessageHash() const;
+
void SetVersion(int nVersionIn)
{
hdrbuf.SetVersion(nVersionIn);
@@ -540,17 +577,16 @@ public:
/** Information about a peer */
class CNode
{
+ friend class CConnman;
public:
// socket
ServiceFlags nServices;
ServiceFlags nServicesExpected;
SOCKET hSocket;
- CDataStream ssSend;
size_t nSendSize; // total size of all vSendMsg entries
size_t nSendOffset; // offset inside the first vSendMsg already sent
- uint64_t nOptimisticBytesWritten;
uint64_t nSendBytes;
- std::deque<CSerializeData> vSendMsg;
+ std::deque<std::vector<unsigned char>> vSendMsg;
CCriticalSection cs_vSend;
std::deque<CInv> vRecvGetData;
@@ -576,10 +612,9 @@ public:
bool fFeeler; // If true this node is being used as a short lived feeler.
bool fOneShot;
bool fClient;
- bool fInbound;
- bool fNetworkNode;
+ const bool fInbound;
bool fSuccessfullyConnected;
- bool fDisconnect;
+ std::atomic_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 its version message that we should not relay tx invs
@@ -590,7 +625,7 @@ public:
CCriticalSection cs_filter;
CBloomFilter* pfilter;
int nRefCount;
- NodeId id;
+ const NodeId id;
const uint64_t nKeyedNetGroup;
protected:
@@ -598,9 +633,6 @@ protected:
mapMsgCmdSize mapSendBytesPerMsgCmd;
mapMsgCmdSize mapRecvBytesPerMsgCmd;
- // Basic fuzz-testing
- void Fuzz(int nChance); // modifies ssSend
-
public:
uint256 hashContinue;
int nStartingHeight;
@@ -656,18 +688,19 @@ public:
CAmount lastSentFeeFilter;
int64_t nextSendTimeFeeFilter;
- CNode(NodeId id, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, const std::string &addrNameIn = "", bool fInboundIn = false);
+ CNode(NodeId id, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const std::string &addrNameIn = "", bool fInboundIn = false);
~CNode();
private:
CNode(const CNode&);
void operator=(const CNode&);
- static uint64_t CalculateKeyedNetGroup(const CAddress& ad);
- uint64_t nLocalHostNonce;
- ServiceFlags nLocalServices;
- int nMyStartingHeight;
+ const uint64_t nLocalHostNonce;
+ // Services offered to this peer
+ const ServiceFlags nLocalServices;
+ const int nMyStartingHeight;
+ int nSendVersion;
public:
NodeId GetId() const {
@@ -678,6 +711,10 @@ public:
return nLocalHostNonce;
}
+ int GetMyStartingHeight() const {
+ return nMyStartingHeight;
+ }
+
int GetRefCount()
{
assert(nRefCount >= 0);
@@ -703,6 +740,25 @@ public:
BOOST_FOREACH(CNetMessage &msg, vRecvMsg)
msg.SetVersion(nVersionIn);
}
+ void SetSendVersion(int nVersionIn)
+ {
+ // Send version may only be changed in the version message, and
+ // only one version message is allowed per session. We can therefore
+ // treat this value as const and even atomic as long as it's only used
+ // once the handshake is complete. Any attempt to set this twice is an
+ // error.
+ assert(nSendVersion == 0);
+ nSendVersion = nVersionIn;
+ }
+
+ int GetSendVersion() const
+ {
+ // The send version should always be explicitly set to
+ // INIT_PROTO_VERSION rather than using this value until the handshake
+ // is complete.
+ assert(nSendVersion != 0);
+ return nSendVersion;
+ }
CNode* AddRef()
{
@@ -722,14 +778,14 @@ public:
addrKnown.insert(_addr.GetKey());
}
- void PushAddress(const CAddress& _addr)
+ void PushAddress(const CAddress& _addr, FastRandomContext &insecure_rand)
{
// 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() && !addrKnown.contains(_addr.GetKey())) {
if (vAddrToSend.size() >= MAX_ADDR_TO_SEND) {
- vAddrToSend[insecure_rand() % vAddrToSend.size()] = _addr;
+ vAddrToSend[insecure_rand.rand32() % vAddrToSend.size()] = _addr;
} else {
vAddrToSend.push_back(_addr);
}
@@ -765,193 +821,6 @@ public:
void AskFor(const CInv& inv);
- // TODO: Document the postcondition of this function. Is cs_vSend locked?
- void BeginMessage(const char* pszCommand) EXCLUSIVE_LOCK_FUNCTION(cs_vSend);
-
- // TODO: Document the precondition of this function. Is cs_vSend locked?
- void AbortMessage() UNLOCK_FUNCTION(cs_vSend);
-
- // TODO: Document the precondition of this function. Is cs_vSend locked?
- void EndMessage(const char* pszCommand) UNLOCK_FUNCTION(cs_vSend);
-
- void PushVersion();
-
-
- void PushMessage(const char* pszCommand)
- {
- try
- {
- BeginMessage(pszCommand);
- EndMessage(pszCommand);
- }
- catch (...)
- {
- AbortMessage();
- throw;
- }
- }
-
- template<typename T1>
- void PushMessage(const char* pszCommand, const T1& a1)
- {
- try
- {
- BeginMessage(pszCommand);
- ssSend << a1;
- EndMessage(pszCommand);
- }
- catch (...)
- {
- AbortMessage();
- throw;
- }
- }
-
- /** Send a message containing a1, serialized with flag flag. */
- template<typename T1>
- void PushMessageWithFlag(int flag, const char* pszCommand, const T1& a1)
- {
- try
- {
- BeginMessage(pszCommand);
- WithOrVersion(&ssSend, flag) << a1;
- EndMessage(pszCommand);
- }
- catch (...)
- {
- AbortMessage();
- throw;
- }
- }
-
- template<typename T1, typename T2>
- void PushMessage(const char* pszCommand, const T1& a1, const T2& a2)
- {
- try
- {
- BeginMessage(pszCommand);
- ssSend << a1 << a2;
- EndMessage(pszCommand);
- }
- catch (...)
- {
- AbortMessage();
- throw;
- }
- }
-
- template<typename T1, typename T2, typename T3>
- void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3)
- {
- try
- {
- BeginMessage(pszCommand);
- ssSend << a1 << a2 << a3;
- EndMessage(pszCommand);
- }
- catch (...)
- {
- AbortMessage();
- throw;
- }
- }
-
- template<typename T1, typename T2, typename T3, typename T4>
- 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(pszCommand);
- }
- catch (...)
- {
- AbortMessage();
- throw;
- }
- }
-
- template<typename T1, typename T2, typename T3, typename T4, typename T5>
- 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(pszCommand);
- }
- catch (...)
- {
- AbortMessage();
- throw;
- }
- }
-
- template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
- 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(pszCommand);
- }
- catch (...)
- {
- AbortMessage();
- throw;
- }
- }
-
- template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
- 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(pszCommand);
- }
- catch (...)
- {
- AbortMessage();
- throw;
- }
- }
-
- template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
- 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(pszCommand);
- }
- catch (...)
- {
- AbortMessage();
- throw;
- }
- }
-
- template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9>
- 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(pszCommand);
- }
- catch (...)
- {
- AbortMessage();
- throw;
- }
- }
-
void CloseSocketDisconnect();
void copyStats(CNodeStats &stats);
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
new file mode 100644
index 0000000000..e10694ed18
--- /dev/null
+++ b/src/net_processing.cpp
@@ -0,0 +1,3077 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2016 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "net_processing.h"
+
+#include "addrman.h"
+#include "arith_uint256.h"
+#include "blockencodings.h"
+#include "chainparams.h"
+#include "consensus/validation.h"
+#include "hash.h"
+#include "init.h"
+#include "validation.h"
+#include "merkleblock.h"
+#include "net.h"
+#include "netmessagemaker.h"
+#include "netbase.h"
+#include "policy/fees.h"
+#include "policy/policy.h"
+#include "primitives/block.h"
+#include "primitives/transaction.h"
+#include "random.h"
+#include "tinyformat.h"
+#include "txmempool.h"
+#include "ui_interface.h"
+#include "util.h"
+#include "utilmoneystr.h"
+#include "utilstrencodings.h"
+#include "validationinterface.h"
+
+#include <boost/thread.hpp>
+
+using namespace std;
+
+#if defined(NDEBUG)
+# error "Bitcoin cannot be compiled without assertions."
+#endif
+
+int64_t nTimeBestReceived = 0; // Used only to inform the wallet of when we last received a block
+
+struct IteratorComparator
+{
+ template<typename I>
+ bool operator()(const I& a, const I& b)
+ {
+ return &(*a) < &(*b);
+ }
+};
+
+struct COrphanTx {
+ // When modifying, adapt the copy of this definition in tests/DoS_tests.
+ CTransaction tx;
+ NodeId fromPeer;
+ int64_t nTimeExpire;
+};
+map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(cs_main);
+map<COutPoint, set<map<uint256, COrphanTx>::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(cs_main);
+void EraseOrphansFor(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+static const uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL; // SHA256("main address relay")[0:8]
+
+// Internal stuff
+namespace {
+ /** Number of nodes with fSyncStarted. */
+ int nSyncStarted = 0;
+
+ /**
+ * Sources of received blocks, saved to be able to send them reject
+ * messages or ban them when processing happens afterwards. Protected by
+ * cs_main.
+ * Set mapBlockSource[hash].second to false if the node should not be
+ * punished if the block is invalid.
+ */
+ map<uint256, std::pair<NodeId, bool>> mapBlockSource;
+
+ /**
+ * Filter for transactions that were recently rejected by
+ * AcceptToMemoryPool. These are not rerequested until the chain tip
+ * changes, at which point the entire filter is reset. Protected by
+ * cs_main.
+ *
+ * Without this filter we'd be re-requesting txs from each of our peers,
+ * increasing bandwidth consumption considerably. For instance, with 100
+ * peers, half of which relay a tx we don't accept, that might be a 50x
+ * bandwidth increase. A flooding attacker attempting to roll-over the
+ * filter using minimum-sized, 60byte, transactions might manage to send
+ * 1000/sec if we have fast peers, so we pick 120,000 to give our peers a
+ * two minute window to send invs to us.
+ *
+ * Decreasing the false positive rate is fairly cheap, so we pick one in a
+ * million to make it highly unlikely for users to have issues with this
+ * filter.
+ *
+ * Memory used: 1.3 MB
+ */
+ std::unique_ptr<CRollingBloomFilter> recentRejects;
+ uint256 hashRecentRejectsChainTip;
+
+ /** Blocks that are in flight, and that are in the queue to be downloaded. Protected by cs_main. */
+ struct QueuedBlock {
+ uint256 hash;
+ CBlockIndex* pindex; //!< Optional.
+ bool fValidatedHeaders; //!< Whether this block has validated headers at the time of request.
+ std::unique_ptr<PartiallyDownloadedBlock> partialBlock; //!< Optional, used for CMPCTBLOCK downloads
+ };
+ map<uint256, pair<NodeId, list<QueuedBlock>::iterator> > mapBlocksInFlight;
+
+ /** Stack of nodes which we have set to announce using compact blocks */
+ list<NodeId> lNodesAnnouncingHeaderAndIDs;
+
+ /** Number of preferable block download peers. */
+ int nPreferredDownload = 0;
+
+ /** Number of peers from which we're downloading blocks. */
+ int nPeersWithValidatedDownloads = 0;
+
+ /** Relay map, protected by cs_main. */
+ typedef std::map<uint256, CTransactionRef> MapRelay;
+ MapRelay mapRelay;
+ /** Expiration-time ordered list of (expire time, relay map entry) pairs, protected by cs_main). */
+ std::deque<std::pair<int64_t, MapRelay::iterator>> vRelayExpiration;
+} // anon namespace
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Registration of network node signals.
+//
+
+namespace {
+
+struct CBlockReject {
+ unsigned char chRejectCode;
+ string strRejectReason;
+ uint256 hashBlock;
+};
+
+/**
+ * Maintain validation-specific state about nodes, protected by cs_main, instead
+ * by CNode's own locks. This simplifies asynchronous operation, where
+ * processing of incoming data is done after the ProcessMessage call returns,
+ * and we're no longer holding the node's locks.
+ */
+struct CNodeState {
+ //! The peer's address
+ const CService address;
+ //! Whether we have a fully established connection.
+ bool fCurrentlyConnected;
+ //! Accumulated misbehaviour score for this peer.
+ int nMisbehavior;
+ //! Whether this peer should be disconnected and banned (unless whitelisted).
+ bool fShouldBan;
+ //! String name of this peer (debugging/logging purposes).
+ const std::string name;
+ //! List of asynchronously-determined block rejections to notify this peer about.
+ std::vector<CBlockReject> rejects;
+ //! The best known block we know this peer has announced.
+ CBlockIndex *pindexBestKnownBlock;
+ //! The hash of the last unknown block this peer has announced.
+ uint256 hashLastUnknownBlock;
+ //! The last full block we both have.
+ CBlockIndex *pindexLastCommonBlock;
+ //! The best header we have sent our peer.
+ CBlockIndex *pindexBestHeaderSent;
+ //! Length of current-streak of unconnecting headers announcements
+ int nUnconnectingHeaders;
+ //! Whether we've started headers synchronization with this peer.
+ bool fSyncStarted;
+ //! Since when we're stalling block download progress (in microseconds), or 0.
+ int64_t nStallingSince;
+ list<QueuedBlock> vBlocksInFlight;
+ //! When the first entry in vBlocksInFlight started downloading. Don't care when vBlocksInFlight is empty.
+ int64_t nDownloadingSince;
+ int nBlocksInFlight;
+ int nBlocksInFlightValidHeaders;
+ //! Whether we consider this a preferred download peer.
+ bool fPreferredDownload;
+ //! Whether this peer wants invs or headers (when possible) for block announcements.
+ bool fPreferHeaders;
+ //! Whether this peer wants invs or cmpctblocks (when possible) for block announcements.
+ bool fPreferHeaderAndIDs;
+ /**
+ * Whether this peer will send us cmpctblocks if we request them.
+ * This is not used to gate request logic, as we really only care about fSupportsDesiredCmpctVersion,
+ * but is used as a flag to "lock in" the version of compact blocks (fWantsCmpctWitness) we send.
+ */
+ bool fProvidesHeaderAndIDs;
+ //! Whether this peer can give us witnesses
+ bool fHaveWitness;
+ //! Whether this peer wants witnesses in cmpctblocks/blocktxns
+ bool fWantsCmpctWitness;
+ /**
+ * If we've announced NODE_WITNESS to this peer: whether the peer sends witnesses in cmpctblocks/blocktxns,
+ * otherwise: whether this peer sends non-witnesses in cmpctblocks/blocktxns.
+ */
+ bool fSupportsDesiredCmpctVersion;
+
+ CNodeState(CAddress addrIn, std::string addrNameIn) : address(addrIn), name(addrNameIn) {
+ fCurrentlyConnected = false;
+ nMisbehavior = 0;
+ fShouldBan = false;
+ pindexBestKnownBlock = NULL;
+ hashLastUnknownBlock.SetNull();
+ pindexLastCommonBlock = NULL;
+ pindexBestHeaderSent = NULL;
+ nUnconnectingHeaders = 0;
+ fSyncStarted = false;
+ nStallingSince = 0;
+ nDownloadingSince = 0;
+ nBlocksInFlight = 0;
+ nBlocksInFlightValidHeaders = 0;
+ fPreferredDownload = false;
+ fPreferHeaders = false;
+ fPreferHeaderAndIDs = false;
+ fProvidesHeaderAndIDs = false;
+ fHaveWitness = false;
+ fWantsCmpctWitness = false;
+ fSupportsDesiredCmpctVersion = false;
+ }
+};
+
+/** Map maintaining per-node state. Requires cs_main. */
+map<NodeId, CNodeState> mapNodeState;
+
+// Requires cs_main.
+CNodeState *State(NodeId pnode) {
+ map<NodeId, CNodeState>::iterator it = mapNodeState.find(pnode);
+ if (it == mapNodeState.end())
+ return NULL;
+ return &it->second;
+}
+
+void UpdatePreferredDownload(CNode* node, CNodeState* state)
+{
+ nPreferredDownload -= state->fPreferredDownload;
+
+ // Whether this node should be marked as a preferred download node.
+ state->fPreferredDownload = (!node->fInbound || node->fWhitelisted) && !node->fOneShot && !node->fClient;
+
+ nPreferredDownload += state->fPreferredDownload;
+}
+
+void PushNodeVersion(CNode *pnode, CConnman& connman, int64_t nTime)
+{
+ ServiceFlags nLocalNodeServices = pnode->GetLocalServices();
+ uint64_t nonce = pnode->GetLocalNonce();
+ int nNodeStartingHeight = pnode->GetMyStartingHeight();
+ NodeId nodeid = pnode->GetId();
+ CAddress addr = pnode->addr;
+
+ CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService(), addr.nServices));
+ CAddress addrMe = CAddress(CService(), nLocalNodeServices);
+
+ connman.PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalNodeServices, nTime, addrYou, addrMe,
+ nonce, strSubVersion, nNodeStartingHeight, ::fRelayTxes));
+
+ if (fLogIPs)
+ LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nNodeStartingHeight, addrMe.ToString(), addrYou.ToString(), nodeid);
+ else
+ LogPrint("net", "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nNodeStartingHeight, addrMe.ToString(), nodeid);
+}
+
+void InitializeNode(CNode *pnode, CConnman& connman) {
+ CAddress addr = pnode->addr;
+ std::string addrName = pnode->addrName;
+ NodeId nodeid = pnode->GetId();
+ {
+ LOCK(cs_main);
+ mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(addr, std::move(addrName)));
+ }
+ if(!pnode->fInbound)
+ PushNodeVersion(pnode, connman, GetTime());
+}
+
+void FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) {
+ fUpdateConnectionTime = false;
+ LOCK(cs_main);
+ CNodeState *state = State(nodeid);
+
+ if (state->fSyncStarted)
+ nSyncStarted--;
+
+ if (state->nMisbehavior == 0 && state->fCurrentlyConnected) {
+ fUpdateConnectionTime = true;
+ }
+
+ BOOST_FOREACH(const QueuedBlock& entry, state->vBlocksInFlight) {
+ mapBlocksInFlight.erase(entry.hash);
+ }
+ EraseOrphansFor(nodeid);
+ nPreferredDownload -= state->fPreferredDownload;
+ nPeersWithValidatedDownloads -= (state->nBlocksInFlightValidHeaders != 0);
+ assert(nPeersWithValidatedDownloads >= 0);
+
+ mapNodeState.erase(nodeid);
+
+ if (mapNodeState.empty()) {
+ // Do a consistency check after the last peer is removed.
+ assert(mapBlocksInFlight.empty());
+ assert(nPreferredDownload == 0);
+ assert(nPeersWithValidatedDownloads == 0);
+ }
+}
+
+// Requires cs_main.
+// Returns a bool indicating whether we requested this block.
+// Also used if a block was /not/ received and timed out or started with another peer
+bool MarkBlockAsReceived(const uint256& hash) {
+ map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash);
+ if (itInFlight != mapBlocksInFlight.end()) {
+ CNodeState *state = State(itInFlight->second.first);
+ state->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders;
+ if (state->nBlocksInFlightValidHeaders == 0 && itInFlight->second.second->fValidatedHeaders) {
+ // Last validated block on the queue was received.
+ nPeersWithValidatedDownloads--;
+ }
+ if (state->vBlocksInFlight.begin() == itInFlight->second.second) {
+ // First block on the queue was received, update the start download time for the next one
+ state->nDownloadingSince = std::max(state->nDownloadingSince, GetTimeMicros());
+ }
+ state->vBlocksInFlight.erase(itInFlight->second.second);
+ state->nBlocksInFlight--;
+ state->nStallingSince = 0;
+ mapBlocksInFlight.erase(itInFlight);
+ return true;
+ }
+ return false;
+}
+
+// Requires cs_main.
+// returns false, still setting pit, if the block was already in flight from the same peer
+// pit will only be valid as long as the same cs_main lock is being held
+bool MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Params& consensusParams, CBlockIndex *pindex = NULL, list<QueuedBlock>::iterator **pit = NULL) {
+ CNodeState *state = State(nodeid);
+ assert(state != NULL);
+
+ // Short-circuit most stuff in case its from the same node
+ map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash);
+ if (itInFlight != mapBlocksInFlight.end() && itInFlight->second.first == nodeid) {
+ *pit = &itInFlight->second.second;
+ return false;
+ }
+
+ // Make sure it's not listed somewhere already.
+ MarkBlockAsReceived(hash);
+
+ list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(),
+ {hash, pindex, pindex != NULL, std::unique_ptr<PartiallyDownloadedBlock>(pit ? new PartiallyDownloadedBlock(&mempool) : NULL)});
+ state->nBlocksInFlight++;
+ state->nBlocksInFlightValidHeaders += it->fValidatedHeaders;
+ if (state->nBlocksInFlight == 1) {
+ // We're starting a block download (batch) from this peer.
+ state->nDownloadingSince = GetTimeMicros();
+ }
+ if (state->nBlocksInFlightValidHeaders == 1 && pindex != NULL) {
+ nPeersWithValidatedDownloads++;
+ }
+ itInFlight = mapBlocksInFlight.insert(std::make_pair(hash, std::make_pair(nodeid, it))).first;
+ if (pit)
+ *pit = &itInFlight->second.second;
+ return true;
+}
+
+/** Check whether the last unknown block a peer advertised is not yet known. */
+void ProcessBlockAvailability(NodeId nodeid) {
+ CNodeState *state = State(nodeid);
+ assert(state != NULL);
+
+ if (!state->hashLastUnknownBlock.IsNull()) {
+ BlockMap::iterator itOld = mapBlockIndex.find(state->hashLastUnknownBlock);
+ if (itOld != mapBlockIndex.end() && itOld->second->nChainWork > 0) {
+ if (state->pindexBestKnownBlock == NULL || itOld->second->nChainWork >= state->pindexBestKnownBlock->nChainWork)
+ state->pindexBestKnownBlock = itOld->second;
+ state->hashLastUnknownBlock.SetNull();
+ }
+ }
+}
+
+/** Update tracking information about which blocks a peer is assumed to have. */
+void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) {
+ CNodeState *state = State(nodeid);
+ assert(state != NULL);
+
+ ProcessBlockAvailability(nodeid);
+
+ BlockMap::iterator it = mapBlockIndex.find(hash);
+ if (it != mapBlockIndex.end() && it->second->nChainWork > 0) {
+ // An actually better block was announced.
+ if (state->pindexBestKnownBlock == NULL || it->second->nChainWork >= state->pindexBestKnownBlock->nChainWork)
+ state->pindexBestKnownBlock = it->second;
+ } else {
+ // An unknown block was announced; just assume that the latest one is the best one.
+ state->hashLastUnknownBlock = hash;
+ }
+}
+
+void MaybeSetPeerAsAnnouncingHeaderAndIDs(const CNodeState* nodestate, CNode* pfrom, CConnman& connman) {
+ if (!nodestate->fSupportsDesiredCmpctVersion) {
+ // Never ask from peers who can't provide witnesses.
+ return;
+ }
+ if (nodestate->fProvidesHeaderAndIDs) {
+ for (std::list<NodeId>::iterator it = lNodesAnnouncingHeaderAndIDs.begin(); it != lNodesAnnouncingHeaderAndIDs.end(); it++) {
+ if (*it == pfrom->GetId()) {
+ lNodesAnnouncingHeaderAndIDs.erase(it);
+ lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId());
+ return;
+ }
+ }
+ bool fAnnounceUsingCMPCTBLOCK = false;
+ uint64_t nCMPCTBLOCKVersion = (pfrom->GetLocalServices() & NODE_WITNESS) ? 2 : 1;
+ if (lNodesAnnouncingHeaderAndIDs.size() >= 3) {
+ // As per BIP152, we only get 3 of our peers to announce
+ // blocks using compact encodings.
+ connman.ForNode(lNodesAnnouncingHeaderAndIDs.front(), [&connman, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion](CNode* pnodeStop){
+ connman.PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetSendVersion()).Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
+ return true;
+ });
+ lNodesAnnouncingHeaderAndIDs.pop_front();
+ }
+ fAnnounceUsingCMPCTBLOCK = true;
+ connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
+ lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId());
+ }
+}
+
+// Requires cs_main
+bool CanDirectFetch(const Consensus::Params &consensusParams)
+{
+ return chainActive.Tip()->GetBlockTime() > GetAdjustedTime() - consensusParams.nPowTargetSpacing * 20;
+}
+
+// Requires cs_main
+bool PeerHasHeader(CNodeState *state, CBlockIndex *pindex)
+{
+ if (state->pindexBestKnownBlock && pindex == state->pindexBestKnownBlock->GetAncestor(pindex->nHeight))
+ return true;
+ if (state->pindexBestHeaderSent && pindex == state->pindexBestHeaderSent->GetAncestor(pindex->nHeight))
+ return true;
+ return false;
+}
+
+/** Find the last common ancestor two blocks have.
+ * Both pa and pb must be non-NULL. */
+CBlockIndex* LastCommonAncestor(CBlockIndex* pa, CBlockIndex* pb) {
+ if (pa->nHeight > pb->nHeight) {
+ pa = pa->GetAncestor(pb->nHeight);
+ } else if (pb->nHeight > pa->nHeight) {
+ pb = pb->GetAncestor(pa->nHeight);
+ }
+
+ while (pa != pb && pa && pb) {
+ pa = pa->pprev;
+ pb = pb->pprev;
+ }
+
+ // Eventually all chain branches meet at the genesis block.
+ assert(pa == pb);
+ return pa;
+}
+
+/** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has
+ * at most count entries. */
+void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<CBlockIndex*>& vBlocks, NodeId& nodeStaller, const Consensus::Params& consensusParams) {
+ if (count == 0)
+ return;
+
+ vBlocks.reserve(vBlocks.size() + count);
+ CNodeState *state = State(nodeid);
+ assert(state != NULL);
+
+ // Make sure pindexBestKnownBlock is up to date, we'll need it.
+ ProcessBlockAvailability(nodeid);
+
+ if (state->pindexBestKnownBlock == NULL || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork) {
+ // This peer has nothing interesting.
+ return;
+ }
+
+ if (state->pindexLastCommonBlock == NULL) {
+ // Bootstrap quickly by guessing a parent of our best tip is the forking point.
+ // Guessing wrong in either direction is not a problem.
+ state->pindexLastCommonBlock = chainActive[std::min(state->pindexBestKnownBlock->nHeight, chainActive.Height())];
+ }
+
+ // If the peer reorganized, our previous pindexLastCommonBlock may not be an ancestor
+ // of its current tip anymore. Go back enough to fix that.
+ state->pindexLastCommonBlock = LastCommonAncestor(state->pindexLastCommonBlock, state->pindexBestKnownBlock);
+ if (state->pindexLastCommonBlock == state->pindexBestKnownBlock)
+ return;
+
+ std::vector<CBlockIndex*> vToFetch;
+ CBlockIndex *pindexWalk = state->pindexLastCommonBlock;
+ // Never fetch further than the best block we know the peer has, or more than BLOCK_DOWNLOAD_WINDOW + 1 beyond the last
+ // linked block we have in common with this peer. The +1 is so we can detect stalling, namely if we would be able to
+ // download that next block if the window were 1 larger.
+ int nWindowEnd = state->pindexLastCommonBlock->nHeight + BLOCK_DOWNLOAD_WINDOW;
+ int nMaxHeight = std::min<int>(state->pindexBestKnownBlock->nHeight, nWindowEnd + 1);
+ NodeId waitingfor = -1;
+ while (pindexWalk->nHeight < nMaxHeight) {
+ // Read up to 128 (or more, if more blocks than that are needed) successors of pindexWalk (towards
+ // pindexBestKnownBlock) into vToFetch. We fetch 128, because CBlockIndex::GetAncestor may be as expensive
+ // as iterating over ~100 CBlockIndex* entries anyway.
+ int nToFetch = std::min(nMaxHeight - pindexWalk->nHeight, std::max<int>(count - vBlocks.size(), 128));
+ vToFetch.resize(nToFetch);
+ pindexWalk = state->pindexBestKnownBlock->GetAncestor(pindexWalk->nHeight + nToFetch);
+ vToFetch[nToFetch - 1] = pindexWalk;
+ for (unsigned int i = nToFetch - 1; i > 0; i--) {
+ vToFetch[i - 1] = vToFetch[i]->pprev;
+ }
+
+ // Iterate over those blocks in vToFetch (in forward direction), adding the ones that
+ // are not yet downloaded and not in flight to vBlocks. In the mean time, update
+ // pindexLastCommonBlock as long as all ancestors are already downloaded, or if it's
+ // already part of our chain (and therefore don't need it even if pruned).
+ BOOST_FOREACH(CBlockIndex* pindex, vToFetch) {
+ if (!pindex->IsValid(BLOCK_VALID_TREE)) {
+ // We consider the chain that this peer is on invalid.
+ return;
+ }
+ if (!State(nodeid)->fHaveWitness && IsWitnessEnabled(pindex->pprev, consensusParams)) {
+ // We wouldn't download this block or its descendants from this peer.
+ return;
+ }
+ if (pindex->nStatus & BLOCK_HAVE_DATA || chainActive.Contains(pindex)) {
+ if (pindex->nChainTx)
+ state->pindexLastCommonBlock = pindex;
+ } else if (mapBlocksInFlight.count(pindex->GetBlockHash()) == 0) {
+ // The block is not already downloaded, and not yet in flight.
+ if (pindex->nHeight > nWindowEnd) {
+ // We reached the end of the window.
+ if (vBlocks.size() == 0 && waitingfor != nodeid) {
+ // We aren't able to fetch anything, but we would be if the download window was one larger.
+ nodeStaller = waitingfor;
+ }
+ return;
+ }
+ vBlocks.push_back(pindex);
+ if (vBlocks.size() == count) {
+ return;
+ }
+ } else if (waitingfor == -1) {
+ // This is the first already-in-flight block.
+ waitingfor = mapBlocksInFlight[pindex->GetBlockHash()].first;
+ }
+ }
+ }
+}
+
+} // anon namespace
+
+bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) {
+ LOCK(cs_main);
+ CNodeState *state = State(nodeid);
+ if (state == NULL)
+ return false;
+ stats.nMisbehavior = state->nMisbehavior;
+ stats.nSyncHeight = state->pindexBestKnownBlock ? state->pindexBestKnownBlock->nHeight : -1;
+ stats.nCommonHeight = state->pindexLastCommonBlock ? state->pindexLastCommonBlock->nHeight : -1;
+ BOOST_FOREACH(const QueuedBlock& queue, state->vBlocksInFlight) {
+ if (queue.pindex)
+ stats.vHeightInFlight.push_back(queue.pindex->nHeight);
+ }
+ return true;
+}
+
+void RegisterNodeSignals(CNodeSignals& nodeSignals)
+{
+ nodeSignals.ProcessMessages.connect(&ProcessMessages);
+ nodeSignals.SendMessages.connect(&SendMessages);
+ nodeSignals.InitializeNode.connect(&InitializeNode);
+ nodeSignals.FinalizeNode.connect(&FinalizeNode);
+}
+
+void UnregisterNodeSignals(CNodeSignals& nodeSignals)
+{
+ nodeSignals.ProcessMessages.disconnect(&ProcessMessages);
+ nodeSignals.SendMessages.disconnect(&SendMessages);
+ nodeSignals.InitializeNode.disconnect(&InitializeNode);
+ nodeSignals.FinalizeNode.disconnect(&FinalizeNode);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// mapOrphanTransactions
+//
+
+bool AddOrphanTx(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+{
+ uint256 hash = tx.GetHash();
+ if (mapOrphanTransactions.count(hash))
+ return false;
+
+ // Ignore big transactions, to avoid a
+ // send-big-orphans memory exhaustion attack. If a peer has a legitimate
+ // large transaction with a missing parent then we assume
+ // it will rebroadcast it later, after the parent transaction(s)
+ // have been mined or received.
+ // 100 orphans, each of which is at most 99,999 bytes big is
+ // at most 10 megabytes of orphans and somewhat more byprev index (in the worst case):
+ unsigned int sz = GetTransactionWeight(tx);
+ if (sz >= MAX_STANDARD_TX_WEIGHT)
+ {
+ LogPrint("mempool", "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString());
+ return false;
+ }
+
+ auto ret = mapOrphanTransactions.emplace(hash, COrphanTx{tx, peer, GetTime() + ORPHAN_TX_EXPIRE_TIME});
+ assert(ret.second);
+ BOOST_FOREACH(const CTxIn& txin, tx.vin) {
+ mapOrphanTransactionsByPrev[txin.prevout].insert(ret.first);
+ }
+
+ LogPrint("mempool", "stored orphan tx %s (mapsz %u outsz %u)\n", hash.ToString(),
+ mapOrphanTransactions.size(), mapOrphanTransactionsByPrev.size());
+ return true;
+}
+
+int static EraseOrphanTx(uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+{
+ map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.find(hash);
+ if (it == mapOrphanTransactions.end())
+ return 0;
+ BOOST_FOREACH(const CTxIn& txin, it->second.tx.vin)
+ {
+ auto itPrev = mapOrphanTransactionsByPrev.find(txin.prevout);
+ if (itPrev == mapOrphanTransactionsByPrev.end())
+ continue;
+ itPrev->second.erase(it);
+ if (itPrev->second.empty())
+ mapOrphanTransactionsByPrev.erase(itPrev);
+ }
+ mapOrphanTransactions.erase(it);
+ return 1;
+}
+
+void EraseOrphansFor(NodeId peer)
+{
+ int nErased = 0;
+ map<uint256, COrphanTx>::iterator iter = mapOrphanTransactions.begin();
+ while (iter != mapOrphanTransactions.end())
+ {
+ map<uint256, COrphanTx>::iterator maybeErase = iter++; // increment to avoid iterator becoming invalid
+ if (maybeErase->second.fromPeer == peer)
+ {
+ nErased += EraseOrphanTx(maybeErase->second.tx.GetHash());
+ }
+ }
+ if (nErased > 0) LogPrint("mempool", "Erased %d orphan tx from peer %d\n", nErased, peer);
+}
+
+
+unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+{
+ unsigned int nEvicted = 0;
+ static int64_t nNextSweep;
+ int64_t nNow = GetTime();
+ if (nNextSweep <= nNow) {
+ // Sweep out expired orphan pool entries:
+ int nErased = 0;
+ int64_t nMinExpTime = nNow + ORPHAN_TX_EXPIRE_TIME - ORPHAN_TX_EXPIRE_INTERVAL;
+ map<uint256, COrphanTx>::iterator iter = mapOrphanTransactions.begin();
+ while (iter != mapOrphanTransactions.end())
+ {
+ map<uint256, COrphanTx>::iterator maybeErase = iter++;
+ if (maybeErase->second.nTimeExpire <= nNow) {
+ nErased += EraseOrphanTx(maybeErase->second.tx.GetHash());
+ } else {
+ nMinExpTime = std::min(maybeErase->second.nTimeExpire, nMinExpTime);
+ }
+ }
+ // Sweep again 5 minutes after the next entry that expires in order to batch the linear scan.
+ nNextSweep = nMinExpTime + ORPHAN_TX_EXPIRE_INTERVAL;
+ if (nErased > 0) LogPrint("mempool", "Erased %d orphan tx due to expiration\n", nErased);
+ }
+ while (mapOrphanTransactions.size() > nMaxOrphans)
+ {
+ // Evict a random orphan:
+ uint256 randomhash = GetRandHash();
+ map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
+ if (it == mapOrphanTransactions.end())
+ it = mapOrphanTransactions.begin();
+ EraseOrphanTx(it->first);
+ ++nEvicted;
+ }
+ return nEvicted;
+}
+
+// Requires cs_main.
+void Misbehaving(NodeId pnode, int howmuch)
+{
+ if (howmuch == 0)
+ return;
+
+ CNodeState *state = State(pnode);
+ if (state == NULL)
+ return;
+
+ state->nMisbehavior += howmuch;
+ int banscore = GetArg("-banscore", DEFAULT_BANSCORE_THRESHOLD);
+ if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore)
+ {
+ LogPrintf("%s: %s peer=%d (%d -> %d) BAN THRESHOLD EXCEEDED\n", __func__, state->name, pnode, state->nMisbehavior-howmuch, state->nMisbehavior);
+ state->fShouldBan = true;
+ } else
+ LogPrintf("%s: %s peer=%d (%d -> %d)\n", __func__, state->name, pnode, state->nMisbehavior-howmuch, state->nMisbehavior);
+}
+
+
+
+
+
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// blockchain -> download logic notification
+//
+
+PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn) : connman(connmanIn) {
+ // Initialize global variables that cannot be constructed at startup.
+ recentRejects.reset(new CRollingBloomFilter(120000, 0.000001));
+}
+
+void PeerLogicValidation::SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, int nPosInBlock) {
+ if (nPosInBlock == CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK)
+ return;
+
+ LOCK(cs_main);
+
+ std::vector<uint256> vOrphanErase;
+ // Which orphan pool entries must we evict?
+ for (size_t j = 0; j < tx.vin.size(); j++) {
+ auto itByPrev = mapOrphanTransactionsByPrev.find(tx.vin[j].prevout);
+ if (itByPrev == mapOrphanTransactionsByPrev.end()) continue;
+ for (auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); ++mi) {
+ const CTransaction& orphanTx = (*mi)->second.tx;
+ const uint256& orphanHash = orphanTx.GetHash();
+ vOrphanErase.push_back(orphanHash);
+ }
+ }
+
+ // Erase orphan transactions include or precluded by this block
+ if (vOrphanErase.size()) {
+ int nErased = 0;
+ BOOST_FOREACH(uint256 &orphanHash, vOrphanErase) {
+ nErased += EraseOrphanTx(orphanHash);
+ }
+ LogPrint("mempool", "Erased %d orphan tx included or conflicted by block\n", nErased);
+ }
+}
+
+void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
+ const int nNewHeight = pindexNew->nHeight;
+ connman->SetBestHeight(nNewHeight);
+
+ if (!fInitialDownload) {
+ // Find the hashes of all blocks that weren't previously in the best chain.
+ std::vector<uint256> vHashes;
+ const CBlockIndex *pindexToAnnounce = pindexNew;
+ while (pindexToAnnounce != pindexFork) {
+ vHashes.push_back(pindexToAnnounce->GetBlockHash());
+ pindexToAnnounce = pindexToAnnounce->pprev;
+ if (vHashes.size() == MAX_BLOCKS_TO_ANNOUNCE) {
+ // Limit announcements in case of a huge reorganization.
+ // Rely on the peer's synchronization mechanism in that case.
+ break;
+ }
+ }
+ // Relay inventory, but don't relay old inventory during initial block download.
+ connman->ForEachNode([nNewHeight, &vHashes](CNode* pnode) {
+ if (nNewHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 0)) {
+ BOOST_REVERSE_FOREACH(const uint256& hash, vHashes) {
+ pnode->PushBlockHash(hash);
+ }
+ }
+ });
+ }
+
+ nTimeBestReceived = GetTime();
+}
+
+void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationState& state) {
+ LOCK(cs_main);
+
+ const uint256 hash(block.GetHash());
+ std::map<uint256, std::pair<NodeId, bool>>::iterator it = mapBlockSource.find(hash);
+
+ int nDoS = 0;
+ if (state.IsInvalid(nDoS)) {
+ if (it != mapBlockSource.end() && State(it->second.first)) {
+ assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes
+ CBlockReject reject = {(unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), hash};
+ State(it->second.first)->rejects.push_back(reject);
+ if (nDoS > 0 && it->second.second)
+ Misbehaving(it->second.first, nDoS);
+ }
+ }
+ if (it != mapBlockSource.end())
+ mapBlockSource.erase(it);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Messages
+//
+
+
+bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+{
+ switch (inv.type)
+ {
+ case MSG_TX:
+ case MSG_WITNESS_TX:
+ {
+ assert(recentRejects);
+ if (chainActive.Tip()->GetBlockHash() != hashRecentRejectsChainTip)
+ {
+ // If the chain tip has changed previously rejected transactions
+ // might be now valid, e.g. due to a nLockTime'd tx becoming valid,
+ // or a double-spend. Reset the rejects filter and give those
+ // txs a second chance.
+ hashRecentRejectsChainTip = chainActive.Tip()->GetBlockHash();
+ recentRejects->reset();
+ }
+
+ // Use pcoinsTip->HaveCoinsInCache as a quick approximation to exclude
+ // requesting or processing some txs which have already been included in a block
+ return recentRejects->contains(inv.hash) ||
+ mempool.exists(inv.hash) ||
+ mapOrphanTransactions.count(inv.hash) ||
+ pcoinsTip->HaveCoinsInCache(inv.hash);
+ }
+ case MSG_BLOCK:
+ case MSG_WITNESS_BLOCK:
+ return mapBlockIndex.count(inv.hash);
+ }
+ // Don't know what it is, just say we already got one
+ return true;
+}
+
+static void RelayTransaction(const CTransaction& tx, CConnman& connman)
+{
+ CInv inv(MSG_TX, tx.GetHash());
+ connman.ForEachNode([&inv](CNode* pnode)
+ {
+ pnode->PushInventory(inv);
+ });
+}
+
+static void RelayAddress(const CAddress& addr, bool fReachable, CConnman& connman)
+{
+ unsigned int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s)
+
+ // Relay to a limited number of other nodes
+ // Use deterministic randomness to send to the same nodes for 24 hours
+ // at a time so the addrKnowns of the chosen nodes prevent repeats
+ uint64_t hashAddr = addr.GetHash();
+ const CSipHasher hasher = connman.GetDeterministicRandomizer(RANDOMIZER_ID_ADDRESS_RELAY).Write(hashAddr << 32).Write((GetTime() + hashAddr) / (24*60*60));
+ FastRandomContext insecure_rand;
+
+ std::array<std::pair<uint64_t, CNode*>,2> best{{{0, nullptr}, {0, nullptr}}};
+ assert(nRelayNodes <= best.size());
+
+ auto sortfunc = [&best, &hasher, nRelayNodes](CNode* pnode) {
+ if (pnode->nVersion >= CADDR_TIME_VERSION) {
+ uint64_t hashKey = CSipHasher(hasher).Write(pnode->id).Finalize();
+ for (unsigned int i = 0; i < nRelayNodes; i++) {
+ if (hashKey > best[i].first) {
+ std::copy(best.begin() + i, best.begin() + nRelayNodes - 1, best.begin() + i + 1);
+ best[i] = std::make_pair(hashKey, pnode);
+ break;
+ }
+ }
+ }
+ };
+
+ auto pushfunc = [&addr, &best, nRelayNodes, &insecure_rand] {
+ for (unsigned int i = 0; i < nRelayNodes && best[i].first != 0; i++) {
+ best[i].second->PushAddress(addr, insecure_rand);
+ }
+ };
+
+ connman.ForEachNodeThen(std::move(sortfunc), std::move(pushfunc));
+}
+
+void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParams, CConnman& connman)
+{
+ std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin();
+ unsigned int nMaxSendBufferSize = connman.GetSendBufferSize();
+ vector<CInv> vNotFound;
+ CNetMsgMaker msgMaker(pfrom->GetSendVersion());
+ LOCK(cs_main);
+
+ while (it != pfrom->vRecvGetData.end()) {
+ // Don't bother if send buffer is too full to respond anyway
+ if (pfrom->nSendSize >= nMaxSendBufferSize)
+ break;
+
+ const CInv &inv = *it;
+ {
+ boost::this_thread::interruption_point();
+ it++;
+
+ if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK)
+ {
+ bool send = false;
+ BlockMap::iterator mi = mapBlockIndex.find(inv.hash);
+ if (mi != mapBlockIndex.end())
+ {
+ if (chainActive.Contains(mi->second)) {
+ send = true;
+ } else {
+ static const int nOneMonth = 30 * 24 * 60 * 60;
+ // To prevent fingerprinting attacks, only send blocks outside of the active
+ // chain if they are valid, and no more than a month older (both in time, and in
+ // best equivalent proof of work) than the best header chain we know about.
+ send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != NULL) &&
+ (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() < nOneMonth) &&
+ (GetBlockProofEquivalentTime(*pindexBestHeader, *mi->second, *pindexBestHeader, consensusParams) < nOneMonth);
+ if (!send) {
+ LogPrintf("%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId());
+ }
+ }
+ }
+ // disconnect node in case we have reached the outbound limit for serving historical blocks
+ // never disconnect whitelisted nodes
+ static const int nOneWeek = 7 * 24 * 60 * 60; // assume > 1 week = historical
+ if (send && connman.OutboundTargetReached(true) && ( ((pindexBestHeader != NULL) && (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() > nOneWeek)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted)
+ {
+ LogPrint("net", "historical block serving limit reached, disconnect peer=%d\n", pfrom->GetId());
+
+ //disconnect node
+ pfrom->fDisconnect = true;
+ send = false;
+ }
+ // Pruned nodes may have deleted the block, so check whether
+ // it's available before trying to send.
+ if (send && (mi->second->nStatus & BLOCK_HAVE_DATA))
+ {
+ // Send block from disk
+ CBlock block;
+ if (!ReadBlockFromDisk(block, (*mi).second, consensusParams))
+ assert(!"cannot load block from disk");
+ if (inv.type == MSG_BLOCK)
+ connman.PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, block));
+ else if (inv.type == MSG_WITNESS_BLOCK)
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::BLOCK, block));
+ else if (inv.type == MSG_FILTERED_BLOCK)
+ {
+ bool sendMerkleBlock = false;
+ CMerkleBlock merkleBlock;
+ {
+ LOCK(pfrom->cs_filter);
+ if (pfrom->pfilter) {
+ sendMerkleBlock = true;
+ merkleBlock = CMerkleBlock(block, *pfrom->pfilter);
+ }
+ }
+ if (sendMerkleBlock) {
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::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 didn't 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<unsigned int, uint256> PairType;
+ BOOST_FOREACH(PairType& pair, merkleBlock.vMatchedTxn)
+ connman.PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *block.vtx[pair.first]));
+ }
+ // else
+ // no response
+ }
+ else if (inv.type == MSG_CMPCT_BLOCK)
+ {
+ // If a peer is asking for old blocks, we're almost guaranteed
+ // they won't have a useful mempool to match against a compact block,
+ // and we don't feel like constructing the object for them, so
+ // instead we respond with the full, non-compact block.
+ bool fPeerWantsWitness = State(pfrom->GetId())->fWantsCmpctWitness;
+ int nSendFlags = fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
+ if (CanDirectFetch(consensusParams) && mi->second->nHeight >= chainActive.Height() - MAX_CMPCTBLOCK_DEPTH) {
+ CBlockHeaderAndShortTxIDs cmpctblock(block, fPeerWantsWitness);
+ connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
+ } else
+ connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, block));
+ }
+
+ // Trigger the peer node 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<CInv> vInv;
+ vInv.push_back(CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash()));
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::INV, vInv));
+ pfrom->hashContinue.SetNull();
+ }
+ }
+ }
+ else if (inv.type == MSG_TX || inv.type == MSG_WITNESS_TX)
+ {
+ // Send stream from relay memory
+ bool push = false;
+ auto mi = mapRelay.find(inv.hash);
+ int nSendFlags = (inv.type == MSG_TX ? SERIALIZE_TRANSACTION_NO_WITNESS : 0);
+ if (mi != mapRelay.end()) {
+ connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *mi->second));
+ push = true;
+ } else if (pfrom->timeLastMempoolReq) {
+ auto txinfo = mempool.info(inv.hash);
+ // To protect privacy, do not answer getdata using the mempool when
+ // that TX couldn't have been INVed in reply to a MEMPOOL request.
+ if (txinfo.tx && txinfo.nTime <= pfrom->timeLastMempoolReq) {
+ connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *txinfo.tx));
+ push = true;
+ }
+ }
+ if (!push) {
+ vNotFound.push_back(inv);
+ }
+ }
+
+ // Track requests for our stuff.
+ GetMainSignals().Inventory(inv.hash);
+
+ if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK)
+ break;
+ }
+ }
+
+ 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.
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::NOTFOUND, vNotFound));
+ }
+}
+
+uint32_t GetFetchFlags(CNode* pfrom, CBlockIndex* pprev, const Consensus::Params& chainparams) {
+ uint32_t nFetchFlags = 0;
+ if ((pfrom->GetLocalServices() & NODE_WITNESS) && State(pfrom->GetId())->fHaveWitness) {
+ nFetchFlags |= MSG_WITNESS_FLAG;
+ }
+ return nFetchFlags;
+}
+
+bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman& connman)
+{
+ unsigned int nMaxSendBufferSize = connman.GetSendBufferSize();
+
+ LogPrint("net", "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id);
+ if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0)
+ {
+ LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n");
+ return true;
+ }
+
+
+ if (!(pfrom->GetLocalServices() & NODE_BLOOM) &&
+ (strCommand == NetMsgType::FILTERLOAD ||
+ strCommand == NetMsgType::FILTERADD))
+ {
+ if (pfrom->nVersion >= NO_BLOOM_VERSION) {
+ LOCK(cs_main);
+ Misbehaving(pfrom->GetId(), 100);
+ return false;
+ } else {
+ pfrom->fDisconnect = true;
+ return false;
+ }
+ }
+
+
+ if (strCommand == NetMsgType::VERSION)
+ {
+ // Each connection can only send one version message
+ if (pfrom->nVersion != 0)
+ {
+ connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_DUPLICATE, string("Duplicate version message")));
+ LOCK(cs_main);
+ Misbehaving(pfrom->GetId(), 1);
+ return false;
+ }
+
+ int64_t nTime;
+ CAddress addrMe;
+ CAddress addrFrom;
+ uint64_t nNonce = 1;
+ uint64_t nServiceInt;
+ vRecv >> pfrom->nVersion >> nServiceInt >> nTime >> addrMe;
+ pfrom->nServices = ServiceFlags(nServiceInt);
+ if (!pfrom->fInbound)
+ {
+ connman.SetServices(pfrom->addr, pfrom->nServices);
+ }
+ if (pfrom->nServicesExpected & ~pfrom->nServices)
+ {
+ LogPrint("net", "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->id, pfrom->nServices, pfrom->nServicesExpected);
+ connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD,
+ strprintf("Expected to offer services %08x", pfrom->nServicesExpected)));
+ pfrom->fDisconnect = true;
+ return false;
+ }
+
+ if (pfrom->nVersion < MIN_PEER_PROTO_VERSION)
+ {
+ // disconnect from peers older than this proto version
+ LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion);
+ connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE,
+ strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION)));
+ pfrom->fDisconnect = true;
+ return false;
+ }
+
+ if (pfrom->nVersion == 10300)
+ pfrom->nVersion = 300;
+ if (!vRecv.empty())
+ vRecv >> addrFrom >> nNonce;
+ if (!vRecv.empty()) {
+ vRecv >> LIMITED_STRING(pfrom->strSubVer, MAX_SUBVERSION_LENGTH);
+ pfrom->cleanSubVer = SanitizeString(pfrom->strSubVer);
+ }
+ if (!vRecv.empty()) {
+ vRecv >> pfrom->nStartingHeight;
+ }
+ {
+ LOCK(pfrom->cs_filter);
+ if (!vRecv.empty())
+ vRecv >> pfrom->fRelayTxes; // set to true after we get the first filter* message
+ else
+ pfrom->fRelayTxes = true;
+ }
+
+ // Disconnect if we connected to ourself
+ if (pfrom->fInbound && !connman.CheckIncomingNonce(nNonce))
+ {
+ LogPrintf("connected to self at %s, disconnecting\n", pfrom->addr.ToString());
+ pfrom->fDisconnect = true;
+ return true;
+ }
+
+ pfrom->addrLocal = addrMe;
+ if (pfrom->fInbound && addrMe.IsRoutable())
+ {
+ SeenLocal(addrMe);
+ }
+
+ // Be shy and don't send version until we hear
+ if (pfrom->fInbound)
+ PushNodeVersion(pfrom, connman, GetAdjustedTime());
+
+ pfrom->fClient = !(pfrom->nServices & NODE_NETWORK);
+
+ if((pfrom->nServices & NODE_WITNESS))
+ {
+ LOCK(cs_main);
+ State(pfrom->GetId())->fHaveWitness = true;
+ }
+
+ // Potentially mark this peer as a preferred download peer.
+ {
+ LOCK(cs_main);
+ UpdatePreferredDownload(pfrom, State(pfrom->GetId()));
+ }
+
+ // Change version
+ connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK));
+ int nSendVersion = std::min(pfrom->nVersion, PROTOCOL_VERSION);
+ pfrom->SetSendVersion(nSendVersion);
+
+ if (!pfrom->fInbound)
+ {
+ // Advertise our address
+ if (fListen && !IsInitialBlockDownload())
+ {
+ CAddress addr = GetLocalAddress(&pfrom->addr, pfrom->GetLocalServices());
+ FastRandomContext insecure_rand;
+ if (addr.IsRoutable())
+ {
+ LogPrint("net", "ProcessMessages: advertising address %s\n", addr.ToString());
+ pfrom->PushAddress(addr, insecure_rand);
+ } else if (IsPeerAddrLocalGood(pfrom)) {
+ addr.SetIP(pfrom->addrLocal);
+ LogPrint("net", "ProcessMessages: advertising address %s\n", addr.ToString());
+ pfrom->PushAddress(addr, insecure_rand);
+ }
+ }
+
+ // Get recent addresses
+ if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || connman.GetAddressCount() < 1000)
+ {
+ connman.PushMessage(pfrom, CNetMsgMaker(nSendVersion).Make(NetMsgType::GETADDR));
+ pfrom->fGetAddr = true;
+ }
+ connman.MarkAddressGood(pfrom->addr);
+ }
+
+ pfrom->fSuccessfullyConnected = true;
+
+ string remoteAddr;
+ if (fLogIPs)
+ remoteAddr = ", peeraddr=" + pfrom->addr.ToString();
+
+ LogPrintf("receive version message: %s: version %d, blocks=%d, us=%s, peer=%d%s\n",
+ pfrom->cleanSubVer, pfrom->nVersion,
+ pfrom->nStartingHeight, addrMe.ToString(), pfrom->id,
+ remoteAddr);
+
+ int64_t nTimeOffset = nTime - GetTime();
+ pfrom->nTimeOffset = nTimeOffset;
+ AddTimeData(pfrom->addr, nTimeOffset);
+
+ // Feeler connections exist only to verify if address is online.
+ if (pfrom->fFeeler) {
+ assert(pfrom->fInbound == false);
+ pfrom->fDisconnect = true;
+ }
+ return true;
+ }
+
+
+ else if (pfrom->nVersion == 0)
+ {
+ // Must have a version message before anything else
+ LOCK(cs_main);
+ Misbehaving(pfrom->GetId(), 1);
+ return false;
+ }
+
+ // At this point, the outgoing message serialization version can't change.
+ CNetMsgMaker msgMaker(pfrom->GetSendVersion());
+
+ if (strCommand == NetMsgType::VERACK)
+ {
+ pfrom->SetRecvVersion(min(pfrom->nVersion, PROTOCOL_VERSION));
+
+ if (!pfrom->fInbound) {
+ // Mark this node as currently connected, so we update its timestamp later.
+ LOCK(cs_main);
+ State(pfrom->GetId())->fCurrentlyConnected = true;
+ }
+
+ if (pfrom->nVersion >= SENDHEADERS_VERSION) {
+ // Tell our peer we prefer to receive headers rather than inv's
+ // We send this to non-NODE NETWORK peers as well, because even
+ // non-NODE NETWORK peers can announce blocks (such as pruning
+ // nodes)
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDHEADERS));
+ }
+ if (pfrom->nVersion >= SHORT_IDS_BLOCKS_VERSION) {
+ // Tell our peer we are willing to provide version 1 or 2 cmpctblocks
+ // However, we do not request new block announcements using
+ // cmpctblock messages.
+ // We send this to non-NODE NETWORK peers as well, because
+ // they may wish to request compact blocks from us
+ bool fAnnounceUsingCMPCTBLOCK = false;
+ uint64_t nCMPCTBLOCKVersion = 2;
+ if (pfrom->GetLocalServices() & NODE_WITNESS)
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
+ nCMPCTBLOCKVersion = 1;
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
+ }
+ }
+
+
+ else if (strCommand == NetMsgType::ADDR)
+ {
+ vector<CAddress> vAddr;
+ vRecv >> vAddr;
+
+ // Don't want addr from older versions unless seeding
+ if (pfrom->nVersion < CADDR_TIME_VERSION && connman.GetAddressCount() > 1000)
+ return true;
+ if (vAddr.size() > 1000)
+ {
+ LOCK(cs_main);
+ Misbehaving(pfrom->GetId(), 20);
+ return error("message addr size() = %u", vAddr.size());
+ }
+
+ // Store the new addresses
+ vector<CAddress> vAddrOk;
+ int64_t nNow = GetAdjustedTime();
+ int64_t nSince = nNow - 10 * 60;
+ BOOST_FOREACH(CAddress& addr, vAddr)
+ {
+ boost::this_thread::interruption_point();
+
+ if ((addr.nServices & REQUIRED_SERVICES) != REQUIRED_SERVICES)
+ continue;
+
+ 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
+ RelayAddress(addr, fReachable, connman);
+ }
+ // Do not store addresses outside our network
+ if (fReachable)
+ vAddrOk.push_back(addr);
+ }
+ connman.AddNewAddresses(vAddrOk, pfrom->addr, 2 * 60 * 60);
+ if (vAddr.size() < 1000)
+ pfrom->fGetAddr = false;
+ if (pfrom->fOneShot)
+ pfrom->fDisconnect = true;
+ }
+
+ else if (strCommand == NetMsgType::SENDHEADERS)
+ {
+ LOCK(cs_main);
+ State(pfrom->GetId())->fPreferHeaders = true;
+ }
+
+ else if (strCommand == NetMsgType::SENDCMPCT)
+ {
+ bool fAnnounceUsingCMPCTBLOCK = false;
+ uint64_t nCMPCTBLOCKVersion = 0;
+ vRecv >> fAnnounceUsingCMPCTBLOCK >> nCMPCTBLOCKVersion;
+ if (nCMPCTBLOCKVersion == 1 || ((pfrom->GetLocalServices() & NODE_WITNESS) && nCMPCTBLOCKVersion == 2)) {
+ LOCK(cs_main);
+ // fProvidesHeaderAndIDs is used to "lock in" version of compact blocks we send (fWantsCmpctWitness)
+ if (!State(pfrom->GetId())->fProvidesHeaderAndIDs) {
+ State(pfrom->GetId())->fProvidesHeaderAndIDs = true;
+ State(pfrom->GetId())->fWantsCmpctWitness = nCMPCTBLOCKVersion == 2;
+ }
+ if (State(pfrom->GetId())->fWantsCmpctWitness == (nCMPCTBLOCKVersion == 2)) // ignore later version announces
+ State(pfrom->GetId())->fPreferHeaderAndIDs = fAnnounceUsingCMPCTBLOCK;
+ if (!State(pfrom->GetId())->fSupportsDesiredCmpctVersion) {
+ if (pfrom->GetLocalServices() & NODE_WITNESS)
+ State(pfrom->GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 2);
+ else
+ State(pfrom->GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 1);
+ }
+ }
+ }
+
+
+ else if (strCommand == NetMsgType::INV)
+ {
+ vector<CInv> vInv;
+ vRecv >> vInv;
+ if (vInv.size() > MAX_INV_SZ)
+ {
+ LOCK(cs_main);
+ Misbehaving(pfrom->GetId(), 20);
+ return error("message inv size() = %u", vInv.size());
+ }
+
+ bool fBlocksOnly = !fRelayTxes;
+
+ // Allow whitelisted peers to send data other than blocks in blocks only mode if whitelistrelay is true
+ if (pfrom->fWhitelisted && GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY))
+ fBlocksOnly = false;
+
+ LOCK(cs_main);
+
+ uint32_t nFetchFlags = GetFetchFlags(pfrom, chainActive.Tip(), chainparams.GetConsensus());
+
+ std::vector<CInv> vToFetch;
+
+ for (unsigned int nInv = 0; nInv < vInv.size(); nInv++)
+ {
+ CInv &inv = vInv[nInv];
+
+ boost::this_thread::interruption_point();
+
+ bool fAlreadyHave = AlreadyHave(inv);
+ LogPrint("net", "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->id);
+
+ if (inv.type == MSG_TX) {
+ inv.type |= nFetchFlags;
+ }
+
+ if (inv.type == MSG_BLOCK) {
+ UpdateBlockAvailability(pfrom->GetId(), inv.hash);
+ if (!fAlreadyHave && !fImporting && !fReindex && !mapBlocksInFlight.count(inv.hash)) {
+ // We used to request the full block here, but since headers-announcements are now the
+ // primary method of announcement on the network, and since, in the case that a node
+ // fell back to inv we probably have a reorg which we should get the headers for first,
+ // we now only provide a getheaders response here. When we receive the headers, we will
+ // then ask for the blocks we need.
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), inv.hash));
+ LogPrint("net", "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, inv.hash.ToString(), pfrom->id);
+ }
+ }
+ else
+ {
+ pfrom->AddInventoryKnown(inv);
+ if (fBlocksOnly)
+ LogPrint("net", "transaction (%s) inv sent in violation of protocol peer=%d\n", inv.hash.ToString(), pfrom->id);
+ else if (!fAlreadyHave && !fImporting && !fReindex && !IsInitialBlockDownload())
+ pfrom->AskFor(inv);
+ }
+
+ // Track requests for our stuff
+ GetMainSignals().Inventory(inv.hash);
+
+ if (pfrom->nSendSize > (nMaxSendBufferSize * 2)) {
+ Misbehaving(pfrom->GetId(), 50);
+ return error("send buffer size() = %u", pfrom->nSendSize);
+ }
+ }
+
+ if (!vToFetch.empty())
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vToFetch));
+ }
+
+
+ else if (strCommand == NetMsgType::GETDATA)
+ {
+ vector<CInv> vInv;
+ vRecv >> vInv;
+ if (vInv.size() > MAX_INV_SZ)
+ {
+ LOCK(cs_main);
+ Misbehaving(pfrom->GetId(), 20);
+ return error("message getdata size() = %u", vInv.size());
+ }
+
+ if (fDebug || (vInv.size() != 1))
+ LogPrint("net", "received getdata (%u invsz) peer=%d\n", vInv.size(), pfrom->id);
+
+ if ((fDebug && vInv.size() > 0) || (vInv.size() == 1))
+ LogPrint("net", "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom->id);
+
+ pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end());
+ ProcessGetData(pfrom, chainparams.GetConsensus(), connman);
+ }
+
+
+ else if (strCommand == NetMsgType::GETBLOCKS)
+ {
+ CBlockLocator locator;
+ uint256 hashStop;
+ vRecv >> locator >> hashStop;
+
+ LOCK(cs_main);
+
+ // Find the last block the caller has in the main chain
+ CBlockIndex* pindex = FindForkInGlobalIndex(chainActive, locator);
+
+ // Send the rest of the chain
+ if (pindex)
+ pindex = chainActive.Next(pindex);
+ int nLimit = 500;
+ LogPrint("net", "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), nLimit, pfrom->id);
+ for (; pindex; pindex = chainActive.Next(pindex))
+ {
+ if (pindex->GetBlockHash() == hashStop)
+ {
+ LogPrint("net", " getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
+ break;
+ }
+ // If pruning, don't inv blocks unless we have on disk and are likely to still have
+ // for some reasonable time window (1 hour) that block relay might require.
+ const int nPrunedBlocksLikelyToHave = MIN_BLOCKS_TO_KEEP - 3600 / chainparams.GetConsensus().nPowTargetSpacing;
+ if (fPruneMode && (!(pindex->nStatus & BLOCK_HAVE_DATA) || pindex->nHeight <= chainActive.Tip()->nHeight - nPrunedBlocksLikelyToHave))
+ {
+ LogPrint("net", " getblocks stopping, pruned or too old block at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
+ break;
+ }
+ pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash()));
+ if (--nLimit <= 0)
+ {
+ // When this block is requested, we'll send an inv that'll
+ // trigger the peer to getblocks the next batch of inventory.
+ LogPrint("net", " getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
+ pfrom->hashContinue = pindex->GetBlockHash();
+ break;
+ }
+ }
+ }
+
+
+ else if (strCommand == NetMsgType::GETBLOCKTXN)
+ {
+ BlockTransactionsRequest req;
+ vRecv >> req;
+
+ LOCK(cs_main);
+
+ BlockMap::iterator it = mapBlockIndex.find(req.blockhash);
+ if (it == mapBlockIndex.end() || !(it->second->nStatus & BLOCK_HAVE_DATA)) {
+ LogPrintf("Peer %d sent us a getblocktxn for a block we don't have", pfrom->id);
+ return true;
+ }
+
+ if (it->second->nHeight < chainActive.Height() - MAX_BLOCKTXN_DEPTH) {
+ // If an older block is requested (should never happen in practice,
+ // but can happen in tests) send a block response instead of a
+ // blocktxn response. Sending a full block response instead of a
+ // small blocktxn response is preferable in the case where a peer
+ // might maliciously send lots of getblocktxn requests to trigger
+ // expensive disk reads, because it will require the peer to
+ // actually receive all the data read from disk over the network.
+ LogPrint("net", "Peer %d sent us a getblocktxn for a block > %i deep", pfrom->id, MAX_BLOCKTXN_DEPTH);
+ CInv inv;
+ inv.type = State(pfrom->GetId())->fWantsCmpctWitness ? MSG_WITNESS_BLOCK : MSG_BLOCK;
+ inv.hash = req.blockhash;
+ pfrom->vRecvGetData.push_back(inv);
+ ProcessGetData(pfrom, chainparams.GetConsensus(), connman);
+ return true;
+ }
+
+ CBlock block;
+ bool ret = ReadBlockFromDisk(block, it->second, chainparams.GetConsensus());
+ assert(ret);
+
+ BlockTransactions resp(req);
+ for (size_t i = 0; i < req.indexes.size(); i++) {
+ if (req.indexes[i] >= block.vtx.size()) {
+ Misbehaving(pfrom->GetId(), 100);
+ LogPrintf("Peer %d sent us a getblocktxn with out-of-bounds tx indices", pfrom->id);
+ return true;
+ }
+ resp.txn[i] = block.vtx[req.indexes[i]];
+ }
+ int nSendFlags = State(pfrom->GetId())->fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
+ connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp));
+ }
+
+
+ else if (strCommand == NetMsgType::GETHEADERS)
+ {
+ CBlockLocator locator;
+ uint256 hashStop;
+ vRecv >> locator >> hashStop;
+
+ LOCK(cs_main);
+ if (IsInitialBlockDownload() && !pfrom->fWhitelisted) {
+ LogPrint("net", "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom->id);
+ return true;
+ }
+
+ CNodeState *nodestate = State(pfrom->GetId());
+ CBlockIndex* pindex = NULL;
+ if (locator.IsNull())
+ {
+ // If locator is null, return the hashStop block
+ BlockMap::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 = FindForkInGlobalIndex(chainActive, locator);
+ if (pindex)
+ pindex = chainActive.Next(pindex);
+ }
+
+ // we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end
+ vector<CBlock> vHeaders;
+ int nLimit = MAX_HEADERS_RESULTS;
+ LogPrint("net", "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), pfrom->id);
+ for (; pindex; pindex = chainActive.Next(pindex))
+ {
+ vHeaders.push_back(pindex->GetBlockHeader());
+ if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop)
+ break;
+ }
+ // pindex can be NULL either if we sent chainActive.Tip() OR
+ // if our peer has chainActive.Tip() (and thus we are sending an empty
+ // headers message). In both cases it's safe to update
+ // pindexBestHeaderSent to be our tip.
+ nodestate->pindexBestHeaderSent = pindex ? pindex : chainActive.Tip();
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
+ }
+
+
+ else if (strCommand == NetMsgType::TX)
+ {
+ // Stop processing the transaction early if
+ // We are in blocks only mode and peer is either not whitelisted or whitelistrelay is off
+ if (!fRelayTxes && (!pfrom->fWhitelisted || !GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)))
+ {
+ LogPrint("net", "transaction sent in violation of protocol peer=%d\n", pfrom->id);
+ return true;
+ }
+
+ deque<COutPoint> vWorkQueue;
+ vector<uint256> vEraseQueue;
+ CTransaction tx(deserialize, vRecv);
+
+ CInv inv(MSG_TX, tx.GetHash());
+ pfrom->AddInventoryKnown(inv);
+
+ LOCK(cs_main);
+
+ bool fMissingInputs = false;
+ CValidationState state;
+
+ pfrom->setAskFor.erase(inv.hash);
+ mapAlreadyAskedFor.erase(inv.hash);
+
+ if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs)) {
+ mempool.check(pcoinsTip);
+ RelayTransaction(tx, connman);
+ for (unsigned int i = 0; i < tx.vout.size(); i++) {
+ vWorkQueue.emplace_back(inv.hash, i);
+ }
+
+ pfrom->nLastTXTime = GetTime();
+
+ LogPrint("mempool", "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n",
+ pfrom->id,
+ tx.GetHash().ToString(),
+ mempool.size(), mempool.DynamicMemoryUsage() / 1000);
+
+ // Recursively process any orphan transactions that depended on this one
+ set<NodeId> setMisbehaving;
+ while (!vWorkQueue.empty()) {
+ auto itByPrev = mapOrphanTransactionsByPrev.find(vWorkQueue.front());
+ vWorkQueue.pop_front();
+ if (itByPrev == mapOrphanTransactionsByPrev.end())
+ continue;
+ for (auto mi = itByPrev->second.begin();
+ mi != itByPrev->second.end();
+ ++mi)
+ {
+ const CTransaction& orphanTx = (*mi)->second.tx;
+ const uint256& orphanHash = orphanTx.GetHash();
+ NodeId fromPeer = (*mi)->second.fromPeer;
+ 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 (setMisbehaving.count(fromPeer))
+ continue;
+ if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2)) {
+ LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString());
+ RelayTransaction(orphanTx, connman);
+ for (unsigned int i = 0; i < orphanTx.vout.size(); i++) {
+ vWorkQueue.emplace_back(orphanHash, i);
+ }
+ vEraseQueue.push_back(orphanHash);
+ }
+ else if (!fMissingInputs2)
+ {
+ int nDos = 0;
+ if (stateDummy.IsInvalid(nDos) && nDos > 0)
+ {
+ // Punish peer that gave us an invalid orphan tx
+ Misbehaving(fromPeer, nDos);
+ setMisbehaving.insert(fromPeer);
+ LogPrint("mempool", " invalid orphan tx %s\n", orphanHash.ToString());
+ }
+ // Has inputs but not accepted to mempool
+ // Probably non-standard or insufficient fee/priority
+ LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString());
+ vEraseQueue.push_back(orphanHash);
+ if (orphanTx.wit.IsNull() && !stateDummy.CorruptionPossible()) {
+ // Do not use rejection cache for witness transactions or
+ // witness-stripped transactions, as they can have been malleated.
+ // See https://github.com/bitcoin/bitcoin/issues/8279 for details.
+ assert(recentRejects);
+ recentRejects->insert(orphanHash);
+ }
+ }
+ mempool.check(pcoinsTip);
+ }
+ }
+
+ BOOST_FOREACH(uint256 hash, vEraseQueue)
+ EraseOrphanTx(hash);
+ }
+ else if (fMissingInputs)
+ {
+ bool fRejectedParents = false; // It may be the case that the orphans parents have all been rejected
+ BOOST_FOREACH(const CTxIn& txin, tx.vin) {
+ if (recentRejects->contains(txin.prevout.hash)) {
+ fRejectedParents = true;
+ break;
+ }
+ }
+ if (!fRejectedParents) {
+ uint32_t nFetchFlags = GetFetchFlags(pfrom, chainActive.Tip(), chainparams.GetConsensus());
+ BOOST_FOREACH(const CTxIn& txin, tx.vin) {
+ CInv _inv(MSG_TX | nFetchFlags, txin.prevout.hash);
+ pfrom->AddInventoryKnown(_inv);
+ if (!AlreadyHave(_inv)) pfrom->AskFor(_inv);
+ }
+ AddOrphanTx(tx, pfrom->GetId());
+
+ // DoS prevention: do not allow mapOrphanTransactions to grow unbounded
+ unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS));
+ unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx);
+ if (nEvicted > 0)
+ LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted);
+ } else {
+ LogPrint("mempool", "not keeping orphan with rejected parents %s\n",tx.GetHash().ToString());
+ }
+ } else {
+ if (tx.wit.IsNull() && !state.CorruptionPossible()) {
+ // Do not use rejection cache for witness transactions or
+ // witness-stripped transactions, as they can have been malleated.
+ // See https://github.com/bitcoin/bitcoin/issues/8279 for details.
+ assert(recentRejects);
+ recentRejects->insert(tx.GetHash());
+ }
+
+ if (pfrom->fWhitelisted && GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) {
+ // Always relay transactions received from whitelisted peers, even
+ // if they were already in the mempool or rejected from it due
+ // to policy, allowing the node to function as a gateway for
+ // nodes hidden behind it.
+ //
+ // Never relay transactions that we would assign a non-zero DoS
+ // score for, as we expect peers to do the same with us in that
+ // case.
+ int nDoS = 0;
+ if (!state.IsInvalid(nDoS) || nDoS == 0) {
+ LogPrintf("Force relaying tx %s from whitelisted peer=%d\n", tx.GetHash().ToString(), pfrom->id);
+ RelayTransaction(tx, connman);
+ } else {
+ LogPrintf("Not relaying invalid transaction %s from whitelisted peer=%d (%s)\n", tx.GetHash().ToString(), pfrom->id, FormatStateMessage(state));
+ }
+ }
+ }
+ int nDoS = 0;
+ if (state.IsInvalid(nDoS))
+ {
+ LogPrint("mempoolrej", "%s from peer=%d was not accepted: %s\n", tx.GetHash().ToString(),
+ pfrom->id,
+ FormatStateMessage(state));
+ if (state.GetRejectCode() < REJECT_INTERNAL) // Never send AcceptToMemoryPool's internal codes over P2P
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(),
+ state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash));
+ if (nDoS > 0) {
+ Misbehaving(pfrom->GetId(), nDoS);
+ }
+ }
+ }
+
+
+ else if (strCommand == NetMsgType::CMPCTBLOCK && !fImporting && !fReindex) // Ignore blocks received while importing
+ {
+ CBlockHeaderAndShortTxIDs cmpctblock;
+ vRecv >> cmpctblock;
+
+ {
+ LOCK(cs_main);
+
+ if (mapBlockIndex.find(cmpctblock.header.hashPrevBlock) == mapBlockIndex.end()) {
+ // Doesn't connect (or is genesis), instead of DoSing in AcceptBlockHeader, request deeper headers
+ if (!IsInitialBlockDownload())
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256()));
+ return true;
+ }
+ }
+
+ CBlockIndex *pindex = NULL;
+ CValidationState state;
+ if (!ProcessNewBlockHeaders({cmpctblock.header}, state, chainparams, &pindex)) {
+ int nDoS;
+ if (state.IsInvalid(nDoS)) {
+ if (nDoS > 0) {
+ LOCK(cs_main);
+ Misbehaving(pfrom->GetId(), nDoS);
+ }
+ LogPrintf("Peer %d sent us invalid header via cmpctblock\n", pfrom->id);
+ return true;
+ }
+ }
+
+ // Keep a CBlock for "optimistic" compactblock reconstructions (see
+ // below)
+ std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
+ bool fBlockReconstructed = false;
+
+ LOCK(cs_main);
+ // If AcceptBlockHeader returned true, it set pindex
+ assert(pindex);
+ UpdateBlockAvailability(pfrom->GetId(), pindex->GetBlockHash());
+
+ std::map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator blockInFlightIt = mapBlocksInFlight.find(pindex->GetBlockHash());
+ bool fAlreadyInFlight = blockInFlightIt != mapBlocksInFlight.end();
+
+ if (pindex->nStatus & BLOCK_HAVE_DATA) // Nothing to do here
+ return true;
+
+ if (pindex->nChainWork <= chainActive.Tip()->nChainWork || // We know something better
+ pindex->nTx != 0) { // We had this block at some point, but pruned it
+ if (fAlreadyInFlight) {
+ // We requested this block for some reason, but our mempool will probably be useless
+ // so we just grab the block via normal getdata
+ std::vector<CInv> vInv(1);
+ vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom, pindex->pprev, chainparams.GetConsensus()), cmpctblock.header.GetHash());
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
+ }
+ return true;
+ }
+
+ // If we're not close to tip yet, give up and let parallel block fetch work its magic
+ if (!fAlreadyInFlight && !CanDirectFetch(chainparams.GetConsensus()))
+ return true;
+
+ CNodeState *nodestate = State(pfrom->GetId());
+
+ if (IsWitnessEnabled(pindex->pprev, chainparams.GetConsensus()) && !nodestate->fSupportsDesiredCmpctVersion) {
+ // Don't bother trying to process compact blocks from v1 peers
+ // after segwit activates.
+ return true;
+ }
+
+ // We want to be a bit conservative just to be extra careful about DoS
+ // possibilities in compact block processing...
+ if (pindex->nHeight <= chainActive.Height() + 2) {
+ if ((!fAlreadyInFlight && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) ||
+ (fAlreadyInFlight && blockInFlightIt->second.first == pfrom->GetId())) {
+ list<QueuedBlock>::iterator *queuedBlockIt = NULL;
+ if (!MarkBlockAsInFlight(pfrom->GetId(), pindex->GetBlockHash(), chainparams.GetConsensus(), pindex, &queuedBlockIt)) {
+ if (!(*queuedBlockIt)->partialBlock)
+ (*queuedBlockIt)->partialBlock.reset(new PartiallyDownloadedBlock(&mempool));
+ else {
+ // The block was already in flight using compact blocks from the same peer
+ LogPrint("net", "Peer sent us compact block we were already syncing!\n");
+ return true;
+ }
+ }
+
+ PartiallyDownloadedBlock& partialBlock = *(*queuedBlockIt)->partialBlock;
+ ReadStatus status = partialBlock.InitData(cmpctblock);
+ if (status == READ_STATUS_INVALID) {
+ MarkBlockAsReceived(pindex->GetBlockHash()); // Reset in-flight state in case of whitelist
+ Misbehaving(pfrom->GetId(), 100);
+ LogPrintf("Peer %d sent us invalid compact block\n", pfrom->id);
+ return true;
+ } else if (status == READ_STATUS_FAILED) {
+ // Duplicate txindexes, the block is now in-flight, so just request it
+ std::vector<CInv> vInv(1);
+ vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom, pindex->pprev, chainparams.GetConsensus()), cmpctblock.header.GetHash());
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
+ return true;
+ }
+
+ if (!fAlreadyInFlight && mapBlocksInFlight.size() == 1 && pindex->pprev->IsValid(BLOCK_VALID_CHAIN)) {
+ // We seem to be rather well-synced, so it appears pfrom was the first to provide us
+ // with this block! Let's get them to announce using compact blocks in the future.
+ MaybeSetPeerAsAnnouncingHeaderAndIDs(nodestate, pfrom, connman);
+ }
+
+ BlockTransactionsRequest req;
+ for (size_t i = 0; i < cmpctblock.BlockTxCount(); i++) {
+ if (!partialBlock.IsTxAvailable(i))
+ req.indexes.push_back(i);
+ }
+ if (req.indexes.empty()) {
+ // Dirty hack to jump to BLOCKTXN code (TODO: move message handling into their own functions)
+ BlockTransactions txn;
+ txn.blockhash = cmpctblock.header.GetHash();
+ CDataStream blockTxnMsg(SER_NETWORK, PROTOCOL_VERSION);
+ blockTxnMsg << txn;
+ return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, nTimeReceived, chainparams, connman);
+ } else {
+ req.blockhash = pindex->GetBlockHash();
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETBLOCKTXN, req));
+ }
+ } else {
+ // This block is either already in flight from a different
+ // peer, or this peer has too many blocks outstanding to
+ // download from.
+ // Optimistically try to reconstruct anyway since we might be
+ // able to without any round trips.
+ PartiallyDownloadedBlock tempBlock(&mempool);
+ ReadStatus status = tempBlock.InitData(cmpctblock);
+ if (status != READ_STATUS_OK) {
+ // TODO: don't ignore failures
+ return true;
+ }
+ std::vector<CTransactionRef> dummy;
+ status = tempBlock.FillBlock(*pblock, dummy);
+ if (status == READ_STATUS_OK) {
+ fBlockReconstructed = true;
+ }
+ }
+ } else {
+ if (fAlreadyInFlight) {
+ // We requested this block, but its far into the future, so our
+ // mempool will probably be useless - request the block normally
+ std::vector<CInv> vInv(1);
+ vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom, pindex->pprev, chainparams.GetConsensus()), cmpctblock.header.GetHash());
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
+ return true;
+ } else {
+ // If this was an announce-cmpctblock, we want the same treatment as a header message
+ // Dirty hack to process as if it were just a headers message (TODO: move message handling into their own functions)
+ std::vector<CBlock> headers;
+ headers.push_back(cmpctblock.header);
+ CDataStream vHeadersMsg(SER_NETWORK, PROTOCOL_VERSION);
+ vHeadersMsg << headers;
+ return ProcessMessage(pfrom, NetMsgType::HEADERS, vHeadersMsg, nTimeReceived, chainparams, connman);
+ }
+ }
+
+ if (fBlockReconstructed) {
+ // If we got here, we were able to optimistically reconstruct a
+ // block that is in flight from some other peer.
+ {
+ LOCK(cs_main);
+ mapBlockSource.emplace(pblock->GetHash(), std::make_pair(pfrom->GetId(), false));
+ }
+ bool fNewBlock = false;
+ ProcessNewBlock(chainparams, pblock, true, &fNewBlock);
+ if (fNewBlock)
+ pfrom->nLastBlockTime = GetTime();
+
+ LOCK(cs_main); // hold cs_main for CBlockIndex::IsValid()
+ if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS)) {
+ // Clear download state for this block, which is in
+ // process from some other peer. We do this after calling
+ // ProcessNewBlock so that a malleated cmpctblock announcement
+ // can't be used to interfere with block relay.
+ MarkBlockAsReceived(pblock->GetHash());
+ }
+ }
+
+ }
+
+ else if (strCommand == NetMsgType::BLOCKTXN && !fImporting && !fReindex) // Ignore blocks received while importing
+ {
+ BlockTransactions resp;
+ vRecv >> resp;
+
+ std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
+ bool fBlockRead = false;
+ {
+ LOCK(cs_main);
+
+ map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator it = mapBlocksInFlight.find(resp.blockhash);
+ if (it == mapBlocksInFlight.end() || !it->second.second->partialBlock ||
+ it->second.first != pfrom->GetId()) {
+ LogPrint("net", "Peer %d sent us block transactions for block we weren't expecting\n", pfrom->id);
+ return true;
+ }
+
+ PartiallyDownloadedBlock& partialBlock = *it->second.second->partialBlock;
+ ReadStatus status = partialBlock.FillBlock(*pblock, resp.txn);
+ if (status == READ_STATUS_INVALID) {
+ MarkBlockAsReceived(resp.blockhash); // Reset in-flight state in case of whitelist
+ Misbehaving(pfrom->GetId(), 100);
+ LogPrintf("Peer %d sent us invalid compact block/non-matching block transactions\n", pfrom->id);
+ return true;
+ } else if (status == READ_STATUS_FAILED) {
+ // Might have collided, fall back to getdata now :(
+ std::vector<CInv> invs;
+ invs.push_back(CInv(MSG_BLOCK | GetFetchFlags(pfrom, chainActive.Tip(), chainparams.GetConsensus()), resp.blockhash));
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, invs));
+ } else {
+ // Block is either okay, or possibly we received
+ // READ_STATUS_CHECKBLOCK_FAILED.
+ // Note that CheckBlock can only fail for one of a few reasons:
+ // 1. bad-proof-of-work (impossible here, because we've already
+ // accepted the header)
+ // 2. merkleroot doesn't match the transactions given (already
+ // caught in FillBlock with READ_STATUS_FAILED, so
+ // impossible here)
+ // 3. the block is otherwise invalid (eg invalid coinbase,
+ // block is too big, too many legacy sigops, etc).
+ // So if CheckBlock failed, #3 is the only possibility.
+ // Under BIP 152, we don't DoS-ban unless proof of work is
+ // invalid (we don't require all the stateless checks to have
+ // been run). This is handled below, so just treat this as
+ // though the block was successfully read, and rely on the
+ // handling in ProcessNewBlock to ensure the block index is
+ // updated, reject messages go out, etc.
+ MarkBlockAsReceived(resp.blockhash); // it is now an empty pointer
+ fBlockRead = true;
+ // mapBlockSource is only used for sending reject messages and DoS scores,
+ // so the race between here and cs_main in ProcessNewBlock is fine.
+ // BIP 152 permits peers to relay compact blocks after validating
+ // the header only; we should not punish peers if the block turns
+ // out to be invalid.
+ mapBlockSource.emplace(resp.blockhash, std::make_pair(pfrom->GetId(), false));
+ }
+ } // Don't hold cs_main when we call into ProcessNewBlock
+ if (fBlockRead) {
+ bool fNewBlock = false;
+ // Since we requested this block (it was in mapBlocksInFlight), force it to be processed,
+ // even if it would not be a candidate for new tip (missing previous block, chain not long enough, etc)
+ ProcessNewBlock(chainparams, pblock, true, &fNewBlock);
+ if (fNewBlock)
+ pfrom->nLastBlockTime = GetTime();
+ }
+ }
+
+
+ else if (strCommand == NetMsgType::HEADERS && !fImporting && !fReindex) // Ignore headers received while importing
+ {
+ std::vector<CBlockHeader> headers;
+
+ // Bypass the normal CBlock deserialization, as we don't want to risk deserializing 2000 full blocks.
+ unsigned int nCount = ReadCompactSize(vRecv);
+ if (nCount > MAX_HEADERS_RESULTS) {
+ LOCK(cs_main);
+ Misbehaving(pfrom->GetId(), 20);
+ return error("headers message size = %u", nCount);
+ }
+ headers.resize(nCount);
+ for (unsigned int n = 0; n < nCount; n++) {
+ vRecv >> headers[n];
+ ReadCompactSize(vRecv); // ignore tx count; assume it is 0.
+ }
+
+ if (nCount == 0) {
+ // Nothing interesting. Stop asking this peers for more headers.
+ return true;
+ }
+
+ CBlockIndex *pindexLast = NULL;
+ {
+ LOCK(cs_main);
+ CNodeState *nodestate = State(pfrom->GetId());
+
+ // If this looks like it could be a block announcement (nCount <
+ // MAX_BLOCKS_TO_ANNOUNCE), use special logic for handling headers that
+ // don't connect:
+ // - Send a getheaders message in response to try to connect the chain.
+ // - The peer can send up to MAX_UNCONNECTING_HEADERS in a row that
+ // don't connect before giving DoS points
+ // - Once a headers message is received that is valid and does connect,
+ // nUnconnectingHeaders gets reset back to 0.
+ if (mapBlockIndex.find(headers[0].hashPrevBlock) == mapBlockIndex.end() && nCount < MAX_BLOCKS_TO_ANNOUNCE) {
+ nodestate->nUnconnectingHeaders++;
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256()));
+ LogPrint("net", "received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, nUnconnectingHeaders=%d)\n",
+ headers[0].GetHash().ToString(),
+ headers[0].hashPrevBlock.ToString(),
+ pindexBestHeader->nHeight,
+ pfrom->id, nodestate->nUnconnectingHeaders);
+ // Set hashLastUnknownBlock for this peer, so that if we
+ // eventually get the headers - even from a different peer -
+ // we can use this peer to download.
+ UpdateBlockAvailability(pfrom->GetId(), headers.back().GetHash());
+
+ if (nodestate->nUnconnectingHeaders % MAX_UNCONNECTING_HEADERS == 0) {
+ Misbehaving(pfrom->GetId(), 20);
+ }
+ return true;
+ }
+
+ uint256 hashLastBlock;
+ for (const CBlockHeader& header : headers) {
+ if (!hashLastBlock.IsNull() && header.hashPrevBlock != hashLastBlock) {
+ Misbehaving(pfrom->GetId(), 20);
+ return error("non-continuous headers sequence");
+ }
+ hashLastBlock = header.GetHash();
+ }
+ }
+
+ CValidationState state;
+ if (!ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast)) {
+ int nDoS;
+ if (state.IsInvalid(nDoS)) {
+ if (nDoS > 0) {
+ LOCK(cs_main);
+ Misbehaving(pfrom->GetId(), nDoS);
+ }
+ return error("invalid header received");
+ }
+ }
+
+ {
+ LOCK(cs_main);
+ CNodeState *nodestate = State(pfrom->GetId());
+ if (nodestate->nUnconnectingHeaders > 0) {
+ LogPrint("net", "peer=%d: resetting nUnconnectingHeaders (%d -> 0)\n", pfrom->id, nodestate->nUnconnectingHeaders);
+ }
+ nodestate->nUnconnectingHeaders = 0;
+
+ assert(pindexLast);
+ UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash());
+
+ if (nCount == MAX_HEADERS_RESULTS) {
+ // Headers message had its maximum size; the peer may have more headers.
+ // TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue
+ // from there instead.
+ LogPrint("net", "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->id, pfrom->nStartingHeight);
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexLast), uint256()));
+ }
+
+ bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus());
+ // If this set of headers is valid and ends in a block with at least as
+ // much work as our tip, download as much as possible.
+ if (fCanDirectFetch && pindexLast->IsValid(BLOCK_VALID_TREE) && chainActive.Tip()->nChainWork <= pindexLast->nChainWork) {
+ vector<CBlockIndex *> vToFetch;
+ CBlockIndex *pindexWalk = pindexLast;
+ // Calculate all the blocks we'd need to switch to pindexLast, up to a limit.
+ while (pindexWalk && !chainActive.Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
+ if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) &&
+ !mapBlocksInFlight.count(pindexWalk->GetBlockHash()) &&
+ (!IsWitnessEnabled(pindexWalk->pprev, chainparams.GetConsensus()) || State(pfrom->GetId())->fHaveWitness)) {
+ // We don't have this block, and it's not yet in flight.
+ vToFetch.push_back(pindexWalk);
+ }
+ pindexWalk = pindexWalk->pprev;
+ }
+ // If pindexWalk still isn't on our main chain, we're looking at a
+ // very large reorg at a time we think we're close to caught up to
+ // the main chain -- this shouldn't really happen. Bail out on the
+ // direct fetch and rely on parallel download instead.
+ if (!chainActive.Contains(pindexWalk)) {
+ LogPrint("net", "Large reorg, won't direct fetch to %s (%d)\n",
+ pindexLast->GetBlockHash().ToString(),
+ pindexLast->nHeight);
+ } else {
+ vector<CInv> vGetData;
+ // Download as much as possible, from earliest to latest.
+ BOOST_REVERSE_FOREACH(CBlockIndex *pindex, vToFetch) {
+ if (nodestate->nBlocksInFlight >= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
+ // Can't download any more from this peer
+ break;
+ }
+ uint32_t nFetchFlags = GetFetchFlags(pfrom, pindex->pprev, chainparams.GetConsensus());
+ vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
+ MarkBlockAsInFlight(pfrom->GetId(), pindex->GetBlockHash(), chainparams.GetConsensus(), pindex);
+ LogPrint("net", "Requesting block %s from peer=%d\n",
+ pindex->GetBlockHash().ToString(), pfrom->id);
+ }
+ if (vGetData.size() > 1) {
+ LogPrint("net", "Downloading blocks toward %s (%d) via headers direct fetch\n",
+ pindexLast->GetBlockHash().ToString(), pindexLast->nHeight);
+ }
+ if (vGetData.size() > 0) {
+ if (nodestate->fSupportsDesiredCmpctVersion && vGetData.size() == 1 && mapBlocksInFlight.size() == 1 && pindexLast->pprev->IsValid(BLOCK_VALID_CHAIN)) {
+ // We seem to be rather well-synced, so it appears pfrom was the first to provide us
+ // with this block! Let's get them to announce using compact blocks in the future.
+ MaybeSetPeerAsAnnouncingHeaderAndIDs(nodestate, pfrom, connman);
+ // In any case, we want to download using a compact block, not a regular one
+ vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash);
+ }
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData));
+ }
+ }
+ }
+ }
+ }
+
+ else if (strCommand == NetMsgType::BLOCK && !fImporting && !fReindex) // Ignore blocks received while importing
+ {
+ std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
+ vRecv >> *pblock;
+
+ LogPrint("net", "received block %s peer=%d\n", pblock->GetHash().ToString(), pfrom->id);
+
+ // Process all blocks from whitelisted peers, even if not requested,
+ // unless we're still syncing with the network.
+ // Such an unrequested block may still be processed, subject to the
+ // conditions in AcceptBlock().
+ bool forceProcessing = pfrom->fWhitelisted && !IsInitialBlockDownload();
+ const uint256 hash(pblock->GetHash());
+ {
+ LOCK(cs_main);
+ // Also always process if we requested the block explicitly, as we may
+ // need it even though it is not a candidate for a new best tip.
+ forceProcessing |= MarkBlockAsReceived(hash);
+ // mapBlockSource is only used for sending reject messages and DoS scores,
+ // so the race between here and cs_main in ProcessNewBlock is fine.
+ mapBlockSource.emplace(hash, std::make_pair(pfrom->GetId(), true));
+ }
+ bool fNewBlock = false;
+ ProcessNewBlock(chainparams, pblock, forceProcessing, &fNewBlock);
+ if (fNewBlock)
+ pfrom->nLastBlockTime = GetTime();
+ }
+
+
+ else if (strCommand == NetMsgType::GETADDR)
+ {
+ // This asymmetric behavior for inbound and outbound connections was introduced
+ // to prevent a fingerprinting attack: an attacker can send specific fake addresses
+ // to users' AddrMan and later request them by sending getaddr messages.
+ // Making nodes which are behind NAT and can only make outgoing connections ignore
+ // the getaddr message mitigates the attack.
+ if (!pfrom->fInbound) {
+ LogPrint("net", "Ignoring \"getaddr\" from outbound connection. peer=%d\n", pfrom->id);
+ return true;
+ }
+
+ // Only send one GetAddr response per connection to reduce resource waste
+ // and discourage addr stamping of INV announcements.
+ if (pfrom->fSentAddr) {
+ LogPrint("net", "Ignoring repeated \"getaddr\". peer=%d\n", pfrom->id);
+ return true;
+ }
+ pfrom->fSentAddr = true;
+
+ pfrom->vAddrToSend.clear();
+ vector<CAddress> vAddr = connman.GetAddresses();
+ FastRandomContext insecure_rand;
+ BOOST_FOREACH(const CAddress &addr, vAddr)
+ pfrom->PushAddress(addr, insecure_rand);
+ }
+
+
+ else if (strCommand == NetMsgType::MEMPOOL)
+ {
+ if (!(pfrom->GetLocalServices() & NODE_BLOOM) && !pfrom->fWhitelisted)
+ {
+ LogPrint("net", "mempool request with bloom filters disabled, disconnect peer=%d\n", pfrom->GetId());
+ pfrom->fDisconnect = true;
+ return true;
+ }
+
+ if (connman.OutboundTargetReached(false) && !pfrom->fWhitelisted)
+ {
+ LogPrint("net", "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom->GetId());
+ pfrom->fDisconnect = true;
+ return true;
+ }
+
+ LOCK(pfrom->cs_inventory);
+ pfrom->fSendMempool = true;
+ }
+
+
+ else if (strCommand == NetMsgType::PING)
+ {
+ if (pfrom->nVersion > BIP0031_VERSION)
+ {
+ uint64_t nonce = 0;
+ vRecv >> nonce;
+ // Echo the message back with the nonce. This allows for two useful features:
+ //
+ // 1) A remote node can quickly check if the connection is operational
+ // 2) Remote nodes can measure the latency of the network thread. If this node
+ // is overloaded it won't respond to pings quickly and the remote node can
+ // avoid sending us more work, like chain download requests.
+ //
+ // The nonce stops the remote getting confused between different pings: without
+ // it, if the remote node sends a ping once per second and this node takes 5
+ // seconds to respond to each, the 5th ping the remote sends would appear to
+ // return very quickly.
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::PONG, nonce));
+ }
+ }
+
+
+ else if (strCommand == NetMsgType::PONG)
+ {
+ int64_t pingUsecEnd = nTimeReceived;
+ uint64_t nonce = 0;
+ size_t nAvail = vRecv.in_avail();
+ bool bPingFinished = false;
+ std::string sProblem;
+
+ if (nAvail >= sizeof(nonce)) {
+ vRecv >> nonce;
+
+ // Only process pong message if there is an outstanding ping (old ping without nonce should never pong)
+ if (pfrom->nPingNonceSent != 0) {
+ if (nonce == pfrom->nPingNonceSent) {
+ // Matching pong received, this ping is no longer outstanding
+ bPingFinished = true;
+ int64_t pingUsecTime = pingUsecEnd - pfrom->nPingUsecStart;
+ if (pingUsecTime > 0) {
+ // Successful ping time measurement, replace previous
+ pfrom->nPingUsecTime = pingUsecTime;
+ pfrom->nMinPingUsecTime = std::min(pfrom->nMinPingUsecTime, pingUsecTime);
+ } else {
+ // This should never happen
+ sProblem = "Timing mishap";
+ }
+ } else {
+ // Nonce mismatches are normal when pings are overlapping
+ sProblem = "Nonce mismatch";
+ if (nonce == 0) {
+ // This is most likely a bug in another implementation somewhere; cancel this ping
+ bPingFinished = true;
+ sProblem = "Nonce zero";
+ }
+ }
+ } else {
+ sProblem = "Unsolicited pong without ping";
+ }
+ } else {
+ // This is most likely a bug in another implementation somewhere; cancel this ping
+ bPingFinished = true;
+ sProblem = "Short payload";
+ }
+
+ if (!(sProblem.empty())) {
+ LogPrint("net", "pong peer=%d: %s, %x expected, %x received, %u bytes\n",
+ pfrom->id,
+ sProblem,
+ pfrom->nPingNonceSent,
+ nonce,
+ nAvail);
+ }
+ if (bPingFinished) {
+ pfrom->nPingNonceSent = 0;
+ }
+ }
+
+
+ else if (strCommand == NetMsgType::FILTERLOAD)
+ {
+ CBloomFilter filter;
+ vRecv >> filter;
+
+ if (!filter.IsWithinSizeConstraints())
+ {
+ // There is no excuse for sending a too-large filter
+ LOCK(cs_main);
+ Misbehaving(pfrom->GetId(), 100);
+ }
+ else
+ {
+ LOCK(pfrom->cs_filter);
+ delete pfrom->pfilter;
+ pfrom->pfilter = new CBloomFilter(filter);
+ pfrom->pfilter->UpdateEmptyFull();
+ pfrom->fRelayTxes = true;
+ }
+ }
+
+
+ else if (strCommand == NetMsgType::FILTERADD)
+ {
+ vector<unsigned char> 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
+ bool bad = false;
+ if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) {
+ bad = true;
+ } else {
+ LOCK(pfrom->cs_filter);
+ if (pfrom->pfilter) {
+ pfrom->pfilter->insert(vData);
+ } else {
+ bad = true;
+ }
+ }
+ if (bad) {
+ LOCK(cs_main);
+ Misbehaving(pfrom->GetId(), 100);
+ }
+ }
+
+
+ else if (strCommand == NetMsgType::FILTERCLEAR)
+ {
+ LOCK(pfrom->cs_filter);
+ if (pfrom->GetLocalServices() & NODE_BLOOM) {
+ delete pfrom->pfilter;
+ pfrom->pfilter = new CBloomFilter();
+ }
+ pfrom->fRelayTxes = true;
+ }
+
+
+ else if (strCommand == NetMsgType::REJECT)
+ {
+ if (fDebug) {
+ try {
+ string strMsg; unsigned char ccode; string strReason;
+ vRecv >> LIMITED_STRING(strMsg, CMessageHeader::COMMAND_SIZE) >> ccode >> LIMITED_STRING(strReason, MAX_REJECT_MESSAGE_LENGTH);
+
+ ostringstream ss;
+ ss << strMsg << " code " << itostr(ccode) << ": " << strReason;
+
+ if (strMsg == NetMsgType::BLOCK || strMsg == NetMsgType::TX)
+ {
+ uint256 hash;
+ vRecv >> hash;
+ ss << ": hash " << hash.ToString();
+ }
+ LogPrint("net", "Reject %s\n", SanitizeString(ss.str()));
+ } catch (const std::ios_base::failure&) {
+ // Avoid feedback loops by preventing reject messages from triggering a new reject message.
+ LogPrint("net", "Unparseable reject message received\n");
+ }
+ }
+ }
+
+ else if (strCommand == NetMsgType::FEEFILTER) {
+ CAmount newFeeFilter = 0;
+ vRecv >> newFeeFilter;
+ if (MoneyRange(newFeeFilter)) {
+ {
+ LOCK(pfrom->cs_feeFilter);
+ pfrom->minFeeFilter = newFeeFilter;
+ }
+ LogPrint("net", "received: feefilter of %s from peer=%d\n", CFeeRate(newFeeFilter).ToString(), pfrom->id);
+ }
+ }
+
+ else if (strCommand == NetMsgType::NOTFOUND) {
+ // We do not care about the NOTFOUND message, but logging an Unknown Command
+ // message would be undesirable as we transmit it ourselves.
+ }
+
+ else {
+ // Ignore unknown commands for extensibility
+ LogPrint("net", "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->id);
+ }
+
+
+
+ return true;
+}
+
+// requires LOCK(cs_vRecvMsg)
+bool ProcessMessages(CNode* pfrom, CConnman& connman)
+{
+ const CChainParams& chainparams = Params();
+ unsigned int nMaxSendBufferSize = connman.GetSendBufferSize();
+ //if (fDebug)
+ // LogPrintf("%s(%u messages)\n", __func__, 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, chainparams.GetConsensus(), connman);
+
+ // this maintains the order of responses
+ if (!pfrom->vRecvGetData.empty()) return fOk;
+
+ std::deque<CNetMessage>::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 >= nMaxSendBufferSize)
+ break;
+
+ // get next message
+ CNetMessage& msg = *it;
+
+ //if (fDebug)
+ // LogPrintf("%s(message %u msgsz, %u bytes, complete:%s)\n", __func__,
+ // 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, chainparams.MessageStart(), CMessageHeader::MESSAGE_START_SIZE) != 0) {
+ LogPrintf("PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", SanitizeString(msg.hdr.GetCommand()), pfrom->id);
+ fOk = false;
+ break;
+ }
+
+ // Read header
+ CMessageHeader& hdr = msg.hdr;
+ if (!hdr.IsValid(chainparams.MessageStart()))
+ {
+ LogPrintf("PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(hdr.GetCommand()), pfrom->id);
+ continue;
+ }
+ string strCommand = hdr.GetCommand();
+
+ // Message size
+ unsigned int nMessageSize = hdr.nMessageSize;
+
+ // Checksum
+ CDataStream& vRecv = msg.vRecv;
+ const uint256& hash = msg.GetMessageHash();
+ if (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) != 0)
+ {
+ LogPrintf("%s(%s, %u bytes): CHECKSUM ERROR expected %s was %s\n", __func__,
+ SanitizeString(strCommand), nMessageSize,
+ HexStr(hash.begin(), hash.begin()+CMessageHeader::CHECKSUM_SIZE),
+ HexStr(hdr.pchChecksum, hdr.pchChecksum+CMessageHeader::CHECKSUM_SIZE));
+ continue;
+ }
+
+ // Process message
+ bool fRet = false;
+ try
+ {
+ fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime, chainparams, connman);
+ boost::this_thread::interruption_point();
+ }
+ catch (const std::ios_base::failure& e)
+ {
+ connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, string("error parsing message")));
+ if (strstr(e.what(), "end of data"))
+ {
+ // Allow exceptions from under-length message on vRecv
+ LogPrintf("%s(%s, %u bytes): Exception '%s' caught, normally caused by a message being shorter than its stated length\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
+ }
+ else if (strstr(e.what(), "size too large"))
+ {
+ // Allow exceptions from over-long size
+ LogPrintf("%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
+ }
+ else if (strstr(e.what(), "non-canonical ReadCompactSize()"))
+ {
+ // Allow exceptions from non-canonical encoding
+ LogPrintf("%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
+ }
+ else
+ {
+ PrintExceptionContinue(&e, "ProcessMessages()");
+ }
+ }
+ catch (const boost::thread_interrupted&) {
+ throw;
+ }
+ catch (const std::exception& e) {
+ PrintExceptionContinue(&e, "ProcessMessages()");
+ } catch (...) {
+ PrintExceptionContinue(NULL, "ProcessMessages()");
+ }
+
+ if (!fRet)
+ LogPrintf("%s(%s, %u bytes) FAILED peer=%d\n", __func__, SanitizeString(strCommand), nMessageSize, pfrom->id);
+
+ break;
+ }
+
+ // In case the connection got shut down, its receive buffer was wiped
+ if (!pfrom->fDisconnect)
+ pfrom->vRecvMsg.erase(pfrom->vRecvMsg.begin(), it);
+
+ return fOk;
+}
+
+class CompareInvMempoolOrder
+{
+ CTxMemPool *mp;
+public:
+ CompareInvMempoolOrder(CTxMemPool *_mempool)
+ {
+ mp = _mempool;
+ }
+
+ bool operator()(std::set<uint256>::iterator a, std::set<uint256>::iterator b)
+ {
+ /* As std::make_heap produces a max-heap, we want the entries with the
+ * fewest ancestors/highest fee to sort later. */
+ return mp->CompareDepthAndScore(*b, *a);
+ }
+};
+
+bool SendMessages(CNode* pto, CConnman& connman)
+{
+ const Consensus::Params& consensusParams = Params().GetConsensus();
+ {
+ // Don't send anything until we get its version message
+ if (pto->nVersion == 0 || pto->fDisconnect)
+ return true;
+
+ // If we get here, the outgoing message serialization version is set and can't change.
+ CNetMsgMaker msgMaker(pto->GetSendVersion());
+
+ //
+ // Message: ping
+ //
+ bool pingSend = false;
+ if (pto->fPingQueued) {
+ // RPC ping request by user
+ pingSend = true;
+ }
+ if (pto->nPingNonceSent == 0 && pto->nPingUsecStart + PING_INTERVAL * 1000000 < GetTimeMicros()) {
+ // Ping automatically sent as a latency probe & keepalive.
+ pingSend = true;
+ }
+ if (pingSend) {
+ uint64_t nonce = 0;
+ while (nonce == 0) {
+ GetRandBytes((unsigned char*)&nonce, sizeof(nonce));
+ }
+ pto->fPingQueued = false;
+ pto->nPingUsecStart = GetTimeMicros();
+ if (pto->nVersion > BIP0031_VERSION) {
+ pto->nPingNonceSent = nonce;
+ connman.PushMessage(pto, msgMaker.Make(NetMsgType::PING, nonce));
+ } else {
+ // Peer is too old to support ping command with nonce, pong will never arrive.
+ pto->nPingNonceSent = 0;
+ connman.PushMessage(pto, msgMaker.Make(NetMsgType::PING));
+ }
+ }
+
+ TRY_LOCK(cs_main, lockMain); // Acquire cs_main for IsInitialBlockDownload() and CNodeState()
+ if (!lockMain)
+ return true;
+
+ CNodeState &state = *State(pto->GetId());
+
+ BOOST_FOREACH(const CBlockReject& reject, state.rejects)
+ connman.PushMessage(pto, msgMaker.Make(NetMsgType::REJECT, (string)NetMsgType::BLOCK, reject.chRejectCode, reject.strRejectReason, reject.hashBlock));
+ state.rejects.clear();
+
+ if (state.fShouldBan) {
+ state.fShouldBan = false;
+ if (pto->fWhitelisted)
+ LogPrintf("Warning: not punishing whitelisted peer %s!\n", pto->addr.ToString());
+ else {
+ pto->fDisconnect = true;
+ if (pto->addr.IsLocal())
+ LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString());
+ else
+ {
+ connman.Ban(pto->addr, BanReasonNodeMisbehaving);
+ }
+ return true;
+ }
+ }
+
+ // Address refresh broadcast
+ int64_t nNow = GetTimeMicros();
+ if (!IsInitialBlockDownload() && pto->nNextLocalAddrSend < nNow) {
+ AdvertiseLocal(pto);
+ pto->nNextLocalAddrSend = PoissonNextSend(nNow, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL);
+ }
+
+ //
+ // Message: addr
+ //
+ if (pto->nNextAddrSend < nNow) {
+ pto->nNextAddrSend = PoissonNextSend(nNow, AVG_ADDRESS_BROADCAST_INTERVAL);
+ vector<CAddress> vAddr;
+ vAddr.reserve(pto->vAddrToSend.size());
+ BOOST_FOREACH(const CAddress& addr, pto->vAddrToSend)
+ {
+ if (!pto->addrKnown.contains(addr.GetKey()))
+ {
+ pto->addrKnown.insert(addr.GetKey());
+ vAddr.push_back(addr);
+ // receiver rejects addr messages larger than 1000
+ if (vAddr.size() >= 1000)
+ {
+ connman.PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr));
+ vAddr.clear();
+ }
+ }
+ }
+ pto->vAddrToSend.clear();
+ if (!vAddr.empty())
+ connman.PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr));
+ // we only send the big addr message once
+ if (pto->vAddrToSend.capacity() > 40)
+ pto->vAddrToSend.shrink_to_fit();
+ }
+
+ // Start block sync
+ if (pindexBestHeader == NULL)
+ pindexBestHeader = chainActive.Tip();
+ bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->fOneShot); // Download if this is a nice peer, or we have no nice peers and this one might do.
+ if (!state.fSyncStarted && !pto->fClient && !fImporting && !fReindex) {
+ // Only actively request headers from a single peer, unless we're close to today.
+ if ((nSyncStarted == 0 && fFetch) || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) {
+ state.fSyncStarted = true;
+ nSyncStarted++;
+ const CBlockIndex *pindexStart = pindexBestHeader;
+ /* If possible, start at the block preceding the currently
+ best known header. This ensures that we always get a
+ non-empty list of headers back as long as the peer
+ is up-to-date. With a non-empty response, we can initialise
+ the peer's known best block. This wouldn't be possible
+ if we requested starting at pindexBestHeader and
+ got back an empty response. */
+ if (pindexStart->pprev)
+ pindexStart = pindexStart->pprev;
+ LogPrint("net", "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->id, pto->nStartingHeight);
+ connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexStart), uint256()));
+ }
+ }
+
+ // 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())
+ {
+ GetMainSignals().Broadcast(nTimeBestReceived, &connman);
+ }
+
+ //
+ // Try sending block announcements via headers
+ //
+ {
+ // If we have less than MAX_BLOCKS_TO_ANNOUNCE in our
+ // list of block hashes we're relaying, and our peer wants
+ // headers announcements, then find the first header
+ // not yet known to our peer but would connect, and send.
+ // If no header would connect, or if we have too many
+ // blocks, or if the peer doesn't want headers, just
+ // add all to the inv queue.
+ LOCK(pto->cs_inventory);
+ vector<CBlock> vHeaders;
+ bool fRevertToInv = ((!state.fPreferHeaders &&
+ (!state.fPreferHeaderAndIDs || pto->vBlockHashesToAnnounce.size() > 1)) ||
+ pto->vBlockHashesToAnnounce.size() > MAX_BLOCKS_TO_ANNOUNCE);
+ CBlockIndex *pBestIndex = NULL; // last header queued for delivery
+ ProcessBlockAvailability(pto->id); // ensure pindexBestKnownBlock is up-to-date
+
+ if (!fRevertToInv) {
+ bool fFoundStartingHeader = false;
+ // Try to find first header that our peer doesn't have, and
+ // then send all headers past that one. If we come across any
+ // headers that aren't on chainActive, give up.
+ BOOST_FOREACH(const uint256 &hash, pto->vBlockHashesToAnnounce) {
+ BlockMap::iterator mi = mapBlockIndex.find(hash);
+ assert(mi != mapBlockIndex.end());
+ CBlockIndex *pindex = mi->second;
+ if (chainActive[pindex->nHeight] != pindex) {
+ // Bail out if we reorged away from this block
+ fRevertToInv = true;
+ break;
+ }
+ if (pBestIndex != NULL && pindex->pprev != pBestIndex) {
+ // This means that the list of blocks to announce don't
+ // connect to each other.
+ // This shouldn't really be possible to hit during
+ // regular operation (because reorgs should take us to
+ // a chain that has some block not on the prior chain,
+ // which should be caught by the prior check), but one
+ // way this could happen is by using invalidateblock /
+ // reconsiderblock repeatedly on the tip, causing it to
+ // be added multiple times to vBlockHashesToAnnounce.
+ // Robustly deal with this rare situation by reverting
+ // to an inv.
+ fRevertToInv = true;
+ break;
+ }
+ pBestIndex = pindex;
+ if (fFoundStartingHeader) {
+ // add this to the headers message
+ vHeaders.push_back(pindex->GetBlockHeader());
+ } else if (PeerHasHeader(&state, pindex)) {
+ continue; // keep looking for the first new block
+ } else if (pindex->pprev == NULL || PeerHasHeader(&state, pindex->pprev)) {
+ // Peer doesn't have this header but they do have the prior one.
+ // Start sending headers.
+ fFoundStartingHeader = true;
+ vHeaders.push_back(pindex->GetBlockHeader());
+ } else {
+ // Peer doesn't have this header or the prior one -- nothing will
+ // connect, so bail out.
+ fRevertToInv = true;
+ break;
+ }
+ }
+ }
+ if (!fRevertToInv && !vHeaders.empty()) {
+ if (vHeaders.size() == 1 && state.fPreferHeaderAndIDs) {
+ // We only send up to 1 block as header-and-ids, as otherwise
+ // probably means we're doing an initial-ish-sync or they're slow
+ LogPrint("net", "%s sending header-and-ids %s to peer %d\n", __func__,
+ vHeaders.front().GetHash().ToString(), pto->id);
+ //TODO: Shouldn't need to reload block from disk, but requires refactor
+ CBlock block;
+ bool ret = ReadBlockFromDisk(block, pBestIndex, consensusParams);
+ assert(ret);
+ CBlockHeaderAndShortTxIDs cmpctblock(block, state.fWantsCmpctWitness);
+ int nSendFlags = state.fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
+ connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
+ state.pindexBestHeaderSent = pBestIndex;
+ } else if (state.fPreferHeaders) {
+ if (vHeaders.size() > 1) {
+ LogPrint("net", "%s: %u headers, range (%s, %s), to peer=%d\n", __func__,
+ vHeaders.size(),
+ vHeaders.front().GetHash().ToString(),
+ vHeaders.back().GetHash().ToString(), pto->id);
+ } else {
+ LogPrint("net", "%s: sending header %s to peer=%d\n", __func__,
+ vHeaders.front().GetHash().ToString(), pto->id);
+ }
+ connman.PushMessage(pto, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
+ state.pindexBestHeaderSent = pBestIndex;
+ } else
+ fRevertToInv = true;
+ }
+ if (fRevertToInv) {
+ // If falling back to using an inv, just try to inv the tip.
+ // The last entry in vBlockHashesToAnnounce was our tip at some point
+ // in the past.
+ if (!pto->vBlockHashesToAnnounce.empty()) {
+ const uint256 &hashToAnnounce = pto->vBlockHashesToAnnounce.back();
+ BlockMap::iterator mi = mapBlockIndex.find(hashToAnnounce);
+ assert(mi != mapBlockIndex.end());
+ CBlockIndex *pindex = mi->second;
+
+ // Warn if we're announcing a block that is not on the main chain.
+ // This should be very rare and could be optimized out.
+ // Just log for now.
+ if (chainActive[pindex->nHeight] != pindex) {
+ LogPrint("net", "Announcing block %s not on main chain (tip=%s)\n",
+ hashToAnnounce.ToString(), chainActive.Tip()->GetBlockHash().ToString());
+ }
+
+ // If the peer's chain has this block, don't inv it back.
+ if (!PeerHasHeader(&state, pindex)) {
+ pto->PushInventory(CInv(MSG_BLOCK, hashToAnnounce));
+ LogPrint("net", "%s: sending inv peer=%d hash=%s\n", __func__,
+ pto->id, hashToAnnounce.ToString());
+ }
+ }
+ }
+ pto->vBlockHashesToAnnounce.clear();
+ }
+
+ //
+ // Message: inventory
+ //
+ vector<CInv> vInv;
+ {
+ LOCK(pto->cs_inventory);
+ vInv.reserve(std::max<size_t>(pto->vInventoryBlockToSend.size(), INVENTORY_BROADCAST_MAX));
+
+ // Add blocks
+ BOOST_FOREACH(const uint256& hash, pto->vInventoryBlockToSend) {
+ vInv.push_back(CInv(MSG_BLOCK, hash));
+ if (vInv.size() == MAX_INV_SZ) {
+ connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
+ vInv.clear();
+ }
+ }
+ pto->vInventoryBlockToSend.clear();
+
+ // Check whether periodic sends should happen
+ bool fSendTrickle = pto->fWhitelisted;
+ if (pto->nNextInvSend < nNow) {
+ fSendTrickle = true;
+ // Use half the delay for outbound peers, as there is less privacy concern for them.
+ pto->nNextInvSend = PoissonNextSend(nNow, INVENTORY_BROADCAST_INTERVAL >> !pto->fInbound);
+ }
+
+ // Time to send but the peer has requested we not relay transactions.
+ if (fSendTrickle) {
+ LOCK(pto->cs_filter);
+ if (!pto->fRelayTxes) pto->setInventoryTxToSend.clear();
+ }
+
+ // Respond to BIP35 mempool requests
+ if (fSendTrickle && pto->fSendMempool) {
+ auto vtxinfo = mempool.infoAll();
+ pto->fSendMempool = false;
+ CAmount filterrate = 0;
+ {
+ LOCK(pto->cs_feeFilter);
+ filterrate = pto->minFeeFilter;
+ }
+
+ LOCK(pto->cs_filter);
+
+ for (const auto& txinfo : vtxinfo) {
+ const uint256& hash = txinfo.tx->GetHash();
+ CInv inv(MSG_TX, hash);
+ pto->setInventoryTxToSend.erase(hash);
+ if (filterrate) {
+ if (txinfo.feeRate.GetFeePerK() < filterrate)
+ continue;
+ }
+ if (pto->pfilter) {
+ if (!pto->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
+ }
+ pto->filterInventoryKnown.insert(hash);
+ vInv.push_back(inv);
+ if (vInv.size() == MAX_INV_SZ) {
+ connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
+ vInv.clear();
+ }
+ }
+ pto->timeLastMempoolReq = GetTime();
+ }
+
+ // Determine transactions to relay
+ if (fSendTrickle) {
+ // Produce a vector with all candidates for sending
+ vector<std::set<uint256>::iterator> vInvTx;
+ vInvTx.reserve(pto->setInventoryTxToSend.size());
+ for (std::set<uint256>::iterator it = pto->setInventoryTxToSend.begin(); it != pto->setInventoryTxToSend.end(); it++) {
+ vInvTx.push_back(it);
+ }
+ CAmount filterrate = 0;
+ {
+ LOCK(pto->cs_feeFilter);
+ filterrate = pto->minFeeFilter;
+ }
+ // Topologically and fee-rate sort the inventory we send for privacy and priority reasons.
+ // A heap is used so that not all items need sorting if only a few are being sent.
+ CompareInvMempoolOrder compareInvMempoolOrder(&mempool);
+ std::make_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder);
+ // No reason to drain out at many times the network's capacity,
+ // especially since we have many peers and some will draw much shorter delays.
+ unsigned int nRelayedTransactions = 0;
+ LOCK(pto->cs_filter);
+ while (!vInvTx.empty() && nRelayedTransactions < INVENTORY_BROADCAST_MAX) {
+ // Fetch the top element from the heap
+ std::pop_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder);
+ std::set<uint256>::iterator it = vInvTx.back();
+ vInvTx.pop_back();
+ uint256 hash = *it;
+ // Remove it from the to-be-sent set
+ pto->setInventoryTxToSend.erase(it);
+ // Check if not in the filter already
+ if (pto->filterInventoryKnown.contains(hash)) {
+ continue;
+ }
+ // Not in the mempool anymore? don't bother sending it.
+ auto txinfo = mempool.info(hash);
+ if (!txinfo.tx) {
+ continue;
+ }
+ if (filterrate && txinfo.feeRate.GetFeePerK() < filterrate) {
+ continue;
+ }
+ if (pto->pfilter && !pto->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
+ // Send
+ vInv.push_back(CInv(MSG_TX, hash));
+ nRelayedTransactions++;
+ {
+ // Expire old relay messages
+ while (!vRelayExpiration.empty() && vRelayExpiration.front().first < nNow)
+ {
+ mapRelay.erase(vRelayExpiration.front().second);
+ vRelayExpiration.pop_front();
+ }
+
+ auto ret = mapRelay.insert(std::make_pair(hash, std::move(txinfo.tx)));
+ if (ret.second) {
+ vRelayExpiration.push_back(std::make_pair(nNow + 15 * 60 * 1000000, ret.first));
+ }
+ }
+ if (vInv.size() == MAX_INV_SZ) {
+ connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
+ vInv.clear();
+ }
+ pto->filterInventoryKnown.insert(hash);
+ }
+ }
+ }
+ if (!vInv.empty())
+ connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
+
+ // Detect whether we're stalling
+ nNow = GetTimeMicros();
+ if (state.nStallingSince && state.nStallingSince < nNow - 1000000 * BLOCK_STALLING_TIMEOUT) {
+ // Stalling only triggers when the block download window cannot move. During normal steady state,
+ // the download window should be much larger than the to-be-downloaded set of blocks, so disconnection
+ // should only happen during initial block download.
+ LogPrintf("Peer=%d is stalling block download, disconnecting\n", pto->id);
+ pto->fDisconnect = true;
+ return true;
+ }
+ // In case there is a block that has been in flight from this peer for 2 + 0.5 * N times the block interval
+ // (with N the number of peers from which we're downloading validated blocks), disconnect due to timeout.
+ // We compensate for other peers to prevent killing off peers due to our own downstream link
+ // being saturated. We only count validated in-flight blocks so peers can't advertise non-existing block hashes
+ // to unreasonably increase our timeout.
+ if (state.vBlocksInFlight.size() > 0) {
+ QueuedBlock &queuedBlock = state.vBlocksInFlight.front();
+ int nOtherPeersWithValidatedDownloads = nPeersWithValidatedDownloads - (state.nBlocksInFlightValidHeaders > 0);
+ if (nNow > state.nDownloadingSince + consensusParams.nPowTargetSpacing * (BLOCK_DOWNLOAD_TIMEOUT_BASE + BLOCK_DOWNLOAD_TIMEOUT_PER_PEER * nOtherPeersWithValidatedDownloads)) {
+ LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", queuedBlock.hash.ToString(), pto->id);
+ pto->fDisconnect = true;
+ return true;
+ }
+ }
+
+ //
+ // Message: getdata (blocks)
+ //
+ vector<CInv> vGetData;
+ if (!pto->fClient && (fFetch || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
+ vector<CBlockIndex*> vToDownload;
+ NodeId staller = -1;
+ FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller, consensusParams);
+ BOOST_FOREACH(CBlockIndex *pindex, vToDownload) {
+ uint32_t nFetchFlags = GetFetchFlags(pto, pindex->pprev, consensusParams);
+ vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
+ MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), consensusParams, pindex);
+ LogPrint("net", "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(),
+ pindex->nHeight, pto->id);
+ }
+ if (state.nBlocksInFlight == 0 && staller != -1) {
+ if (State(staller)->nStallingSince == 0) {
+ State(staller)->nStallingSince = nNow;
+ LogPrint("net", "Stall started peer=%d\n", staller);
+ }
+ }
+ }
+
+ //
+ // Message: getdata (non-blocks)
+ //
+ while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow)
+ {
+ const CInv& inv = (*pto->mapAskFor.begin()).second;
+ if (!AlreadyHave(inv))
+ {
+ if (fDebug)
+ LogPrint("net", "Requesting %s peer=%d\n", inv.ToString(), pto->id);
+ vGetData.push_back(inv);
+ if (vGetData.size() >= 1000)
+ {
+ connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));
+ vGetData.clear();
+ }
+ } else {
+ //If we're not going to ask, don't expect a response.
+ pto->setAskFor.erase(inv.hash);
+ }
+ pto->mapAskFor.erase(pto->mapAskFor.begin());
+ }
+ if (!vGetData.empty())
+ connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));
+
+ //
+ // Message: feefilter
+ //
+ // We don't want white listed peers to filter txs to us if we have -whitelistforcerelay
+ if (pto->nVersion >= FEEFILTER_VERSION && GetBoolArg("-feefilter", DEFAULT_FEEFILTER) &&
+ !(pto->fWhitelisted && GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY))) {
+ CAmount currentFilter = mempool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK();
+ int64_t timeNow = GetTimeMicros();
+ if (timeNow > pto->nextSendTimeFeeFilter) {
+ static CFeeRate default_feerate(DEFAULT_MIN_RELAY_TX_FEE);
+ static FeeFilterRounder filterRounder(default_feerate);
+ CAmount filterToSend = filterRounder.round(currentFilter);
+ // If we don't allow free transactions, then we always have a fee filter of at least minRelayTxFee
+ if (GetArg("-limitfreerelay", DEFAULT_LIMITFREERELAY) <= 0)
+ filterToSend = std::max(filterToSend, ::minRelayTxFee.GetFeePerK());
+ if (filterToSend != pto->lastSentFeeFilter) {
+ connman.PushMessage(pto, msgMaker.Make(NetMsgType::FEEFILTER, filterToSend));
+ pto->lastSentFeeFilter = filterToSend;
+ }
+ pto->nextSendTimeFeeFilter = PoissonNextSend(timeNow, AVG_FEEFILTER_BROADCAST_INTERVAL);
+ }
+ // If the fee filter has changed substantially and it's still more than MAX_FEEFILTER_CHANGE_DELAY
+ // until scheduled broadcast, then move the broadcast to within MAX_FEEFILTER_CHANGE_DELAY.
+ else if (timeNow + MAX_FEEFILTER_CHANGE_DELAY * 1000000 < pto->nextSendTimeFeeFilter &&
+ (currentFilter < 3 * pto->lastSentFeeFilter / 4 || currentFilter > 4 * pto->lastSentFeeFilter / 3)) {
+ pto->nextSendTimeFeeFilter = timeNow + GetRandInt(MAX_FEEFILTER_CHANGE_DELAY) * 1000000;
+ }
+ }
+ }
+ return true;
+}
+
+class CNetProcessingCleanup
+{
+public:
+ CNetProcessingCleanup() {}
+ ~CNetProcessingCleanup() {
+ // orphan transactions
+ mapOrphanTransactions.clear();
+ mapOrphanTransactionsByPrev.clear();
+ }
+} instance_of_cnetprocessingcleanup;
diff --git a/src/net_processing.h b/src/net_processing.h
new file mode 100644
index 0000000000..130433cc7c
--- /dev/null
+++ b/src/net_processing.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2015 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_NET_PROCESSING_H
+#define BITCOIN_NET_PROCESSING_H
+
+#include "net.h"
+#include "validationinterface.h"
+
+/** Register with a network node to receive its signals */
+void RegisterNodeSignals(CNodeSignals& nodeSignals);
+/** Unregister a network node */
+void UnregisterNodeSignals(CNodeSignals& nodeSignals);
+
+class PeerLogicValidation : public CValidationInterface {
+private:
+ CConnman* connman;
+
+public:
+ PeerLogicValidation(CConnman* connmanIn);
+
+ virtual void SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, int nPosInBlock);
+ virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload);
+ virtual void BlockChecked(const CBlock& block, const CValidationState& state);
+};
+
+struct CNodeStateStats {
+ int nMisbehavior;
+ int nSyncHeight;
+ int nCommonHeight;
+ std::vector<int> vHeightInFlight;
+};
+
+/** Get statistics from node state */
+bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats);
+/** Increase a node's misbehavior score. */
+void Misbehaving(NodeId nodeid, int howmuch);
+
+/** Process protocol messages received from a given node */
+bool ProcessMessages(CNode* pfrom, CConnman& connman);
+/**
+ * Send queued protocol messages to be sent to a give node.
+ *
+ * @param[in] pto The node which we are sending messages to.
+ * @param[in] connman The connection manager for that node.
+ */
+bool SendMessages(CNode* pto, CConnman& connman);
+
+#endif // BITCOIN_NET_PROCESSING_H
diff --git a/src/netaddress.h b/src/netaddress.h
index 9330fe3328..9dffaa57e7 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -85,7 +85,7 @@ class CNetAddr
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(FLATDATA(ip));
}
@@ -122,7 +122,7 @@ class CSubNet
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(network);
READWRITE(FLATDATA(netmask));
READWRITE(FLATDATA(valid));
@@ -159,7 +159,7 @@ class CService : public CNetAddr
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(FLATDATA(ip));
unsigned short portN = htons(port);
READWRITE(FLATDATA(portN));
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 7d7f1b6788..da94fd4d13 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -94,30 +94,9 @@ bool static LookupIntern(const char *pszName, std::vector<CNetAddr>& vIP, unsign
}
}
-#ifdef HAVE_GETADDRINFO_A
- struct in_addr ipv4_addr;
-#ifdef HAVE_INET_PTON
- if (inet_pton(AF_INET, pszName, &ipv4_addr) > 0) {
- vIP.push_back(CNetAddr(ipv4_addr));
- return true;
- }
-
- struct in6_addr ipv6_addr;
- if (inet_pton(AF_INET6, pszName, &ipv6_addr) > 0) {
- vIP.push_back(CNetAddr(ipv6_addr));
- return true;
- }
-#else
- ipv4_addr.s_addr = inet_addr(pszName);
- if (ipv4_addr.s_addr != INADDR_NONE) {
- vIP.push_back(CNetAddr(ipv4_addr));
- return true;
- }
-#endif
-#endif
-
struct addrinfo aiHint;
memset(&aiHint, 0, sizeof(struct addrinfo));
+
aiHint.ai_socktype = SOCK_STREAM;
aiHint.ai_protocol = IPPROTO_TCP;
aiHint.ai_family = AF_UNSPEC;
@@ -126,33 +105,8 @@ bool static LookupIntern(const char *pszName, std::vector<CNetAddr>& vIP, unsign
#else
aiHint.ai_flags = fAllowLookup ? AI_ADDRCONFIG : AI_NUMERICHOST;
#endif
-
struct addrinfo *aiRes = NULL;
-#ifdef HAVE_GETADDRINFO_A
- struct gaicb gcb, *query = &gcb;
- memset(query, 0, sizeof(struct gaicb));
- gcb.ar_name = pszName;
- gcb.ar_request = &aiHint;
- int nErr = getaddrinfo_a(GAI_NOWAIT, &query, 1, NULL);
- if (nErr)
- return false;
-
- do {
- // Should set the timeout limit to a reasonable value to avoid
- // generating unnecessary checking call during the polling loop,
- // while it can still response to stop request quick enough.
- // 2 seconds looks fine in our situation.
- struct timespec ts = { 2, 0 };
- gai_suspend(&query, 1, &ts);
- boost::this_thread::interruption_point();
-
- nErr = gai_error(query);
- if (0 == nErr)
- aiRes = query->ar_result;
- } while (nErr == EAI_INPROGRESS);
-#else
int nErr = getaddrinfo(pszName, NULL, &aiHint, &aiRes);
-#endif
if (nErr)
return false;
@@ -338,7 +292,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
vSocks5Init.push_back(0x01); // # METHODS
vSocks5Init.push_back(0x00); // X'00' NO AUTHENTICATION REQUIRED
}
- ssize_t ret = send(hSocket, (const char*)begin_ptr(vSocks5Init), vSocks5Init.size(), MSG_NOSIGNAL);
+ ssize_t ret = send(hSocket, (const char*)vSocks5Init.data(), vSocks5Init.size(), MSG_NOSIGNAL);
if (ret != (ssize_t)vSocks5Init.size()) {
CloseSocket(hSocket);
return error("Error sending to proxy");
@@ -363,7 +317,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
vAuth.insert(vAuth.end(), auth->username.begin(), auth->username.end());
vAuth.push_back(auth->password.size());
vAuth.insert(vAuth.end(), auth->password.begin(), auth->password.end());
- ret = send(hSocket, (const char*)begin_ptr(vAuth), vAuth.size(), MSG_NOSIGNAL);
+ ret = send(hSocket, (const char*)vAuth.data(), vAuth.size(), MSG_NOSIGNAL);
if (ret != (ssize_t)vAuth.size()) {
CloseSocket(hSocket);
return error("Error sending authentication to proxy");
@@ -393,7 +347,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
vSocks5.insert(vSocks5.end(), strDest.begin(), strDest.end());
vSocks5.push_back((port >> 8) & 0xFF);
vSocks5.push_back((port >> 0) & 0xFF);
- ret = send(hSocket, (const char*)begin_ptr(vSocks5), vSocks5.size(), MSG_NOSIGNAL);
+ ret = send(hSocket, (const char*)vSocks5.data(), vSocks5.size(), MSG_NOSIGNAL);
if (ret != (ssize_t)vSocks5.size()) {
CloseSocket(hSocket);
return error("Error sending to proxy");
@@ -596,8 +550,8 @@ static bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDe
// do socks negotiation
if (proxy.randomize_credentials) {
ProxyCredentials random_auth;
- random_auth.username = strprintf("%i", insecure_rand());
- random_auth.password = strprintf("%i", insecure_rand());
+ static std::atomic_int counter;
+ random_auth.username = random_auth.password = strprintf("%i", counter++);
if (!Socks5(strDest, (unsigned short)port, &random_auth, hSocket))
return false;
} else {
diff --git a/src/netmessagemaker.h b/src/netmessagemaker.h
new file mode 100644
index 0000000000..7167434a19
--- /dev/null
+++ b/src/netmessagemaker.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2016 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_NETMESSAGEMAKER_H
+#define BITCOIN_NETMESSAGEMAKER_H
+
+#include "net.h"
+#include "serialize.h"
+
+class CNetMsgMaker
+{
+public:
+ CNetMsgMaker(int nVersionIn) : nVersion(nVersionIn){}
+
+ template <typename... Args>
+ CSerializedNetMsg Make(int nFlags, std::string sCommand, Args&&... args)
+ {
+ CSerializedNetMsg msg;
+ msg.command = std::move(sCommand);
+ CVectorWriter{ SER_NETWORK, nFlags | nVersion, msg.data, 0, std::forward<Args>(args)... };
+ return msg;
+ }
+
+ template <typename... Args>
+ CSerializedNetMsg Make(std::string sCommand, Args&&... args)
+ {
+ return Make(0, std::move(sCommand), std::forward<Args>(args)...);
+ }
+
+private:
+ const int nVersion;
+};
+
+#endif // BITCOIN_NETMESSAGEMAKER_H
diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp
index 7b0e8b7d08..9eb831bc17 100644
--- a/src/policy/fees.cpp
+++ b/src/policy/fees.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2015 The Bitcoin developers
+// Copyright (c) 2009-2015 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -14,10 +14,9 @@
#include "util.h"
void TxConfirmStats::Initialize(std::vector<double>& defaultBuckets,
- unsigned int maxConfirms, double _decay, std::string _dataTypeString)
+ unsigned int maxConfirms, double _decay)
{
decay = _decay;
- dataTypeString = _dataTypeString;
for (unsigned int i = 0; i < defaultBuckets.size(); i++) {
buckets.push_back(defaultBuckets[i]);
bucketMap[defaultBuckets[i]] = i;
@@ -87,10 +86,10 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
int maxbucketindex = buckets.size() - 1;
- // requireGreater means we are looking for the lowest fee/priority such that all higher
- // values pass, so we start at maxbucketindex (highest fee) and look at successively
+ // requireGreater means we are looking for the lowest feerate such that all higher
+ // values pass, so we start at maxbucketindex (highest feerate) and look at successively
// smaller buckets until we reach failure. Otherwise, we are looking for the highest
- // fee/priority such that all lower values fail, and we go in the opposite direction.
+ // feerate such that all lower values fail, and we go in the opposite direction.
unsigned int startbucket = requireGreater ? maxbucketindex : 0;
int step = requireGreater ? -1 : 1;
@@ -107,7 +106,7 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
bool foundAnswer = false;
unsigned int bins = unconfTxs.size();
- // Start counting from highest(default) or lowest fee/pri transactions
+ // Start counting from highest(default) or lowest feerate transactions
for (int bucket = startbucket; bucket >= 0 && bucket <= maxbucketindex; bucket += step) {
curFarBucket = bucket;
nConf += confAvg[confTarget - 1][bucket];
@@ -145,8 +144,8 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
double median = -1;
double txSum = 0;
- // Calculate the "average" fee of the best bucket range that met success conditions
- // Find the bucket with the median transaction and then report the average fee from that bucket
+ // Calculate the "average" feerate of the best bucket range that met success conditions
+ // Find the bucket with the median transaction and then report the average feerate from that bucket
// This is a compromise between finding the median which we can't since we don't save all tx's
// and reporting the average which is less accurate
unsigned int minBucket = bestNearBucket < bestFarBucket ? bestNearBucket : bestFarBucket;
@@ -166,8 +165,8 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
}
}
- LogPrint("estimatefee", "%3d: For conf success %s %4.2f need %s %s: %12.5g from buckets %8g - %8g Cur Bucket stats %6.2f%% %8.1f/(%.1f+%d mempool)\n",
- confTarget, requireGreater ? ">" : "<", successBreakPoint, dataTypeString,
+ LogPrint("estimatefee", "%3d: For conf success %s %4.2f need feerate %s: %12.5g from buckets %8g - %8g Cur Bucket stats %6.2f%% %8.1f/(%.1f+%d mempool)\n",
+ confTarget, requireGreater ? ">" : "<", successBreakPoint,
requireGreater ? ">" : "<", median, buckets[minBucket], buckets[maxBucket],
100 * nConf / (totalNum + extraNum), nConf, totalNum, extraNum);
@@ -200,10 +199,10 @@ void TxConfirmStats::Read(CAutoFile& filein)
filein >> fileBuckets;
numBuckets = fileBuckets.size();
if (numBuckets <= 1 || numBuckets > 1000)
- throw std::runtime_error("Corrupt estimates file. Must have between 2 and 1000 fee/pri buckets");
+ throw std::runtime_error("Corrupt estimates file. Must have between 2 and 1000 feerate buckets");
filein >> fileAvg;
if (fileAvg.size() != numBuckets)
- throw std::runtime_error("Corrupt estimates file. Mismatch in fee/pri average bucket count");
+ throw std::runtime_error("Corrupt estimates file. Mismatch in feerate average bucket count");
filein >> fileTxCtAvg;
if (fileTxCtAvg.size() != numBuckets)
throw std::runtime_error("Corrupt estimates file. Mismatch in tx count bucket count");
@@ -213,9 +212,9 @@ void TxConfirmStats::Read(CAutoFile& filein)
throw std::runtime_error("Corrupt estimates file. Must maintain estimates for between 1 and 1008 (one week) confirms");
for (unsigned int i = 0; i < maxConfirms; i++) {
if (fileConfAvg[i].size() != numBuckets)
- throw std::runtime_error("Corrupt estimates file. Mismatch in fee/pri conf average bucket count");
+ throw std::runtime_error("Corrupt estimates file. Mismatch in feerate conf average bucket count");
}
- // Now that we've processed the entire fee estimate data file and not
+ // Now that we've processed the entire feerate estimate data file and not
// thrown any errors, we can copy it to our data structures
decay = fileDecay;
buckets = fileBuckets;
@@ -242,8 +241,8 @@ void TxConfirmStats::Read(CAutoFile& filein)
for (unsigned int i = 0; i < buckets.size(); i++)
bucketMap[buckets[i]] = i;
- LogPrint("estimatefee", "Reading estimates: %u %s buckets counting confirms up to %u blocks\n",
- numBuckets, dataTypeString, maxConfirms);
+ LogPrint("estimatefee", "Reading estimates: %u buckets counting confirms up to %u blocks\n",
+ numBuckets, maxConfirms);
}
unsigned int TxConfirmStats::NewTx(unsigned int nBlockHeight, double val)
@@ -251,7 +250,6 @@ unsigned int TxConfirmStats::NewTx(unsigned int nBlockHeight, double val)
unsigned int bucketindex = bucketMap.lower_bound(val)->second;
unsigned int blockIndex = nBlockHeight % unconfTxs.size();
unconfTxs[blockIndex][bucketindex]++;
- LogPrint("estimatefee", "adding to %s", dataTypeString);
return bucketindex;
}
@@ -291,12 +289,10 @@ void CBlockPolicyEstimator::removeTx(uint256 hash)
hash.ToString().c_str());
return;
}
- TxConfirmStats *stats = pos->second.stats;
unsigned int entryHeight = pos->second.blockHeight;
unsigned int bucketIndex = pos->second.bucketIndex;
- if (stats != NULL)
- stats->removeTx(entryHeight, nBestSeenHeight, bucketIndex);
+ feeStats.removeTx(entryHeight, nBestSeenHeight, bucketIndex);
mapMemPoolTxs.erase(hash);
}
@@ -309,45 +305,14 @@ CBlockPolicyEstimator::CBlockPolicyEstimator(const CFeeRate& _minRelayFee)
vfeelist.push_back(bucketBoundary);
}
vfeelist.push_back(INF_FEERATE);
- feeStats.Initialize(vfeelist, MAX_BLOCK_CONFIRMS, DEFAULT_DECAY, "FeeRate");
-
- minTrackedPriority = AllowFreeThreshold() < MIN_PRIORITY ? MIN_PRIORITY : AllowFreeThreshold();
- std::vector<double> vprilist;
- for (double bucketBoundary = minTrackedPriority; bucketBoundary <= MAX_PRIORITY; bucketBoundary *= PRI_SPACING) {
- vprilist.push_back(bucketBoundary);
- }
- vprilist.push_back(INF_PRIORITY);
- priStats.Initialize(vprilist, MAX_BLOCK_CONFIRMS, DEFAULT_DECAY, "Priority");
-
- feeUnlikely = CFeeRate(0);
- feeLikely = CFeeRate(INF_FEERATE);
- priUnlikely = 0;
- priLikely = INF_PRIORITY;
-}
-
-bool CBlockPolicyEstimator::isFeeDataPoint(const CFeeRate &fee, double pri)
-{
- if ((pri < minTrackedPriority && fee >= minTrackedFee) ||
- (pri < priUnlikely && fee > feeLikely)) {
- return true;
- }
- return false;
-}
-
-bool CBlockPolicyEstimator::isPriDataPoint(const CFeeRate &fee, double pri)
-{
- if ((fee < minTrackedFee && pri >= minTrackedPriority) ||
- (fee < feeUnlikely && pri > priLikely)) {
- return true;
- }
- return false;
+ feeStats.Initialize(vfeelist, MAX_BLOCK_CONFIRMS, DEFAULT_DECAY);
}
void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, bool fCurrentEstimate)
{
unsigned int txHeight = entry.GetHeight();
uint256 hash = entry.GetTx().GetHash();
- if (mapMemPoolTxs[hash].stats != NULL) {
+ if (mapMemPoolTxs.count(hash)) {
LogPrint("estimatefee", "Blockpolicy error mempool tx %s already being tracked\n",
hash.ToString().c_str());
return;
@@ -371,30 +336,11 @@ void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, boo
return;
}
- // Fees are stored and reported as BTC-per-kb:
+ // Feerates are stored and reported as BTC-per-kb:
CFeeRate feeRate(entry.GetFee(), entry.GetTxSize());
- // Want the priority of the tx at confirmation. However we don't know
- // what that will be and its too hard to continue updating it
- // so use starting priority as a proxy
- double curPri = entry.GetPriority(txHeight);
mapMemPoolTxs[hash].blockHeight = txHeight;
-
- LogPrint("estimatefee", "Blockpolicy mempool tx %s ", hash.ToString().substr(0,10));
- // Record this as a priority estimate
- if (entry.GetFee() == 0 || isPriDataPoint(feeRate, curPri)) {
- mapMemPoolTxs[hash].stats = &priStats;
- mapMemPoolTxs[hash].bucketIndex = priStats.NewTx(txHeight, curPri);
- }
- // Record this as a fee estimate
- else if (isFeeDataPoint(feeRate, curPri)) {
- mapMemPoolTxs[hash].stats = &feeStats;
- mapMemPoolTxs[hash].bucketIndex = feeStats.NewTx(txHeight, (double)feeRate.GetFeePerK());
- }
- else {
- LogPrint("estimatefee", "not adding");
- }
- LogPrint("estimatefee", "\n");
+ mapMemPoolTxs[hash].bucketIndex = feeStats.NewTx(txHeight, (double)feeRate.GetFeePerK());
}
void CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry& entry)
@@ -417,21 +363,10 @@ void CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const CTxM
return;
}
- // Fees are stored and reported as BTC-per-kb:
+ // Feerates are stored and reported as BTC-per-kb:
CFeeRate feeRate(entry.GetFee(), entry.GetTxSize());
- // Want the priority of the tx at confirmation. The priority when it
- // entered the mempool could easily be very small and change quickly
- double curPri = entry.GetPriority(nBlockHeight);
-
- // Record this as a priority estimate
- if (entry.GetFee() == 0 || isPriDataPoint(feeRate, curPri)) {
- priStats.Record(blocksToConfirm, curPri);
- }
- // Record this as a fee estimate
- else if (isFeeDataPoint(feeRate, curPri)) {
- feeStats.Record(blocksToConfirm, (double)feeRate.GetFeePerK());
- }
+ feeStats.Record(blocksToConfirm, (double)feeRate.GetFeePerK());
}
void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight,
@@ -452,41 +387,15 @@ void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight,
if (!fCurrentEstimate)
return;
- // Update the dynamic cutoffs
- // a fee/priority is "likely" the reason your tx was included in a block if >85% of such tx's
- // were confirmed in 2 blocks and is "unlikely" if <50% were confirmed in 10 blocks
- LogPrint("estimatefee", "Blockpolicy recalculating dynamic cutoffs:\n");
- priLikely = priStats.EstimateMedianVal(2, SUFFICIENT_PRITXS, MIN_SUCCESS_PCT, true, nBlockHeight);
- if (priLikely == -1)
- priLikely = INF_PRIORITY;
-
- double feeLikelyEst = feeStats.EstimateMedianVal(2, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true, nBlockHeight);
- if (feeLikelyEst == -1)
- feeLikely = CFeeRate(INF_FEERATE);
- else
- feeLikely = CFeeRate(feeLikelyEst);
-
- priUnlikely = priStats.EstimateMedianVal(10, SUFFICIENT_PRITXS, UNLIKELY_PCT, false, nBlockHeight);
- if (priUnlikely == -1)
- priUnlikely = 0;
-
- double feeUnlikelyEst = feeStats.EstimateMedianVal(10, SUFFICIENT_FEETXS, UNLIKELY_PCT, false, nBlockHeight);
- if (feeUnlikelyEst == -1)
- feeUnlikely = CFeeRate(0);
- else
- feeUnlikely = CFeeRate(feeUnlikelyEst);
-
- // Clear the current block states
+ // Clear the current block state
feeStats.ClearCurrent(nBlockHeight);
- priStats.ClearCurrent(nBlockHeight);
// Repopulate the current block states
for (unsigned int i = 0; i < entries.size(); i++)
processBlockTx(nBlockHeight, entries[i]);
- // Update all exponential averages with the current block states
+ // Update all exponential averages with the current block state
feeStats.UpdateMovingAverages();
- priStats.UpdateMovingAverages();
LogPrint("estimatefee", "Blockpolicy after updating estimates for %u confirmed entries, new mempool map size %u\n",
entries.size(), mapMemPoolTxs.size());
@@ -495,7 +404,8 @@ void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight,
CFeeRate CBlockPolicyEstimator::estimateFee(int confTarget)
{
// Return failure if trying to analyze a target we're not tracking
- if (confTarget <= 0 || (unsigned int)confTarget > feeStats.GetMaxConfirms())
+ // It's not possible to get reasonable estimates for confTarget of 1
+ if (confTarget <= 1 || (unsigned int)confTarget > feeStats.GetMaxConfirms())
return CFeeRate(0);
double median = feeStats.EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true, nBestSeenHeight);
@@ -514,6 +424,10 @@ CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, int *answerFoun
if (confTarget <= 0 || (unsigned int)confTarget > feeStats.GetMaxConfirms())
return CFeeRate(0);
+ // It's not possible to get reasonable estimates for confTarget of 1
+ if (confTarget == 1)
+ confTarget = 2;
+
double median = -1;
while (median < 0 && (unsigned int)confTarget <= feeStats.GetMaxConfirms()) {
median = feeStats.EstimateMedianVal(confTarget++, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true, nBestSeenHeight);
@@ -522,7 +436,7 @@ CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, int *answerFoun
if (answerFoundAtTarget)
*answerFoundAtTarget = confTarget - 1;
- // If mempool is limiting txs , return at least the min fee from the mempool
+ // If mempool is limiting txs , return at least the min feerate from the mempool
CAmount minPoolFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK();
if (minPoolFee > 0 && minPoolFee > median)
return CFeeRate(minPoolFee);
@@ -535,51 +449,38 @@ CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, int *answerFoun
double CBlockPolicyEstimator::estimatePriority(int confTarget)
{
- // Return failure if trying to analyze a target we're not tracking
- if (confTarget <= 0 || (unsigned int)confTarget > priStats.GetMaxConfirms())
- return -1;
-
- return priStats.EstimateMedianVal(confTarget, SUFFICIENT_PRITXS, MIN_SUCCESS_PCT, true, nBestSeenHeight);
+ return -1;
}
double CBlockPolicyEstimator::estimateSmartPriority(int confTarget, int *answerFoundAtTarget, const CTxMemPool& pool)
{
if (answerFoundAtTarget)
*answerFoundAtTarget = confTarget;
- // Return failure if trying to analyze a target we're not tracking
- if (confTarget <= 0 || (unsigned int)confTarget > priStats.GetMaxConfirms())
- return -1;
// If mempool is limiting txs, no priority txs are allowed
CAmount minPoolFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK();
if (minPoolFee > 0)
return INF_PRIORITY;
- double median = -1;
- while (median < 0 && (unsigned int)confTarget <= priStats.GetMaxConfirms()) {
- median = priStats.EstimateMedianVal(confTarget++, SUFFICIENT_PRITXS, MIN_SUCCESS_PCT, true, nBestSeenHeight);
- }
-
- if (answerFoundAtTarget)
- *answerFoundAtTarget = confTarget - 1;
-
- return median;
+ return -1;
}
void CBlockPolicyEstimator::Write(CAutoFile& fileout)
{
fileout << nBestSeenHeight;
feeStats.Write(fileout);
- priStats.Write(fileout);
}
-void CBlockPolicyEstimator::Read(CAutoFile& filein)
+void CBlockPolicyEstimator::Read(CAutoFile& filein, int nFileVersion)
{
int nFileBestSeenHeight;
filein >> nFileBestSeenHeight;
feeStats.Read(filein);
- priStats.Read(filein);
nBestSeenHeight = nFileBestSeenHeight;
+ if (nFileVersion < 139900) {
+ TxConfirmStats priStats;
+ priStats.Read(filein);
+ }
}
FeeFilterRounder::FeeFilterRounder(const CFeeRate& minIncrementalFee)
@@ -594,7 +495,7 @@ FeeFilterRounder::FeeFilterRounder(const CFeeRate& minIncrementalFee)
CAmount FeeFilterRounder::round(CAmount currentMinFee)
{
std::set<double>::iterator it = feeset.lower_bound(currentMinFee);
- if ((it != feeset.begin() && insecure_rand() % 3 != 0) || it == feeset.end()) {
+ if ((it != feeset.begin() && insecure_rand.rand32() % 3 != 0) || it == feeset.end()) {
it--;
}
return *it;
diff --git a/src/policy/fees.h b/src/policy/fees.h
index 463f62f710..ea4c70e616 100644
--- a/src/policy/fees.h
+++ b/src/policy/fees.h
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2015 The Bitcoin developers
+// Copyright (c) 2009-2015 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_POLICYESTIMATOR_H
@@ -7,6 +7,7 @@
#include "amount.h"
#include "uint256.h"
+#include "random.h"
#include <map>
#include <string>
@@ -18,60 +19,50 @@ class CTxMemPoolEntry;
class CTxMemPool;
/** \class CBlockPolicyEstimator
- * The BlockPolicyEstimator is used for estimating the fee or priority needed
+ * The BlockPolicyEstimator is used for estimating the feerate needed
* for a transaction to be included in a block within a certain number of
* blocks.
*
* At a high level the algorithm works by grouping transactions into buckets
- * based on having similar priorities or fees and then tracking how long it
+ * based on having similar feerates and then tracking how long it
* takes transactions in the various buckets to be mined. It operates under
- * the assumption that in general transactions of higher fee/priority will be
- * included in blocks before transactions of lower fee/priority. So for
- * example if you wanted to know what fee you should put on a transaction to
+ * the assumption that in general transactions of higher feerate will be
+ * included in blocks before transactions of lower feerate. So for
+ * example if you wanted to know what feerate you should put on a transaction to
* be included in a block within the next 5 blocks, you would start by looking
- * at the bucket with the highest fee transactions and verifying that a
+ * at the bucket with the highest feerate transactions and verifying that a
* sufficiently high percentage of them were confirmed within 5 blocks and
- * then you would look at the next highest fee bucket, and so on, stopping at
- * the last bucket to pass the test. The average fee of transactions in this
- * bucket will give you an indication of the lowest fee you can put on a
+ * then you would look at the next highest feerate bucket, and so on, stopping at
+ * the last bucket to pass the test. The average feerate of transactions in this
+ * bucket will give you an indication of the lowest feerate you can put on a
* transaction and still have a sufficiently high chance of being confirmed
* within your desired 5 blocks.
*
- * When a transaction enters the mempool or is included within a block we
- * decide whether it can be used as a data point for fee estimation, priority
- * estimation or neither. If the value of exactly one of those properties was
- * below the required minimum it can be used to estimate the other. In
- * addition, if a priori our estimation code would indicate that the
- * transaction would be much more quickly included in a block because of one
- * of the properties compared to the other, we can also decide to use it as
- * an estimate for that property.
- *
- * Here is a brief description of the implementation for fee estimation.
- * When a transaction that counts for fee estimation enters the mempool, we
+ * Here is a brief description of the implementation:
+ * When a transaction enters the mempool, we
* track the height of the block chain at entry. Whenever a block comes in,
- * we count the number of transactions in each bucket and the total amount of fee
+ * we count the number of transactions in each bucket and the total amount of feerate
* paid in each bucket. Then we calculate how many blocks Y it took each
* transaction to be mined and we track an array of counters in each bucket
* for how long it to took transactions to get confirmed from 1 to a max of 25
* and we increment all the counters from Y up to 25. This is because for any
* number Z>=Y the transaction was successfully mined within Z blocks. We
* want to save a history of this information, so at any time we have a
- * counter of the total number of transactions that happened in a given fee
+ * counter of the total number of transactions that happened in a given feerate
* bucket and the total number that were confirmed in each number 1-25 blocks
* or less for any bucket. We save this history by keeping an exponentially
* decaying moving average of each one of these stats. Furthermore we also
* keep track of the number unmined (in mempool) transactions in each bucket
* and for how many blocks they have been outstanding and use that to increase
- * the number of transactions we've seen in that fee bucket when calculating
+ * the number of transactions we've seen in that feerate bucket when calculating
* an estimate for any number of confirmations below the number of blocks
* they've been outstanding.
*/
/**
- * We will instantiate two instances of this class, one to track transactions
- * that were included in a block due to fee, and one for tx's included due to
- * priority. We will lump transactions into a bucket according to their approximate
- * fee or priority and then track how long it took for those txs to be included in a block
+ * We will instantiate an instance of this class to track transactions that were
+ * included in a block. We will lump transactions into a bucket according to their
+ * approximate feerate and then track how long it took for those txs to be included in a block
*
* The tracking of unconfirmed (mempool) transactions is completely independent of the
* historical tracking of transactions that have been confirmed in a block.
@@ -79,7 +70,7 @@ class CTxMemPool;
class TxConfirmStats
{
private:
- //Define the buckets we will group transactions into (both fee buckets and priority buckets)
+ //Define the buckets we will group transactions into
std::vector<double> buckets; // The upper-bound of the range for the bucket (inclusive)
std::map<double, unsigned int> bucketMap; // Map of bucket upper-bound to index into all vectors by bucket
@@ -96,16 +87,15 @@ private:
// and calculate the totals for the current block to update the moving averages
std::vector<std::vector<int> > curBlockConf; // curBlockConf[Y][X]
- // Sum the total priority/fee of all tx's in each bucket
+ // Sum the total feerate of all tx's in each bucket
// Track the historical moving average of this total over blocks
std::vector<double> avg;
// and calculate the total for the current block to update the moving average
std::vector<double> curBlockVal;
// Combine the conf counts with tx counts to calculate the confirmation % for each Y,X
- // Combine the total value with the tx counts to calculate the avg fee/priority per bucket
+ // Combine the total value with the tx counts to calculate the avg feerate per bucket
- std::string dataTypeString;
double decay;
// Mempool counts of outstanding transactions
@@ -122,9 +112,8 @@ public:
* @param defaultBuckets contains the upper limits for the bucket boundaries
* @param maxConfirms max number of confirms to track
* @param decay how much to decay the historical moving average per block
- * @param dataTypeString for logging purposes
*/
- void Initialize(std::vector<double>& defaultBuckets, unsigned int maxConfirms, double decay, std::string dataTypeString);
+ void Initialize(std::vector<double>& defaultBuckets, unsigned int maxConfirms, double decay);
/** Clear the state of the curBlock variables to start counting for the new block */
void ClearCurrent(unsigned int nBlockHeight);
@@ -132,7 +121,7 @@ public:
/**
* Record a new transaction data point in the current block stats
* @param blocksToConfirm the number of blocks it took this transaction to confirm
- * @param val either the fee or the priority when entered of the transaction
+ * @param val the feerate of the transaction
* @warning blocksToConfirm is 1-based and has to be >= 1
*/
void Record(int blocksToConfirm, double val);
@@ -149,14 +138,14 @@ public:
void UpdateMovingAverages();
/**
- * Calculate a fee or priority estimate. Find the lowest value bucket (or range of buckets
+ * Calculate a feerate estimate. Find the lowest value bucket (or range of buckets
* to make sure we have enough data points) whose transactions still have sufficient likelihood
* of being confirmed within the target number of confirmations
* @param confTarget target number of confirmations
* @param sufficientTxVal required average number of transactions per block in a bucket range
* @param minSuccess the success probability we require
- * @param requireGreater return the lowest fee/pri such that all higher values pass minSuccess OR
- * return the highest fee/pri such that all lower values fail minSuccess
+ * @param requireGreater return the lowest feerate such that all higher values pass minSuccess OR
+ * return the highest feerate such that all lower values fail minSuccess
* @param nBlockHeight the current block height
*/
double EstimateMedianVal(int confTarget, double sufficientTxVal,
@@ -183,35 +172,26 @@ static const unsigned int MAX_BLOCK_CONFIRMS = 25;
/** Decay of .998 is a half-life of 346 blocks or about 2.4 days */
static const double DEFAULT_DECAY = .998;
-/** Require greater than 95% of X fee transactions to be confirmed within Y blocks for X to be big enough */
+/** Require greater than 95% of X feerate transactions to be confirmed within Y blocks for X to be big enough */
static const double MIN_SUCCESS_PCT = .95;
-static const double UNLIKELY_PCT = .5;
-/** Require an avg of 1 tx in the combined fee bucket per block to have stat significance */
+/** Require an avg of 1 tx in the combined feerate bucket per block to have stat significance */
static const double SUFFICIENT_FEETXS = 1;
-/** Require only an avg of 1 tx every 5 blocks in the combined pri bucket (way less pri txs) */
-static const double SUFFICIENT_PRITXS = .2;
-
-// Minimum and Maximum values for tracking fees and priorities
+// Minimum and Maximum values for tracking feerates
static const double MIN_FEERATE = 10;
static const double MAX_FEERATE = 1e7;
static const double INF_FEERATE = MAX_MONEY;
-static const double MIN_PRIORITY = 10;
-static const double MAX_PRIORITY = 1e16;
static const double INF_PRIORITY = 1e9 * MAX_MONEY;
-// We have to lump transactions into buckets based on fee or priority, but we want to be able
-// to give accurate estimates over a large range of potential fees and priorities
+// We have to lump transactions into buckets based on feerate, but we want to be able
+// to give accurate estimates over a large range of potential feerates
// Therefore it makes sense to exponentially space the buckets
/** Spacing of FeeRate buckets */
static const double FEE_SPACING = 1.1;
-/** Spacing of Priority buckets */
-static const double PRI_SPACING = 2;
-
/**
- * We want to be able to estimate fees or priorities that are needed on tx's to be included in
+ * We want to be able to estimate feerates that are needed on tx's to be included in
* a certain number of blocks. Every time a block is added to the best chain, this class records
* stats on the transactions included in that block
*/
@@ -234,27 +214,26 @@ public:
/** Remove a transaction from the mempool tracking stats*/
void removeTx(uint256 hash);
- /** Is this transaction likely included in a block because of its fee?*/
- bool isFeeDataPoint(const CFeeRate &fee, double pri);
-
- /** Is this transaction likely included in a block because of its priority?*/
- bool isPriDataPoint(const CFeeRate &fee, double pri);
-
- /** Return a fee estimate */
+ /** Return a feerate estimate */
CFeeRate estimateFee(int confTarget);
- /** Estimate fee rate needed to get be included in a block within
+ /** Estimate feerate needed to get be included in a block within
* confTarget blocks. If no answer can be given at confTarget, return an
* estimate at the lowest target where one can be given.
*/
CFeeRate estimateSmartFee(int confTarget, int *answerFoundAtTarget, const CTxMemPool& pool);
- /** Return a priority estimate */
+ /** Return a priority estimate.
+ * DEPRECATED
+ * Returns -1
+ */
double estimatePriority(int confTarget);
/** Estimate priority needed to get be included in a block within
- * confTarget blocks. If no answer can be given at confTarget, return an
- * estimate at the lowest target where one can be given.
+ * confTarget blocks.
+ * DEPRECATED
+ * Returns -1 unless mempool is currently limited then returns INF_PRIORITY
+ * answerFoundAtTarget is set to confTarget
*/
double estimateSmartPriority(int confTarget, int *answerFoundAtTarget, const CTxMemPool& pool);
@@ -262,29 +241,23 @@ public:
void Write(CAutoFile& fileout);
/** Read estimation data from a file */
- void Read(CAutoFile& filein);
+ void Read(CAutoFile& filein, int nFileVersion);
private:
CFeeRate minTrackedFee; //!< Passed to constructor to avoid dependency on main
- double minTrackedPriority; //!< Set to AllowFreeThreshold
unsigned int nBestSeenHeight;
struct TxStatsInfo
{
- TxConfirmStats *stats;
unsigned int blockHeight;
unsigned int bucketIndex;
- TxStatsInfo() : stats(NULL), blockHeight(0), bucketIndex(0) {}
+ TxStatsInfo() : blockHeight(0), bucketIndex(0) {}
};
// map of txids to information about that transaction
std::map<uint256, TxStatsInfo> mapMemPoolTxs;
/** Classes to track historical data on transaction confirmations */
- TxConfirmStats feeStats, priStats;
-
- /** Breakpoints to help determine whether a transaction was confirmed by priority or Fee */
- CFeeRate feeLikely, feeUnlikely;
- double priLikely, priUnlikely;
+ TxConfirmStats feeStats;
};
class FeeFilterRounder
@@ -298,5 +271,6 @@ public:
private:
std::set<double> feeset;
+ FastRandomContext insecure_rand;
};
#endif /*BITCOIN_POLICYESTIMATOR_H */
diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp
index 48080abc77..0c71a079fb 100644
--- a/src/policy/policy.cpp
+++ b/src/policy/policy.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2015 The Bitcoin developers
+// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -7,7 +7,7 @@
#include "policy/policy.h"
-#include "main.h"
+#include "validation.h"
#include "tinyformat.h"
#include "util.h"
#include "utilstrencodings.h"
@@ -66,7 +66,7 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnes
// 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.
+ // to MAX_STANDARD_TX_WEIGHT mitigates CPU exhaustion attacks.
unsigned int sz = GetTransactionWeight(tx);
if (sz >= MAX_STANDARD_TX_WEIGHT) {
reason = "tx-size";
@@ -154,6 +154,58 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
return true;
}
+bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
+{
+ if (tx.IsCoinBase())
+ return true; // Coinbases are skipped
+
+ for (unsigned int i = 0; i < tx.vin.size(); i++)
+ {
+ // We don't care if witness for this input is empty, since it must not be bloated.
+ // If the script is invalid without witness, it would be caught sooner or later during validation.
+ if (tx.wit.vtxinwit[i].IsNull())
+ continue;
+
+ const CTxOut &prev = mapInputs.GetOutputFor(tx.vin[i]);
+
+ // get the scriptPubKey corresponding to this input:
+ CScript prevScript = prev.scriptPubKey;
+
+ if (prevScript.IsPayToScriptHash()) {
+ std::vector <std::vector<unsigned char> > stack;
+ // If the scriptPubKey is P2SH, we try to extract the redeemScript casually by converting the scriptSig
+ // into a stack. We do not check IsPushOnly nor compare the hash as these will be done later anyway.
+ // If the check fails at this stage, we know that this txid must be a bad one.
+ if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), SIGVERSION_BASE))
+ return false;
+ if (stack.empty())
+ return false;
+ prevScript = CScript(stack.back().begin(), stack.back().end());
+ }
+
+ int witnessversion = 0;
+ std::vector<unsigned char> witnessprogram;
+
+ // Non-witness program must not be associated with any witness
+ if (!prevScript.IsWitnessProgram(witnessversion, witnessprogram))
+ return false;
+
+ // Check P2WSH standard limits
+ if (witnessversion == 0 && witnessprogram.size() == 32) {
+ if (tx.wit.vtxinwit[i].scriptWitness.stack.back().size() > MAX_STANDARD_P2WSH_SCRIPT_SIZE)
+ return false;
+ size_t sizeWitnessStack = tx.wit.vtxinwit[i].scriptWitness.stack.size() - 1;
+ if (sizeWitnessStack > MAX_STANDARD_P2WSH_STACK_ITEMS)
+ return false;
+ for (unsigned int j = 0; j < sizeWitnessStack; j++) {
+ if (tx.wit.vtxinwit[i].scriptWitness.stack[j].size() > MAX_STANDARD_P2WSH_STACK_ITEM_SIZE)
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP;
int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost)
diff --git a/src/policy/policy.h b/src/policy/policy.h
index 6bf5ca0ee5..764ee27806 100644
--- a/src/policy/policy.h
+++ b/src/policy/policy.h
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2015 The Bitcoin developers
+// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -30,6 +30,12 @@ static const unsigned int MAX_STANDARD_TX_SIGOPS_COST = MAX_BLOCK_SIGOPS_COST/5;
static const unsigned int DEFAULT_MAX_MEMPOOL_SIZE = 300;
/** Default for -bytespersigop */
static const unsigned int DEFAULT_BYTES_PER_SIGOP = 20;
+/** The maximum number of witness stack items in a standard P2WSH script */
+static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEMS = 100;
+/** The maximum size of each witness stack item in a standard P2WSH script */
+static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEM_SIZE = 80;
+/** The maximum size of a standard witnessScript */
+static const unsigned int MAX_STANDARD_P2WSH_SCRIPT_SIZE = 3600;
/**
* Standard script verification flags that standard transactions will comply
* with. However scripts violating these flags may still be present in valid
@@ -42,11 +48,14 @@ static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY
SCRIPT_VERIFY_NULLDUMMY |
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS |
SCRIPT_VERIFY_CLEANSTACK |
+ SCRIPT_VERIFY_MINIMALIF |
+ SCRIPT_VERIFY_NULLFAIL |
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY |
SCRIPT_VERIFY_CHECKSEQUENCEVERIFY |
SCRIPT_VERIFY_LOW_S |
SCRIPT_VERIFY_WITNESS |
- SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM;
+ SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM |
+ SCRIPT_VERIFY_WITNESS_PUBKEYTYPE;
/** For convenience, standard but not mandatory verify flags. */
static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS;
@@ -67,6 +76,12 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnes
* @return True if all inputs (scriptSigs) use only standard transaction forms
*/
bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs);
+ /**
+ * Check if the transaction is over standard P2WSH resources limit:
+ * 3600bytes witnessScript size, 80bytes per witness stack element, 100 witness stack elements
+ * These limits are adequate for multi-signature up to n-of-100 using OP_CHECKSIG, OP_ADD, and OP_EQUAL,
+ */
+bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs);
extern unsigned int nBytesPerSigOp;
diff --git a/src/policy/rbf.cpp b/src/policy/rbf.cpp
index 133cff611d..d9b47e71bb 100644
--- a/src/policy/rbf.cpp
+++ b/src/policy/rbf.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 The Bitcoin developers
+// Copyright (c) 2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/policy/rbf.h b/src/policy/rbf.h
index 5a711dba07..139aec5760 100644
--- a/src/policy/rbf.h
+++ b/src/policy/rbf.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 The Bitcoin developers
+// Copyright (c) 2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/prevector.h b/src/prevector.h
index a0e1e140b4..25bce522dc 100644
--- a/src/prevector.h
+++ b/src/prevector.h
@@ -475,6 +475,14 @@ public:
return ((size_t)(sizeof(T))) * _union.capacity;
}
}
+
+ value_type* data() {
+ return item_ptr(0);
+ }
+
+ const value_type* data() const {
+ return item_ptr(0);
+ }
};
#pragma pack(pop)
diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp
index 0e6ab4dd71..95bd2211f9 100644
--- a/src/primitives/block.cpp
+++ b/src/primitives/block.cpp
@@ -27,7 +27,7 @@ std::string CBlock::ToString() const
vtx.size());
for (unsigned int i = 0; i < vtx.size(); i++)
{
- s << " " << vtx[i].ToString() << "\n";
+ s << " " << vtx[i]->ToString() << "\n";
}
return s.str();
}
diff --git a/src/primitives/block.h b/src/primitives/block.h
index 72dfed985a..b037fc839c 100644
--- a/src/primitives/block.h
+++ b/src/primitives/block.h
@@ -36,7 +36,7 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(this->nVersion);
READWRITE(hashPrevBlock);
READWRITE(hashMerkleRoot);
@@ -73,7 +73,7 @@ class CBlock : public CBlockHeader
{
public:
// network and disk
- std::vector<CTransaction> vtx;
+ std::vector<CTransactionRef> vtx;
// memory only
mutable bool fChecked;
@@ -92,7 +92,7 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(*(CBlockHeader*)this);
READWRITE(vtx);
}
@@ -137,8 +137,9 @@ struct CBlockLocator
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
- if (!(nType & SER_GETHASH))
+ inline void SerializationOp(Stream& s, Operation ser_action) {
+ int nVersion = s.GetVersion();
+ if (!(s.GetType() & SER_GETHASH))
READWRITE(nVersion);
READWRITE(vHave);
}
diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp
index 2fdc59ea07..11d7eace55 100644
--- a/src/primitives/transaction.cpp
+++ b/src/primitives/transaction.cpp
@@ -49,11 +49,6 @@ CTxOut::CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn)
scriptPubKey = scriptPubKeyIn;
}
-uint256 CTxOut::GetHash() const
-{
- return SerializeHash(*this);
-}
-
std::string CTxOut::ToString() const
{
return strprintf("CTxOut(nValue=%d.%08d, scriptPubKey=%s)", nValue / COIN, nValue % COIN, HexStr(scriptPubKey).substr(0, 30));
@@ -67,9 +62,9 @@ uint256 CMutableTransaction::GetHash() const
return SerializeHash(*this, SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS);
}
-void CTransaction::UpdateHash() const
+uint256 CTransaction::ComputeHash() const
{
- *const_cast<uint256*>(&hash) = SerializeHash(*this, SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS);
+ return SerializeHash(*this, SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS);
}
uint256 CTransaction::GetWitnessHash() const
@@ -77,21 +72,10 @@ uint256 CTransaction::GetWitnessHash() const
return SerializeHash(*this, SER_GETHASH, 0);
}
-CTransaction::CTransaction() : nVersion(CTransaction::CURRENT_VERSION), vin(), vout(), nLockTime(0) { }
-
-CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), wit(tx.wit), nLockTime(tx.nLockTime) {
- UpdateHash();
-}
-
-CTransaction& CTransaction::operator=(const CTransaction &tx) {
- *const_cast<int*>(&nVersion) = tx.nVersion;
- *const_cast<std::vector<CTxIn>*>(&vin) = tx.vin;
- *const_cast<std::vector<CTxOut>*>(&vout) = tx.vout;
- *const_cast<CTxWitness*>(&wit) = tx.wit;
- *const_cast<unsigned int*>(&nLockTime) = tx.nLockTime;
- *const_cast<uint256*>(&hash) = tx.hash;
- return *this;
-}
+/* For backward compatibility, the hash is initialized to 0. TODO: remove the need for this default constructor entirely. */
+CTransaction::CTransaction() : nVersion(CTransaction::CURRENT_VERSION), vin(), vout(), nLockTime(0), hash() {}
+CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), wit(tx.wit), nLockTime(tx.nLockTime), hash(ComputeHash()) {}
+CTransaction::CTransaction(CMutableTransaction &&tx) : nVersion(tx.nVersion), vin(std::move(tx.vin)), vout(std::move(tx.vout)), wit(std::move(tx.wit)), nLockTime(tx.nLockTime), hash(ComputeHash()) {}
CAmount CTransaction::GetValueOut() const
{
@@ -131,6 +115,11 @@ unsigned int CTransaction::CalculateModifiedSize(unsigned int nTxSize) const
return nTxSize;
}
+unsigned int CTransaction::GetTotalSize() const
+{
+ return ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION);
+}
+
std::string CTransaction::ToString() const
{
std::string str;
diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h
index 5689d15bf7..b594cab458 100644
--- a/src/primitives/transaction.h
+++ b/src/primitives/transaction.h
@@ -28,7 +28,7 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(hash);
READWRITE(n);
}
@@ -104,7 +104,7 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(prevout);
READWRITE(*(CScriptBase*)(&scriptSig));
READWRITE(nSequence);
@@ -144,7 +144,7 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(nValue);
READWRITE(*(CScriptBase*)(&scriptPubKey));
}
@@ -160,8 +160,6 @@ public:
return (nValue == -1);
}
- uint256 GetHash() const;
-
CAmount GetDustThreshold(const CFeeRate &minRelayTxFee) const
{
// "Dust" is defined in terms of CTransaction::minRelayTxFee,
@@ -179,7 +177,7 @@ public:
if (scriptPubKey.IsUnspendable())
return 0;
- size_t nSize = GetSerializeSize(SER_DISK, 0);
+ size_t nSize = GetSerializeSize(*this, SER_DISK, 0);
int witnessversion = 0;
std::vector<unsigned char> witnessprogram;
@@ -221,7 +219,7 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
+ inline void SerializationOp(Stream& s, Operation ser_action)
{
READWRITE(scriptWitness.stack);
}
@@ -257,7 +255,7 @@ public:
}
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
+ inline void SerializationOp(Stream& s, Operation ser_action)
{
for (size_t n = 0; n < vtxinwit.size(); n++) {
READWRITE(vtxinwit[n]);
@@ -288,76 +286,84 @@ struct CMutableTransaction;
* - CTxWitness wit;
* - uint32_t nLockTime
*/
-template<typename Stream, typename Operation, typename TxType>
-inline void SerializeTransaction(TxType& tx, Stream& s, Operation ser_action, int nType, int nVersion) {
- const bool fAllowWitness = !(nVersion & SERIALIZE_TRANSACTION_NO_WITNESS);
+template<typename Stream, typename TxType>
+inline void UnserializeTransaction(TxType& tx, Stream& s) {
+ const bool fAllowWitness = !(s.GetVersion() & SERIALIZE_TRANSACTION_NO_WITNESS);
- READWRITE(*const_cast<int32_t*>(&tx.nVersion));
+ s >> tx.nVersion;
unsigned char flags = 0;
- if (ser_action.ForRead()) {
- const_cast<std::vector<CTxIn>*>(&tx.vin)->clear();
- const_cast<std::vector<CTxOut>*>(&tx.vout)->clear();
- const_cast<CTxWitness*>(&tx.wit)->SetNull();
- /* Try to read the vin. In case the dummy is there, this will be read as an empty vector. */
- READWRITE(*const_cast<std::vector<CTxIn>*>(&tx.vin));
- if (tx.vin.size() == 0 && fAllowWitness) {
- /* We read a dummy or an empty vin. */
- READWRITE(flags);
- if (flags != 0) {
- READWRITE(*const_cast<std::vector<CTxIn>*>(&tx.vin));
- READWRITE(*const_cast<std::vector<CTxOut>*>(&tx.vout));
- }
- } else {
- /* We read a non-empty vin. Assume a normal vout follows. */
- READWRITE(*const_cast<std::vector<CTxOut>*>(&tx.vout));
- }
- if ((flags & 1) && fAllowWitness) {
- /* The witness flag is present, and we support witnesses. */
- flags ^= 1;
- const_cast<CTxWitness*>(&tx.wit)->vtxinwit.resize(tx.vin.size());
- READWRITE(tx.wit);
- }
- if (flags) {
- /* Unknown flag in the serialization */
- throw std::ios_base::failure("Unknown transaction optional data");
+ tx.vin.clear();
+ tx.vout.clear();
+ tx.wit.SetNull();
+ /* Try to read the vin. In case the dummy is there, this will be read as an empty vector. */
+ s >> tx.vin;
+ if (tx.vin.size() == 0 && fAllowWitness) {
+ /* We read a dummy or an empty vin. */
+ s >> flags;
+ if (flags != 0) {
+ s >> tx.vin;
+ s >> tx.vout;
}
} else {
- // Consistency check
- assert(tx.wit.vtxinwit.size() <= tx.vin.size());
- if (fAllowWitness) {
- /* Check whether witnesses need to be serialized. */
- if (!tx.wit.IsNull()) {
- flags |= 1;
- }
- }
- if (flags) {
- /* Use extended format in case witnesses are to be serialized. */
- std::vector<CTxIn> vinDummy;
- READWRITE(vinDummy);
- READWRITE(flags);
+ /* We read a non-empty vin. Assume a normal vout follows. */
+ s >> tx.vout;
+ }
+ if ((flags & 1) && fAllowWitness) {
+ /* The witness flag is present, and we support witnesses. */
+ flags ^= 1;
+ tx.wit.vtxinwit.resize(tx.vin.size());
+ s >> tx.wit;
+ }
+ if (flags) {
+ /* Unknown flag in the serialization */
+ throw std::ios_base::failure("Unknown transaction optional data");
+ }
+ s >> tx.nLockTime;
+}
+
+template<typename Stream, typename TxType>
+inline void SerializeTransaction(const TxType& tx, Stream& s) {
+ const bool fAllowWitness = !(s.GetVersion() & SERIALIZE_TRANSACTION_NO_WITNESS);
+
+ s << tx.nVersion;
+ unsigned char flags = 0;
+ // Consistency check
+ assert(tx.wit.vtxinwit.size() <= tx.vin.size());
+ if (fAllowWitness) {
+ /* Check whether witnesses need to be serialized. */
+ if (!tx.wit.IsNull()) {
+ flags |= 1;
}
- READWRITE(*const_cast<std::vector<CTxIn>*>(&tx.vin));
- READWRITE(*const_cast<std::vector<CTxOut>*>(&tx.vout));
- if (flags & 1) {
- const_cast<CTxWitness*>(&tx.wit)->vtxinwit.resize(tx.vin.size());
- READWRITE(tx.wit);
+ }
+ if (flags) {
+ /* Use extended format in case witnesses are to be serialized. */
+ std::vector<CTxIn> vinDummy;
+ s << vinDummy;
+ s << flags;
+ }
+ s << tx.vin;
+ s << tx.vout;
+ if (flags & 1) {
+ for (size_t i = 0; i < tx.vin.size(); i++) {
+ if (i < tx.wit.vtxinwit.size()) {
+ s << tx.wit.vtxinwit[i];
+ } else {
+ s << CTxInWitness();
+ }
}
}
- READWRITE(*const_cast<uint32_t*>(&tx.nLockTime));
+ s << tx.nLockTime;
}
+
/** The basic transaction that is broadcasted on the network and contained in
* blocks. A transaction can contain multiple inputs and outputs.
*/
class CTransaction
{
-private:
- /** Memory only. */
- const uint256 hash;
-
public:
// Default transaction version.
- static const int32_t CURRENT_VERSION=1;
+ static const int32_t CURRENT_VERSION=2;
// Changing the default transaction version requires a two step process: first
// adapting relay policy by bumping MAX_STANDARD_VERSION, and then later date
@@ -376,24 +382,30 @@ public:
CTxWitness wit; // Not const: can change without invalidating the txid cache
const uint32_t nLockTime;
+private:
+ /** Memory only. */
+ const uint256 hash;
+
+ uint256 ComputeHash() const;
+
+public:
/** Construct a CTransaction that qualifies as IsNull() */
CTransaction();
/** Convert a CMutableTransaction into a CTransaction. */
CTransaction(const CMutableTransaction &tx);
+ CTransaction(CMutableTransaction &&tx);
- CTransaction& operator=(const CTransaction& tx);
-
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
- SerializeTransaction(*this, s, ser_action, nType, nVersion);
- if (ser_action.ForRead()) {
- UpdateHash();
- }
+ template <typename Stream>
+ inline void Serialize(Stream& s) const {
+ SerializeTransaction(*this, s);
}
+ /** This deserializing constructor is provided instead of an Unserialize method.
+ * Unserialize is not possible, since it would require overwriting const fields. */
+ template <typename Stream>
+ CTransaction(deserialize_type, Stream& s) : CTransaction(CMutableTransaction(deserialize, s)) {}
+
bool IsNull() const {
return vin.empty() && vout.empty();
}
@@ -416,6 +428,13 @@ public:
// Compute modified tx size for priority calculation (optionally given tx size)
unsigned int CalculateModifiedSize(unsigned int nTxSize=0) const;
+ /**
+ * Get the total transaction size in bytes, including witness data.
+ * "Total Size" defined in BIP141 and BIP144.
+ * @return Total transaction size in bytes
+ */
+ unsigned int GetTotalSize() const;
+
bool IsCoinBase() const
{
return (vin.size() == 1 && vin[0].prevout.IsNull());
@@ -432,8 +451,6 @@ public:
}
std::string ToString() const;
-
- void UpdateHash() const;
};
/** A mutable version of CTransaction. */
@@ -448,11 +465,20 @@ struct CMutableTransaction
CMutableTransaction();
CMutableTransaction(const CTransaction& tx);
- ADD_SERIALIZE_METHODS;
+ template <typename Stream>
+ inline void Serialize(Stream& s) const {
+ SerializeTransaction(*this, s);
+ }
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
- SerializeTransaction(*this, s, ser_action, nType, nVersion);
+
+ template <typename Stream>
+ inline void Unserialize(Stream& s) {
+ UnserializeTransaction(*this, s);
+ }
+
+ template <typename Stream>
+ CMutableTransaction(deserialize_type, Stream& s) {
+ Unserialize(s);
}
/** Compute the hash of this CMutableTransaction. This is computed on the
@@ -461,6 +487,12 @@ struct CMutableTransaction
uint256 GetHash() const;
};
+typedef std::shared_ptr<const CTransaction> CTransactionRef;
+static inline CTransactionRef MakeTransactionRef() { return std::make_shared<const CTransaction>(); }
+template <typename Tx> static inline CTransactionRef MakeTransactionRef(Tx&& txIn) { return std::make_shared<const CTransaction>(std::forward<Tx>(txIn)); }
+static inline CTransactionRef MakeTransactionRef(const CTransactionRef& txIn) { return txIn; }
+static inline CTransactionRef MakeTransactionRef(CTransactionRef&& txIn) { return std::move(txIn); }
+
/** Compute the weight of a transaction, as defined by BIP 141 */
int64_t GetTransactionWeight(const CTransaction &tx);
diff --git a/src/protocol.cpp b/src/protocol.cpp
index 247c6c2120..87d6e06848 100644
--- a/src/protocol.cpp
+++ b/src/protocol.cpp
@@ -79,7 +79,7 @@ CMessageHeader::CMessageHeader(const MessageStartChars& pchMessageStartIn)
memcpy(pchMessageStart, pchMessageStartIn, MESSAGE_START_SIZE);
memset(pchCommand, 0, sizeof(pchCommand));
nMessageSize = -1;
- nChecksum = 0;
+ memset(pchChecksum, 0, CHECKSUM_SIZE);
}
CMessageHeader::CMessageHeader(const MessageStartChars& pchMessageStartIn, const char* pszCommand, unsigned int nMessageSizeIn)
@@ -88,7 +88,7 @@ CMessageHeader::CMessageHeader(const MessageStartChars& pchMessageStartIn, const
memset(pchCommand, 0, sizeof(pchCommand));
strncpy(pchCommand, pszCommand, COMMAND_SIZE);
nMessageSize = nMessageSizeIn;
- nChecksum = 0;
+ memset(pchChecksum, 0, CHECKSUM_SIZE);
}
std::string CMessageHeader::GetCommand() const
@@ -181,7 +181,11 @@ std::string CInv::GetCommand() const
std::string CInv::ToString() const
{
- return strprintf("%s %s", GetCommand(), hash.ToString());
+ try {
+ return strprintf("%s %s", GetCommand(), hash.ToString());
+ } catch(const std::out_of_range &) {
+ return strprintf("0x%08x %s", type, hash.ToString());
+ }
}
const std::vector<std::string> &getAllNetMessageTypes()
diff --git a/src/protocol.h b/src/protocol.h
index 9b474ec79c..dea409c5b7 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -18,8 +18,6 @@
#include <stdint.h>
#include <string>
-#define MESSAGE_START_SIZE 4
-
/** Message header.
* (4) message start.
* (12) command.
@@ -29,6 +27,16 @@
class CMessageHeader
{
public:
+ enum {
+ MESSAGE_START_SIZE = 4,
+ COMMAND_SIZE = 12,
+ MESSAGE_SIZE_SIZE = 4,
+ CHECKSUM_SIZE = 4,
+
+ 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
+ };
typedef unsigned char MessageStartChars[MESSAGE_START_SIZE];
CMessageHeader(const MessageStartChars& pchMessageStartIn);
@@ -40,29 +48,18 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
+ inline void SerializationOp(Stream& s, Operation ser_action)
{
READWRITE(FLATDATA(pchMessageStart));
READWRITE(FLATDATA(pchCommand));
READWRITE(nMessageSize);
- READWRITE(nChecksum);
+ READWRITE(FLATDATA(pchChecksum));
}
- // TODO: make private (improves encapsulation)
-public:
- enum {
- 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;
+ uint32_t nMessageSize;
+ uint8_t pchChecksum[CHECKSUM_SIZE];
};
/**
@@ -264,7 +261,7 @@ enum ServiceFlags : uint64_t {
// Bitcoin Core nodes used to support this by default, without advertising this bit,
// but no longer do as of protocol version 70011 (= NO_BLOOM_VERSION)
NODE_BLOOM = (1 << 2),
- // Indicates that a node can be asked for blocks and transactions including
+ // NODE_WITNESS indicates that a node can be asked for blocks and transactions including
// witness data.
NODE_WITNESS = (1 << 3),
// NODE_XTHIN means the node supports Xtreme Thinblocks
@@ -292,14 +289,15 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
+ inline void SerializationOp(Stream& s, Operation ser_action)
{
if (ser_action.ForRead())
Init();
- if (nType & SER_DISK)
+ int nVersion = s.GetVersion();
+ if (s.GetType() & SER_DISK)
READWRITE(nVersion);
- if ((nType & SER_DISK) ||
- (nVersion >= CADDR_TIME_VERSION && !(nType & SER_GETHASH)))
+ if ((s.GetType() & SER_DISK) ||
+ (nVersion >= CADDR_TIME_VERSION && !(s.GetType() & SER_GETHASH)))
READWRITE(nTime);
uint64_t nServicesInt = nServices;
READWRITE(nServicesInt);
@@ -315,20 +313,24 @@ public:
unsigned int nTime;
};
-/** getdata message types */
+/** getdata message type flags */
const uint32_t MSG_WITNESS_FLAG = 1 << 30;
const uint32_t MSG_TYPE_MASK = 0xffffffff >> 2;
+
+/** getdata / inv message types.
+ * These numbers are defined by the protocol. When adding a new value, be sure
+ * to mention it in the respective BIP.
+ */
enum GetDataMsg
{
UNDEFINED = 0,
- MSG_TX,
- MSG_BLOCK,
- MSG_TYPE_MAX = MSG_BLOCK,
+ MSG_TX = 1,
+ MSG_BLOCK = 2,
// The following can only occur in getdata. Invs always use TX or BLOCK.
- MSG_FILTERED_BLOCK,
- MSG_CMPCT_BLOCK,
- MSG_WITNESS_BLOCK = MSG_BLOCK | MSG_WITNESS_FLAG,
- MSG_WITNESS_TX = MSG_TX | MSG_WITNESS_FLAG,
+ MSG_FILTERED_BLOCK = 3, //!< Defined in BIP37
+ MSG_CMPCT_BLOCK = 4, //!< Defined in BIP152
+ MSG_WITNESS_BLOCK = MSG_BLOCK | MSG_WITNESS_FLAG, //!< Defined in BIP144
+ MSG_WITNESS_TX = MSG_TX | MSG_WITNESS_FLAG, //!< Defined in BIP144
MSG_FILTERED_WITNESS_BLOCK = MSG_FILTERED_BLOCK | MSG_WITNESS_FLAG,
};
@@ -342,7 +344,7 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
+ inline void SerializationOp(Stream& s, Operation ser_action)
{
READWRITE(type);
READWRITE(hash);
diff --git a/src/pubkey.cpp b/src/pubkey.cpp
index be4ee27cd4..91a657593a 100644
--- a/src/pubkey.cpp
+++ b/src/pubkey.cpp
@@ -264,12 +264,12 @@ void CExtPubKey::Decode(const unsigned char code[BIP32_EXTKEY_SIZE]) {
pubkey.Set(code+41, code+BIP32_EXTKEY_SIZE);
}
-bool CExtPubKey::Derive(CExtPubKey &out, unsigned int nChild) const {
+bool CExtPubKey::Derive(CExtPubKey &out, unsigned int _nChild) const {
out.nDepth = nDepth + 1;
CKeyID id = pubkey.GetID();
memcpy(&out.vchFingerprint[0], &id, 4);
- out.nChild = nChild;
- return pubkey.Derive(out.pubkey, out.chaincode, nChild, chaincode);
+ out.nChild = _nChild;
+ return pubkey.Derive(out.pubkey, out.chaincode, _nChild, chaincode);
}
/* static */ bool CPubKey::CheckLowS(const std::vector<unsigned char>& vchSig) {
diff --git a/src/pubkey.h b/src/pubkey.h
index aebfdbc826..9499862210 100644
--- a/src/pubkey.h
+++ b/src/pubkey.h
@@ -88,9 +88,9 @@ public:
}
//! Construct a public key from a byte vector.
- CPubKey(const std::vector<unsigned char>& vch)
+ CPubKey(const std::vector<unsigned char>& _vch)
{
- Set(vch.begin(), vch.end());
+ Set(_vch.begin(), _vch.end());
}
//! Simple read-only vector-like interface to the pubkey data.
@@ -116,19 +116,15 @@ public:
}
//! Implement serialization, as if this was a byte vector.
- unsigned int GetSerializeSize(int nType, int nVersion) const
- {
- return size() + 1;
- }
template <typename Stream>
- void Serialize(Stream& s, int nType, int nVersion) const
+ void Serialize(Stream& s) const
{
unsigned int len = size();
::WriteCompactSize(s, len);
s.write((char*)vch, len);
}
template <typename Stream>
- void Unserialize(Stream& s, int nType, int nVersion)
+ void Unserialize(Stream& s)
{
unsigned int len = ::ReadCompactSize(s);
if (len <= 65) {
@@ -214,12 +210,13 @@ struct CExtPubKey {
void Decode(const unsigned char code[BIP32_EXTKEY_SIZE]);
bool Derive(CExtPubKey& out, unsigned int nChild) const;
- unsigned int GetSerializeSize(int nType, int nVersion) const
+ void Serialize(CSizeComputer& s) const
{
- return BIP32_EXTKEY_SIZE+1; //add one byte for the size (compact int)
+ // Optimized implementation for ::GetSerializeSize that avoids copying.
+ s.seek(BIP32_EXTKEY_SIZE + 1); // add one byte for the size (compact int)
}
template <typename Stream>
- void Serialize(Stream& s, int nType, int nVersion) const
+ void Serialize(Stream& s) const
{
unsigned int len = BIP32_EXTKEY_SIZE;
::WriteCompactSize(s, len);
@@ -228,7 +225,7 @@ struct CExtPubKey {
s.write((const char *)&code[0], len);
}
template <typename Stream>
- void Unserialize(Stream& s, int nType, int nVersion)
+ void Unserialize(Stream& s)
{
unsigned int len = ::ReadCompactSize(s);
unsigned char code[BIP32_EXTKEY_SIZE];
diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp
index 135f15ffa8..6076837fde 100644
--- a/src/qt/addressbookpage.cpp
+++ b/src/qt/addressbookpage.cpp
@@ -21,12 +21,12 @@
#include <QMessageBox>
#include <QSortFilterProxyModel>
-AddressBookPage::AddressBookPage(const PlatformStyle *platformStyle, Mode mode, Tabs tab, QWidget *parent) :
+AddressBookPage::AddressBookPage(const PlatformStyle *platformStyle, Mode _mode, Tabs _tab, QWidget *parent) :
QDialog(parent),
ui(new Ui::AddressBookPage),
model(0),
- mode(mode),
- tab(tab)
+ mode(_mode),
+ tab(_tab)
{
ui->setupUi(this);
@@ -83,7 +83,7 @@ AddressBookPage::AddressBookPage(const PlatformStyle *platformStyle, Mode mode,
deleteAction = new QAction(ui->deleteAddress->text(), this);
// Build context menu
- contextMenu = new QMenu();
+ contextMenu = new QMenu(this);
contextMenu->addAction(copyAddressAction);
contextMenu->addAction(copyLabelAction);
contextMenu->addAction(editAction);
@@ -107,14 +107,14 @@ AddressBookPage::~AddressBookPage()
delete ui;
}
-void AddressBookPage::setModel(AddressTableModel *model)
+void AddressBookPage::setModel(AddressTableModel *_model)
{
- this->model = model;
- if(!model)
+ this->model = _model;
+ if(!_model)
return;
proxyModel = new QSortFilterProxyModel(this);
- proxyModel->setSourceModel(model);
+ proxyModel->setSourceModel(_model);
proxyModel->setDynamicSortFilter(true);
proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
@@ -147,7 +147,7 @@ void AddressBookPage::setModel(AddressTableModel *model)
this, SLOT(selectionChanged()));
// Select row for newly created address
- connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(selectNewAddress(QModelIndex,int,int)));
+ connect(_model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(selectNewAddress(QModelIndex,int,int)));
selectionChanged();
}
diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp
index 71ed3618e4..830c9cdf19 100644
--- a/src/qt/addresstablemodel.cpp
+++ b/src/qt/addresstablemodel.cpp
@@ -31,8 +31,8 @@ struct AddressTableEntry
QString address;
AddressTableEntry() {}
- AddressTableEntry(Type type, const QString &label, const QString &address):
- type(type), label(label), address(address) {}
+ AddressTableEntry(Type _type, const QString &_label, const QString &_address):
+ type(_type), label(_label), address(_address) {}
};
struct AddressTableEntryLessThan
@@ -73,8 +73,8 @@ public:
QList<AddressTableEntry> cachedAddressTable;
AddressTableModel *parent;
- AddressTablePriv(CWallet *wallet, AddressTableModel *parent):
- wallet(wallet), parent(parent) {}
+ AddressTablePriv(CWallet *_wallet, AddressTableModel *_parent):
+ wallet(_wallet), parent(_parent) {}
void refreshAddressTable()
{
@@ -164,8 +164,8 @@ public:
}
};
-AddressTableModel::AddressTableModel(CWallet *wallet, WalletModel *parent) :
- QAbstractTableModel(parent),walletModel(parent),wallet(wallet),priv(0)
+AddressTableModel::AddressTableModel(CWallet *_wallet, WalletModel *parent) :
+ QAbstractTableModel(parent),walletModel(parent),wallet(_wallet),priv(0)
{
columns << tr("Label") << tr("Address");
priv = new AddressTablePriv(wallet, this);
diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp
index e8aa79679c..129ea1efa4 100644
--- a/src/qt/askpassphrasedialog.cpp
+++ b/src/qt/askpassphrasedialog.cpp
@@ -18,10 +18,10 @@
#include <QMessageBox>
#include <QPushButton>
-AskPassphraseDialog::AskPassphraseDialog(Mode mode, QWidget *parent) :
+AskPassphraseDialog::AskPassphraseDialog(Mode _mode, QWidget *parent) :
QDialog(parent),
ui(new Ui::AskPassphraseDialog),
- mode(mode),
+ mode(_mode),
model(0),
fCapsLock(false)
{
@@ -81,9 +81,9 @@ AskPassphraseDialog::~AskPassphraseDialog()
delete ui;
}
-void AskPassphraseDialog::setModel(WalletModel *model)
+void AskPassphraseDialog::setModel(WalletModel *_model)
{
- this->model = model;
+ this->model = _model;
}
void AskPassphraseDialog::accept()
diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp
index 6e11e23904..fe53d89ba9 100644
--- a/src/qt/bantablemodel.cpp
+++ b/src/qt/bantablemodel.cpp
@@ -87,7 +87,7 @@ BanTableModel::BanTableModel(ClientModel *parent) :
clientModel(parent)
{
columns << tr("IP/Netmask") << tr("Banned Until");
- priv = new BanTablePriv();
+ priv.reset(new BanTablePriv());
// default to unsorted
priv->sortColumn = -1;
@@ -95,6 +95,11 @@ BanTableModel::BanTableModel(ClientModel *parent) :
refresh();
}
+BanTableModel::~BanTableModel()
+{
+ // Intentionally left empty
+}
+
int BanTableModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
diff --git a/src/qt/bantablemodel.h b/src/qt/bantablemodel.h
index fe9600ac0b..3c03d05c05 100644
--- a/src/qt/bantablemodel.h
+++ b/src/qt/bantablemodel.h
@@ -40,6 +40,7 @@ class BanTableModel : public QAbstractTableModel
public:
explicit BanTableModel(ClientModel *parent = 0);
+ ~BanTableModel();
void startAutoRefresh();
void stopAutoRefresh();
@@ -66,7 +67,7 @@ public Q_SLOTS:
private:
ClientModel *clientModel;
QStringList columns;
- BanTablePriv *priv;
+ std::unique_ptr<BanTablePriv> priv;
};
#endif // BITCOIN_QT_BANTABLEMODEL_H
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 430e6dd0e8..cfd0955a74 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -30,6 +30,7 @@
#include "scheduler.h"
#include "ui_interface.h"
#include "util.h"
+#include "warnings.h"
#ifdef ENABLE_WALLET
#include "wallet/wallet.h"
@@ -245,6 +246,7 @@ private:
#endif
int returnValue;
const PlatformStyle *platformStyle;
+ std::unique_ptr<QWidget> shutdownWindow;
void startThread();
};
@@ -259,7 +261,7 @@ BitcoinCore::BitcoinCore():
void BitcoinCore::handleRunawayException(const std::exception *e)
{
PrintExceptionContinue(e, "Runaway exception");
- Q_EMIT runawayException(QString::fromStdString(strMiscWarning));
+ Q_EMIT runawayException(QString::fromStdString(GetWarnings("gui")));
}
void BitcoinCore::initialize()
@@ -267,7 +269,22 @@ void BitcoinCore::initialize()
try
{
qDebug() << __func__ << ": Running AppInit2 in thread";
- int rv = AppInit2(threadGroup, scheduler);
+ if (!AppInitBasicSetup())
+ {
+ Q_EMIT initializeResult(false);
+ return;
+ }
+ if (!AppInitParameterInteraction())
+ {
+ Q_EMIT initializeResult(false);
+ return;
+ }
+ if (!AppInitSanityChecks())
+ {
+ Q_EMIT initializeResult(false);
+ return;
+ }
+ int rv = AppInitMain(threadGroup, scheduler);
Q_EMIT initializeResult(rv);
} catch (const std::exception& e) {
handleRunawayException(&e);
@@ -365,9 +382,8 @@ void BitcoinApplication::createWindow(const NetworkStyle *networkStyle)
void BitcoinApplication::createSplashScreen(const NetworkStyle *networkStyle)
{
SplashScreen *splash = new SplashScreen(0, networkStyle);
- // We don't hold a direct pointer to the splash screen after creation, so use
- // Qt::WA_DeleteOnClose to make sure that the window will be deleted eventually.
- splash->setAttribute(Qt::WA_DeleteOnClose);
+ // We don't hold a direct pointer to the splash screen after creation, but the splash
+ // screen will take care of deleting itself when slotFinish happens.
splash->show();
connect(this, SIGNAL(splashFinished(QWidget*)), splash, SLOT(slotFinish(QWidget*)));
connect(this, SIGNAL(requestedShutdown()), splash, SLOT(close()));
@@ -409,6 +425,11 @@ void BitcoinApplication::requestInitialize()
void BitcoinApplication::requestShutdown()
{
+ // Show a simple window indicating shutdown status
+ // Do this first as some of the steps may take some time below,
+ // for example the RPC console may still be executing a command.
+ shutdownWindow.reset(ShutdownWindow::showShutdownWindow(window));
+
qDebug() << __func__ << ": Requesting shutdown";
startThread();
window->hide();
@@ -423,9 +444,6 @@ void BitcoinApplication::requestShutdown()
delete clientModel;
clientModel = 0;
- // Show a simple window indicating shutdown status
- ShutdownWindow::showShutdownWindow(window);
-
// Request shutdown from core thread
Q_EMIT requestedShutdown();
}
@@ -496,7 +514,7 @@ void BitcoinApplication::shutdownResult(int retval)
void BitcoinApplication::handleRunawayException(const QString &message)
{
QMessageBox::critical(0, "Runaway exception", BitcoinGUI::tr("A fatal error occurred. Bitcoin can no longer continue safely and will quit.") + QString("\n\n") + message);
- ::exit(1);
+ ::exit(EXIT_FAILURE);
}
WId BitcoinApplication::getMainWinId() const
@@ -573,13 +591,13 @@ int main(int argc, char *argv[])
{
HelpMessageDialog help(NULL, mapArgs.count("-version"));
help.showOrPrint();
- return 1;
+ return EXIT_SUCCESS;
}
/// 5. Now that settings and translations are available, ask user for data directory
// User language is set up: pick a data directory
if (!Intro::pickDataDirectory())
- return 0;
+ return EXIT_SUCCESS;
/// 6. Determine availability of data directory and parse bitcoin.conf
/// - Do not call GetDataDir(true) before this step finishes
@@ -587,14 +605,14 @@ int main(int argc, char *argv[])
{
QMessageBox::critical(0, QObject::tr(PACKAGE_NAME),
QObject::tr("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(mapArgs["-datadir"])));
- return 1;
+ return EXIT_FAILURE;
}
try {
- ReadConfigFile(mapArgs, mapMultiArgs);
+ ReadConfigFile(GetArg("-conf", BITCOIN_CONF_FILENAME), mapArgs, mapMultiArgs);
} catch (const std::exception& e) {
QMessageBox::critical(0, QObject::tr(PACKAGE_NAME),
QObject::tr("Error: Cannot parse configuration file: %1. Only use key=value syntax.").arg(e.what()));
- return false;
+ return EXIT_FAILURE;
}
/// 7. Determine network (and switch to network specific options)
@@ -608,7 +626,7 @@ int main(int argc, char *argv[])
SelectParams(ChainNameFromCommandLine());
} catch(std::exception &e) {
QMessageBox::critical(0, QObject::tr(PACKAGE_NAME), QObject::tr("Error: %1").arg(e.what()));
- return 1;
+ return EXIT_FAILURE;
}
#ifdef ENABLE_WALLET
// Parse URIs on command line -- this can affect Params()
@@ -630,7 +648,7 @@ int main(int argc, char *argv[])
// - Do this after creating app and setting up translations, so errors are
// translated properly.
if (PaymentServer::ipcSendCommandLine())
- exit(0);
+ exit(EXIT_SUCCESS);
// Start up the payment server early, too, so impatient users that click on
// bitcoin: links repeatedly have their payment requests routed to this process:
@@ -674,10 +692,10 @@ int main(int argc, char *argv[])
app.exec();
} catch (const std::exception& e) {
PrintExceptionContinue(&e, "Runaway exception");
- app.handleRunawayException(QString::fromStdString(strMiscWarning));
+ app.handleRunawayException(QString::fromStdString(GetWarnings("gui")));
} catch (...) {
PrintExceptionContinue(NULL, "Runaway exception");
- app.handleRunawayException(QString::fromStdString(strMiscWarning));
+ app.handleRunawayException(QString::fromStdString(GetWarnings("gui")));
}
return app.getReturnValue();
}
diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc
index ca5b1fa673..451d391237 100644
--- a/src/qt/bitcoin.qrc
+++ b/src/qt/bitcoin.qrc
@@ -52,6 +52,7 @@
<file alias="transaction_abandoned">res/icons/transaction_abandoned.png</file>
<file alias="hd_enabled">res/icons/hd_enabled.png</file>
<file alias="hd_disabled">res/icons/hd_disabled.png</file>
+ <file alias="network_disabled">res/icons/network_disabled.png</file>
</qresource>
<qresource prefix="/movies">
<file alias="spinner-000">res/movies/spinner-000.png</file>
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 272df3fdae..651ff84293 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -12,6 +12,7 @@
#include "clientmodel.h"
#include "guiconstants.h"
#include "guiutil.h"
+#include "modaloverlay.h"
#include "networkstyle.h"
#include "notificator.h"
#include "openuridialog.h"
@@ -45,7 +46,6 @@
#include <QMenuBar>
#include <QMessageBox>
#include <QMimeData>
-#include <QProgressBar>
#include <QProgressDialog>
#include <QSettings>
#include <QShortcut>
@@ -73,16 +73,19 @@ const std::string BitcoinGUI::DEFAULT_UIPLATFORM =
#endif
;
+/** Display name for default wallet name. Uses tilde to avoid name
+ * collisions in the future with additional wallets */
const QString BitcoinGUI::DEFAULT_WALLET = "~Default";
-BitcoinGUI::BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *networkStyle, QWidget *parent) :
+BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle *networkStyle, QWidget *parent) :
QMainWindow(parent),
+ enableWallet(false),
clientModel(0),
walletFrame(0),
unitDisplayControl(0),
labelWalletEncryptionIcon(0),
labelWalletHDStatusIcon(0),
- labelConnectionsIcon(0),
+ connectionsControl(0),
labelBlocksIcon(0),
progressBarLabel(0),
progressBar(0),
@@ -114,18 +117,16 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *n
notificator(0),
rpcConsole(0),
helpMessageDialog(0),
+ modalOverlay(0),
prevBlocks(0),
spinnerFrame(0),
- platformStyle(platformStyle)
+ platformStyle(_platformStyle)
{
GUIUtil::restoreWindowGeometry("nWindow", QSize(850, 550), this);
QString windowTitle = tr(PACKAGE_NAME) + " - ";
#ifdef ENABLE_WALLET
- /* if compiled with wallet support, -disablewallet can still disable the wallet */
- enableWallet = !GetBoolArg("-disablewallet", false);
-#else
- enableWallet = false;
+ enableWallet = WalletModel::isWalletEnabled();
#endif // ENABLE_WALLET
if(enableWallet)
{
@@ -148,13 +149,13 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *n
setUnifiedTitleAndToolBarOnMac(true);
#endif
- rpcConsole = new RPCConsole(platformStyle, 0);
+ rpcConsole = new RPCConsole(_platformStyle, 0);
helpMessageDialog = new HelpMessageDialog(this, false);
#ifdef ENABLE_WALLET
if(enableWallet)
{
/** Create wallet frame and make it the central widget */
- walletFrame = new WalletFrame(platformStyle, this);
+ walletFrame = new WalletFrame(_platformStyle, this);
setCentralWidget(walletFrame);
} else
#endif // ENABLE_WALLET
@@ -197,8 +198,8 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *n
unitDisplayControl = new UnitDisplayStatusBarControl(platformStyle);
labelWalletEncryptionIcon = new QLabel();
labelWalletHDStatusIcon = new QLabel();
- labelConnectionsIcon = new QLabel();
- labelBlocksIcon = new QLabel();
+ connectionsControl = new GUIUtil::ClickableLabel();
+ labelBlocksIcon = new GUIUtil::ClickableLabel();
if(enableWallet)
{
frameBlocksLayout->addStretch();
@@ -208,7 +209,7 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *n
frameBlocksLayout->addWidget(labelWalletHDStatusIcon);
}
frameBlocksLayout->addStretch();
- frameBlocksLayout->addWidget(labelConnectionsIcon);
+ frameBlocksLayout->addWidget(connectionsControl);
frameBlocksLayout->addStretch();
frameBlocksLayout->addWidget(labelBlocksIcon);
frameBlocksLayout->addStretch();
@@ -241,6 +242,17 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *n
// Subscribe to notifications from core
subscribeToCoreSignals();
+
+ connect(connectionsControl, SIGNAL(clicked(QPoint)), this, SLOT(toggleNetworkActive()));
+
+ modalOverlay = new ModalOverlay(this->centralWidget());
+#ifdef ENABLE_WALLET
+ if(enableWallet) {
+ connect(walletFrame, SIGNAL(requestedSyncWarningInfo()), this, SLOT(showModalOverlay()));
+ connect(labelBlocksIcon, SIGNAL(clicked(QPoint)), this, SLOT(showModalOverlay()));
+ connect(progressBar, SIGNAL(clicked(QPoint)), this, SLOT(showModalOverlay()));
+ }
+#endif
}
BitcoinGUI::~BitcoinGUI()
@@ -451,38 +463,39 @@ void BitcoinGUI::createToolBars()
}
}
-void BitcoinGUI::setClientModel(ClientModel *clientModel)
+void BitcoinGUI::setClientModel(ClientModel *_clientModel)
{
- this->clientModel = clientModel;
- if(clientModel)
+ this->clientModel = _clientModel;
+ if(_clientModel)
{
// 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)));
+ updateNetworkState();
+ connect(_clientModel, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int)));
+ connect(_clientModel, SIGNAL(networkActiveChanged(bool)), this, SLOT(setNetworkActive(bool)));
- setNumBlocks(clientModel->getNumBlocks(), clientModel->getLastBlockDate(), clientModel->getVerificationProgress(NULL), false);
- connect(clientModel, SIGNAL(numBlocksChanged(int,QDateTime,double,bool)), this, SLOT(setNumBlocks(int,QDateTime,double,bool)));
+ setNumBlocks(_clientModel->getNumBlocks(), _clientModel->getLastBlockDate(), _clientModel->getVerificationProgress(NULL), false);
+ connect(_clientModel, SIGNAL(numBlocksChanged(int,QDateTime,double,bool)), this, SLOT(setNumBlocks(int,QDateTime,double,bool)));
// Receive and report messages from client model
- connect(clientModel, SIGNAL(message(QString,QString,unsigned int)), this, SLOT(message(QString,QString,unsigned int)));
+ connect(_clientModel, SIGNAL(message(QString,QString,unsigned int)), this, SLOT(message(QString,QString,unsigned int)));
// Show progress dialog
- connect(clientModel, SIGNAL(showProgress(QString,int)), this, SLOT(showProgress(QString,int)));
+ connect(_clientModel, SIGNAL(showProgress(QString,int)), this, SLOT(showProgress(QString,int)));
- rpcConsole->setClientModel(clientModel);
+ rpcConsole->setClientModel(_clientModel);
#ifdef ENABLE_WALLET
if(walletFrame)
{
- walletFrame->setClientModel(clientModel);
+ walletFrame->setClientModel(_clientModel);
}
#endif // ENABLE_WALLET
- unitDisplayControl->setOptionsModel(clientModel->getOptionsModel());
+ unitDisplayControl->setOptionsModel(_clientModel->getOptionsModel());
- OptionsModel* optionsModel = clientModel->getOptionsModel();
+ OptionsModel* optionsModel = _clientModel->getOptionsModel();
if(optionsModel)
{
// be aware of the tray icon disable state change reported by the OptionsModel object.
@@ -491,6 +504,8 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel)
// initialize the disable state of the tray icon with the current value in the model.
setTrayIconVisible(optionsModel->getHideTrayIcon());
}
+
+ modalOverlay->setKnownBestHeight(clientModel->getHeaderTipHeight(), QDateTime::fromTime_t(clientModel->getHeaderTipTime()));
} else {
// Disable possibility to show main window via action
toggleHideAction->setEnabled(false);
@@ -499,6 +514,12 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel)
// Disable context menu on tray icon
trayIconMenu->clear();
}
+ // Propagate cleared model to child objects
+ rpcConsole->setClientModel(nullptr);
+#ifdef ENABLE_WALLET
+ walletFrame->setClientModel(nullptr);
+#endif // ENABLE_WALLET
+ unitDisplayControl->setOptionsModel(nullptr);
}
}
@@ -688,8 +709,9 @@ void BitcoinGUI::gotoVerifyMessageTab(QString addr)
}
#endif // ENABLE_WALLET
-void BitcoinGUI::setNumConnections(int count)
+void BitcoinGUI::updateNetworkState()
{
+ int count = clientModel->getNumConnections();
QString icon;
switch(count)
{
@@ -699,13 +721,43 @@ void BitcoinGUI::setNumConnections(int count)
case 7: case 8: case 9: icon = ":/icons/connect_3"; break;
default: icon = ":/icons/connect_4"; break;
}
- labelConnectionsIcon->setPixmap(platformStyle->SingleColorIcon(icon).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
- labelConnectionsIcon->setToolTip(tr("%n active connection(s) to Bitcoin network", "", count));
+
+ QString tooltip;
+
+ if (clientModel->getNetworkActive()) {
+ tooltip = tr("%n active connection(s) to Bitcoin network", "", count) + QString(".<br>") + tr("Click to disable network activity.");
+ } else {
+ tooltip = tr("Network activity disabled.") + QString("<br>") + tr("Click to enable network activity again.");
+ icon = ":/icons/network_disabled";
+ }
+
+ // Don't word-wrap this (fixed-width) tooltip
+ tooltip = QString("<nobr>") + tooltip + QString("</nobr>");
+ connectionsControl->setToolTip(tooltip);
+
+ connectionsControl->setPixmap(platformStyle->SingleColorIcon(icon).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
+}
+
+void BitcoinGUI::setNumConnections(int count)
+{
+ updateNetworkState();
+}
+
+void BitcoinGUI::setNetworkActive(bool networkActive)
+{
+ updateNetworkState();
}
void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool header)
{
- if(!clientModel)
+ if (modalOverlay)
+ {
+ if (header)
+ modalOverlay->setKnownBestHeight(count, blockDate);
+ else
+ modalOverlay->tipUpdate(count, blockDate, nVerificationProgress);
+ }
+ if (!clientModel)
return;
// Prevent orphan statusbar messages (e.g. hover Quit in main menu, wait until chain-sync starts -> garbelled text)
@@ -754,7 +806,10 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer
#ifdef ENABLE_WALLET
if(walletFrame)
+ {
walletFrame->showOutOfSyncWarning(false);
+ modalOverlay->showHide(true, true);
+ }
#endif // ENABLE_WALLET
progressBarLabel->setVisible(false);
@@ -762,30 +817,7 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer
}
else
{
- // Represent time from last generated block in human readable text
- QString timeBehindText;
- const int HOUR_IN_SECONDS = 60*60;
- const int DAY_IN_SECONDS = 24*60*60;
- const int WEEK_IN_SECONDS = 7*24*60*60;
- const int YEAR_IN_SECONDS = 31556952; // Average length of year in Gregorian calendar
- if(secs < 2*DAY_IN_SECONDS)
- {
- timeBehindText = tr("%n hour(s)","",secs/HOUR_IN_SECONDS);
- }
- else if(secs < 2*WEEK_IN_SECONDS)
- {
- timeBehindText = tr("%n day(s)","",secs/DAY_IN_SECONDS);
- }
- else if(secs < YEAR_IN_SECONDS)
- {
- timeBehindText = tr("%n week(s)","",secs/WEEK_IN_SECONDS);
- }
- else
- {
- qint64 years = secs / YEAR_IN_SECONDS;
- qint64 remainder = secs % YEAR_IN_SECONDS;
- timeBehindText = tr("%1 and %2").arg(tr("%n year(s)", "", years)).arg(tr("%n week(s)","", remainder/WEEK_IN_SECONDS));
- }
+ QString timeBehindText = GUIUtil::formateNiceTimeOffset(secs);
progressBarLabel->setVisible(true);
progressBar->setFormat(tr("%1 behind").arg(timeBehindText));
@@ -805,7 +837,10 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer
#ifdef ENABLE_WALLET
if(walletFrame)
+ {
walletFrame->showOutOfSyncWarning(true);
+ modalOverlay->showHide();
+ }
#endif // ENABLE_WALLET
tooltip += QString("<br>");
@@ -1101,6 +1136,12 @@ void BitcoinGUI::setTrayIconVisible(bool fHideTrayIcon)
}
}
+void BitcoinGUI::showModalOverlay()
+{
+ if (modalOverlay && (progressBar->isVisible() || modalOverlay->isLayerVisible()))
+ modalOverlay->toggleVisibility();
+}
+
static bool ThreadSafeMessageBox(BitcoinGUI *gui, const std::string& message, const std::string& caption, unsigned int style)
{
bool modal = (style & CClientUIInterface::MODAL);
@@ -1132,6 +1173,13 @@ void BitcoinGUI::unsubscribeFromCoreSignals()
uiInterface.ThreadSafeQuestion.disconnect(boost::bind(ThreadSafeMessageBox, this, _1, _3, _4));
}
+void BitcoinGUI::toggleNetworkActive()
+{
+ if (clientModel) {
+ clientModel->setNetworkActive(!clientModel->getNetworkActive());
+ }
+}
+
UnitDisplayStatusBarControl::UnitDisplayStatusBarControl(const PlatformStyle *platformStyle) :
optionsModel(0),
menu(0)
@@ -1159,7 +1207,7 @@ void UnitDisplayStatusBarControl::mousePressEvent(QMouseEvent *event)
/** Creates context menu, its actions, and wires up all the relevant signals for mouse events. */
void UnitDisplayStatusBarControl::createContextMenu()
{
- menu = new QMenu();
+ menu = new QMenu(this);
Q_FOREACH(BitcoinUnits::Unit u, BitcoinUnits::availableUnits())
{
QAction *menuAction = new QAction(QString(BitcoinUnits::name(u)), this);
@@ -1170,17 +1218,17 @@ void UnitDisplayStatusBarControl::createContextMenu()
}
/** Lets the control know about the Options Model (and its signals) */
-void UnitDisplayStatusBarControl::setOptionsModel(OptionsModel *optionsModel)
+void UnitDisplayStatusBarControl::setOptionsModel(OptionsModel *_optionsModel)
{
- if (optionsModel)
+ if (_optionsModel)
{
- this->optionsModel = optionsModel;
+ this->optionsModel = _optionsModel;
// be aware of a display unit change reported by the OptionsModel object.
- connect(optionsModel,SIGNAL(displayUnitChanged(int)),this,SLOT(updateDisplayUnit(int)));
+ connect(_optionsModel,SIGNAL(displayUnitChanged(int)),this,SLOT(updateDisplayUnit(int)));
// initialize the display units label with the current value in the model.
- updateDisplayUnit(optionsModel->getDisplayUnit());
+ updateDisplayUnit(_optionsModel->getDisplayUnit());
}
}
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index 41770929b4..59540bfe6b 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -29,6 +29,7 @@ class UnitDisplayStatusBarControl;
class WalletFrame;
class WalletModel;
class HelpMessageDialog;
+class ModalOverlay;
class CWallet;
@@ -84,7 +85,7 @@ private:
UnitDisplayStatusBarControl *unitDisplayControl;
QLabel *labelWalletEncryptionIcon;
QLabel *labelWalletHDStatusIcon;
- QLabel *labelConnectionsIcon;
+ QLabel *connectionsControl;
QLabel *labelBlocksIcon;
QLabel *progressBarLabel;
QProgressBar *progressBar;
@@ -118,6 +119,7 @@ private:
Notificator *notificator;
RPCConsole *rpcConsole;
HelpMessageDialog *helpMessageDialog;
+ ModalOverlay *modalOverlay;
/** Keep track of previous number of blocks, to detect progress */
int prevBlocks;
@@ -144,6 +146,9 @@ private:
/** Disconnect core signals from GUI client */
void unsubscribeFromCoreSignals();
+ /** Update UI with latest network info from model. */
+ void updateNetworkState();
+
Q_SIGNALS:
/** Signal raised when a URI was entered or dragged to the GUI */
void receivedURI(const QString &uri);
@@ -151,6 +156,8 @@ Q_SIGNALS:
public Q_SLOTS:
/** Set number of connections shown in the UI */
void setNumConnections(int count);
+ /** Set network state shown in the UI */
+ void setNetworkActive(bool networkActive);
/** Set number of blocks and last block date shown in the UI */
void setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers);
@@ -229,6 +236,11 @@ private Q_SLOTS:
/** When hideTrayIcon setting is changed in OptionsModel hide or show the icon accordingly. */
void setTrayIconVisible(bool);
+
+ /** Toggle networking */
+ void toggleNetworkActive();
+
+ void showModalOverlay();
};
class UnitDisplayStatusBarControl : public QLabel
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index 83c78850e2..e63a9cc7b8 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -6,11 +6,13 @@
#include "bantablemodel.h"
#include "guiconstants.h"
+#include "guiutil.h"
#include "peertablemodel.h"
#include "chainparams.h"
#include "checkpoints.h"
#include "clientversion.h"
+#include "validation.h"
#include "net.h"
#include "txmempool.h"
#include "ui_interface.h"
@@ -27,9 +29,9 @@ static const int64_t nClientStartupTime = GetTime();
static int64_t nLastHeaderTipUpdateNotification = 0;
static int64_t nLastBlockTipUpdateNotification = 0;
-ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) :
+ClientModel::ClientModel(OptionsModel *_optionsModel, QObject *parent) :
QObject(parent),
- optionsModel(optionsModel),
+ optionsModel(_optionsModel),
peerTableModel(0),
banTableModel(0),
pollTimer(0)
@@ -70,6 +72,22 @@ int ClientModel::getNumBlocks() const
return chainActive.Height();
}
+int ClientModel::getHeaderTipHeight() const
+{
+ LOCK(cs_main);
+ if (!pindexBestHeader)
+ return 0;
+ return pindexBestHeader->nHeight;
+}
+
+int64_t ClientModel::getHeaderTipTime() const
+{
+ LOCK(cs_main);
+ if (!pindexBestHeader)
+ return 0;
+ return pindexBestHeader->GetBlockTime();
+}
+
quint64 ClientModel::getTotalBytesRecv() const
{
if(!g_connman)
@@ -128,6 +146,11 @@ void ClientModel::updateNumConnections(int numConnections)
Q_EMIT numConnectionsChanged(numConnections);
}
+void ClientModel::updateNetworkActive(bool networkActive)
+{
+ Q_EMIT networkActiveChanged(networkActive);
+}
+
void ClientModel::updateAlert()
{
Q_EMIT alertsChanged(getStatusBarWarnings());
@@ -150,6 +173,21 @@ enum BlockSource ClientModel::getBlockSource() const
return BLOCK_SOURCE_NONE;
}
+void ClientModel::setNetworkActive(bool active)
+{
+ if (g_connman) {
+ g_connman->SetNetworkActive(active);
+ }
+}
+
+bool ClientModel::getNetworkActive() const
+{
+ if (g_connman) {
+ return g_connman->GetNetworkActive();
+ }
+ return false;
+}
+
QString ClientModel::getStatusBarWarnings() const
{
return QString::fromStdString(GetWarnings("gui"));
@@ -192,7 +230,7 @@ QString ClientModel::formatClientStartupTime() const
QString ClientModel::dataDir() const
{
- return QString::fromStdString(GetDataDir().string());
+ return GUIUtil::boostPathToQString(GetDataDir());
}
void ClientModel::updateBanlist()
@@ -216,6 +254,12 @@ static void NotifyNumConnectionsChanged(ClientModel *clientmodel, int newNumConn
Q_ARG(int, newNumConnections));
}
+static void NotifyNetworkActiveChanged(ClientModel *clientmodel, bool networkActive)
+{
+ QMetaObject::invokeMethod(clientmodel, "updateNetworkActive", Qt::QueuedConnection,
+ Q_ARG(bool, networkActive));
+}
+
static void NotifyAlertChanged(ClientModel *clientmodel)
{
qDebug() << "NotifyAlertChanged";
@@ -256,6 +300,7 @@ void ClientModel::subscribeToCoreSignals()
// Connect signals to client
uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2));
uiInterface.NotifyNumConnectionsChanged.connect(boost::bind(NotifyNumConnectionsChanged, this, _1));
+ uiInterface.NotifyNetworkActiveChanged.connect(boost::bind(NotifyNetworkActiveChanged, this, _1));
uiInterface.NotifyAlertChanged.connect(boost::bind(NotifyAlertChanged, this));
uiInterface.BannedListChanged.connect(boost::bind(BannedListChanged, this));
uiInterface.NotifyBlockTip.connect(boost::bind(BlockTipChanged, this, _1, _2, false));
@@ -267,6 +312,7 @@ void ClientModel::unsubscribeFromCoreSignals()
// Disconnect signals from client
uiInterface.ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2));
uiInterface.NotifyNumConnectionsChanged.disconnect(boost::bind(NotifyNumConnectionsChanged, this, _1));
+ uiInterface.NotifyNetworkActiveChanged.disconnect(boost::bind(NotifyNetworkActiveChanged, this, _1));
uiInterface.NotifyAlertChanged.disconnect(boost::bind(NotifyAlertChanged, this));
uiInterface.BannedListChanged.disconnect(boost::bind(BannedListChanged, this));
uiInterface.NotifyBlockTip.disconnect(boost::bind(BlockTipChanged, this, _1, _2, false));
diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h
index 99fd574b9e..a641401425 100644
--- a/src/qt/clientmodel.h
+++ b/src/qt/clientmodel.h
@@ -51,7 +51,8 @@ public:
//! Return number of connections, default is in- and outbound (total)
int getNumConnections(unsigned int flags = CONNECTIONS_ALL) const;
int getNumBlocks() const;
-
+ int getHeaderTipHeight() const;
+ int64_t getHeaderTipTime() const;
//! Return number of transactions in the mempool
long getMempoolSize() const;
//! Return the dynamic memory usage of the mempool
@@ -67,6 +68,10 @@ public:
bool inInitialBlockDownload() const;
//! Return true if core is importing blocks
enum BlockSource getBlockSource() const;
+ //! Return true if network activity in core is enabled
+ bool getNetworkActive() const;
+ //! Toggle network activity state in core
+ void setNetworkActive(bool active);
//! Return warnings to be displayed in status bar
QString getStatusBarWarnings() const;
@@ -90,6 +95,7 @@ Q_SIGNALS:
void numConnectionsChanged(int count);
void numBlocksChanged(int count, const QDateTime& blockDate, double nVerificationProgress, bool header);
void mempoolSizeChanged(long count, size_t mempoolSizeInBytes);
+ void networkActiveChanged(bool networkActive);
void alertsChanged(const QString &warnings);
void bytesChanged(quint64 totalBytesIn, quint64 totalBytesOut);
@@ -102,6 +108,7 @@ Q_SIGNALS:
public Q_SLOTS:
void updateTimer();
void updateNumConnections(int numConnections);
+ void updateNetworkActive(bool networkActive);
void updateAlert();
void updateBanlist();
};
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index f53242100c..4d34bf95e9 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -13,9 +13,9 @@
#include "txmempool.h"
#include "walletmodel.h"
-#include "coincontrol.h"
+#include "wallet/coincontrol.h"
#include "init.h"
-#include "main.h" // For minRelayTxFee
+#include "validation.h" // For minRelayTxFee
#include "wallet/wallet.h"
#include <boost/assign/list_of.hpp> // for 'map_list_of()'
@@ -35,11 +35,18 @@ QList<CAmount> CoinControlDialog::payAmounts;
CCoinControl* CoinControlDialog::coinControl = new CCoinControl();
bool CoinControlDialog::fSubtractFeeFromAmount = false;
-CoinControlDialog::CoinControlDialog(const PlatformStyle *platformStyle, QWidget *parent) :
+bool CCoinControlWidgetItem::operator<(const QTreeWidgetItem &other) const {
+ int column = treeWidget()->sortColumn();
+ if (column == CoinControlDialog::COLUMN_AMOUNT || column == CoinControlDialog::COLUMN_DATE || column == CoinControlDialog::COLUMN_CONFIRMATIONS)
+ return data(column, Qt::UserRole).toLongLong() < other.data(column, Qt::UserRole).toLongLong();
+ return QTreeWidgetItem::operator<(other);
+}
+
+CoinControlDialog::CoinControlDialog(const PlatformStyle *_platformStyle, QWidget *parent) :
QDialog(parent),
ui(new Ui::CoinControlDialog),
model(0),
- platformStyle(platformStyle)
+ platformStyle(_platformStyle)
{
ui->setupUi(this);
@@ -52,7 +59,7 @@ CoinControlDialog::CoinControlDialog(const PlatformStyle *platformStyle, QWidget
unlockAction = new QAction(tr("Unlock unspent"), this); // we need to enable/disable this
// context menu
- contextMenu = new QMenu();
+ contextMenu = new QMenu(this);
contextMenu->addAction(copyAddressAction);
contextMenu->addAction(copyLabelAction);
contextMenu->addAction(copyAmountAction);
@@ -128,11 +135,9 @@ CoinControlDialog::CoinControlDialog(const PlatformStyle *platformStyle, QWidget
ui->treeWidget->setColumnWidth(COLUMN_CONFIRMATIONS, 110);
ui->treeWidget->setColumnHidden(COLUMN_TXHASH, true); // store transaction hash in this column, but don't show it
ui->treeWidget->setColumnHidden(COLUMN_VOUT_INDEX, true); // store vout index in this column, but don't show it
- ui->treeWidget->setColumnHidden(COLUMN_AMOUNT_INT64, true); // store amount int64 in this column, but don't show it
- ui->treeWidget->setColumnHidden(COLUMN_DATE_INT64, true); // store date int64 in this column, but don't show it
// default view is sorted by amount desc
- sortView(COLUMN_AMOUNT_INT64, Qt::DescendingOrder);
+ sortView(COLUMN_AMOUNT, Qt::DescendingOrder);
// restore list mode and sortorder as a convenience feature
QSettings settings;
@@ -152,27 +157,18 @@ CoinControlDialog::~CoinControlDialog()
delete ui;
}
-void CoinControlDialog::setModel(WalletModel *model)
+void CoinControlDialog::setModel(WalletModel *_model)
{
- this->model = model;
+ this->model = _model;
- if(model && model->getOptionsModel() && model->getAddressTableModel())
+ if(_model && _model->getOptionsModel() && _model->getAddressTableModel())
{
updateView();
updateLabelLocked();
- CoinControlDialog::updateLabels(model, this);
+ CoinControlDialog::updateLabels(_model, this);
}
}
-// helper function str_pad
-QString CoinControlDialog::strPad(QString s, int nPadLength, QString sPadding)
-{
- while (s.length() < nPadLength)
- s = sPadding + s;
-
- return s;
-}
-
// ok button
void CoinControlDialog::buttonBoxClicked(QAbstractButton* button)
{
@@ -338,7 +334,7 @@ void CoinControlDialog::sortView(int column, Qt::SortOrder order)
sortColumn = column;
sortOrder = order;
ui->treeWidget->sortItems(column, order);
- ui->treeWidget->header()->setSortIndicator(getMappedColumn(sortColumn), sortOrder);
+ ui->treeWidget->header()->setSortIndicator(sortColumn, sortOrder);
}
// treeview: clicked on header
@@ -346,12 +342,10 @@ void CoinControlDialog::headerSectionClicked(int logicalIndex)
{
if (logicalIndex == COLUMN_CHECKBOX) // click on most left column -> do nothing
{
- ui->treeWidget->header()->setSortIndicator(getMappedColumn(sortColumn), sortOrder);
+ ui->treeWidget->header()->setSortIndicator(sortColumn, sortOrder);
}
else
{
- logicalIndex = getMappedColumn(logicalIndex, false);
-
if (sortColumn == logicalIndex)
sortOrder = ((sortOrder == Qt::AscendingOrder) ? Qt::DescendingOrder : Qt::AscendingOrder);
else
@@ -476,21 +470,21 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
nQuantity++;
// Amount
- nAmount += out.tx->vout[out.i].nValue;
+ nAmount += out.tx->tx->vout[out.i].nValue;
// Priority
- dPriorityInputs += (double)out.tx->vout[out.i].nValue * (out.nDepth+1);
+ dPriorityInputs += (double)out.tx->tx->vout[out.i].nValue * (out.nDepth+1);
// Bytes
CTxDestination address;
int witnessversion = 0;
std::vector<unsigned char> witnessprogram;
- if (out.tx->vout[out.i].scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram))
+ if (out.tx->tx->vout[out.i].scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram))
{
nBytesInputs += (32 + 4 + 1 + (107 / WITNESS_SCALE_FACTOR) + 4);
fWitness = true;
}
- else if(ExtractDestination(out.tx->vout[out.i].scriptPubKey, address))
+ else if(ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, address))
{
CPubKey pubkey;
CKeyID *keyid = boost::get<CKeyID>(&address);
@@ -658,7 +652,7 @@ void CoinControlDialog::updateView()
model->listCoins(mapCoins);
BOOST_FOREACH(const PAIRTYPE(QString, std::vector<COutput>)& coins, mapCoins) {
- QTreeWidgetItem *itemWalletAddress = new QTreeWidgetItem();
+ CCoinControlWidgetItem *itemWalletAddress = new CCoinControlWidgetItem();
itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
QString sWalletAddress = coins.first;
QString sWalletLabel = model->getAddressTableModel()->labelForAddress(sWalletAddress);
@@ -683,19 +677,19 @@ void CoinControlDialog::updateView()
CAmount nSum = 0;
int nChildren = 0;
BOOST_FOREACH(const COutput& out, coins.second) {
- nSum += out.tx->vout[out.i].nValue;
+ nSum += out.tx->tx->vout[out.i].nValue;
nChildren++;
- QTreeWidgetItem *itemOutput;
- if (treeMode) itemOutput = new QTreeWidgetItem(itemWalletAddress);
- else itemOutput = new QTreeWidgetItem(ui->treeWidget);
+ CCoinControlWidgetItem *itemOutput;
+ if (treeMode) itemOutput = new CCoinControlWidgetItem(itemWalletAddress);
+ else itemOutput = new CCoinControlWidgetItem(ui->treeWidget);
itemOutput->setFlags(flgCheckbox);
itemOutput->setCheckState(COLUMN_CHECKBOX,Qt::Unchecked);
// address
CTxDestination outputAddress;
QString sAddress = "";
- if(ExtractDestination(out.tx->vout[out.i].scriptPubKey, outputAddress))
+ if(ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, outputAddress))
{
sAddress = QString::fromStdString(CBitcoinAddress(outputAddress).ToString());
@@ -720,15 +714,16 @@ void CoinControlDialog::updateView()
}
// amount
- itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->vout[out.i].nValue));
- itemOutput->setText(COLUMN_AMOUNT_INT64, strPad(QString::number(out.tx->vout[out.i].nValue), 15, " ")); // padding so that sorting works correctly
+ itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->tx->vout[out.i].nValue));
+ itemOutput->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong)out.tx->tx->vout[out.i].nValue)); // padding so that sorting works correctly
// date
itemOutput->setText(COLUMN_DATE, GUIUtil::dateTimeStr(out.tx->GetTxTime()));
- itemOutput->setText(COLUMN_DATE_INT64, strPad(QString::number(out.tx->GetTxTime()), 20, " "));
+ itemOutput->setData(COLUMN_DATE, Qt::UserRole, QVariant((qlonglong)out.tx->GetTxTime()));
// confirmations
- itemOutput->setText(COLUMN_CONFIRMATIONS, strPad(QString::number(out.nDepth), 8, " "));
+ itemOutput->setText(COLUMN_CONFIRMATIONS, QString::number(out.nDepth));
+ itemOutput->setData(COLUMN_CONFIRMATIONS, Qt::UserRole, QVariant((qlonglong)out.nDepth));
// transaction hash
uint256 txhash = out.tx->GetHash();
@@ -756,7 +751,7 @@ void CoinControlDialog::updateView()
{
itemWalletAddress->setText(COLUMN_CHECKBOX, "(" + QString::number(nChildren) + ")");
itemWalletAddress->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nSum));
- itemWalletAddress->setText(COLUMN_AMOUNT_INT64, strPad(QString::number(nSum), 15, " "));
+ itemWalletAddress->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong)nSum));
}
}
diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h
index 7d73421e3a..7c2269b1eb 100644
--- a/src/qt/coincontroldialog.h
+++ b/src/qt/coincontroldialog.h
@@ -28,6 +28,17 @@ namespace Ui {
#define ASYMP_UTF8 "\xE2\x89\x88"
+class CCoinControlWidgetItem : public QTreeWidgetItem
+{
+public:
+ CCoinControlWidgetItem(QTreeWidget *parent, int type = Type) : QTreeWidgetItem(parent, type) {}
+ CCoinControlWidgetItem(int type = Type) : QTreeWidgetItem(type) {}
+ CCoinControlWidgetItem(QTreeWidgetItem *parent, int type = Type) : QTreeWidgetItem(parent, type) {}
+
+ bool operator<(const QTreeWidgetItem &other) const;
+};
+
+
class CoinControlDialog : public QDialog
{
Q_OBJECT
@@ -59,13 +70,12 @@ private:
const PlatformStyle *platformStyle;
- QString strPad(QString, int, QString);
void sortView(int, Qt::SortOrder);
void updateView();
enum
{
- COLUMN_CHECKBOX,
+ COLUMN_CHECKBOX = 0,
COLUMN_AMOUNT,
COLUMN_LABEL,
COLUMN_ADDRESS,
@@ -73,30 +83,8 @@ private:
COLUMN_CONFIRMATIONS,
COLUMN_TXHASH,
COLUMN_VOUT_INDEX,
- COLUMN_AMOUNT_INT64,
- COLUMN_DATE_INT64
};
-
- // some columns have a hidden column containing the value used for sorting
- int getMappedColumn(int column, bool fVisibleColumn = true)
- {
- if (fVisibleColumn)
- {
- if (column == COLUMN_AMOUNT_INT64)
- return COLUMN_AMOUNT;
- else if (column == COLUMN_DATE_INT64)
- return COLUMN_DATE;
- }
- else
- {
- if (column == COLUMN_AMOUNT)
- return COLUMN_AMOUNT_INT64;
- else if (column == COLUMN_DATE)
- return COLUMN_DATE_INT64;
- }
-
- return column;
- }
+ friend class CCoinControlWidgetItem;
private Q_SLOTS:
void showMenu(const QPoint &);
diff --git a/src/qt/csvmodelwriter.cpp b/src/qt/csvmodelwriter.cpp
index 8a1a49bb06..f424e6cd98 100644
--- a/src/qt/csvmodelwriter.cpp
+++ b/src/qt/csvmodelwriter.cpp
@@ -8,15 +8,15 @@
#include <QFile>
#include <QTextStream>
-CSVModelWriter::CSVModelWriter(const QString &filename, QObject *parent) :
+CSVModelWriter::CSVModelWriter(const QString &_filename, QObject *parent) :
QObject(parent),
- filename(filename), model(0)
+ filename(_filename), model(0)
{
}
-void CSVModelWriter::setModel(const QAbstractItemModel *model)
+void CSVModelWriter::setModel(const QAbstractItemModel *_model)
{
- this->model = model;
+ this->model = _model;
}
void CSVModelWriter::addColumn(const QString &title, int column, int role)
diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp
index 5f45031e9e..a9ffe016fd 100644
--- a/src/qt/editaddressdialog.cpp
+++ b/src/qt/editaddressdialog.cpp
@@ -11,11 +11,11 @@
#include <QDataWidgetMapper>
#include <QMessageBox>
-EditAddressDialog::EditAddressDialog(Mode mode, QWidget *parent) :
+EditAddressDialog::EditAddressDialog(Mode _mode, QWidget *parent) :
QDialog(parent),
ui(new Ui::EditAddressDialog),
mapper(0),
- mode(mode),
+ mode(_mode),
model(0)
{
ui->setupUi(this);
@@ -49,13 +49,13 @@ EditAddressDialog::~EditAddressDialog()
delete ui;
}
-void EditAddressDialog::setModel(AddressTableModel *model)
+void EditAddressDialog::setModel(AddressTableModel *_model)
{
- this->model = model;
- if(!model)
+ this->model = _model;
+ if(!_model)
return;
- mapper->setModel(model);
+ mapper->setModel(_model);
mapper->addMapping(ui->labelEdit, AddressTableModel::Label);
mapper->addMapping(ui->addressEdit, AddressTableModel::Address);
}
@@ -137,8 +137,8 @@ QString EditAddressDialog::getAddress() const
return address;
}
-void EditAddressDialog::setAddress(const QString &address)
+void EditAddressDialog::setAddress(const QString &_address)
{
- this->address = address;
- ui->addressEdit->setText(address);
+ this->address = _address;
+ ui->addressEdit->setText(_address);
}
diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui
index 9dc641979e..8be4a955b3 100644
--- a/src/qt/forms/debugwindow.ui
+++ b/src/qt/forms/debugwindow.ui
@@ -1353,13 +1353,36 @@
</widget>
</item>
<item row="16" column="0">
+ <widget class="QLabel" name="peerMinPingLabel">
+ <property name="text">
+ <string>Min Ping</string>
+ </property>
+ </widget>
+ </item>
+ <item row="16" column="2">
+ <widget class="QLabel" name="peerMinPing">
+ <property name="cursor">
+ <cursorShape>IBeamCursor</cursorShape>
+ </property>
+ <property name="text">
+ <string>N/A</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="17" column="0">
<widget class="QLabel" name="label_timeoffset">
<property name="text">
<string>Time Offset</string>
</property>
</widget>
</item>
- <item row="16" column="2">
+ <item row="17" column="2">
<widget class="QLabel" name="timeoffset">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1375,7 +1398,7 @@
</property>
</widget>
</item>
- <item row="17" column="1">
+ <item row="18" column="1">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
diff --git a/src/qt/forms/modaloverlay.ui b/src/qt/forms/modaloverlay.ui
new file mode 100644
index 0000000000..a37672ad53
--- /dev/null
+++ b/src/qt/forms/modaloverlay.ui
@@ -0,0 +1,376 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ModalOverlay</class>
+ <widget class="ModalOverlay" name="ModalOverlay">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>640</width>
+ <height>385</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout" stretch="0">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="bgWidget" native="true">
+ <property name="styleSheet">
+ <string notr="true">#bgWidget { background: rgba(0,0,0,220); }</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayoutMain" stretch="1">
+ <property name="leftMargin">
+ <number>60</number>
+ </property>
+ <property name="topMargin">
+ <number>60</number>
+ </property>
+ <property name="rightMargin">
+ <number>60</number>
+ </property>
+ <property name="bottomMargin">
+ <number>60</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="contentWidget" native="true">
+ <property name="styleSheet">
+ <string notr="true">#contentWidget { background: rgba(255,255,255,240); border-radius: 6px; }
+
+QLabel { color: rgb(40,40,40); }</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayoutSub" stretch="1,0,0,0">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>10</number>
+ </property>
+ <property name="topMargin">
+ <number>10</number>
+ </property>
+ <property name="rightMargin">
+ <number>10</number>
+ </property>
+ <property name="bottomMargin">
+ <number>10</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayoutIconText" stretch="0,1">
+ <property name="topMargin">
+ <number>20</number>
+ </property>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayoutIcon">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="warningIcon">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset>
+ <normaloff>:/icons/warning</normaloff>
+ <disabledoff>:/icons/warning</disabledoff>:/icons/warning</iconset>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>48</width>
+ <height>48</height>
+ </size>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacerWarningIcon">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayoutInfoText">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="infoText">
+ <property name="text">
+ <string>Recent transactions may not yet be visible, and therefore your wallet's balance might be incorrect. This information will be correct once your wallet has finished synchronizing with the bitcoin network, as detailed below.</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="infoTextStrong">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Attempting to spend bitcoins that are affected by not-yet-displayed transactions will not be accepted by the network.</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacerInTextSpace">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacerAfterText">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QFormLayout" name="formLayout">
+ <property name="fieldGrowthPolicy">
+ <enum>QFormLayout::FieldsStayAtSizeHint</enum>
+ </property>
+ <property name="horizontalSpacing">
+ <number>6</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
+ <number>10</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="labelNumberOfBlocksLeft">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Number of blocks left</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="numberOfBlocksLeft">
+ <property name="text">
+ <string>Unknown...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="labelLastBlockTime">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Last block time</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="newestBlockDate">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Unknown...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="labelSyncDone">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Progress</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayoutSync" stretch="0,1">
+ <item>
+ <widget class="QLabel" name="percentageProgress">
+ <property name="text">
+ <string>~</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QProgressBar" name="progressBar">
+ <property name="value">
+ <number>24</number>
+ </property>
+ <property name="format">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="labelProgressIncrease">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Progress increase per hour</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QLabel" name="progressIncreasePerH">
+ <property name="text">
+ <string>calculating...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <widget class="QLabel" name="labelEstimatedTimeLeft">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Estimated time left until synced</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QLabel" name="expectedTimeLeft">
+ <property name="text">
+ <string>calculating...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayoutButtons">
+ <property name="leftMargin">
+ <number>10</number>
+ </property>
+ <property name="topMargin">
+ <number>10</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="closeButton">
+ <property name="text">
+ <string>Hide</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>ModalOverlay</class>
+ <extends>QWidget</extends>
+ <header>modaloverlay.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui
index 6d792d1475..710801ee96 100644
--- a/src/qt/forms/overviewpage.ui
+++ b/src/qt/forms/overviewpage.ui
@@ -20,7 +20,7 @@
<bool>false</bool>
</property>
<property name="styleSheet">
- <string notr="true">background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop:0 #F0D0A0, stop:1 #F8D488); color:#000000;</string>
+ <string notr="true">QLabel { background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop:0 #F0D0A0, stop:1 #F8D488); color:#000000; }</string>
</property>
<property name="wordWrap">
<bool>true</bool>
@@ -28,6 +28,9 @@
<property name="margin">
<number>3</number>
</property>
+ <property name="textInteractionFlags">
+ <set>Qt::TextSelectableByMouse</set>
+ </property>
</widget>
</item>
<item>
@@ -61,7 +64,7 @@
<item>
<widget class="QPushButton" name="labelWalletStatus">
<property name="enabled">
- <bool>false</bool>
+ <bool>true</bool>
</property>
<property name="maximumSize">
<size>
@@ -447,7 +450,7 @@
<item>
<widget class="QPushButton" name="labelTransactionsStatus">
<property name="enabled">
- <bool>false</bool>
+ <bool>true</bool>
</property>
<property name="maximumSize">
<size>
diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui
index 06e09074d1..0a8afa2e76 100644
--- a/src/qt/forms/sendcoinsdialog.ui
+++ b/src/qt/forms/sendcoinsdialog.ui
@@ -411,7 +411,7 @@
</property>
</widget>
</item>
- </layout>
+ </layout>
</item>
<item>
<layout class="QFormLayout" name="formLayoutCoinControl4">
@@ -1031,7 +1031,7 @@
<item>
<widget class="QLabel" name="labelSmartFee3">
<property name="text">
- <string>Confirmation time:</string>
+ <string>Confirmation time target:</string>
</property>
<property name="margin">
<number>2</number>
@@ -1064,7 +1064,7 @@
<number>0</number>
</property>
<property name="maximum">
- <number>24</number>
+ <number>23</number>
</property>
<property name="pageStep">
<number>1</number>
@@ -1096,6 +1096,26 @@
</widget>
</item>
<item>
+ <spacer name="horizontalSpacer_7">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="confirmationTargetLabel">
+ <property name="text">
+ <string>(count)</string>
+ </property>
+ </widget>
+ </item>
+ <item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index 444e35de8a..8132e4fe0d 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -11,7 +11,7 @@
#include "primitives/transaction.h"
#include "init.h"
-#include "main.h" // For minRelayTxFee
+#include "validation.h" // For minRelayTxFee
#include "protocol.h"
#include "script/script.h"
#include "script/standard.h"
@@ -55,6 +55,7 @@
#include <QSettings>
#include <QTextDocument> // for Qt::mightBeRichText
#include <QThread>
+#include <QMouseEvent>
#if QT_VERSION < 0x050000
#include <QUrl>
@@ -116,7 +117,7 @@ static std::string DummyAddress(const CChainParams &params)
std::vector<unsigned char> sourcedata = params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
sourcedata.insert(sourcedata.end(), dummydata, dummydata + sizeof(dummydata));
for(int i=0; i<256; ++i) { // Try every trailing byte
- std::string s = EncodeBase58(begin_ptr(sourcedata), end_ptr(sourcedata));
+ std::string s = EncodeBase58(sourcedata.data(), sourcedata.data() + sourcedata.size());
if (!CBitcoinAddress(s).IsValid())
return s;
sourcedata[sourcedata.size()-1] += 1;
@@ -291,17 +292,11 @@ void copyEntryData(QAbstractItemView *view, int column, int role)
}
}
-QVariant getEntryData(QAbstractItemView *view, int column, int role)
+QList<QModelIndex> getEntryData(QAbstractItemView *view, int column)
{
if(!view || !view->selectionModel())
- return QVariant();
- QModelIndexList selection = view->selectionModel()->selectedRows(column);
-
- if(!selection.isEmpty()) {
- // Return first item
- return (selection.at(0).data(role));
- }
- return QVariant();
+ return QList<QModelIndex>();
+ return view->selectionModel()->selectedRows(column);
}
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir,
@@ -462,9 +457,9 @@ void SubstituteFonts(const QString& language)
#endif
}
-ToolTipToRichTextFilter::ToolTipToRichTextFilter(int size_threshold, QObject *parent) :
+ToolTipToRichTextFilter::ToolTipToRichTextFilter(int _size_threshold, QObject *parent) :
QObject(parent),
- size_threshold(size_threshold)
+ size_threshold(_size_threshold)
{
}
@@ -591,7 +586,8 @@ void TableViewLastColumnResizingFixer::on_geometriesChanged()
* Initializes all internal variables and prepares the
* the resize modes of the last 2 columns of the table and
*/
-TableViewLastColumnResizingFixer::TableViewLastColumnResizingFixer(QTableView* table, int lastColMinimumWidth, int allColsMinimumWidth) :
+TableViewLastColumnResizingFixer::TableViewLastColumnResizingFixer(QTableView* table, int lastColMinimumWidth, int allColsMinimumWidth, QObject *parent) :
+ QObject(parent),
tableView(table),
lastColumnMinimumWidth(lastColMinimumWidth),
allColumnsMinimumWidth(allColsMinimumWidth)
@@ -947,7 +943,7 @@ QString formatServicesStr(quint64 mask)
QString formatPingTime(double dPingTime)
{
- return dPingTime == 0 ? QObject::tr("N/A") : QString(QObject::tr("%1 ms")).arg(QString::number((int)(dPingTime * 1000), 10));
+ return (dPingTime == std::numeric_limits<int64_t>::max()/1e6 || dPingTime == 0) ? QObject::tr("N/A") : QString(QObject::tr("%1 ms")).arg(QString::number((int)(dPingTime * 1000), 10));
}
QString formatTimeOffset(int64_t nTimeOffset)
@@ -955,4 +951,51 @@ QString formatTimeOffset(int64_t nTimeOffset)
return QString(QObject::tr("%1 s")).arg(QString::number((int)nTimeOffset, 10));
}
+QString formateNiceTimeOffset(qint64 secs)
+{
+ // Represent time from last generated block in human readable text
+ QString timeBehindText;
+ const int HOUR_IN_SECONDS = 60*60;
+ const int DAY_IN_SECONDS = 24*60*60;
+ const int WEEK_IN_SECONDS = 7*24*60*60;
+ const int YEAR_IN_SECONDS = 31556952; // Average length of year in Gregorian calendar
+ if(secs < 60)
+ {
+ timeBehindText = QObject::tr("%n seconds(s)","",secs);
+ }
+ else if(secs < 2*HOUR_IN_SECONDS)
+ {
+ timeBehindText = QObject::tr("%n minutes(s)","",secs/60);
+ }
+ else if(secs < 2*DAY_IN_SECONDS)
+ {
+ timeBehindText = QObject::tr("%n hour(s)","",secs/HOUR_IN_SECONDS);
+ }
+ else if(secs < 2*WEEK_IN_SECONDS)
+ {
+ timeBehindText = QObject::tr("%n day(s)","",secs/DAY_IN_SECONDS);
+ }
+ else if(secs < YEAR_IN_SECONDS)
+ {
+ timeBehindText = QObject::tr("%n week(s)","",secs/WEEK_IN_SECONDS);
+ }
+ else
+ {
+ qint64 years = secs / YEAR_IN_SECONDS;
+ qint64 remainder = secs % YEAR_IN_SECONDS;
+ timeBehindText = QObject::tr("%1 and %2").arg(QObject::tr("%n year(s)", "", years)).arg(QObject::tr("%n week(s)","", remainder/WEEK_IN_SECONDS));
+ }
+ return timeBehindText;
+}
+
+void ClickableLabel::mouseReleaseEvent(QMouseEvent *event)
+{
+ Q_EMIT clicked(event->pos());
+}
+
+void ClickableProgressBar::mouseReleaseEvent(QMouseEvent *event)
+{
+ Q_EMIT clicked(event->pos());
+}
+
} // namespace GUIUtil
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index d5a658e7c0..4ea2aa36e7 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -14,6 +14,7 @@
#include <QProgressBar>
#include <QString>
#include <QTableView>
+#include <QLabel>
#include <boost/filesystem.hpp>
@@ -67,10 +68,9 @@ namespace GUIUtil
/** Return a field of the currently selected entry as a QString. 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
*/
- QVariant getEntryData(QAbstractItemView *view, int column, int role);
+ QList<QModelIndex> getEntryData(QAbstractItemView *view, int column);
void setClipboard(const QString& str);
@@ -150,7 +150,7 @@ namespace GUIUtil
Q_OBJECT
public:
- TableViewLastColumnResizingFixer(QTableView* table, int lastColMinimumWidth, int allColsMinimumWidth);
+ TableViewLastColumnResizingFixer(QTableView* table, int lastColMinimumWidth, int allColsMinimumWidth, QObject *parent);
void stretchColumnWidth(int column);
private:
@@ -200,18 +200,46 @@ namespace GUIUtil
/* Format a CNodeCombinedStats.nTimeOffset into a user-readable string. */
QString formatTimeOffset(int64_t nTimeOffset);
+ QString formateNiceTimeOffset(qint64 secs);
+
+ class ClickableLabel : public QLabel
+ {
+ Q_OBJECT
+
+ Q_SIGNALS:
+ /** Emitted when the label is clicked. The relative mouse coordinates of the click are
+ * passed to the signal.
+ */
+ void clicked(const QPoint& point);
+ protected:
+ void mouseReleaseEvent(QMouseEvent *event);
+ };
+
+ class ClickableProgressBar : public QProgressBar
+ {
+ Q_OBJECT
+
+ Q_SIGNALS:
+ /** Emitted when the progressbar is clicked. The relative mouse coordinates of the click are
+ * passed to the signal.
+ */
+ void clicked(const QPoint& point);
+ protected:
+ void mouseReleaseEvent(QMouseEvent *event);
+ };
+
#if defined(Q_OS_MAC) && QT_VERSION >= 0x050000
// workaround for Qt OSX Bug:
// https://bugreports.qt-project.org/browse/QTBUG-15631
// QProgressBar uses around 10% CPU even when app is in background
- class ProgressBar : public QProgressBar
+ class ProgressBar : public ClickableProgressBar
{
bool event(QEvent *e) {
return (e->type() != QEvent::StyleAnimationUpdate) ? QProgressBar::event(e) : false;
}
};
#else
- typedef QProgressBar ProgressBar;
+ typedef ClickableProgressBar ProgressBar;
#endif
} // namespace GUIUtil
diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp
index 1a241ae0f0..5a336b105e 100644
--- a/src/qt/intro.cpp
+++ b/src/qt/intro.cpp
@@ -63,9 +63,9 @@ private:
#include "intro.moc"
-FreespaceChecker::FreespaceChecker(Intro *intro)
+FreespaceChecker::FreespaceChecker(Intro *_intro)
{
- this->intro = intro;
+ this->intro = _intro;
}
void FreespaceChecker::check()
diff --git a/src/qt/modaloverlay.cpp b/src/qt/modaloverlay.cpp
new file mode 100644
index 0000000000..3e8282e63d
--- /dev/null
+++ b/src/qt/modaloverlay.cpp
@@ -0,0 +1,170 @@
+// Copyright (c) 2016 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "modaloverlay.h"
+#include "ui_modaloverlay.h"
+
+#include "guiutil.h"
+
+#include <QResizeEvent>
+#include <QPropertyAnimation>
+
+ModalOverlay::ModalOverlay(QWidget *parent) :
+QWidget(parent),
+ui(new Ui::ModalOverlay),
+bestHeaderHeight(0),
+bestHeaderDate(QDateTime()),
+layerIsVisible(false),
+userClosed(false)
+{
+ ui->setupUi(this);
+ connect(ui->closeButton, SIGNAL(clicked()), this, SLOT(closeClicked()));
+ if (parent) {
+ parent->installEventFilter(this);
+ raise();
+ }
+
+ blockProcessTime.clear();
+ setVisible(false);
+}
+
+ModalOverlay::~ModalOverlay()
+{
+ delete ui;
+}
+
+bool ModalOverlay::eventFilter(QObject * obj, QEvent * ev) {
+ if (obj == parent()) {
+ if (ev->type() == QEvent::Resize) {
+ QResizeEvent * rev = static_cast<QResizeEvent*>(ev);
+ resize(rev->size());
+ if (!layerIsVisible)
+ setGeometry(0, height(), width(), height());
+
+ }
+ else if (ev->type() == QEvent::ChildAdded) {
+ raise();
+ }
+ }
+ return QWidget::eventFilter(obj, ev);
+}
+
+//! Tracks parent widget changes
+bool ModalOverlay::event(QEvent* ev) {
+ if (ev->type() == QEvent::ParentAboutToChange) {
+ if (parent()) parent()->removeEventFilter(this);
+ }
+ else if (ev->type() == QEvent::ParentChange) {
+ if (parent()) {
+ parent()->installEventFilter(this);
+ raise();
+ }
+ }
+ return QWidget::event(ev);
+}
+
+void ModalOverlay::setKnownBestHeight(int count, const QDateTime& blockDate)
+{
+ if (count > bestHeaderHeight) {
+ bestHeaderHeight = count;
+ bestHeaderDate = blockDate;
+ }
+}
+
+void ModalOverlay::tipUpdate(int count, const QDateTime& blockDate, double nVerificationProgress)
+{
+ QDateTime currentDate = QDateTime::currentDateTime();
+
+ // keep a vector of samples of verification progress at height
+ blockProcessTime.push_front(qMakePair(currentDate.toMSecsSinceEpoch(), nVerificationProgress));
+
+ // show progress speed if we have more then one sample
+ if (blockProcessTime.size() >= 2)
+ {
+ double progressStart = blockProcessTime[0].second;
+ double progressDelta = 0;
+ double progressPerHour = 0;
+ qint64 timeDelta = 0;
+ qint64 remainingMSecs = 0;
+ double remainingProgress = 1.0 - nVerificationProgress;
+ for (int i = 1; i < blockProcessTime.size(); i++)
+ {
+ QPair<qint64, double> sample = blockProcessTime[i];
+
+ // take first sample after 500 seconds or last available one
+ if (sample.first < (currentDate.toMSecsSinceEpoch() - 500 * 1000) || i == blockProcessTime.size() - 1) {
+ progressDelta = progressStart-sample.second;
+ timeDelta = blockProcessTime[0].first - sample.first;
+ progressPerHour = progressDelta/(double)timeDelta*1000*3600;
+ remainingMSecs = remainingProgress / progressDelta * timeDelta;
+ break;
+ }
+ }
+ // show progress increase per hour
+ ui->progressIncreasePerH->setText(QString::number(progressPerHour*100, 'f', 2)+"%");
+
+ // show expected remaining time
+ ui->expectedTimeLeft->setText(GUIUtil::formateNiceTimeOffset(remainingMSecs/1000.0));
+
+ static const int MAX_SAMPLES = 5000;
+ if (blockProcessTime.count() > MAX_SAMPLES)
+ blockProcessTime.remove(MAX_SAMPLES, blockProcessTime.count()-MAX_SAMPLES);
+ }
+
+ // show the last block date
+ ui->newestBlockDate->setText(blockDate.toString());
+
+ // show the percentage done according to nVerificationProgress
+ ui->percentageProgress->setText(QString::number(nVerificationProgress*100, 'f', 2)+"%");
+ ui->progressBar->setValue(nVerificationProgress*100);
+
+ if (!bestHeaderDate.isValid())
+ // not syncing
+ return;
+
+ // estimate the number of headers left based on nPowTargetSpacing
+ // and check if the gui is not aware of the the best header (happens rarely)
+ int estimateNumHeadersLeft = bestHeaderDate.secsTo(currentDate) / 600;
+ bool hasBestHeader = bestHeaderHeight >= count;
+
+ // show remaining number of blocks
+ if (estimateNumHeadersLeft < 24 && hasBestHeader) {
+ ui->numberOfBlocksLeft->setText(QString::number(bestHeaderHeight - count));
+ } else {
+ ui->numberOfBlocksLeft->setText(tr("Unknown. Syncing Headers (%1)...").arg(bestHeaderHeight));
+ ui->expectedTimeLeft->setText(tr("Unknown..."));
+ }
+}
+
+void ModalOverlay::toggleVisibility()
+{
+ showHide(layerIsVisible, true);
+ if (!layerIsVisible)
+ userClosed = true;
+}
+
+void ModalOverlay::showHide(bool hide, bool userRequested)
+{
+ if ( (layerIsVisible && !hide) || (!layerIsVisible && hide) || (!hide && userClosed && !userRequested))
+ return;
+
+ if (!isVisible() && !hide)
+ setVisible(true);
+
+ setGeometry(0, hide ? 0 : height(), width(), height());
+
+ QPropertyAnimation* animation = new QPropertyAnimation(this, "pos");
+ animation->setDuration(300);
+ animation->setStartValue(QPoint(0, hide ? 0 : this->height()));
+ animation->setEndValue(QPoint(0, hide ? this->height() : 0));
+ animation->setEasingCurve(QEasingCurve::OutQuad);
+ animation->start(QAbstractAnimation::DeleteWhenStopped);
+ layerIsVisible = !hide;
+}
+
+void ModalOverlay::closeClicked()
+{
+ showHide(true);
+ userClosed = true;
+}
diff --git a/src/qt/modaloverlay.h b/src/qt/modaloverlay.h
new file mode 100644
index 0000000000..70d37b87ad
--- /dev/null
+++ b/src/qt/modaloverlay.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2016 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_QT_MODALOVERLAY_H
+#define BITCOIN_QT_MODALOVERLAY_H
+
+#include <QDateTime>
+#include <QWidget>
+
+namespace Ui {
+ class ModalOverlay;
+}
+
+/** Modal overlay to display information about the chain-sync state */
+class ModalOverlay : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit ModalOverlay(QWidget *parent);
+ ~ModalOverlay();
+
+public Q_SLOTS:
+ void tipUpdate(int count, const QDateTime& blockDate, double nVerificationProgress);
+ void setKnownBestHeight(int count, const QDateTime& blockDate);
+
+ void toggleVisibility();
+ // will show or hide the modal layer
+ void showHide(bool hide = false, bool userRequested = false);
+ void closeClicked();
+ bool isLayerVisible() { return layerIsVisible; }
+
+protected:
+ bool eventFilter(QObject * obj, QEvent * ev);
+ bool event(QEvent* ev);
+
+private:
+ Ui::ModalOverlay *ui;
+ int bestHeaderHeight; //best known height (based on the headers)
+ QDateTime bestHeaderDate;
+ QVector<QPair<qint64, double> > blockProcessTime;
+ bool layerIsVisible;
+ bool userClosed;
+};
+
+#endif // BITCOIN_QT_MODALOVERLAY_H
diff --git a/src/qt/networkstyle.cpp b/src/qt/networkstyle.cpp
index 5f31f49372..acbfee0868 100644
--- a/src/qt/networkstyle.cpp
+++ b/src/qt/networkstyle.cpp
@@ -22,9 +22,9 @@ static const struct {
static const unsigned network_styles_count = sizeof(network_styles)/sizeof(*network_styles);
// titleAddText needs to be const char* for tr()
-NetworkStyle::NetworkStyle(const QString &appName, const int iconColorHueShift, const int iconColorSaturationReduction, const char *titleAddText):
- appName(appName),
- titleAddText(qApp->translate("SplashScreen", titleAddText))
+NetworkStyle::NetworkStyle(const QString &_appName, const int iconColorHueShift, const int iconColorSaturationReduction, const char *_titleAddText):
+ appName(_appName),
+ titleAddText(qApp->translate("SplashScreen", _titleAddText))
{
// load pixmap
QPixmap pixmap(":/icons/bitcoin");
diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp
index a45afde566..8277e20c90 100644
--- a/src/qt/notificator.cpp
+++ b/src/qt/notificator.cpp
@@ -33,17 +33,17 @@
const int FREEDESKTOP_NOTIFICATION_ICON_SIZE = 128;
#endif
-Notificator::Notificator(const QString &programName, QSystemTrayIcon *trayicon, QWidget *parent) :
- QObject(parent),
- parent(parent),
- programName(programName),
+Notificator::Notificator(const QString &_programName, QSystemTrayIcon *_trayIcon, QWidget *_parent) :
+ QObject(_parent),
+ parent(_parent),
+ programName(_programName),
mode(None),
- trayIcon(trayicon)
+ trayIcon(_trayIcon)
#ifdef USE_DBUS
,interface(0)
#endif
{
- if(trayicon && trayicon->supportsMessages())
+ if(_trayIcon && _trayIcon->supportsMessages())
{
mode = QSystemTray;
}
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index f73bb87064..2d5e9ff686 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -13,7 +13,7 @@
#include "guiutil.h"
#include "optionsmodel.h"
-#include "main.h" // for DEFAULT_SCRIPTCHECK_THREADS and MAX_SCRIPTCHECK_THREADS
+#include "validation.h" // for DEFAULT_SCRIPTCHECK_THREADS and MAX_SCRIPTCHECK_THREADS
#include "netbase.h"
#include "txdb.h" // for -dbcache defaults
@@ -135,22 +135,22 @@ OptionsDialog::~OptionsDialog()
delete ui;
}
-void OptionsDialog::setModel(OptionsModel *model)
+void OptionsDialog::setModel(OptionsModel *_model)
{
- this->model = model;
+ this->model = _model;
- if(model)
+ if(_model)
{
/* check if client restart is needed and show persistent message */
- if (model->isRestartRequired())
+ if (_model->isRestartRequired())
showRestartWarning(true);
- QString strLabel = model->getOverriddenByCommandLine();
+ QString strLabel = _model->getOverriddenByCommandLine();
if (strLabel.isEmpty())
strLabel = tr("none");
ui->overriddenByCommandLineLabel->setText(strLabel);
- mapper->setModel(model);
+ mapper->setModel(_model);
setMapper();
mapper->toFirst();
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index f82e153b67..d48c4d91e7 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -13,7 +13,7 @@
#include "amount.h"
#include "init.h"
-#include "main.h" // For DEFAULT_SCRIPTCHECK_THREADS
+#include "validation.h" // For DEFAULT_SCRIPTCHECK_THREADS
#include "net.h"
#include "netbase.h"
#include "txdb.h" // for -dbcache defaults
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index b23b5f2607..d2e1fb23a3 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -81,7 +81,7 @@ private:
int nDisplayUnit;
QString strThirdPartyTxUrls;
bool fCoinControlFeatures;
- /* settings that were overriden by command-line */
+ /* settings that were overridden by command-line */
QString strOverriddenByCommandLine;
// Add option to list of GUI options overridden through command line/config file
diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp
index 6a0404cbf7..ea1bb2fc94 100644
--- a/src/qt/overviewpage.cpp
+++ b/src/qt/overviewpage.cpp
@@ -25,9 +25,9 @@ class TxViewDelegate : public QAbstractItemDelegate
{
Q_OBJECT
public:
- TxViewDelegate(const PlatformStyle *platformStyle):
- QAbstractItemDelegate(), unit(BitcoinUnits::BTC),
- platformStyle(platformStyle)
+ TxViewDelegate(const PlatformStyle *_platformStyle, QObject *parent=nullptr):
+ QAbstractItemDelegate(parent), unit(BitcoinUnits::BTC),
+ platformStyle(_platformStyle)
{
}
@@ -119,8 +119,7 @@ OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent)
currentWatchOnlyBalance(-1),
currentWatchUnconfBalance(-1),
currentWatchImmatureBalance(-1),
- txdelegate(new TxViewDelegate(platformStyle)),
- filter(0)
+ txdelegate(new TxViewDelegate(platformStyle, this))
{
ui->setupUi(this);
@@ -140,6 +139,8 @@ OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent)
// start with displaying the "out of sync" warnings
showOutOfSyncWarning(true);
+ connect(ui->labelWalletStatus, SIGNAL(clicked()), this, SLOT(handleOutOfSyncWarningClicks()));
+ connect(ui->labelTransactionsStatus, SIGNAL(clicked()), this, SLOT(handleOutOfSyncWarningClicks()));
}
void OverviewPage::handleTransactionClicked(const QModelIndex &index)
@@ -148,6 +149,11 @@ void OverviewPage::handleTransactionClicked(const QModelIndex &index)
Q_EMIT transactionClicked(filter->mapToSource(index));
}
+void OverviewPage::handleOutOfSyncWarningClicks()
+{
+ Q_EMIT outOfSyncWarningClicked();
+}
+
OverviewPage::~OverviewPage()
{
delete ui;
@@ -213,7 +219,7 @@ void OverviewPage::setWalletModel(WalletModel *model)
if(model && model->getOptionsModel())
{
// Set up transaction list
- filter = new TransactionFilterProxy();
+ filter.reset(new TransactionFilterProxy());
filter->setSourceModel(model->getTransactionTableModel());
filter->setLimit(NUM_ITEMS);
filter->setDynamicSortFilter(true);
@@ -221,7 +227,7 @@ void OverviewPage::setWalletModel(WalletModel *model)
filter->setShowInactive(false);
filter->sort(TransactionTableModel::Date, Qt::DescendingOrder);
- ui->listTransactions->setModel(filter);
+ ui->listTransactions->setModel(filter.get());
ui->listTransactions->setModelColumn(TransactionTableModel::ToAddress);
// Keep up to date with wallet
diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h
index 911443c76a..ffe0de328c 100644
--- a/src/qt/overviewpage.h
+++ b/src/qt/overviewpage.h
@@ -8,6 +8,7 @@
#include "amount.h"
#include <QWidget>
+#include <memory>
class ClientModel;
class TransactionFilterProxy;
@@ -42,6 +43,7 @@ public Q_SLOTS:
Q_SIGNALS:
void transactionClicked(const QModelIndex &index);
+ void outOfSyncWarningClicked();
private:
Ui::OverviewPage *ui;
@@ -55,13 +57,14 @@ private:
CAmount currentWatchImmatureBalance;
TxViewDelegate *txdelegate;
- TransactionFilterProxy *filter;
+ std::unique_ptr<TransactionFilterProxy> filter;
private Q_SLOTS:
void updateDisplayUnit();
void handleTransactionClicked(const QModelIndex &index);
void updateAlerts(const QString &warnings);
void updateWatchOnlyLabels(bool showWatchOnly);
+ void handleOutOfSyncWarningClicks();
};
#endif // BITCOIN_QT_OVERVIEWPAGE_H
diff --git a/src/qt/paymentrequest.proto b/src/qt/paymentrequest.proto
index b2281c4c7b..d2721a34bd 100644
--- a/src/qt/paymentrequest.proto
+++ b/src/qt/paymentrequest.proto
@@ -6,6 +6,8 @@
// https://en.bitcoin.it/wiki/Payment_Request
//
+syntax = "proto2";
+
package payments;
option java_package = "org.bitcoin.protocols.payments";
option java_outer_classname = "Protos";
diff --git a/src/qt/paymentrequestplus.cpp b/src/qt/paymentrequestplus.cpp
index 20e1f79ffa..82be4d831f 100644
--- a/src/qt/paymentrequestplus.cpp
+++ b/src/qt/paymentrequestplus.cpp
@@ -159,14 +159,24 @@ bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) c
std::string data_to_verify; // Everything but the signature
rcopy.SerializeToString(&data_to_verify);
- EVP_MD_CTX ctx;
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ EVP_MD_CTX *ctx = EVP_MD_CTX_new();
+ if (!ctx) throw SSLVerifyError("Error allocating OpenSSL context.");
+#else
+ EVP_MD_CTX _ctx;
+ EVP_MD_CTX *ctx;
+ ctx = &_ctx;
+#endif
EVP_PKEY *pubkey = X509_get_pubkey(signing_cert);
- EVP_MD_CTX_init(&ctx);
- if (!EVP_VerifyInit_ex(&ctx, digestAlgorithm, NULL) ||
- !EVP_VerifyUpdate(&ctx, data_to_verify.data(), data_to_verify.size()) ||
- !EVP_VerifyFinal(&ctx, (const unsigned char*)paymentRequest.signature().data(), (unsigned int)paymentRequest.signature().size(), pubkey)) {
+ EVP_MD_CTX_init(ctx);
+ if (!EVP_VerifyInit_ex(ctx, digestAlgorithm, NULL) ||
+ !EVP_VerifyUpdate(ctx, data_to_verify.data(), data_to_verify.size()) ||
+ !EVP_VerifyFinal(ctx, (const unsigned char*)paymentRequest.signature().data(), (unsigned int)paymentRequest.signature().size(), pubkey)) {
throw SSLVerifyError("Bad signature, invalid payment request.");
}
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ EVP_MD_CTX_free(ctx);
+#endif
// OpenSSL API for getting human printable strings from certs is baroque.
int textlen = X509_NAME_get_text_by_NID(certname, NID_commonName, NULL, 0);
diff --git a/src/qt/paymentrequestplus.h b/src/qt/paymentrequestplus.h
index a73fe5f29d..a8bfcd2ac4 100644
--- a/src/qt/paymentrequestplus.h
+++ b/src/qt/paymentrequestplus.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2015 The Bitcoin developers
+// Copyright (c) 2011-2015 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index c80aebb009..d5f0156d6c 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -10,7 +10,7 @@
#include "base58.h"
#include "chainparams.h"
-#include "main.h" // For minRelayTxFee
+#include "validation.h" // For minRelayTxFee
#include "ui_interface.h"
#include "util.h"
#include "wallet/wallet.h"
@@ -58,14 +58,19 @@ const char* BIP71_MIMETYPE_PAYMENTREQUEST = "application/bitcoin-paymentrequest"
// BIP70 max payment request size in bytes (DoS protection)
const qint64 BIP70_MAX_PAYMENTREQUEST_SIZE = 50000;
-X509_STORE* PaymentServer::certStore = NULL;
-void PaymentServer::freeCertStore()
+struct X509StoreDeleter {
+ void operator()(X509_STORE* b) {
+ X509_STORE_free(b);
+ }
+};
+
+struct X509Deleter {
+ void operator()(X509* b) { X509_free(b); }
+};
+
+namespace // Anon namespace
{
- if (PaymentServer::certStore != NULL)
- {
- X509_STORE_free(PaymentServer::certStore);
- PaymentServer::certStore = NULL;
- }
+ std::unique_ptr<X509_STORE, X509StoreDeleter> certStore;
}
//
@@ -80,7 +85,7 @@ static QString ipcServerName()
// Append a simple hash of the datadir
// Note that GetDataDir(true) returns a different path
// for -testnet versus main net
- QString ddir(QString::fromStdString(GetDataDir(true).string()));
+ QString ddir(GUIUtil::boostPathToQString(GetDataDir(true)));
name.append(QString::number(qHash(ddir)));
return name;
@@ -107,20 +112,15 @@ static void ReportInvalidCertificate(const QSslCertificate& cert)
//
void PaymentServer::LoadRootCAs(X509_STORE* _store)
{
- if (PaymentServer::certStore == NULL)
- atexit(PaymentServer::freeCertStore);
- else
- freeCertStore();
-
// Unit tests mostly use this, to pass in fake root CAs:
if (_store)
{
- PaymentServer::certStore = _store;
+ certStore.reset(_store);
return;
}
// Normal execution, use either -rootcertificates or system certs:
- PaymentServer::certStore = X509_STORE_new();
+ certStore.reset(X509_STORE_new());
// Note: use "-system-" default here so that users can pass -rootcertificates=""
// and get 'I don't like X.509 certificates, don't trust anybody' behavior:
@@ -167,11 +167,11 @@ void PaymentServer::LoadRootCAs(X509_STORE* _store)
QByteArray certData = cert.toDer();
const unsigned char *data = (const unsigned char *)certData.data();
- X509* x509 = d2i_X509(0, &data, certData.size());
- if (x509 && X509_STORE_add_cert(PaymentServer::certStore, x509))
+ std::unique_ptr<X509, X509Deleter> x509(d2i_X509(0, &data, certData.size()));
+ if (x509 && X509_STORE_add_cert(certStore.get(), x509.get()))
{
- // Note: X509_STORE_free will free the X509* objects when
- // the PaymentServer is destroyed
+ // Note: X509_STORE increases the reference count to the X509 object,
+ // we still have to release our reference to it.
++nRootCerts;
}
else
@@ -550,7 +550,7 @@ bool PaymentServer::processPaymentRequest(const PaymentRequestPlus& request, Sen
recipient.paymentRequest = request;
recipient.message = GUIUtil::HtmlEscape(request.getDetails().memo());
- request.getMerchant(PaymentServer::certStore, recipient.authenticatedMerchant);
+ request.getMerchant(certStore.get(), recipient.authenticatedMerchant);
QList<std::pair<CScript, CAmount> > sendingTos = request.getPayTo();
QStringList addresses;
@@ -749,9 +749,9 @@ void PaymentServer::reportSslErrors(QNetworkReply* reply, const QList<QSslError>
Q_EMIT message(tr("Network request error"), errString, CClientUIInterface::MSG_ERROR);
}
-void PaymentServer::setOptionsModel(OptionsModel *optionsModel)
+void PaymentServer::setOptionsModel(OptionsModel *_optionsModel)
{
- this->optionsModel = optionsModel;
+ this->optionsModel = _optionsModel;
}
void PaymentServer::handlePaymentACK(const QString& paymentACKMsg)
@@ -807,3 +807,8 @@ bool PaymentServer::verifyAmount(const CAmount& requestAmount)
}
return fVerified;
}
+
+X509_STORE* PaymentServer::getCertStore()
+{
+ return certStore.get();
+}
diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h
index 2d27ed078b..7202e7dada 100644
--- a/src/qt/paymentserver.h
+++ b/src/qt/paymentserver.h
@@ -83,7 +83,7 @@ public:
static void LoadRootCAs(X509_STORE* store = NULL);
// Return certificate store
- static X509_STORE* getCertStore() { return certStore; }
+ static X509_STORE* getCertStore();
// OptionsModel is used for getting proxy settings and display unit
void setOptionsModel(OptionsModel *optionsModel);
@@ -140,9 +140,6 @@ private:
bool saveURIs; // true during startup
QLocalServer* uriServer;
- static X509_STORE* certStore; // Trusted root certificates
- static void freeCertStore();
-
QNetworkAccessManager* netManager; // Used to fetch payment requests
OptionsModel *optionsModel;
diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp
index a820bd791f..dd4bd55396 100644
--- a/src/qt/peertablemodel.cpp
+++ b/src/qt/peertablemodel.cpp
@@ -8,6 +8,7 @@
#include "guiconstants.h"
#include "guiutil.h"
+#include "validation.h" // for cs_main
#include "sync.h"
#include <QDebug>
@@ -31,7 +32,7 @@ bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombine
case PeerTableModel::Subversion:
return pLeft->cleanSubVer.compare(pRight->cleanSubVer) < 0;
case PeerTableModel::Ping:
- return pLeft->dPingTime < pRight->dPingTime;
+ return pLeft->dMinPing < pRight->dMinPing;
}
return false;
@@ -113,13 +114,13 @@ PeerTableModel::PeerTableModel(ClientModel *parent) :
clientModel(parent),
timer(0)
{
- columns << tr("NodeId") << tr("Node/Service") << tr("User Agent") << tr("Ping Time");
- priv = new PeerTablePriv();
+ columns << tr("NodeId") << tr("Node/Service") << tr("User Agent") << tr("Ping");
+ priv.reset(new PeerTablePriv());
// default to unsorted
priv->sortColumn = -1;
// set up timer for auto refresh
- timer = new QTimer();
+ timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), SLOT(refresh()));
timer->setInterval(MODEL_UPDATE_DELAY);
@@ -127,6 +128,11 @@ PeerTableModel::PeerTableModel(ClientModel *parent) :
refresh();
}
+PeerTableModel::~PeerTableModel()
+{
+ // Intentionally left empty
+}
+
void PeerTableModel::startAutoRefresh()
{
timer->start();
@@ -166,7 +172,7 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const
case Subversion:
return QString::fromStdString(rec->nodeStats.cleanSubVer);
case Ping:
- return GUIUtil::formatPingTime(rec->nodeStats.dPingTime);
+ return GUIUtil::formatPingTime(rec->nodeStats.dMinPing);
}
} else if (role == Qt::TextAlignmentRole) {
if (index.column() == Ping)
diff --git a/src/qt/peertablemodel.h b/src/qt/peertablemodel.h
index a4f7bbdb3d..3e4f0a68c4 100644
--- a/src/qt/peertablemodel.h
+++ b/src/qt/peertablemodel.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_QT_PEERTABLEMODEL_H
#define BITCOIN_QT_PEERTABLEMODEL_H
-#include "main.h" // For CNodeStateStats
+#include "net_processing.h" // For CNodeStateStats
#include "net.h"
#include <QAbstractTableModel>
@@ -46,6 +46,7 @@ class PeerTableModel : public QAbstractTableModel
public:
explicit PeerTableModel(ClientModel *parent = 0);
+ ~PeerTableModel();
const CNodeCombinedStats *getNodeStats(int idx);
int getRowByNodeId(NodeId nodeid);
void startAutoRefresh();
@@ -75,7 +76,7 @@ public Q_SLOTS:
private:
ClientModel *clientModel;
QStringList columns;
- PeerTablePriv *priv;
+ std::unique_ptr<PeerTablePriv> priv;
QTimer *timer;
};
diff --git a/src/qt/platformstyle.cpp b/src/qt/platformstyle.cpp
index 11cbc7a47c..e4438cc43d 100644
--- a/src/qt/platformstyle.cpp
+++ b/src/qt/platformstyle.cpp
@@ -73,11 +73,11 @@ QIcon ColorizeIcon(const QString& filename, const QColor& colorbase)
}
-PlatformStyle::PlatformStyle(const QString &name, bool imagesOnButtons, bool colorizeIcons, bool useExtraSpacing):
- name(name),
- imagesOnButtons(imagesOnButtons),
- colorizeIcons(colorizeIcons),
- useExtraSpacing(useExtraSpacing),
+PlatformStyle::PlatformStyle(const QString &_name, bool _imagesOnButtons, bool _colorizeIcons, bool _useExtraSpacing):
+ name(_name),
+ imagesOnButtons(_imagesOnButtons),
+ colorizeIcons(_colorizeIcons),
+ useExtraSpacing(_useExtraSpacing),
singleColor(0,0,0),
textColor(0,0,0)
{
diff --git a/src/qt/qvalidatedlineedit.cpp b/src/qt/qvalidatedlineedit.cpp
index baa2eb67f7..492b96ff09 100644
--- a/src/qt/qvalidatedlineedit.cpp
+++ b/src/qt/qvalidatedlineedit.cpp
@@ -15,14 +15,14 @@ QValidatedLineEdit::QValidatedLineEdit(QWidget *parent) :
connect(this, SIGNAL(textChanged(QString)), this, SLOT(markValid()));
}
-void QValidatedLineEdit::setValid(bool valid)
+void QValidatedLineEdit::setValid(bool _valid)
{
- if(valid == this->valid)
+ if(_valid == this->valid)
{
return;
}
- if(valid)
+ if(_valid)
{
setStyleSheet("");
}
@@ -30,7 +30,7 @@ void QValidatedLineEdit::setValid(bool valid)
{
setStyleSheet(STYLE_INVALID);
}
- this->valid = valid;
+ this->valid = _valid;
}
void QValidatedLineEdit::focusInEvent(QFocusEvent *evt)
diff --git a/src/qt/qvaluecombobox.cpp b/src/qt/qvaluecombobox.cpp
index 146f3dd578..2f2478783c 100644
--- a/src/qt/qvaluecombobox.cpp
+++ b/src/qt/qvaluecombobox.cpp
@@ -20,9 +20,9 @@ void QValueComboBox::setValue(const QVariant &value)
setCurrentIndex(findData(value, role));
}
-void QValueComboBox::setRole(int role)
+void QValueComboBox::setRole(int _role)
{
- this->role = role;
+ this->role = _role;
}
void QValueComboBox::handleSelectionChanged(int idx)
diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp
index 0b355837ab..dd83547a91 100644
--- a/src/qt/receivecoinsdialog.cpp
+++ b/src/qt/receivecoinsdialog.cpp
@@ -22,39 +22,43 @@
#include <QScrollBar>
#include <QTextDocument>
-ReceiveCoinsDialog::ReceiveCoinsDialog(const PlatformStyle *platformStyle, QWidget *parent) :
+ReceiveCoinsDialog::ReceiveCoinsDialog(const PlatformStyle *_platformStyle, QWidget *parent) :
QDialog(parent),
ui(new Ui::ReceiveCoinsDialog),
+ columnResizingFixer(0),
model(0),
- platformStyle(platformStyle)
+ platformStyle(_platformStyle)
{
ui->setupUi(this);
- if (!platformStyle->getImagesOnButtons()) {
+ if (!_platformStyle->getImagesOnButtons()) {
ui->clearButton->setIcon(QIcon());
ui->receiveButton->setIcon(QIcon());
ui->showRequestButton->setIcon(QIcon());
ui->removeRequestButton->setIcon(QIcon());
} else {
- ui->clearButton->setIcon(platformStyle->SingleColorIcon(":/icons/remove"));
- ui->receiveButton->setIcon(platformStyle->SingleColorIcon(":/icons/receiving_addresses"));
- ui->showRequestButton->setIcon(platformStyle->SingleColorIcon(":/icons/edit"));
- ui->removeRequestButton->setIcon(platformStyle->SingleColorIcon(":/icons/remove"));
+ ui->clearButton->setIcon(_platformStyle->SingleColorIcon(":/icons/remove"));
+ ui->receiveButton->setIcon(_platformStyle->SingleColorIcon(":/icons/receiving_addresses"));
+ ui->showRequestButton->setIcon(_platformStyle->SingleColorIcon(":/icons/edit"));
+ ui->removeRequestButton->setIcon(_platformStyle->SingleColorIcon(":/icons/remove"));
}
// context menu actions
+ QAction *copyURIAction = new QAction(tr("Copy URI"), this);
QAction *copyLabelAction = new QAction(tr("Copy label"), this);
QAction *copyMessageAction = new QAction(tr("Copy message"), this);
QAction *copyAmountAction = new QAction(tr("Copy amount"), this);
// context menu
- contextMenu = new QMenu();
+ contextMenu = new QMenu(this);
+ contextMenu->addAction(copyURIAction);
contextMenu->addAction(copyLabelAction);
contextMenu->addAction(copyMessageAction);
contextMenu->addAction(copyAmountAction);
// context menu signals
connect(ui->recentRequestsView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showMenu(QPoint)));
+ connect(copyURIAction, SIGNAL(triggered()), this, SLOT(copyURI()));
connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel()));
connect(copyMessageAction, SIGNAL(triggered()), this, SLOT(copyMessage()));
connect(copyAmountAction, SIGNAL(triggered()), this, SLOT(copyAmount()));
@@ -62,21 +66,21 @@ ReceiveCoinsDialog::ReceiveCoinsDialog(const PlatformStyle *platformStyle, QWidg
connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
}
-void ReceiveCoinsDialog::setModel(WalletModel *model)
+void ReceiveCoinsDialog::setModel(WalletModel *_model)
{
- this->model = model;
+ this->model = _model;
- if(model && model->getOptionsModel())
+ if(_model && _model->getOptionsModel())
{
- model->getRecentRequestsTableModel()->sort(RecentRequestsTableModel::Date, Qt::DescendingOrder);
- connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
+ _model->getRecentRequestsTableModel()->sort(RecentRequestsTableModel::Date, Qt::DescendingOrder);
+ connect(_model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
updateDisplayUnit();
QTableView* tableView = ui->recentRequestsView;
tableView->verticalHeader()->hide();
tableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- tableView->setModel(model->getRecentRequestsTableModel());
+ tableView->setModel(_model->getRecentRequestsTableModel());
tableView->setAlternatingRowColors(true);
tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
tableView->setSelectionMode(QAbstractItemView::ContiguousSelection);
@@ -88,7 +92,7 @@ void ReceiveCoinsDialog::setModel(WalletModel *model)
SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this,
SLOT(recentRequestsView_selectionChanged(QItemSelection, QItemSelection)));
// Last 2 columns are set by the columnResizingFixer, when the table geometry is ready.
- columnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(tableView, AMOUNT_MINIMUM_COLUMN_WIDTH, DATE_COLUMN_WIDTH);
+ columnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(tableView, AMOUNT_MINIMUM_COLUMN_WIDTH, DATE_COLUMN_WIDTH, this);
}
}
@@ -228,30 +232,50 @@ void ReceiveCoinsDialog::keyPressEvent(QKeyEvent *event)
this->QDialog::keyPressEvent(event);
}
-// copy column of selected row to clipboard
-void ReceiveCoinsDialog::copyColumnToClipboard(int column)
+QModelIndex ReceiveCoinsDialog::selectedRow()
{
if(!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel())
- return;
+ return QModelIndex();
QModelIndexList selection = ui->recentRequestsView->selectionModel()->selectedRows();
if(selection.empty())
- return;
+ return QModelIndex();
// correct for selection mode ContiguousSelection
QModelIndex firstIndex = selection.at(0);
+ return firstIndex;
+}
+
+// copy column of selected row to clipboard
+void ReceiveCoinsDialog::copyColumnToClipboard(int column)
+{
+ QModelIndex firstIndex = selectedRow();
+ if (!firstIndex.isValid()) {
+ return;
+ }
GUIUtil::setClipboard(model->getRecentRequestsTableModel()->data(firstIndex.child(firstIndex.row(), column), Qt::EditRole).toString());
}
// context menu
void ReceiveCoinsDialog::showMenu(const QPoint &point)
{
- if(!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel())
- return;
- QModelIndexList selection = ui->recentRequestsView->selectionModel()->selectedRows();
- if(selection.empty())
+ if (!selectedRow().isValid()) {
return;
+ }
contextMenu->exec(QCursor::pos());
}
+// context menu action: copy URI
+void ReceiveCoinsDialog::copyURI()
+{
+ QModelIndex sel = selectedRow();
+ if (!sel.isValid()) {
+ return;
+ }
+
+ const RecentRequestsTableModel * const submodel = model->getRecentRequestsTableModel();
+ const QString uri = GUIUtil::formatBitcoinURI(submodel->entry(sel.row()).recipient);
+ GUIUtil::setClipboard(uri);
+}
+
// context menu action: copy label
void ReceiveCoinsDialog::copyLabel()
{
diff --git a/src/qt/receivecoinsdialog.h b/src/qt/receivecoinsdialog.h
index 226fd65cfa..d137f1616e 100644
--- a/src/qt/receivecoinsdialog.h
+++ b/src/qt/receivecoinsdialog.h
@@ -60,6 +60,7 @@ private:
QMenu *contextMenu;
const PlatformStyle *platformStyle;
+ QModelIndex selectedRow();
void copyColumnToClipboard(int column);
virtual void resizeEvent(QResizeEvent *event);
@@ -71,6 +72,7 @@ private Q_SLOTS:
void recentRequestsView_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
void updateDisplayUnit();
void showMenu(const QPoint &point);
+ void copyURI();
void copyLabel();
void copyMessage();
void copyAmount();
diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp
index b13ea3df70..f896461735 100644
--- a/src/qt/receiverequestdialog.cpp
+++ b/src/qt/receiverequestdialog.cpp
@@ -32,7 +32,7 @@
QRImageWidget::QRImageWidget(QWidget *parent):
QLabel(parent), contextMenu(0)
{
- contextMenu = new QMenu();
+ contextMenu = new QMenu(this);
QAction *saveImageAction = new QAction(tr("&Save Image..."), this);
connect(saveImageAction, SIGNAL(triggered()), this, SLOT(saveImage()));
contextMenu->addAction(saveImageAction);
@@ -109,20 +109,20 @@ ReceiveRequestDialog::~ReceiveRequestDialog()
delete ui;
}
-void ReceiveRequestDialog::setModel(OptionsModel *model)
+void ReceiveRequestDialog::setModel(OptionsModel *_model)
{
- this->model = model;
+ this->model = _model;
- if (model)
- connect(model, SIGNAL(displayUnitChanged(int)), this, SLOT(update()));
+ if (_model)
+ connect(_model, SIGNAL(displayUnitChanged(int)), this, SLOT(update()));
// update the display unit if necessary
update();
}
-void ReceiveRequestDialog::setInfo(const SendCoinsRecipient &info)
+void ReceiveRequestDialog::setInfo(const SendCoinsRecipient &_info)
{
- this->info = info;
+ this->info = _info;
update();
}
diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp
index 2335d6b282..35d37bb22b 100644
--- a/src/qt/recentrequeststablemodel.cpp
+++ b/src/qt/recentrequeststablemodel.cpp
@@ -14,7 +14,7 @@
#include <boost/foreach.hpp>
RecentRequestsTableModel::RecentRequestsTableModel(CWallet *wallet, WalletModel *parent) :
- walletModel(parent)
+ QAbstractTableModel(parent), walletModel(parent)
{
Q_UNUSED(wallet);
nReceiveRequestsMaxId = 0;
diff --git a/src/qt/recentrequeststablemodel.h b/src/qt/recentrequeststablemodel.h
index f3cf03f4e3..8ee2c9cbac 100644
--- a/src/qt/recentrequeststablemodel.h
+++ b/src/qt/recentrequeststablemodel.h
@@ -27,11 +27,10 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
unsigned int nDate = date.toTime_t();
READWRITE(this->nVersion);
- nVersion = this->nVersion;
READWRITE(id);
READWRITE(nDate);
READWRITE(recipient);
diff --git a/src/qt/res/icons/network_disabled.png b/src/qt/res/icons/network_disabled.png
new file mode 100644
index 0000000000..269c3cfab8
--- /dev/null
+++ b/src/qt/res/icons/network_disabled.png
Binary files differ
diff --git a/src/qt/res/movies/makespinner.sh b/src/qt/res/movies/makespinner.sh
index a4c2fddbbf..d0deb1238c 100755
--- a/src/qt/res/movies/makespinner.sh
+++ b/src/qt/res/movies/makespinner.sh
@@ -1,3 +1,7 @@
+# Copyright (c) 2014-2015 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
FRAMEDIR=$(dirname $0)
for i in {0..35}
do
diff --git a/src/qt/res/src/connect-0.svg b/src/qt/res/src/connect-0.svg
index 7d2afac622..0920555b96 100644
--- a/src/qt/res/src/connect-0.svg
+++ b/src/qt/res/src/connect-0.svg
@@ -7,8 +7,8 @@
xmlns="http://www.w3.org/2000/svg"
id="svg2"
viewBox="0 0 24 24"
- height="24"
- width="24"
+ height="92"
+ width="92"
version="1.2">
<metadata
id="metadata10">
diff --git a/src/qt/res/src/connect-1.svg b/src/qt/res/src/connect-1.svg
index d17928c97d..25dea4cd3a 100644
--- a/src/qt/res/src/connect-1.svg
+++ b/src/qt/res/src/connect-1.svg
@@ -6,8 +6,8 @@
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.2"
- width="24"
- height="24"
+ width="92"
+ height="92"
viewBox="0 0 24 24"
id="svg2">
<metadata
diff --git a/src/qt/res/src/connect-2.svg b/src/qt/res/src/connect-2.svg
index 841ca6071d..bb98333d23 100644
--- a/src/qt/res/src/connect-2.svg
+++ b/src/qt/res/src/connect-2.svg
@@ -6,8 +6,8 @@
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.2"
- width="24"
- height="24"
+ width="92"
+ height="92"
viewBox="0 0 24 24"
id="svg2">
<metadata
diff --git a/src/qt/res/src/connect-3.svg b/src/qt/res/src/connect-3.svg
index b06e67daf8..a54a55ef61 100644
--- a/src/qt/res/src/connect-3.svg
+++ b/src/qt/res/src/connect-3.svg
@@ -7,8 +7,8 @@
xmlns="http://www.w3.org/2000/svg"
id="svg2"
viewBox="0 0 24 24"
- height="24"
- width="24"
+ height="92"
+ width="92"
version="1.2">
<metadata
id="metadata10">
diff --git a/src/qt/res/src/connect-4.svg b/src/qt/res/src/connect-4.svg
index 0abc7955fd..b83b9f9d03 100644
--- a/src/qt/res/src/connect-4.svg
+++ b/src/qt/res/src/connect-4.svg
@@ -6,8 +6,8 @@
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.2"
- width="24"
- height="24"
+ width="92"
+ height="92"
viewBox="0 0 24 24"
id="svg2">
<metadata
diff --git a/src/qt/res/src/network_disabled.svg b/src/qt/res/src/network_disabled.svg
new file mode 100644
index 0000000000..a041d77439
--- /dev/null
+++ b/src/qt/res/src/network_disabled.svg
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ id="svg2"
+ viewBox="0 0 24 24"
+ width="92"
+ height="92"
+ version="1.2">
+ <metadata
+ id="metadata10">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs8" />
+ <g
+ id="g4142"
+ transform="matrix(0,-1,-1,0,23.96,24)">
+ <g
+ id="g4210"
+ transform="matrix(-1,0,0,1,59.86,-106.6)">
+ <g
+ id="g4289"
+ transform="matrix(-1,0,0,1,-16.98,0.8136)">
+ <g
+ id="g4291">
+ <path
+ id="path4293"
+ transform="matrix(0,-1,-1,0,-52.84,129.7464)"
+ d="M 20.146484 1.0097656 C 18.746484 1.0097656 17.646484 1.8597656 17.146484 3.0097656 L 14.447266 3.0097656 C 12.247266 3.0097656 10.447266 4.7997656 10.447266 7.0097656 L 10.447266 8.1425781 C 10.128283 8.0634395 9.7980674 8.0097656 9.4472656 8.0097656 L 6.8457031 8.0097656 C 6.3457031 6.8597656 5.2457031 6.0097656 3.8457031 6.0097656 C 1.8457031 6.0097656 0.34570312 7.5997656 0.34570312 9.5097656 C 0.34570312 11.419766 1.8457031 13.009766 3.8457031 13.009766 C 5.2457031 13.009766 6.3457031 12.159766 6.8457031 11.009766 L 8.9746094 11.009766 C 8.8693536 11.330059 8.8007812 11.663345 8.8007812 12.001953 C 8.8007813 12.841953 9.1402344 13.671625 9.7402344 14.265625 C 9.9479364 14.475439 10.191281 14.640988 10.447266 14.783203 L 10.447266 16.980469 C 10.447266 17.530469 9.9472656 17.980469 9.4472656 17.980469 L 6.8457031 17.980469 C 6.3457031 16.830469 5.2457031 15.980469 3.8457031 15.980469 C 1.8457031 15.980469 0.34570312 17.570469 0.34570312 19.480469 C 0.34570312 21.390469 1.8457031 22.990234 3.8457031 22.990234 C 5.2457031 22.990234 6.2457031 22.14 6.8457031 21 L 9.4472656 21 C 11.747266 21 13.447266 19.19 13.447266 17 L 13.447266 15.869141 C 13.768504 15.952624 14.100702 16.009766 14.447266 16.009766 L 17.146484 16.009766 C 17.646484 17.159766 18.746484 18.009766 20.146484 18.009766 C 22.046484 18.009766 23.646484 16.449766 23.646484 14.509766 C 23.646484 12.579766 22.046484 11.009766 20.146484 11.009766 C 18.746484 11.009766 17.646484 11.859766 17.146484 13.009766 L 15.009766 13.009766 C 15.119625 12.684735 15.189453 12.346256 15.189453 12 C 15.189453 11.16 14.849906 10.339953 14.253906 9.7519531 C 14.0189 9.51021 13.74069 9.3244522 13.447266 9.171875 L 13.447266 7.0097656 C 13.447266 6.4597656 13.947266 6.0097656 14.447266 6.0097656 L 17.146484 6.0097656 C 17.646484 7.1597656 18.746484 8.0097656 20.146484 8.0097656 C 22.046484 8.0097656 23.646484 6.4397656 23.646484 4.5097656 C 23.646484 2.5697656 22.046484 1.0097656 20.146484 1.0097656 z M 20.146484 2.0097656 C 21.446484 2.0097656 22.646484 3.1297656 22.646484 4.5097656 C 22.646484 5.8797656 21.446484 7.0097656 20.146484 7.0097656 C 19.046484 7.0097656 18.145703 6.3096094 17.845703 5.3496094 L 17.746094 5.0097656 L 14.447266 5.0097656 C 13.347266 5.0097656 12.447266 5.8997656 12.447266 7.0097656 L 12.447266 8.8476562 C 12.298996 8.8261586 12.150754 8.8027344 12 8.8027344 C 11.954455 8.8027344 11.910576 8.8144662 11.865234 8.8164062 C 11.733157 8.716719 11.592447 8.6297054 11.447266 8.546875 L 11.447266 7.0097656 C 11.447266 5.3597656 12.847266 4.0097656 14.447266 4.0097656 L 17.746094 4.0097656 L 17.845703 3.6699219 C 18.145703 2.7099219 19.046484 2.0097656 20.146484 2.0097656 z M 3.8457031 7.0097656 C 4.9457031 7.0097656 5.8464844 7.7099219 6.1464844 8.6699219 L 6.2460938 9.0097656 L 9.4472656 9.0097656 C 9.8222656 9.0097656 10.165234 9.0792969 10.474609 9.2050781 C 10.207952 9.3508551 9.9554097 9.5233651 9.7402344 9.7421875 C 9.6554755 9.8255337 9.5878282 9.9233484 9.5136719 10.015625 C 9.4909069 10.014746 9.470428 10.009766 9.4472656 10.009766 L 6.2460938 10.009766 L 6.1464844 10.349609 C 5.8464844 11.319609 4.9457031 12.009766 3.8457031 12.009766 C 2.4457031 12.009766 1.3457031 10.899766 1.3457031 9.5097656 C 1.3457031 8.1197656 2.4457031 7.0097656 3.8457031 7.0097656 z M 20.146484 12.009766 C 21.446484 12.009766 22.646484 13.139766 22.646484 14.509766 C 22.646484 15.889766 21.446484 17.009766 20.146484 17.009766 C 19.046484 17.009766 18.145703 16.309609 17.845703 15.349609 L 17.746094 15.009766 L 14.447266 15.009766 C 14.100959 15.009766 13.772729 14.94045 13.470703 14.816406 C 13.754756 14.666178 14.02454 14.485593 14.253906 14.253906 C 14.328913 14.179151 14.386367 14.091269 14.453125 14.009766 L 17.746094 14.009766 L 17.845703 13.669922 C 18.145703 12.709922 19.046484 12.009766 20.146484 12.009766 z M 11.447266 15.144531 C 11.629002 15.17624 11.813246 15.199219 12 15.199219 C 12.018544 15.199153 12.036184 15.193748 12.054688 15.193359 C 12.180437 15.288088 12.3107 15.373496 12.447266 15.453125 L 12.447266 17 C 12.447266 18.67 11.147266 20 9.4472656 20 L 6.6464844 20 L 6.2460938 20 L 6.1464844 20.330078 C 5.8464844 21.290078 4.9457031 21.990234 3.8457031 21.990234 C 2.4457031 21.990234 1.3457031 20.870469 1.3457031 19.480469 C 1.3457031 18.090469 2.4457031 16.980469 3.8457031 16.980469 C 4.9457031 16.980469 5.8464844 17.680625 6.1464844 18.640625 L 6.2460938 18.980469 L 9.4472656 18.980469 C 10.547266 18.980469 11.447266 18.090469 11.447266 16.980469 L 11.447266 15.144531 z "
+ style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:0.5;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ </g>
+ </g>
+ </g>
+ </g>
+ <path d="M 3,3 l 18,18" style="stroke-width: 3; stroke: #000000; stroke-linecap: round;" />
+ <path d="M 21,3 l -18,18" style="stroke-width: 3; stroke: #000000; stroke-linecap: round;" />
+</svg>
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index f35f401d06..cfe197de2a 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -83,8 +83,8 @@ class QtRPCTimerBase: public QObject, public RPCTimerBase
{
Q_OBJECT
public:
- QtRPCTimerBase(boost::function<void(void)>& func, int64_t millis):
- func(func)
+ QtRPCTimerBase(boost::function<void(void)>& _func, int64_t millis):
+ func(_func)
{
timer.setSingleShot(true);
connect(&timer, SIGNAL(timeout()), this, SLOT(timeout()));
@@ -113,9 +113,11 @@ public:
#include "rpcconsole.moc"
/**
- * Split shell command line into a list of arguments. Aims to emulate \c bash and friends.
+ * Split shell command line into a list of arguments and execute the command(s).
+ * Aims to emulate \c bash and friends.
*
- * - Arguments are delimited with whitespace
+ * - Command nesting is possible with brackets [example: validateaddress(getnewaddress())]
+ * - Arguments are delimited with whitespace or comma
* - Extra whitespace at the beginning and end and between arguments will be ignored
* - Text can be "double" or 'single' quoted
* - The backslash \c \ is used as escape character
@@ -123,107 +125,211 @@ public:
* - Within double quotes, only escape \c " and backslashes before a \c " or another backslash
* - Within single quotes, no escaping is possible and no special interpretation takes place
*
- * @param[out] args Parsed arguments will be appended to this list
+ * @param[out] result stringified Result from the executed command(chain)
* @param[in] strCommand Command line to split
*/
-bool parseCommandLine(std::vector<std::string> &args, const std::string &strCommand)
+
+bool RPCConsole::RPCExecuteCommandLine(std::string &strResult, const std::string &strCommand)
{
+ std::vector< std::vector<std::string> > stack;
+ stack.push_back(std::vector<std::string>());
+
enum CmdParseState
{
STATE_EATING_SPACES,
+ STATE_EATING_SPACES_IN_ARG,
+ STATE_EATING_SPACES_IN_BRACKETS,
STATE_ARGUMENT,
STATE_SINGLEQUOTED,
STATE_DOUBLEQUOTED,
STATE_ESCAPE_OUTER,
- STATE_ESCAPE_DOUBLEQUOTED
+ STATE_ESCAPE_DOUBLEQUOTED,
+ STATE_COMMAND_EXECUTED,
+ STATE_COMMAND_EXECUTED_INNER
} state = STATE_EATING_SPACES;
std::string curarg;
- Q_FOREACH(char ch, strCommand)
+ UniValue lastResult;
+
+ std::string strCommandTerminated = strCommand;
+ if (strCommandTerminated.back() != '\n')
+ strCommandTerminated += "\n";
+ for(char ch: strCommandTerminated)
{
switch(state)
{
- case STATE_ARGUMENT: // In or after argument
- case STATE_EATING_SPACES: // Handle runs of whitespace
- switch(ch)
+ case STATE_COMMAND_EXECUTED_INNER:
+ case STATE_COMMAND_EXECUTED:
{
- case '"': state = STATE_DOUBLEQUOTED; break;
- case '\'': state = STATE_SINGLEQUOTED; break;
- case '\\': state = STATE_ESCAPE_OUTER; break;
- case ' ': case '\n': case '\t':
- if(state == STATE_ARGUMENT) // Space ends argument
+ bool breakParsing = true;
+ switch(ch)
{
- args.push_back(curarg);
- curarg.clear();
+ case '[': curarg.clear(); state = STATE_COMMAND_EXECUTED_INNER; break;
+ default:
+ if (state == STATE_COMMAND_EXECUTED_INNER)
+ {
+ if (ch != ']')
+ {
+ // append char to the current argument (which is also used for the query command)
+ curarg += ch;
+ break;
+ }
+ if (curarg.size())
+ {
+ // if we have a value query, query arrays with index and objects with a string key
+ UniValue subelement;
+ if (lastResult.isArray())
+ {
+ for(char argch: curarg)
+ if (!std::isdigit(argch))
+ throw std::runtime_error("Invalid result query");
+ subelement = lastResult[atoi(curarg.c_str())];
+ }
+ else if (lastResult.isObject())
+ subelement = find_value(lastResult, curarg);
+ else
+ throw std::runtime_error("Invalid result query"); //no array or object: abort
+ lastResult = subelement;
+ }
+
+ state = STATE_COMMAND_EXECUTED;
+ break;
+ }
+ // don't break parsing when the char is required for the next argument
+ breakParsing = false;
+
+ // pop the stack and return the result to the current command arguments
+ stack.pop_back();
+
+ // don't stringify the json in case of a string to avoid doublequotes
+ if (lastResult.isStr())
+ curarg = lastResult.get_str();
+ else
+ curarg = lastResult.write(2);
+
+ // if we have a non empty result, use it as stack argument otherwise as general result
+ if (curarg.size())
+ {
+ if (stack.size())
+ stack.back().push_back(curarg);
+ else
+ strResult = curarg;
+ }
+ curarg.clear();
+ // assume eating space state
+ state = STATE_EATING_SPACES;
}
- state = STATE_EATING_SPACES;
- break;
- default: curarg += ch; state = STATE_ARGUMENT;
+ if (breakParsing)
+ break;
}
- break;
- case STATE_SINGLEQUOTED: // Single-quoted string
- switch(ch)
+ case STATE_ARGUMENT: // In or after argument
+ case STATE_EATING_SPACES_IN_ARG:
+ case STATE_EATING_SPACES_IN_BRACKETS:
+ case STATE_EATING_SPACES: // Handle runs of whitespace
+ switch(ch)
{
- case '\'': state = STATE_ARGUMENT; break;
- default: curarg += ch;
+ case '"': state = STATE_DOUBLEQUOTED; break;
+ case '\'': state = STATE_SINGLEQUOTED; break;
+ case '\\': state = STATE_ESCAPE_OUTER; break;
+ case '(': case ')': case '\n':
+ if (state == STATE_EATING_SPACES_IN_ARG)
+ throw std::runtime_error("Invalid Syntax");
+ if (state == STATE_ARGUMENT)
+ {
+ if (ch == '(' && stack.size() && stack.back().size() > 0)
+ stack.push_back(std::vector<std::string>());
+
+ // don't allow commands after executed commands on baselevel
+ if (!stack.size())
+ throw std::runtime_error("Invalid Syntax");
+
+ stack.back().push_back(curarg);
+ curarg.clear();
+ state = STATE_EATING_SPACES_IN_BRACKETS;
+ }
+ if ((ch == ')' || ch == '\n') && stack.size() > 0)
+ {
+ std::string strPrint;
+ // Convert argument list to JSON objects in method-dependent way,
+ // and pass it along with the method name to the dispatcher.
+ JSONRPCRequest req;
+ req.params = RPCConvertValues(stack.back()[0], std::vector<std::string>(stack.back().begin() + 1, stack.back().end()));
+ req.strMethod = stack.back()[0];
+ lastResult = tableRPC.execute(req);
+
+ state = STATE_COMMAND_EXECUTED;
+ curarg.clear();
+ }
+ break;
+ case ' ': case ',': case '\t':
+ if(state == STATE_EATING_SPACES_IN_ARG && curarg.empty() && ch == ',')
+ throw std::runtime_error("Invalid Syntax");
+
+ else if(state == STATE_ARGUMENT) // Space ends argument
+ {
+ stack.back().push_back(curarg);
+ curarg.clear();
+ }
+ if ((state == STATE_EATING_SPACES_IN_BRACKETS || state == STATE_ARGUMENT) && ch == ',')
+ {
+ state = STATE_EATING_SPACES_IN_ARG;
+ break;
+ }
+ state = STATE_EATING_SPACES;
+ break;
+ default: curarg += ch; state = STATE_ARGUMENT;
}
- break;
- case STATE_DOUBLEQUOTED: // Double-quoted string
- switch(ch)
+ break;
+ case STATE_SINGLEQUOTED: // Single-quoted string
+ switch(ch)
{
- case '"': state = STATE_ARGUMENT; break;
- case '\\': state = STATE_ESCAPE_DOUBLEQUOTED; break;
- default: curarg += ch;
+ case '\'': state = STATE_ARGUMENT; break;
+ default: curarg += ch;
}
- break;
- case STATE_ESCAPE_OUTER: // '\' outside quotes
- curarg += ch; state = STATE_ARGUMENT;
- break;
- case STATE_ESCAPE_DOUBLEQUOTED: // '\' in double-quoted text
- if(ch != '"' && ch != '\\') curarg += '\\'; // keep '\' for everything but the quote and '\' itself
- curarg += ch; state = STATE_DOUBLEQUOTED;
- break;
+ break;
+ case STATE_DOUBLEQUOTED: // Double-quoted string
+ switch(ch)
+ {
+ case '"': state = STATE_ARGUMENT; break;
+ case '\\': state = STATE_ESCAPE_DOUBLEQUOTED; break;
+ default: curarg += ch;
+ }
+ break;
+ case STATE_ESCAPE_OUTER: // '\' outside quotes
+ curarg += ch; state = STATE_ARGUMENT;
+ break;
+ case STATE_ESCAPE_DOUBLEQUOTED: // '\' in double-quoted text
+ if(ch != '"' && ch != '\\') curarg += '\\'; // keep '\' for everything but the quote and '\' itself
+ curarg += ch; state = STATE_DOUBLEQUOTED;
+ break;
}
}
switch(state) // final state
{
- case STATE_EATING_SPACES:
- return true;
- case STATE_ARGUMENT:
- args.push_back(curarg);
- return true;
- default: // ERROR to end in one of the other states
- return false;
+ case STATE_COMMAND_EXECUTED:
+ if (lastResult.isStr())
+ strResult = lastResult.get_str();
+ else
+ strResult = lastResult.write(2);
+ case STATE_ARGUMENT:
+ case STATE_EATING_SPACES:
+ return true;
+ default: // ERROR to end in one of the other states
+ return false;
}
}
void RPCExecutor::request(const QString &command)
{
- std::vector<std::string> args;
- if(!parseCommandLine(args, command.toStdString()))
- {
- Q_EMIT reply(RPCConsole::CMD_ERROR, QString("Parse error: unbalanced ' or \""));
- return;
- }
- if(args.empty())
- return; // Nothing to do
try
{
- std::string strPrint;
- // Convert argument list to JSON objects in method-dependent way,
- // and pass it along with the method name to the dispatcher.
- UniValue result = tableRPC.execute(
- args[0],
- RPCConvertValues(args[0], std::vector<std::string>(args.begin() + 1, args.end())));
-
- // Format result reply
- if (result.isNull())
- strPrint = "";
- else if (result.isStr())
- strPrint = result.get_str();
- else
- strPrint = result.write(2);
-
- Q_EMIT reply(RPCConsole::CMD_REPLY, QString::fromStdString(strPrint));
+ std::string result;
+ std::string executableCommand = command.toStdString() + "\n";
+ if(!RPCConsole::RPCExecuteCommandLine(result, executableCommand))
+ {
+ Q_EMIT reply(RPCConsole::CMD_ERROR, QString("Parse error: unbalanced ' or \""));
+ return;
+ }
+ Q_EMIT reply(RPCConsole::CMD_REPLY, QString::fromStdString(result));
}
catch (UniValue& objError)
{
@@ -244,13 +350,12 @@ void RPCExecutor::request(const QString &command)
}
}
-RPCConsole::RPCConsole(const PlatformStyle *platformStyle, QWidget *parent) :
+RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) :
QWidget(parent),
ui(new Ui::RPCConsole),
clientModel(0),
historyPtr(0),
- cachedNodeid(-1),
- platformStyle(platformStyle),
+ platformStyle(_platformStyle),
peersTableContextMenu(0),
banTableContextMenu(0),
consoleFontSize(0)
@@ -289,7 +394,6 @@ RPCConsole::RPCConsole(const PlatformStyle *platformStyle, QWidget *parent) :
// based timer interface
RPCSetTimerInterfaceIfUnset(rpcTimerInterface);
- startExecutor();
setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS);
ui->detailWidget->hide();
@@ -303,7 +407,6 @@ RPCConsole::RPCConsole(const PlatformStyle *platformStyle, QWidget *parent) :
RPCConsole::~RPCConsole()
{
GUIUtil::saveWindowGeometry("nRPCConsoleWindow", this);
- Q_EMIT stopExecutor();
RPCUnsetTimerInterface(rpcTimerInterface);
delete rpcTimerInterface;
delete ui;
@@ -365,6 +468,9 @@ void RPCConsole::setClientModel(ClientModel *model)
setNumBlocks(model->getNumBlocks(), model->getLastBlockDate(), model->getVerificationProgress(NULL), false);
connect(model, SIGNAL(numBlocksChanged(int,QDateTime,double,bool)), this, SLOT(setNumBlocks(int,QDateTime,double,bool)));
+ updateNetworkState();
+ connect(model, SIGNAL(networkActiveChanged(bool)), this, SLOT(setNetworkActive(bool)));
+
updateTrafficStats(model->getTotalBytesRecv(), model->getTotalBytesSent());
connect(model, SIGNAL(bytesChanged(quint64,quint64)), this, SLOT(updateTrafficStats(quint64, quint64)));
@@ -375,7 +481,7 @@ void RPCConsole::setClientModel(ClientModel *model)
ui->peerWidget->verticalHeader()->hide();
ui->peerWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
ui->peerWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
- ui->peerWidget->setSelectionMode(QAbstractItemView::SingleSelection);
+ ui->peerWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
ui->peerWidget->setContextMenuPolicy(Qt::CustomContextMenu);
ui->peerWidget->setColumnWidth(PeerTableModel::Address, ADDRESS_COLUMN_WIDTH);
ui->peerWidget->setColumnWidth(PeerTableModel::Subversion, SUBVERSION_COLUMN_WIDTH);
@@ -383,14 +489,14 @@ void RPCConsole::setClientModel(ClientModel *model)
ui->peerWidget->horizontalHeader()->setStretchLastSection(true);
// create peer table context menu actions
- QAction* disconnectAction = new QAction(tr("&Disconnect Node"), this);
- QAction* banAction1h = new QAction(tr("Ban Node for") + " " + tr("1 &hour"), this);
- QAction* banAction24h = new QAction(tr("Ban Node for") + " " + tr("1 &day"), this);
- QAction* banAction7d = new QAction(tr("Ban Node for") + " " + tr("1 &week"), this);
- QAction* banAction365d = new QAction(tr("Ban Node for") + " " + tr("1 &year"), this);
+ QAction* disconnectAction = new QAction(tr("&Disconnect"), this);
+ QAction* banAction1h = new QAction(tr("Ban for") + " " + tr("1 &hour"), this);
+ QAction* banAction24h = new QAction(tr("Ban for") + " " + tr("1 &day"), this);
+ QAction* banAction7d = new QAction(tr("Ban for") + " " + tr("1 &week"), this);
+ QAction* banAction365d = new QAction(tr("Ban for") + " " + tr("1 &year"), this);
// create peer table context menu
- peersTableContextMenu = new QMenu();
+ peersTableContextMenu = new QMenu(this);
peersTableContextMenu->addAction(disconnectAction);
peersTableContextMenu->addAction(banAction1h);
peersTableContextMenu->addAction(banAction24h);
@@ -420,7 +526,9 @@ void RPCConsole::setClientModel(ClientModel *model)
this, SLOT(peerSelected(const QItemSelection &, const QItemSelection &)));
// peer table signal handling - update peer details when new nodes are added to the model
connect(model->getPeerTableModel(), SIGNAL(layoutChanged()), this, SLOT(peerLayoutChanged()));
-
+ // peer table signal handling - cache selected node ids
+ connect(model->getPeerTableModel(), SIGNAL(layoutAboutToBeChanged()), this, SLOT(peerLayoutAboutToChange()));
+
// set up ban table
ui->banlistWidget->setModel(model->getBanTableModel());
ui->banlistWidget->verticalHeader()->hide();
@@ -433,10 +541,10 @@ void RPCConsole::setClientModel(ClientModel *model)
ui->banlistWidget->horizontalHeader()->setStretchLastSection(true);
// create ban table context menu action
- QAction* unbanAction = new QAction(tr("&Unban Node"), this);
+ QAction* unbanAction = new QAction(tr("&Unban"), this);
// create ban table context menu
- banTableContextMenu = new QMenu();
+ banTableContextMenu = new QMenu(this);
banTableContextMenu->addAction(unbanAction);
// ban table context menu signals
@@ -467,6 +575,14 @@ void RPCConsole::setClientModel(ClientModel *model)
autoCompleter = new QCompleter(wordList, this);
ui->lineEdit->setCompleter(autoCompleter);
autoCompleter->popup()->installEventFilter(this);
+ // Start thread to execute RPC commands.
+ startExecutor();
+ }
+ if (!model) {
+ // Client model is being set to 0, this means shutdown() is about to be called.
+ // Make sure we clean up the executor thread
+ Q_EMIT stopExecutor();
+ thread.wait();
}
}
@@ -546,13 +662,18 @@ void RPCConsole::clear(bool clearHistory)
"td.message { font-family: %1; font-size: %2; white-space:pre-wrap; } "
"td.cmd-request { color: #006060; } "
"td.cmd-error { color: red; } "
+ ".secwarning { color: red; }"
"b { color: #006060; } "
).arg(fixedFontInfo.family(), QString("%1pt").arg(consoleFontSize))
);
message(CMD_REPLY, (tr("Welcome to the %1 RPC console.").arg(tr(PACKAGE_NAME)) + "<br>" +
tr("Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen.") + "<br>" +
- tr("Type <b>help</b> for an overview of available commands.")), true);
+ tr("Type <b>help</b> for an overview of available commands.")) +
+ "<br><span class=\"secwarning\">" +
+ tr("WARNING: Scammers have been active, telling users to type commands here, stealing their wallet contents. Do not use this console without fully understanding the ramification of a command.") +
+ "</span>",
+ true);
}
void RPCConsole::keyPressEvent(QKeyEvent *event)
@@ -579,16 +700,30 @@ void RPCConsole::message(int category, const QString &message, bool html)
ui->messagesWidget->append(out);
}
+void RPCConsole::updateNetworkState()
+{
+ QString connections = QString::number(clientModel->getNumConnections()) + " (";
+ connections += tr("In:") + " " + QString::number(clientModel->getNumConnections(CONNECTIONS_IN)) + " / ";
+ connections += tr("Out:") + " " + QString::number(clientModel->getNumConnections(CONNECTIONS_OUT)) + ")";
+
+ if(!clientModel->getNetworkActive()) {
+ connections += " (" + tr("Network activity disabled") + ")";
+ }
+
+ ui->numberOfConnections->setText(connections);
+}
+
void RPCConsole::setNumConnections(int count)
{
if (!clientModel)
return;
- QString connections = QString::number(count) + " (";
- connections += tr("In:") + " " + QString::number(clientModel->getNumConnections(CONNECTIONS_IN)) + " / ";
- connections += tr("Out:") + " " + QString::number(clientModel->getNumConnections(CONNECTIONS_OUT)) + ")";
+ updateNetworkState();
+}
- ui->numberOfConnections->setText(connections);
+void RPCConsole::setNetworkActive(bool networkActive)
+{
+ updateNetworkState();
}
void RPCConsole::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers)
@@ -647,9 +782,8 @@ void RPCConsole::browseHistory(int offset)
void RPCConsole::startExecutor()
{
- QThread *thread = new QThread;
RPCExecutor *executor = new RPCExecutor();
- executor->moveToThread(thread);
+ executor->moveToThread(&thread);
// Replies from executor object must go to this object
connect(executor, SIGNAL(reply(int,QString)), this, SLOT(message(int,QString)));
@@ -657,16 +791,14 @@ void RPCConsole::startExecutor()
connect(this, SIGNAL(cmdRequest(QString)), executor, SLOT(request(QString)));
// On stopExecutor signal
- // - queue executor for deletion (in execution thread)
// - quit the Qt event loop in the execution thread
- connect(this, SIGNAL(stopExecutor()), executor, SLOT(deleteLater()));
- connect(this, SIGNAL(stopExecutor()), thread, SLOT(quit()));
- // Queue the thread for deletion (in this thread) when it is finished
- connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
+ connect(this, SIGNAL(stopExecutor()), &thread, SLOT(quit()));
+ // - queue executor for deletion (in execution thread)
+ connect(&thread, SIGNAL(finished()), executor, SLOT(deleteLater()), Qt::DirectConnection);
// Default implementation of QThread::run() simply spins up an event loop in the thread,
// which is what we want.
- thread->start();
+ thread.start();
}
void RPCConsole::on_tabWidget_currentChanged(int index)
@@ -731,6 +863,17 @@ void RPCConsole::peerSelected(const QItemSelection &selected, const QItemSelecti
updateNodeDetail(stats);
}
+void RPCConsole::peerLayoutAboutToChange()
+{
+ QModelIndexList selected = ui->peerWidget->selectionModel()->selectedIndexes();
+ cachedNodeids.clear();
+ for(int i = 0; i < selected.size(); i++)
+ {
+ const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(selected.at(i).row());
+ cachedNodeids.append(stats->nodeStats.nodeid);
+ }
+}
+
void RPCConsole::peerLayoutChanged()
{
if (!clientModel || !clientModel->getPeerTableModel())
@@ -740,7 +883,7 @@ void RPCConsole::peerLayoutChanged()
bool fUnselect = false;
bool fReselect = false;
- if (cachedNodeid == -1) // no node selected yet
+ if (cachedNodeids.empty()) // no node selected yet
return;
// find the currently selected row
@@ -752,7 +895,7 @@ void RPCConsole::peerLayoutChanged()
// check if our detail node has a row in the table (it may not necessarily
// be at selectedRow since its position can change after a layout change)
- int detailNodeRow = clientModel->getPeerTableModel()->getRowByNodeId(cachedNodeid);
+ int detailNodeRow = clientModel->getPeerTableModel()->getRowByNodeId(cachedNodeids.first());
if (detailNodeRow < 0)
{
@@ -778,7 +921,10 @@ void RPCConsole::peerLayoutChanged()
if (fReselect)
{
- ui->peerWidget->selectRow(detailNodeRow);
+ for(int i = 0; i < cachedNodeids.size(); i++)
+ {
+ ui->peerWidget->selectRow(clientModel->getPeerTableModel()->getRowByNodeId(cachedNodeids.at(i)));
+ }
}
if (stats)
@@ -787,9 +933,6 @@ void RPCConsole::peerLayoutChanged()
void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats)
{
- // Update cached nodeid
- cachedNodeid = stats->nodeStats.nodeid;
-
// update the detail ui with latest node information
QString peerAddrDetails(QString::fromStdString(stats->nodeStats.addrName) + " ");
peerAddrDetails += tr("(node id: %1)").arg(QString::number(stats->nodeStats.nodeid));
@@ -804,6 +947,7 @@ void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats)
ui->peerConnTime->setText(GUIUtil::formatDurationStr(GetTime() - stats->nodeStats.nTimeConnected));
ui->peerPingTime->setText(GUIUtil::formatPingTime(stats->nodeStats.dPingTime));
ui->peerPingWait->setText(GUIUtil::formatPingTime(stats->nodeStats.dPingWait));
+ ui->peerMinPing->setText(GUIUtil::formatPingTime(stats->nodeStats.dMinPing));
ui->timeoffset->setText(GUIUtil::formatTimeOffset(stats->nodeStats.nTimeOffset));
ui->peerVersion->setText(QString("%1").arg(QString::number(stats->nodeStats.nVersion)));
ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer));
@@ -878,30 +1022,42 @@ void RPCConsole::disconnectSelectedNode()
{
if(!g_connman)
return;
- // Get currently selected peer address
- NodeId id = GUIUtil::getEntryData(ui->peerWidget, 0, PeerTableModel::NetNodeId).toInt();
- // Find the node, disconnect it and clear the selected node
- if(g_connman->DisconnectNode(id))
- clearSelectedNode();
+
+ // Get selected peer addresses
+ QList<QModelIndex> nodes = GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId);
+ for(int i = 0; i < nodes.count(); i++)
+ {
+ // Get currently selected peer address
+ NodeId id = nodes.at(i).data().toInt();
+ // Find the node, disconnect it and clear the selected node
+ if(g_connman->DisconnectNode(id))
+ clearSelectedNode();
+ }
}
void RPCConsole::banSelectedNode(int bantime)
{
if (!clientModel || !g_connman)
return;
-
- // Get currently selected peer address
- QString strNode = GUIUtil::getEntryData(ui->peerWidget, 0, PeerTableModel::Address).toString();
- // Find possible nodes, ban it and clear the selected node
- std::string nStr = strNode.toStdString();
- std::string addr;
- int port = 0;
- SplitHostPort(nStr, port, addr);
-
- CNetAddr resolved;
- if(!LookupHost(addr.c_str(), resolved, false))
- return;
- g_connman->Ban(resolved, BanReasonManuallyAdded, bantime);
+
+ // Get selected peer addresses
+ QList<QModelIndex> nodes = GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId);
+ for(int i = 0; i < nodes.count(); i++)
+ {
+ // Get currently selected peer address
+ NodeId id = nodes.at(i).data().toInt();
+
+ // Get currently selected peer address
+ int detailNodeRow = clientModel->getPeerTableModel()->getRowByNodeId(id);
+ if(detailNodeRow < 0)
+ return;
+
+ // Find possible nodes, ban it and clear the selected node
+ const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(detailNodeRow);
+ if(stats) {
+ g_connman->Ban(stats->nodeStats.addr, BanReasonManuallyAdded, bantime);
+ }
+ }
clearSelectedNode();
clientModel->getBanTableModel()->refresh();
}
@@ -911,22 +1067,27 @@ void RPCConsole::unbanSelectedNode()
if (!clientModel)
return;
- // Get currently selected ban address
- QString strNode = GUIUtil::getEntryData(ui->banlistWidget, 0, BanTableModel::Address).toString();
- CSubNet possibleSubnet;
-
- LookupSubNet(strNode.toStdString().c_str(), possibleSubnet);
- if (possibleSubnet.IsValid() && g_connman)
+ // Get selected ban addresses
+ QList<QModelIndex> nodes = GUIUtil::getEntryData(ui->banlistWidget, BanTableModel::Address);
+ for(int i = 0; i < nodes.count(); i++)
{
- g_connman->Unban(possibleSubnet);
- clientModel->getBanTableModel()->refresh();
+ // Get currently selected ban address
+ QString strNode = nodes.at(i).data().toString();
+ CSubNet possibleSubnet;
+
+ LookupSubNet(strNode.toStdString().c_str(), possibleSubnet);
+ if (possibleSubnet.IsValid() && g_connman)
+ {
+ g_connman->Unban(possibleSubnet);
+ clientModel->getBanTableModel()->refresh();
+ }
}
}
void RPCConsole::clearSelectedNode()
{
ui->peerWidget->selectionModel()->clearSelection();
- cachedNodeid = -1;
+ cachedNodeids.clear();
ui->detailWidget->hide();
ui->peerHeading->setText(tr("Select a peer to view detailed information."));
}
diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h
index 28affa954d..ab8c3dc914 100644
--- a/src/qt/rpcconsole.h
+++ b/src/qt/rpcconsole.h
@@ -12,6 +12,7 @@
#include <QWidget>
#include <QCompleter>
+#include <QThread>
class ClientModel;
class PlatformStyle;
@@ -35,6 +36,8 @@ public:
explicit RPCConsole(const PlatformStyle *platformStyle, QWidget *parent);
~RPCConsole();
+ static bool RPCExecuteCommandLine(std::string &strResult, const std::string &strCommand);
+
void setClientModel(ClientModel *model);
enum MessageClass {
@@ -86,6 +89,8 @@ public Q_SLOTS:
void message(int category, const QString &message, bool html = false);
/** Set number of connections shown in the UI */
void setNumConnections(int count);
+ /** Set network state shown in the UI */
+ void setNetworkActive(bool networkActive);
/** Set number of blocks and last block date shown in the UI */
void setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers);
/** Set size (number of transactions and memory usage) of the mempool in the UI */
@@ -96,6 +101,8 @@ public Q_SLOTS:
void scrollToEnd();
/** Handle selection of peer in peers list */
void peerSelected(const QItemSelection &selected, const QItemSelection &deselected);
+ /** Handle selection caching before update */
+ void peerLayoutAboutToChange();
/** Handle updated peer information */
void peerLayoutChanged();
/** Disconnect a selected node on the Peers tab */
@@ -133,13 +140,17 @@ private:
ClientModel *clientModel;
QStringList history;
int historyPtr;
- NodeId cachedNodeid;
+ QList<NodeId> cachedNodeids;
const PlatformStyle *platformStyle;
RPCTimerInterface *rpcTimerInterface;
QMenu *peersTableContextMenu;
QMenu *banTableContextMenu;
int consoleFontSize;
QCompleter *autoCompleter;
+ QThread thread;
+
+ /** Update UI with latest network info from model. */
+ void updateNetworkState();
};
#endif // BITCOIN_QT_RPCCONSOLE_H
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 8433818a64..13210a1adc 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -16,8 +16,8 @@
#include "walletmodel.h"
#include "base58.h"
-#include "coincontrol.h"
-#include "main.h" // mempool and minRelayTxFee
+#include "wallet/coincontrol.h"
+#include "validation.h" // mempool and minRelayTxFee
#include "ui_interface.h"
#include "txmempool.h"
#include "wallet/wallet.h"
@@ -30,25 +30,25 @@
#define SEND_CONFIRM_DELAY 3
-SendCoinsDialog::SendCoinsDialog(const PlatformStyle *platformStyle, QWidget *parent) :
+SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *parent) :
QDialog(parent),
ui(new Ui::SendCoinsDialog),
clientModel(0),
model(0),
fNewRecipientAllowed(true),
fFeeMinimized(true),
- platformStyle(platformStyle)
+ platformStyle(_platformStyle)
{
ui->setupUi(this);
- if (!platformStyle->getImagesOnButtons()) {
+ if (!_platformStyle->getImagesOnButtons()) {
ui->addButton->setIcon(QIcon());
ui->clearButton->setIcon(QIcon());
ui->sendButton->setIcon(QIcon());
} else {
- ui->addButton->setIcon(platformStyle->SingleColorIcon(":/icons/add"));
- ui->clearButton->setIcon(platformStyle->SingleColorIcon(":/icons/remove"));
- ui->sendButton->setIcon(platformStyle->SingleColorIcon(":/icons/send"));
+ ui->addButton->setIcon(_platformStyle->SingleColorIcon(":/icons/add"));
+ ui->clearButton->setIcon(_platformStyle->SingleColorIcon(":/icons/remove"));
+ ui->sendButton->setIcon(_platformStyle->SingleColorIcon(":/icons/send"));
}
GUIUtil::setupAddressWidget(ui->lineEditCoinControlChange, this);
@@ -110,46 +110,45 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *platformStyle, QWidget *pa
ui->groupCustomFee->setId(ui->radioCustomPerKilobyte, 0);
ui->groupCustomFee->setId(ui->radioCustomAtLeast, 1);
ui->groupCustomFee->button((int)std::max(0, std::min(1, settings.value("nCustomFeeRadio").toInt())))->setChecked(true);
- ui->sliderSmartFee->setValue(settings.value("nSmartFeeSliderPosition").toInt());
ui->customFee->setValue(settings.value("nTransactionFee").toLongLong());
ui->checkBoxMinimumFee->setChecked(settings.value("fPayOnlyMinFee").toBool());
minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool());
}
-void SendCoinsDialog::setClientModel(ClientModel *clientModel)
+void SendCoinsDialog::setClientModel(ClientModel *_clientModel)
{
- this->clientModel = clientModel;
+ this->clientModel = _clientModel;
- if (clientModel) {
- connect(clientModel, SIGNAL(numBlocksChanged(int,QDateTime,double,bool)), this, SLOT(updateSmartFeeLabel()));
+ if (_clientModel) {
+ connect(_clientModel, SIGNAL(numBlocksChanged(int,QDateTime,double,bool)), this, SLOT(updateSmartFeeLabel()));
}
}
-void SendCoinsDialog::setModel(WalletModel *model)
+void SendCoinsDialog::setModel(WalletModel *_model)
{
- this->model = model;
+ this->model = _model;
- if(model && model->getOptionsModel())
+ if(_model && _model->getOptionsModel())
{
for(int i = 0; i < ui->entries->count(); ++i)
{
SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
if(entry)
{
- entry->setModel(model);
+ entry->setModel(_model);
}
}
- setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance(),
- model->getWatchBalance(), model->getWatchUnconfirmedBalance(), model->getWatchImmatureBalance());
- connect(model, SIGNAL(balanceChanged(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount)), this, SLOT(setBalance(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount)));
- connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
+ setBalance(_model->getBalance(), _model->getUnconfirmedBalance(), _model->getImmatureBalance(),
+ _model->getWatchBalance(), _model->getWatchUnconfirmedBalance(), _model->getWatchImmatureBalance());
+ connect(_model, SIGNAL(balanceChanged(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount)), this, SLOT(setBalance(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount)));
+ connect(_model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
updateDisplayUnit();
// Coin Control
- connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(coinControlUpdateLabels()));
- connect(model->getOptionsModel(), SIGNAL(coinControlFeaturesChanged(bool)), this, SLOT(coinControlFeatureChanged(bool)));
- ui->frameCoinControl->setVisible(model->getOptionsModel()->getCoinControlFeatures());
+ connect(_model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(coinControlUpdateLabels()));
+ connect(_model->getOptionsModel(), SIGNAL(coinControlFeaturesChanged(bool)), this, SLOT(coinControlFeatureChanged(bool)));
+ ui->frameCoinControl->setVisible(_model->getOptionsModel()->getCoinControlFeatures());
coinControlUpdateLabels();
// fee section
@@ -172,6 +171,13 @@ void SendCoinsDialog::setModel(WalletModel *model)
updateMinFeeLabel();
updateSmartFeeLabel();
updateGlobalFeeVariables();
+
+ // set the smartfee-sliders default value (wallets default conf.target or last stored value)
+ QSettings settings;
+ if (settings.value("nSmartFeeSliderPosition").toInt() == 0)
+ ui->sliderSmartFee->setValue(ui->sliderSmartFee->maximum() - model->getDefaultConfirmTarget() + 2);
+ else
+ ui->sliderSmartFee->setValue(settings.value("nSmartFeeSliderPosition").toInt());
}
}
@@ -229,10 +235,17 @@ void SendCoinsDialog::on_sendButton_clicked()
// prepare transaction for getting txFee earlier
WalletModelTransaction currentTransaction(recipients);
WalletModel::SendCoinsReturn prepareStatus;
- if (model->getOptionsModel()->getCoinControlFeatures()) // coin control enabled
- prepareStatus = model->prepareTransaction(currentTransaction, CoinControlDialog::coinControl);
+
+ // Always use a CCoinControl instance, use the CoinControlDialog instance if CoinControl has been enabled
+ CCoinControl ctrl;
+ if (model->getOptionsModel()->getCoinControlFeatures())
+ ctrl = *CoinControlDialog::coinControl;
+ if (ui->radioSmartFee->isChecked())
+ ctrl.nConfirmTarget = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2;
else
- prepareStatus = model->prepareTransaction(currentTransaction);
+ ctrl.nConfirmTarget = 0;
+
+ prepareStatus = model->prepareTransaction(currentTransaction, &ctrl);
// process prepareStatus and on error generate message shown to user
processSendCoinsReturn(prepareStatus,
@@ -521,7 +534,7 @@ void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn
msgParams.second = CClientUIInterface::MSG_ERROR;
break;
case WalletModel::TransactionCommitFailed:
- msgParams.first = tr("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.");
+ msgParams.first = tr("The transaction was rejected with the following reason: %1").arg(sendCoinsReturn.reasonCommitFailed);
msgParams.second = CClientUIInterface::MSG_ERROR;
break;
case WalletModel::AbsurdFee:
@@ -576,6 +589,7 @@ void SendCoinsDialog::updateFeeSectionControls()
ui->labelFeeEstimation ->setEnabled(ui->radioSmartFee->isChecked());
ui->labelSmartFeeNormal ->setEnabled(ui->radioSmartFee->isChecked());
ui->labelSmartFeeFast ->setEnabled(ui->radioSmartFee->isChecked());
+ ui->confirmationTargetLabel ->setEnabled(ui->radioSmartFee->isChecked());
ui->checkBoxMinimumFee ->setEnabled(ui->radioCustomFee->isChecked());
ui->labelMinFeeWarning ->setEnabled(ui->radioCustomFee->isChecked());
ui->radioCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
@@ -587,15 +601,17 @@ void SendCoinsDialog::updateGlobalFeeVariables()
{
if (ui->radioSmartFee->isChecked())
{
- nTxConfirmTarget = defaultConfirmTarget - ui->sliderSmartFee->value();
+ int nConfirmTarget = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2;
payTxFee = CFeeRate(0);
// set nMinimumTotalFee to 0 to not accidentally pay a custom fee
CoinControlDialog::coinControl->nMinimumTotalFee = 0;
+
+ // show the estimated reuquired time for confirmation
+ ui->confirmationTargetLabel->setText(GUIUtil::formatDurationStr(nConfirmTarget*600)+" / "+tr("%n block(s)", "", nConfirmTarget));
}
else
{
- nTxConfirmTarget = defaultConfirmTarget;
payTxFee = CFeeRate(ui->customFee->value());
// if user has selected to set a minimum absolute fee, pass the value to coincontrol
@@ -630,7 +646,7 @@ void SendCoinsDialog::updateSmartFeeLabel()
if(!model || !model->getOptionsModel())
return;
- int nBlocksToConfirm = defaultConfirmTarget - ui->sliderSmartFee->value();
+ int nBlocksToConfirm = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2;
int estimateFoundAtBlocks = nBlocksToConfirm;
CFeeRate feeRate = mempool.estimateSmartFee(nBlocksToConfirm, &estimateFoundAtBlocks);
if (feeRate <= CFeeRate(0)) // not enough data => minfee
@@ -701,6 +717,8 @@ void SendCoinsDialog::coinControlFeatureChanged(bool checked)
if (!checked && model) // coin control features disabled
CoinControlDialog::coinControl->SetNull();
+ // make sure we set back the confirmation target
+ updateGlobalFeeVariables();
coinControlUpdateLabels();
}
@@ -826,9 +844,9 @@ void SendCoinsDialog::coinControlUpdateLabels()
}
}
-SendConfirmationDialog::SendConfirmationDialog(const QString &title, const QString &text, int secDelay,
+SendConfirmationDialog::SendConfirmationDialog(const QString &title, const QString &text, int _secDelay,
QWidget *parent) :
- QMessageBox(QMessageBox::Question, title, text, QMessageBox::Yes | QMessageBox::Cancel, parent), secDelay(secDelay)
+ QMessageBox(QMessageBox::Question, title, text, QMessageBox::Yes | QMessageBox::Cancel, parent), secDelay(_secDelay)
{
setDefaultButton(QMessageBox::Cancel);
yesButton = button(QMessageBox::Yes);
diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h
index 83dac0bd11..b0df495a98 100644
--- a/src/qt/sendcoinsdialog.h
+++ b/src/qt/sendcoinsdialog.h
@@ -26,8 +26,6 @@ QT_BEGIN_NAMESPACE
class QUrl;
QT_END_NAMESPACE
-const int defaultConfirmTarget = 25;
-
/** Dialog for sending bitcoins */
class SendCoinsDialog : public QDialog
{
diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp
index d063f2c891..7eb1eb7e3a 100644
--- a/src/qt/sendcoinsentry.cpp
+++ b/src/qt/sendcoinsentry.cpp
@@ -15,11 +15,11 @@
#include <QApplication>
#include <QClipboard>
-SendCoinsEntry::SendCoinsEntry(const PlatformStyle *platformStyle, QWidget *parent) :
+SendCoinsEntry::SendCoinsEntry(const PlatformStyle *_platformStyle, QWidget *parent) :
QStackedWidget(parent),
ui(new Ui::SendCoinsEntry),
model(0),
- platformStyle(platformStyle)
+ platformStyle(_platformStyle)
{
ui->setupUi(this);
@@ -79,12 +79,12 @@ void SendCoinsEntry::on_payTo_textChanged(const QString &address)
updateLabel(address);
}
-void SendCoinsEntry::setModel(WalletModel *model)
+void SendCoinsEntry::setModel(WalletModel *_model)
{
- this->model = model;
+ this->model = _model;
- if (model && model->getOptionsModel())
- connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
+ if (_model && _model->getOptionsModel())
+ connect(_model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
clear();
}
diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp
index 8e2e8a5098..e28a6b47af 100644
--- a/src/qt/signverifymessagedialog.cpp
+++ b/src/qt/signverifymessagedialog.cpp
@@ -12,7 +12,7 @@
#include "base58.h"
#include "init.h"
-#include "main.h" // For strMessageMagic
+#include "validation.h" // For strMessageMagic
#include "wallet/wallet.h"
#include <string>
@@ -20,11 +20,11 @@
#include <QClipboard>
-SignVerifyMessageDialog::SignVerifyMessageDialog(const PlatformStyle *platformStyle, QWidget *parent) :
+SignVerifyMessageDialog::SignVerifyMessageDialog(const PlatformStyle *_platformStyle, QWidget *parent) :
QDialog(parent),
ui(new Ui::SignVerifyMessageDialog),
model(0),
- platformStyle(platformStyle)
+ platformStyle(_platformStyle)
{
ui->setupUi(this);
@@ -60,9 +60,9 @@ SignVerifyMessageDialog::~SignVerifyMessageDialog()
delete ui;
}
-void SignVerifyMessageDialog::setModel(WalletModel *model)
+void SignVerifyMessageDialog::setModel(WalletModel *_model)
{
- this->model = model;
+ this->model = _model;
}
void SignVerifyMessageDialog::setAddress_SM(const QString &address)
@@ -142,7 +142,7 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked()
}
CKey key;
- if (!pwalletMain->GetKey(keyID, key))
+ if (!model->getPrivKey(keyID, key))
{
ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_SM->setText(tr("Private key for the entered address is not available."));
diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp
index e36d86fddd..1b6e28e93e 100644
--- a/src/qt/splashscreen.cpp
+++ b/src/qt/splashscreen.cpp
@@ -147,6 +147,7 @@ void SplashScreen::slotFinish(QWidget *mainWin)
if (isMinimized())
showNormal();
hide();
+ deleteLater(); // No more need for this
}
static void InitMessage(SplashScreen *splash, const std::string &message)
@@ -164,9 +165,10 @@ static void ShowProgress(SplashScreen *splash, const std::string &title, int nPr
}
#ifdef ENABLE_WALLET
-static void ConnectWallet(SplashScreen *splash, CWallet* wallet)
+void SplashScreen::ConnectWallet(CWallet* wallet)
{
- wallet->ShowProgress.connect(boost::bind(ShowProgress, splash, _1, _2));
+ wallet->ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2));
+ connectedWallets.push_back(wallet);
}
#endif
@@ -176,7 +178,7 @@ void SplashScreen::subscribeToCoreSignals()
uiInterface.InitMessage.connect(boost::bind(InitMessage, this, _1));
uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2));
#ifdef ENABLE_WALLET
- uiInterface.LoadWallet.connect(boost::bind(ConnectWallet, this, _1));
+ uiInterface.LoadWallet.connect(boost::bind(&SplashScreen::ConnectWallet, this, _1));
#endif
}
@@ -186,8 +188,9 @@ void SplashScreen::unsubscribeFromCoreSignals()
uiInterface.InitMessage.disconnect(boost::bind(InitMessage, this, _1));
uiInterface.ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2));
#ifdef ENABLE_WALLET
- if(pwalletMain)
- pwalletMain->ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2));
+ Q_FOREACH(CWallet* const & pwallet, connectedWallets) {
+ pwallet->ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2));
+ }
#endif
}
diff --git a/src/qt/splashscreen.h b/src/qt/splashscreen.h
index 821f39db1c..d1727b66c9 100644
--- a/src/qt/splashscreen.h
+++ b/src/qt/splashscreen.h
@@ -7,6 +7,7 @@
#include <QSplashScreen>
+class CWallet;
class NetworkStyle;
/** Class for the splashscreen with information of the running client.
@@ -39,11 +40,15 @@ private:
void subscribeToCoreSignals();
/** Disconnect core signals to splash screen */
void unsubscribeFromCoreSignals();
+ /** Connect wallet signals to splash screen */
+ void ConnectWallet(CWallet*);
QPixmap pixmap;
QString curMessage;
QColor curColor;
int curAlignment;
+
+ QList<CWallet*> connectedWallets;
};
#endif // BITCOIN_QT_SPLASHSCREEN_H
diff --git a/src/qt/test/compattests.cpp b/src/qt/test/compattests.cpp
new file mode 100644
index 0000000000..2a7284b5b2
--- /dev/null
+++ b/src/qt/test/compattests.cpp
@@ -0,0 +1,23 @@
+// Copyright (c) 2016 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "paymentrequestplus.h" // this includes protobuf's port.h which defines its own bswap macos
+
+#include "compattests.h"
+
+#include "compat/byteswap.h"
+
+void CompatTests::bswapTests()
+{
+ // Sibling in bitcoin/src/test/bswap_tests.cpp
+ uint16_t u1 = 0x1234;
+ uint32_t u2 = 0x56789abc;
+ uint64_t u3 = 0xdef0123456789abc;
+ uint16_t e1 = 0x3412;
+ uint32_t e2 = 0xbc9a7856;
+ uint64_t e3 = 0xbc9a78563412f0de;
+ QVERIFY(bswap_16(u1) == e1);
+ QVERIFY(bswap_32(u2) == e2);
+ QVERIFY(bswap_64(u3) == e3);
+}
diff --git a/src/qt/test/compattests.h b/src/qt/test/compattests.h
new file mode 100644
index 0000000000..35dede7743
--- /dev/null
+++ b/src/qt/test/compattests.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2009-2015 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_QT_TEST_COMPATTESTS_H
+#define BITCOIN_QT_TEST_COMPATTESTS_H
+
+#include <QObject>
+#include <QTest>
+
+class CompatTests : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void bswapTests();
+};
+
+#endif // BITCOIN_QT_TEST_COMPATTESTS_H
diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp
new file mode 100644
index 0000000000..26ac5e2114
--- /dev/null
+++ b/src/qt/test/rpcnestedtests.cpp
@@ -0,0 +1,132 @@
+// Copyright (c) 2016 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "rpcnestedtests.h"
+
+#include "chainparams.h"
+#include "consensus/validation.h"
+#include "validation.h"
+#include "rpc/register.h"
+#include "rpc/server.h"
+#include "rpcconsole.h"
+#include "test/testutil.h"
+#include "univalue.h"
+#include "util.h"
+
+#include <QDir>
+#include <QtGlobal>
+
+#include <boost/filesystem.hpp>
+
+static UniValue rpcNestedTest_rpc(const JSONRPCRequest& request)
+{
+ if (request.fHelp) {
+ return "help message";
+ }
+ return request.params.write(0, 0);
+}
+
+static const CRPCCommand vRPCCommands[] =
+{
+ { "test", "rpcNestedTest", &rpcNestedTest_rpc, true },
+};
+
+void RPCNestedTests::rpcNestedTests()
+{
+ UniValue jsonRPCError;
+
+ // do some test setup
+ // could be moved to a more generic place when we add more tests on QT level
+ const CChainParams& chainparams = Params();
+ RegisterAllCoreRPCCommands(tableRPC);
+ tableRPC.appendCommand("rpcNestedTest", &vRPCCommands[0]);
+ ClearDatadirCache();
+ std::string path = QDir::tempPath().toStdString() + "/" + strprintf("test_bitcoin_qt_%lu_%i", (unsigned long)GetTime(), (int)(GetRand(100000)));
+ QDir dir(QString::fromStdString(path));
+ dir.mkpath(".");
+ mapArgs["-datadir"] = path;
+ //mempool.setSanityCheck(1.0);
+ pblocktree = new CBlockTreeDB(1 << 20, true);
+ pcoinsdbview = new CCoinsViewDB(1 << 23, true);
+ pcoinsTip = new CCoinsViewCache(pcoinsdbview);
+ InitBlockIndex(chainparams);
+ {
+ CValidationState state;
+ bool ok = ActivateBestChain(state, chainparams);
+ QVERIFY(ok);
+ }
+
+ SetRPCWarmupFinished();
+
+ std::string result;
+ std::string result2;
+ RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo()[chain]"); //simple result filtering with path
+ QVERIFY(result=="main");
+
+ RPCConsole::RPCExecuteCommandLine(result, "getblock(getbestblockhash())"); //simple 2 level nesting
+ RPCConsole::RPCExecuteCommandLine(result, "getblock(getblock(getbestblockhash())[hash], true)");
+
+ RPCConsole::RPCExecuteCommandLine(result, "getblock( getblock( getblock(getbestblockhash())[hash] )[hash], true)"); //4 level nesting with whitespace, filtering path and boolean parameter
+
+ RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo");
+ QVERIFY(result.substr(0,1) == "{");
+
+ RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo()");
+ QVERIFY(result.substr(0,1) == "{");
+
+ RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo "); //whitespace at the end will be tolerated
+ QVERIFY(result.substr(0,1) == "{");
+
+ (RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo()[\"chain\"]")); //Quote path identifier are allowed, but look after a child contaning the quotes in the key
+ QVERIFY(result == "null");
+
+ (RPCConsole::RPCExecuteCommandLine(result, "createrawtransaction [] {} 0")); //parameter not in brackets are allowed
+ (RPCConsole::RPCExecuteCommandLine(result2, "createrawtransaction([],{},0)")); //parameter in brackets are allowed
+ QVERIFY(result == result2);
+ (RPCConsole::RPCExecuteCommandLine(result2, "createrawtransaction( [], {} , 0 )")); //whitespace between parametres is allowed
+ QVERIFY(result == result2);
+
+ RPCConsole::RPCExecuteCommandLine(result, "getblock(getbestblockhash())[tx][0]");
+ QVERIFY(result == "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b");
+
+ RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest");
+ QVERIFY(result == "[]");
+ RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest ''");
+ QVERIFY(result == "[\"\"]");
+ RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest \"\"");
+ QVERIFY(result == "[\"\"]");
+ RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest '' abc");
+ QVERIFY(result == "[\"\",\"abc\"]");
+ RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest abc '' abc");
+ QVERIFY(result == "[\"abc\",\"\",\"abc\"]");
+ RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest abc abc");
+ QVERIFY(result == "[\"abc\",\"abc\"]");
+ RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest abc\t\tabc");
+ QVERIFY(result == "[\"abc\",\"abc\"]");
+ RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest(abc )");
+ QVERIFY(result == "[\"abc\"]");
+ RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest( abc )");
+ QVERIFY(result == "[\"abc\"]");
+ RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest( abc , cba )");
+ QVERIFY(result == "[\"abc\",\"cba\"]");
+
+#if QT_VERSION >= 0x050300
+ // do the QVERIFY_EXCEPTION_THROWN checks only with Qt5.3 and higher (QVERIFY_EXCEPTION_THROWN was introduced in Qt5.3)
+ QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo() .\n"), std::runtime_error); //invalid syntax
+ QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo() getblockchaininfo()"), std::runtime_error); //invalid syntax
+ (RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo(")); //tolerate non closing brackets if we have no arguments
+ (RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo()()()")); //tolerate non command brackts
+ QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo(True)"), UniValue); //invalid argument
+ QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "a(getblockchaininfo(True))"), UniValue); //method not found
+ QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest abc,,abc"), std::runtime_error); //don't tollerate empty arguments when using ,
+ QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest(abc,,abc)"), std::runtime_error); //don't tollerate empty arguments when using ,
+ QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest(abc,,)"), std::runtime_error); //don't tollerate empty arguments when using ,
+#endif
+
+ delete pcoinsTip;
+ delete pcoinsdbview;
+ delete pblocktree;
+
+ boost::filesystem::remove_all(boost::filesystem::path(path));
+}
diff --git a/src/qt/test/rpcnestedtests.h b/src/qt/test/rpcnestedtests.h
new file mode 100644
index 0000000000..9ad409019f
--- /dev/null
+++ b/src/qt/test/rpcnestedtests.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2016 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_QT_TEST_RPC_NESTED_TESTS_H
+#define BITCOIN_QT_TEST_RPC_NESTED_TESTS_H
+
+#include <QObject>
+#include <QTest>
+
+#include "txdb.h"
+#include "txmempool.h"
+
+class RPCNestedTests : public QObject
+{
+ Q_OBJECT
+
+ private Q_SLOTS:
+ void rpcNestedTests();
+
+private:
+ CCoinsViewDB *pcoinsdbview;
+};
+
+#endif // BITCOIN_QT_TEST_RPC_NESTED_TESTS_H
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index db193420bf..d44d711315 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2009-2015 The Bitcoin Core developers
+// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -6,8 +6,12 @@
#include "config/bitcoin-config.h"
#endif
+#include "chainparams.h"
+#include "key.h"
+#include "rpcnestedtests.h"
#include "util.h"
#include "uritests.h"
+#include "compattests.h"
#ifdef ENABLE_WALLET
#include "paymentservertests.h"
@@ -27,10 +31,17 @@ Q_IMPORT_PLUGIN(qtwcodecs)
Q_IMPORT_PLUGIN(qkrcodecs)
#endif
+extern void noui_connect();
+
// This is all you need to run all the tests
int main(int argc, char *argv[])
{
+ ECC_Start();
SetupEnvironment();
+ SetupNetworking();
+ SelectParams(CBaseChainParams::MAIN);
+ noui_connect();
+
bool fInvalid = false;
// Don't remove this, it's needed to access
@@ -48,6 +59,13 @@ int main(int argc, char *argv[])
if (QTest::qExec(&test2) != 0)
fInvalid = true;
#endif
+ RPCNestedTests test3;
+ if (QTest::qExec(&test3) != 0)
+ fInvalid = true;
+ CompatTests test4;
+ if (QTest::qExec(&test4) != 0)
+ fInvalid = true;
+ ECC_Stop();
return fInvalid;
}
diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp
index bae0cbd1c8..e7d03a6119 100644
--- a/src/qt/transactiondesc.cpp
+++ b/src/qt/transactiondesc.cpp
@@ -11,7 +11,7 @@
#include "base58.h"
#include "consensus/consensus.h"
-#include "main.h"
+#include "validation.h"
#include "script/script.h"
#include "timedata.h"
#include "util.h"
@@ -26,10 +26,10 @@ QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx)
AssertLockHeld(cs_main);
if (!CheckFinalTx(wtx))
{
- if (wtx.nLockTime < LOCKTIME_THRESHOLD)
- return tr("Open for %n more block(s)", "", wtx.nLockTime - chainActive.Height());
+ if (wtx.tx->nLockTime < LOCKTIME_THRESHOLD)
+ return tr("Open for %n more block(s)", "", wtx.tx->nLockTime - chainActive.Height());
else
- return tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx.nLockTime));
+ return tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx.tx->nLockTime));
}
else
{
@@ -133,7 +133,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
// Coinbase
//
CAmount nUnmatured = 0;
- BOOST_FOREACH(const CTxOut& txout, wtx.vout)
+ BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout)
nUnmatured += wallet->GetCredit(txout, ISMINE_ALL);
strHTML += "<b>" + tr("Credit") + ":</b> ";
if (wtx.IsInMainChain())
@@ -152,14 +152,14 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
else
{
isminetype fAllFromMe = ISMINE_SPENDABLE;
- BOOST_FOREACH(const CTxIn& txin, wtx.vin)
+ BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin)
{
isminetype mine = wallet->IsMine(txin);
if(fAllFromMe > mine) fAllFromMe = mine;
}
isminetype fAllToMe = ISMINE_SPENDABLE;
- BOOST_FOREACH(const CTxOut& txout, wtx.vout)
+ BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout)
{
isminetype mine = wallet->IsMine(txout);
if(fAllToMe > mine) fAllToMe = mine;
@@ -173,7 +173,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
//
// Debit
//
- BOOST_FOREACH(const CTxOut& txout, wtx.vout)
+ BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout)
{
// Ignore change
isminetype toSelf = wallet->IsMine(txout);
@@ -212,7 +212,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
strHTML += "<b>" + tr("Total credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, nValue) + "<br>";
}
- CAmount nTxFee = nDebit - wtx.GetValueOut();
+ CAmount nTxFee = nDebit - wtx.tx->GetValueOut();
if (nTxFee > 0)
strHTML += "<b>" + tr("Transaction fee") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -nTxFee) + "<br>";
}
@@ -221,10 +221,10 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
//
// Mixed debit transaction
//
- BOOST_FOREACH(const CTxIn& txin, wtx.vin)
+ BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin)
if (wallet->IsMine(txin))
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>";
- BOOST_FOREACH(const CTxOut& txout, wtx.vout)
+ BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout)
if (wallet->IsMine(txout))
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>";
}
@@ -241,6 +241,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
strHTML += "<br><b>" + tr("Comment") + ":</b><br>" + GUIUtil::HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
strHTML += "<b>" + tr("Transaction ID") + ":</b> " + rec->getTxID() + "<br>";
+ strHTML += "<b>" + tr("Transaction total size") + ":</b> " + QString::number(wtx.tx->GetTotalSize()) + " bytes<br>";
strHTML += "<b>" + tr("Output index") + ":</b> " + QString::number(rec->getOutputIndex()) + "<br>";
// Message from normal bitcoin:URI (bitcoin:123...?message=example)
@@ -275,20 +276,20 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
if (fDebug)
{
strHTML += "<hr><br>" + tr("Debug information") + "<br><br>";
- BOOST_FOREACH(const CTxIn& txin, wtx.vin)
+ BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin)
if(wallet->IsMine(txin))
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>";
- BOOST_FOREACH(const CTxOut& txout, wtx.vout)
+ BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout)
if(wallet->IsMine(txout))
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>";
strHTML += "<br><b>" + tr("Transaction") + ":</b><br>";
- strHTML += GUIUtil::HtmlEscape(wtx.ToString(), true);
+ strHTML += GUIUtil::HtmlEscape(wtx.tx->ToString(), true);
strHTML += "<br><b>" + tr("Inputs") + ":</b>";
strHTML += "<ul>";
- BOOST_FOREACH(const CTxIn& txin, wtx.vin)
+ BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin)
{
COutPoint prevout = txin.prevout;
diff --git a/src/qt/transactionfilterproxy.cpp b/src/qt/transactionfilterproxy.cpp
index 9dcb72f55e..e21b89b935 100644
--- a/src/qt/transactionfilterproxy.cpp
+++ b/src/qt/transactionfilterproxy.cpp
@@ -66,9 +66,9 @@ void TransactionFilterProxy::setDateRange(const QDateTime &from, const QDateTime
invalidateFilter();
}
-void TransactionFilterProxy::setAddressPrefix(const QString &addrPrefix)
+void TransactionFilterProxy::setAddressPrefix(const QString &_addrPrefix)
{
- this->addrPrefix = addrPrefix;
+ this->addrPrefix = _addrPrefix;
invalidateFilter();
}
@@ -95,9 +95,9 @@ void TransactionFilterProxy::setLimit(int limit)
this->limitRows = limit;
}
-void TransactionFilterProxy::setShowInactive(bool showInactive)
+void TransactionFilterProxy::setShowInactive(bool _showInactive)
{
- this->showInactive = showInactive;
+ this->showInactive = _showInactive;
invalidateFilter();
}
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index 4fe47181f6..a1d1422317 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -6,7 +6,7 @@
#include "base58.h"
#include "consensus/consensus.h"
-#include "main.h"
+#include "validation.h"
#include "timedata.h"
#include "wallet/wallet.h"
@@ -47,7 +47,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
//
// Credit
//
- BOOST_FOREACH(const CTxOut& txout, wtx.vout)
+ BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout)
{
isminetype mine = wallet->IsMine(txout);
if(mine)
@@ -83,7 +83,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
{
bool involvesWatchAddress = false;
isminetype fAllFromMe = ISMINE_SPENDABLE;
- BOOST_FOREACH(const CTxIn& txin, wtx.vin)
+ BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin)
{
isminetype mine = wallet->IsMine(txin);
if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true;
@@ -91,7 +91,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
}
isminetype fAllToMe = ISMINE_SPENDABLE;
- BOOST_FOREACH(const CTxOut& txout, wtx.vout)
+ BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout)
{
isminetype mine = wallet->IsMine(txout);
if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true;
@@ -112,11 +112,11 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
//
// Debit
//
- CAmount nTxFee = nDebit - wtx.GetValueOut();
+ CAmount nTxFee = nDebit - wtx.tx->GetValueOut();
- for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++)
+ for (unsigned int nOut = 0; nOut < wtx.tx->vout.size(); nOut++)
{
- const CTxOut& txout = wtx.vout[nOut];
+ const CTxOut& txout = wtx.tx->vout[nOut];
TransactionRecord sub(hash, nTime);
sub.idx = parts.size();
sub.involvesWatchAddress = involvesWatchAddress;
@@ -190,15 +190,15 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx)
if (!CheckFinalTx(wtx))
{
- if (wtx.nLockTime < LOCKTIME_THRESHOLD)
+ if (wtx.tx->nLockTime < LOCKTIME_THRESHOLD)
{
status.status = TransactionStatus::OpenUntilBlock;
- status.open_for = wtx.nLockTime - chainActive.Height();
+ status.open_for = wtx.tx->nLockTime - chainActive.Height();
}
else
{
status.status = TransactionStatus::OpenUntilDate;
- status.open_for = wtx.nLockTime;
+ status.open_for = wtx.tx->nLockTime;
}
}
// For generated transactions, determine maturity
diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h
index 8c754c3aad..8eff302aff 100644
--- a/src/qt/transactionrecord.h
+++ b/src/qt/transactionrecord.h
@@ -88,16 +88,16 @@ public:
{
}
- TransactionRecord(uint256 hash, qint64 time):
- hash(hash), time(time), type(Other), address(""), debit(0),
+ TransactionRecord(uint256 _hash, qint64 _time):
+ hash(_hash), time(_time), type(Other), address(""), debit(0),
credit(0), idx(0)
{
}
- TransactionRecord(uint256 hash, qint64 time,
- Type type, const std::string &address,
- const CAmount& debit, const CAmount& credit):
- hash(hash), time(time), type(type), address(address), debit(debit), credit(credit),
+ TransactionRecord(uint256 _hash, qint64 _time,
+ Type _type, const std::string &_address,
+ const CAmount& _debit, const CAmount& _credit):
+ hash(_hash), time(_time), type(_type), address(_address), debit(_debit), credit(_credit),
idx(0)
{
}
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index b29ecf8348..da0742aa6a 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -14,7 +14,7 @@
#include "walletmodel.h"
#include "core_io.h"
-#include "main.h"
+#include "validation.h"
#include "sync.h"
#include "uint256.h"
#include "util.h"
@@ -59,9 +59,9 @@ struct TxLessThan
class TransactionTablePriv
{
public:
- TransactionTablePriv(CWallet *wallet, TransactionTableModel *parent) :
- wallet(wallet),
- parent(parent)
+ TransactionTablePriv(CWallet *_wallet, TransactionTableModel *_parent) :
+ wallet(_wallet),
+ parent(_parent)
{
}
@@ -235,13 +235,13 @@ public:
}
};
-TransactionTableModel::TransactionTableModel(const PlatformStyle *platformStyle, CWallet* wallet, WalletModel *parent):
+TransactionTableModel::TransactionTableModel(const PlatformStyle *_platformStyle, CWallet* _wallet, WalletModel *parent):
QAbstractTableModel(parent),
- wallet(wallet),
+ wallet(_wallet),
walletModel(parent),
- priv(new TransactionTablePriv(wallet, this)),
+ priv(new TransactionTablePriv(_wallet, this)),
fProcessingQueuedTransactions(false),
- platformStyle(platformStyle)
+ platformStyle(_platformStyle)
{
columns << QString() << QString() << tr("Date") << tr("Type") << tr("Label") << BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit());
priv->refreshWallet();
@@ -714,8 +714,8 @@ struct TransactionNotification
{
public:
TransactionNotification() {}
- TransactionNotification(uint256 hash, ChangeType status, bool showTransaction):
- hash(hash), status(status), showTransaction(showTransaction) {}
+ TransactionNotification(uint256 _hash, ChangeType _status, bool _showTransaction):
+ hash(_hash), status(_status), showTransaction(_showTransaction) {}
void invoke(QObject *ttm)
{
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index 48cf940502..f5cbde6238 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -37,7 +37,7 @@
TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *parent) :
QWidget(parent), model(0), transactionProxyModel(0),
- transactionView(0), abandonAction(0)
+ transactionView(0), abandonAction(0), columnResizingFixer(0)
{
// Build filter row
setContentsMargins(0,0,0,0);
@@ -147,7 +147,7 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
QAction *editLabelAction = new QAction(tr("Edit label"), this);
QAction *showDetailsAction = new QAction(tr("Show transaction details"), this);
- contextMenu = new QMenu();
+ contextMenu = new QMenu(this);
contextMenu->addAction(copyAddressAction);
contextMenu->addAction(copyLabelAction);
contextMenu->addAction(copyAmountAction);
@@ -184,13 +184,13 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
connect(showDetailsAction, SIGNAL(triggered()), this, SLOT(showDetails()));
}
-void TransactionView::setModel(WalletModel *model)
+void TransactionView::setModel(WalletModel *_model)
{
- this->model = model;
- if(model)
+ this->model = _model;
+ if(_model)
{
transactionProxyModel = new TransactionFilterProxy(this);
- transactionProxyModel->setSourceModel(model->getTransactionTableModel());
+ transactionProxyModel->setSourceModel(_model->getTransactionTableModel());
transactionProxyModel->setDynamicSortFilter(true);
transactionProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
transactionProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
@@ -212,12 +212,12 @@ void TransactionView::setModel(WalletModel *model)
transactionView->setColumnWidth(TransactionTableModel::Type, TYPE_COLUMN_WIDTH);
transactionView->setColumnWidth(TransactionTableModel::Amount, AMOUNT_MINIMUM_COLUMN_WIDTH);
- columnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(transactionView, AMOUNT_MINIMUM_COLUMN_WIDTH, MINIMUM_COLUMN_WIDTH);
+ columnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(transactionView, AMOUNT_MINIMUM_COLUMN_WIDTH, MINIMUM_COLUMN_WIDTH, this);
- if (model->getOptionsModel())
+ if (_model->getOptionsModel())
{
// Add third party transaction URLs to context menu
- QStringList listUrls = model->getOptionsModel()->getThirdPartyTxUrls().split("|", QString::SkipEmptyParts);
+ QStringList listUrls = _model->getOptionsModel()->getThirdPartyTxUrls().split("|", QString::SkipEmptyParts);
for (int i = 0; i < listUrls.size(); ++i)
{
QString host = QUrl(listUrls[i].trimmed(), QUrl::StrictMode).host();
@@ -234,10 +234,10 @@ void TransactionView::setModel(WalletModel *model)
}
// show/hide column Watch-only
- updateWatchOnlyColumn(model->haveWatchOnly());
+ updateWatchOnlyColumn(_model->haveWatchOnly());
// Watch-only signal
- connect(model, SIGNAL(notifyWatchonlyChanged(bool)), this, SLOT(updateWatchOnlyColumn(bool)));
+ connect(_model, SIGNAL(notifyWatchonlyChanged(bool)), this, SLOT(updateWatchOnlyColumn(bool)));
}
}
diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp
index 947bcdb15a..4ec022881c 100644
--- a/src/qt/utilitydialog.cpp
+++ b/src/qt/utilitydialog.cpp
@@ -171,22 +171,20 @@ ShutdownWindow::ShutdownWindow(QWidget *parent, Qt::WindowFlags f):
setLayout(layout);
}
-void ShutdownWindow::showShutdownWindow(BitcoinGUI *window)
+QWidget *ShutdownWindow::showShutdownWindow(BitcoinGUI *window)
{
if (!window)
- return;
+ return nullptr;
// Show a simple window indicating shutdown status
QWidget *shutdownWindow = new ShutdownWindow();
- // We don't hold a direct pointer to the shutdown window after creation, so use
- // Qt::WA_DeleteOnClose to make sure that the window will be deleted eventually.
- shutdownWindow->setAttribute(Qt::WA_DeleteOnClose);
shutdownWindow->setWindowTitle(window->windowTitle());
// Center shutdown window at where main window was
const QPoint global = window->mapToGlobal(window->rect().center());
shutdownWindow->move(global.x() - shutdownWindow->width() / 2, global.y() - shutdownWindow->height() / 2);
shutdownWindow->show();
+ return shutdownWindow;
}
void ShutdownWindow::closeEvent(QCloseEvent *event)
diff --git a/src/qt/utilitydialog.h b/src/qt/utilitydialog.h
index 843bd7f67b..b930429578 100644
--- a/src/qt/utilitydialog.h
+++ b/src/qt/utilitydialog.h
@@ -43,7 +43,7 @@ class ShutdownWindow : public QWidget
public:
ShutdownWindow(QWidget *parent=0, Qt::WindowFlags f=0);
- static void showShutdownWindow(BitcoinGUI *window);
+ static QWidget *showShutdownWindow(BitcoinGUI *window);
protected:
void closeEvent(QCloseEvent *event);
diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp
index e4ca5e1831..69dcc9abb1 100644
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -12,10 +12,10 @@
#include <QHBoxLayout>
#include <QLabel>
-WalletFrame::WalletFrame(const PlatformStyle *platformStyle, BitcoinGUI *_gui) :
+WalletFrame::WalletFrame(const PlatformStyle *_platformStyle, BitcoinGUI *_gui) :
QFrame(_gui),
gui(_gui),
- platformStyle(platformStyle)
+ platformStyle(_platformStyle)
{
// Leave HBox hook for adding a list view later
QHBoxLayout *walletFrameLayout = new QHBoxLayout(this);
@@ -33,9 +33,9 @@ WalletFrame::~WalletFrame()
{
}
-void WalletFrame::setClientModel(ClientModel *clientModel)
+void WalletFrame::setClientModel(ClientModel *_clientModel)
{
- this->clientModel = clientModel;
+ this->clientModel = _clientModel;
}
bool WalletFrame::addWallet(const QString& name, WalletModel *walletModel)
@@ -57,6 +57,8 @@ bool WalletFrame::addWallet(const QString& name, WalletModel *walletModel)
// Ensure a walletView is able to show the main window
connect(walletView, SIGNAL(showNormalIfMinimized()), gui, SLOT(showNormalIfMinimized()));
+ connect(walletView, SIGNAL(outOfSyncWarningClicked()), this, SLOT(outOfSyncWarningClicked()));
+
return true;
}
@@ -195,3 +197,7 @@ WalletView *WalletFrame::currentWalletView()
return qobject_cast<WalletView*>(walletStack->currentWidget());
}
+void WalletFrame::outOfSyncWarningClicked()
+{
+ Q_EMIT requestedSyncWarningInfo();
+}
diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h
index 9a5bc273c2..7bc6412910 100644
--- a/src/qt/walletframe.h
+++ b/src/qt/walletframe.h
@@ -19,6 +19,13 @@ QT_BEGIN_NAMESPACE
class QStackedWidget;
QT_END_NAMESPACE
+/**
+ * A container for embedding all wallet-related
+ * controls into 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.
+ */
class WalletFrame : public QFrame
{
Q_OBJECT
@@ -38,6 +45,10 @@ public:
void showOutOfSyncWarning(bool fShow);
+Q_SIGNALS:
+ /** Notify that the user has requested more information about the out-of-sync warning */
+ void requestedSyncWarningInfo();
+
private:
QStackedWidget *walletStack;
BitcoinGUI *gui;
@@ -78,6 +89,8 @@ public Q_SLOTS:
void usedSendingAddresses();
/** Show used receiving addresses */
void usedReceivingAddresses();
+ /** Pass on signal over requested out-of-sync-warning information */
+ void outOfSyncWarningClicked();
};
#endif // BITCOIN_QT_WALLETFRAME_H
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 73851e97fc..afc72fae69 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -5,6 +5,7 @@
#include "walletmodel.h"
#include "addresstablemodel.h"
+#include "consensus/validation.h"
#include "guiconstants.h"
#include "guiutil.h"
#include "paymentserver.h"
@@ -13,9 +14,11 @@
#include "base58.h"
#include "keystore.h"
-#include "main.h"
+#include "validation.h"
+#include "net.h" // for g_connman
#include "sync.h"
#include "ui_interface.h"
+#include "util.h" // for GetBoolArg
#include "wallet/wallet.h"
#include "wallet/walletdb.h" // for BackupWallet
@@ -27,8 +30,8 @@
#include <boost/foreach.hpp>
-WalletModel::WalletModel(const PlatformStyle *platformStyle, CWallet *wallet, OptionsModel *optionsModel, QObject *parent) :
- QObject(parent), wallet(wallet), optionsModel(optionsModel), addressTableModel(0),
+WalletModel::WalletModel(const PlatformStyle *platformStyle, CWallet *_wallet, OptionsModel *_optionsModel, QObject *parent) :
+ QObject(parent), wallet(_wallet), optionsModel(_optionsModel), addressTableModel(0),
transactionTableModel(0),
recentRequestsTableModel(0),
cachedBalance(0), cachedUnconfirmedBalance(0), cachedImmatureBalance(0),
@@ -64,7 +67,7 @@ CAmount WalletModel::getBalance(const CCoinControl *coinControl) const
wallet->AvailableCoins(vCoins, true, coinControl);
BOOST_FOREACH(const COutput& out, vCoins)
if(out.fSpendable)
- nBalance += out.tx->vout[out.i].nValue;
+ nBalance += out.tx->tx->vout[out.i].nValue;
return nBalance;
}
@@ -328,12 +331,12 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran
}
CReserveKey *keyChange = transaction.getPossibleKeyChange();
- if(!wallet->CommitTransaction(*newTx, *keyChange, g_connman.get()))
- return TransactionCommitFailed;
+ CValidationState state;
+ if(!wallet->CommitTransaction(*newTx, *keyChange, g_connman.get(), state))
+ return SendCoinsReturn(TransactionCommitFailed, QString::fromStdString(state.GetRejectReason()));
- CTransaction* t = (CTransaction*)newTx;
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
- ssTx << *t;
+ ssTx << *newTx->tx;
transaction_array.append(&(ssTx[0]), ssTx.size());
}
@@ -531,10 +534,10 @@ WalletModel::UnlockContext WalletModel::requestUnlock()
return UnlockContext(this, valid, was_locked);
}
-WalletModel::UnlockContext::UnlockContext(WalletModel *wallet, bool valid, bool relock):
- wallet(wallet),
- valid(valid),
- relock(relock)
+WalletModel::UnlockContext::UnlockContext(WalletModel *_wallet, bool _valid, bool _relock):
+ wallet(_wallet),
+ valid(_valid),
+ relock(_relock)
{
}
@@ -563,6 +566,11 @@ bool WalletModel::havePrivKey(const CKeyID &address) const
return wallet->HaveKey(address);
}
+bool WalletModel::getPrivKey(const CKeyID &address, CKey& vchPrivKeyOut) const
+{
+ return wallet->GetKey(address, vchPrivKeyOut);
+}
+
// returns a list of COutputs from COutPoints
void WalletModel::getOutputs(const std::vector<COutPoint>& vOutpoints, std::vector<COutput>& vOutputs)
{
@@ -600,7 +608,7 @@ void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins)
int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain();
if (nDepth < 0) continue;
COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true, true);
- if (outpoint.n < out.tx->vout.size() && wallet->IsMine(out.tx->vout[outpoint.n]) == ISMINE_SPENDABLE)
+ if (outpoint.n < out.tx->tx->vout.size() && wallet->IsMine(out.tx->tx->vout[outpoint.n]) == ISMINE_SPENDABLE)
vCoins.push_back(out);
}
@@ -608,14 +616,14 @@ void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins)
{
COutput cout = out;
- while (wallet->IsChange(cout.tx->vout[cout.i]) && cout.tx->vin.size() > 0 && wallet->IsMine(cout.tx->vin[0]))
+ while (wallet->IsChange(cout.tx->tx->vout[cout.i]) && cout.tx->tx->vin.size() > 0 && wallet->IsMine(cout.tx->tx->vin[0]))
{
- if (!wallet->mapWallet.count(cout.tx->vin[0].prevout.hash)) break;
- cout = COutput(&wallet->mapWallet[cout.tx->vin[0].prevout.hash], cout.tx->vin[0].prevout.n, 0, true, true);
+ if (!wallet->mapWallet.count(cout.tx->tx->vin[0].prevout.hash)) break;
+ cout = COutput(&wallet->mapWallet[cout.tx->tx->vin[0].prevout.hash], cout.tx->tx->vin[0].prevout.n, 0, true, true);
}
CTxDestination address;
- if(!out.fSpendable || !ExtractDestination(cout.tx->vout[cout.i].scriptPubKey, address))
+ if(!out.fSpendable || !ExtractDestination(cout.tx->tx->vout[cout.i].scriptPubKey, address))
continue;
mapCoins[QString::fromStdString(CBitcoinAddress(address).ToString())].push_back(out);
}
@@ -684,7 +692,17 @@ bool WalletModel::abandonTransaction(uint256 hash) const
return wallet->AbandonTransaction(hash);
}
+bool WalletModel::isWalletEnabled()
+{
+ return !GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET);
+}
+
bool WalletModel::hdEnabled() const
{
return wallet->IsHDEnabled();
}
+
+int WalletModel::getDefaultConfirmTarget() const
+{
+ return nTxConfirmTarget;
+}
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index a15ecf899b..eedf6e8cea 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -38,8 +38,8 @@ class SendCoinsRecipient
{
public:
explicit SendCoinsRecipient() : amount(0), fSubtractFeeFromAmount(false), nVersion(SendCoinsRecipient::CURRENT_VERSION) { }
- explicit SendCoinsRecipient(const QString &addr, const QString &label, const CAmount& amount, const QString &message):
- address(addr), label(label), amount(amount), message(message), fSubtractFeeFromAmount(false), nVersion(SendCoinsRecipient::CURRENT_VERSION) {}
+ explicit SendCoinsRecipient(const QString &addr, const QString &_label, const CAmount& _amount, const QString &_message):
+ address(addr), label(_label), amount(_amount), message(_message), fSubtractFeeFromAmount(false), nVersion(SendCoinsRecipient::CURRENT_VERSION) {}
// If from an unauthenticated payment request, this is used for storing
// the addresses, e.g. address-A<br />address-B<br />address-C.
@@ -65,7 +65,7 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
std::string sAddress = address.toStdString();
std::string sLabel = label.toStdString();
std::string sMessage = message.toStdString();
@@ -75,7 +75,6 @@ public:
std::string sAuthenticatedMerchant = authenticatedMerchant.toStdString();
READWRITE(this->nVersion);
- nVersion = this->nVersion;
READWRITE(sAddress);
READWRITE(sLabel);
READWRITE(amount);
@@ -145,9 +144,13 @@ public:
// Return status record for SendCoins, contains error id + information
struct SendCoinsReturn
{
- SendCoinsReturn(StatusCode status = OK):
- status(status) {}
+ SendCoinsReturn(StatusCode _status = OK, QString _reasonCommitFailed = "")
+ : status(_status),
+ reasonCommitFailed(_reasonCommitFailed)
+ {
+ }
StatusCode status;
+ QString reasonCommitFailed;
};
// prepare transaction for getting txfee before sending coins
@@ -188,6 +191,7 @@ public:
bool getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const;
bool havePrivKey(const CKeyID &address) const;
+ bool getPrivKey(const CKeyID &address, CKey& vchPrivKeyOut) const;
void getOutputs(const std::vector<COutPoint>& vOutpoints, std::vector<COutput>& vOutputs);
bool isSpent(const COutPoint& outpoint) const;
void listCoins(std::map<QString, std::vector<COutput> >& mapCoins) const;
@@ -203,8 +207,12 @@ public:
bool transactionCanBeAbandoned(uint256 hash) const;
bool abandonTransaction(uint256 hash) const;
+ static bool isWalletEnabled();
+
bool hdEnabled() const;
+ int getDefaultConfirmTarget() const;
+
private:
CWallet *wallet;
bool fHaveWatchOnly;
diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp
index ffadf89cc8..f9a95bed42 100644
--- a/src/qt/walletmodeltransaction.cpp
+++ b/src/qt/walletmodeltransaction.cpp
@@ -7,8 +7,8 @@
#include "policy/policy.h"
#include "wallet/wallet.h"
-WalletModelTransaction::WalletModelTransaction(const QList<SendCoinsRecipient> &recipients) :
- recipients(recipients),
+WalletModelTransaction::WalletModelTransaction(const QList<SendCoinsRecipient> &_recipients) :
+ recipients(_recipients),
walletTransaction(0),
keyChange(0),
fee(0)
@@ -64,7 +64,7 @@ void WalletModelTransaction::reassignAmounts(int nChangePosRet)
if (out.amount() <= 0) continue;
if (i == nChangePosRet)
i++;
- subtotal += walletTransaction->vout[i].nValue;
+ subtotal += walletTransaction->tx->vout[i].nValue;
i++;
}
rcp.amount = subtotal;
@@ -73,7 +73,7 @@ void WalletModelTransaction::reassignAmounts(int nChangePosRet)
{
if (i == nChangePosRet)
i++;
- rcp.amount = walletTransaction->vout[i].nValue;
+ rcp.amount = walletTransaction->tx->vout[i].nValue;
i++;
}
}
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index 495ebfd834..a9518413c2 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -29,11 +29,11 @@
#include <QPushButton>
#include <QVBoxLayout>
-WalletView::WalletView(const PlatformStyle *platformStyle, QWidget *parent):
+WalletView::WalletView(const PlatformStyle *_platformStyle, QWidget *parent):
QStackedWidget(parent),
clientModel(0),
walletModel(0),
- platformStyle(platformStyle)
+ platformStyle(_platformStyle)
{
// Create tabs
overviewPage = new OverviewPage(platformStyle);
@@ -66,6 +66,7 @@ WalletView::WalletView(const PlatformStyle *platformStyle, QWidget *parent):
// Clicking on a transaction on the overview pre-selects the transaction on the transaction history page
connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), transactionView, SLOT(focusTransaction(QModelIndex)));
+ connect(overviewPage, SIGNAL(outOfSyncWarningClicked()), this, SLOT(requestedSyncWarningInfo()));
// Double-clicking on a transaction on the transaction history page shows details
connect(transactionView, SIGNAL(doubleClicked(QModelIndex)), transactionView, SLOT(showDetails()));
@@ -104,47 +105,47 @@ void WalletView::setBitcoinGUI(BitcoinGUI *gui)
}
}
-void WalletView::setClientModel(ClientModel *clientModel)
+void WalletView::setClientModel(ClientModel *_clientModel)
{
- this->clientModel = clientModel;
+ this->clientModel = _clientModel;
- overviewPage->setClientModel(clientModel);
- sendCoinsPage->setClientModel(clientModel);
+ overviewPage->setClientModel(_clientModel);
+ sendCoinsPage->setClientModel(_clientModel);
}
-void WalletView::setWalletModel(WalletModel *walletModel)
+void WalletView::setWalletModel(WalletModel *_walletModel)
{
- this->walletModel = walletModel;
+ this->walletModel = _walletModel;
// Put transaction list in tabs
- transactionView->setModel(walletModel);
- overviewPage->setWalletModel(walletModel);
- receiveCoinsPage->setModel(walletModel);
- sendCoinsPage->setModel(walletModel);
- usedReceivingAddressesPage->setModel(walletModel->getAddressTableModel());
- usedSendingAddressesPage->setModel(walletModel->getAddressTableModel());
-
- if (walletModel)
+ transactionView->setModel(_walletModel);
+ overviewPage->setWalletModel(_walletModel);
+ receiveCoinsPage->setModel(_walletModel);
+ sendCoinsPage->setModel(_walletModel);
+ usedReceivingAddressesPage->setModel(_walletModel->getAddressTableModel());
+ usedSendingAddressesPage->setModel(_walletModel->getAddressTableModel());
+
+ if (_walletModel)
{
// Receive and pass through messages from wallet model
- connect(walletModel, SIGNAL(message(QString,QString,unsigned int)), this, SIGNAL(message(QString,QString,unsigned int)));
+ connect(_walletModel, SIGNAL(message(QString,QString,unsigned int)), this, SIGNAL(message(QString,QString,unsigned int)));
// Handle changes in encryption status
- connect(walletModel, SIGNAL(encryptionStatusChanged(int)), this, SIGNAL(encryptionStatusChanged(int)));
+ connect(_walletModel, SIGNAL(encryptionStatusChanged(int)), this, SIGNAL(encryptionStatusChanged(int)));
updateEncryptionStatus();
// update HD status
- Q_EMIT hdEnabledStatusChanged(walletModel->hdEnabled());
+ Q_EMIT hdEnabledStatusChanged(_walletModel->hdEnabled());
// Balloon pop-up for new transaction
- connect(walletModel->getTransactionTableModel(), SIGNAL(rowsInserted(QModelIndex,int,int)),
+ connect(_walletModel->getTransactionTableModel(), SIGNAL(rowsInserted(QModelIndex,int,int)),
this, SLOT(processNewTransaction(QModelIndex,int,int)));
// Ask for passphrase if needed
- connect(walletModel, SIGNAL(requireUnlock()), this, SLOT(unlockWallet()));
+ connect(_walletModel, SIGNAL(requireUnlock()), this, SLOT(unlockWallet()));
// Show progress dialog
- connect(walletModel, SIGNAL(showProgress(QString,int)), this, SLOT(showProgress(QString,int)));
+ connect(_walletModel, SIGNAL(showProgress(QString,int)), this, SLOT(showProgress(QString,int)));
}
}
@@ -322,3 +323,8 @@ void WalletView::showProgress(const QString &title, int nProgress)
else if (progressDialog)
progressDialog->setValue(nProgress);
}
+
+void WalletView::requestedSyncWarningInfo()
+{
+ Q_EMIT outOfSyncWarningClicked();
+}
diff --git a/src/qt/walletview.h b/src/qt/walletview.h
index 2045605954..aaa6aacbf0 100644
--- a/src/qt/walletview.h
+++ b/src/qt/walletview.h
@@ -110,6 +110,9 @@ public Q_SLOTS:
/** Show progress dialog e.g. for rescan */
void showProgress(const QString &title, int nProgress);
+ /** User has requested more information about the out of sync state */
+ void requestedSyncWarningInfo();
+
Q_SIGNALS:
/** Signal that we want to show the main window */
void showNormalIfMinimized();
@@ -121,6 +124,8 @@ Q_SIGNALS:
void hdEnabledStatusChanged(int hdEnabled);
/** Notify that a new transaction appeared */
void incomingTransaction(const QString& date, int unit, const CAmount& amount, const QString& type, const QString& address, const QString& label);
+ /** Notify that the out of sync warning icon has been pressed */
+ void outOfSyncWarningClicked();
};
#endif // BITCOIN_QT_WALLETVIEW_H
diff --git a/src/random.cpp b/src/random.cpp
index d9a8cc145e..c2605b45bd 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -11,7 +11,6 @@
#include "compat.h" // for Windows API
#include <wincrypt.h>
#endif
-#include "serialize.h" // for begin_ptr(vec)
#include "util.h" // for LogPrint()
#include "utilstrencodings.h" // for GetTime()
@@ -72,15 +71,15 @@ static void RandAddSeedPerfmon()
const size_t nMaxSize = 10000000; // Bail out at more than 10MB of performance data
while (true) {
nSize = vData.size();
- ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, begin_ptr(vData), &nSize);
+ ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, vData.data(), &nSize);
if (ret != ERROR_MORE_DATA || vData.size() >= nMaxSize)
break;
vData.resize(std::max((vData.size() * 3) / 2, nMaxSize)); // Grow size of buffer exponentially
}
RegCloseKey(HKEY_PERFORMANCE_DATA);
if (ret == ERROR_SUCCESS) {
- RAND_add(begin_ptr(vData), nSize, nSize / 100.0);
- memory_cleanse(begin_ptr(vData), nSize);
+ RAND_add(vData.data(), nSize, nSize / 100.0);
+ memory_cleanse(vData.data(), nSize);
LogPrint("rand", "%s: %lu bytes\n", __func__, nSize);
} else {
static bool warned = false; // Warn only once
@@ -178,22 +177,21 @@ uint256 GetRandHash()
return hash;
}
-uint32_t insecure_rand_Rz = 11;
-uint32_t insecure_rand_Rw = 11;
-void seed_insecure_rand(bool fDeterministic)
+FastRandomContext::FastRandomContext(bool fDeterministic)
{
// The seed values have some unlikely fixed points which we avoid.
if (fDeterministic) {
- insecure_rand_Rz = insecure_rand_Rw = 11;
+ Rz = Rw = 11;
} else {
uint32_t tmp;
do {
GetRandBytes((unsigned char*)&tmp, 4);
} while (tmp == 0 || tmp == 0x9068ffffU);
- insecure_rand_Rz = tmp;
+ Rz = tmp;
do {
GetRandBytes((unsigned char*)&tmp, 4);
} while (tmp == 0 || tmp == 0x464fffffU);
- insecure_rand_Rw = tmp;
+ Rw = tmp;
}
}
+
diff --git a/src/random.h b/src/random.h
index 31b80bd565..e97d2d1fb0 100644
--- a/src/random.h
+++ b/src/random.h
@@ -28,25 +28,22 @@ uint256 GetRandHash();
void GetStrongRandBytes(unsigned char* buf, int num);
/**
- * Seed insecure_rand using the random pool.
- * @param Deterministic Use a deterministic seed
+ * Fast randomness source. This is seeded once with secure random data, but
+ * is completely deterministic and insecure after that.
+ * This class is not thread-safe.
*/
-void seed_insecure_rand(bool fDeterministic = false);
-
-/**
- * MWC RNG of George Marsaglia
- * This is intended to be fast. It has a period of 2^59.3, though the
- * least significant 16 bits only have a period of about 2^30.1.
- *
- * @return random value
- */
-extern uint32_t insecure_rand_Rz;
-extern uint32_t insecure_rand_Rw;
-static inline uint32_t insecure_rand(void)
-{
- insecure_rand_Rz = 36969 * (insecure_rand_Rz & 65535) + (insecure_rand_Rz >> 16);
- insecure_rand_Rw = 18000 * (insecure_rand_Rw & 65535) + (insecure_rand_Rw >> 16);
- return (insecure_rand_Rw << 16) + insecure_rand_Rz;
-}
+class FastRandomContext {
+public:
+ explicit FastRandomContext(bool fDeterministic=false);
+
+ uint32_t rand32() {
+ Rz = 36969 * (Rz & 65535) + (Rz >> 16);
+ Rw = 18000 * (Rw & 65535) + (Rw >> 16);
+ return (Rw << 16) + Rz;
+ }
+
+ uint32_t Rz;
+ uint32_t Rw;
+};
#endif // BITCOIN_RANDOM_H
diff --git a/src/rest.cpp b/src/rest.cpp
index c815592124..6b6b7401f6 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -7,7 +7,7 @@
#include "chainparams.h"
#include "primitives/block.h"
#include "primitives/transaction.h"
-#include "main.h"
+#include "validation.h"
#include "httpserver.h"
#include "rpc/server.h"
#include "streams.h"
@@ -50,7 +50,7 @@ struct CCoin {
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
+ inline void SerializationOp(Stream& s, Operation ser_action)
{
READWRITE(nTxVer);
READWRITE(nHeight);
@@ -228,7 +228,7 @@ static bool rest_block(HTTPRequest* req,
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
}
- CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
+ CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
ssBlock << block;
switch (rf) {
@@ -274,7 +274,7 @@ static bool rest_block_notxdetails(HTTPRequest* req, const std::string& strURIPa
}
// A bit of a hack - dependency on a function defined in rpc/blockchain.cpp
-UniValue getblockchaininfo(const UniValue& params, bool fHelp);
+UniValue getblockchaininfo(const JSONRPCRequest& request);
static bool rest_chaininfo(HTTPRequest* req, const std::string& strURIPart)
{
@@ -285,8 +285,9 @@ static bool rest_chaininfo(HTTPRequest* req, const std::string& strURIPart)
switch (rf) {
case RF_JSON: {
- UniValue rpcParams(UniValue::VARR);
- UniValue chainInfoObject = getblockchaininfo(rpcParams, false);
+ JSONRPCRequest jsonRequest;
+ jsonRequest.params = UniValue(UniValue::VARR);
+ UniValue chainInfoObject = getblockchaininfo(jsonRequest);
string strJSON = chainInfoObject.write() + "\n";
req->WriteHeader("Content-Type", "application/json");
req->WriteReply(HTTP_OK, strJSON);
@@ -362,12 +363,12 @@ static bool rest_tx(HTTPRequest* req, const std::string& strURIPart)
if (!ParseHashStr(hashStr, hash))
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
- CTransaction tx;
+ CTransactionRef tx;
uint256 hashBlock = uint256();
if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true))
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
- CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
ssTx << tx;
switch (rf) {
@@ -387,7 +388,7 @@ static bool rest_tx(HTTPRequest* req, const std::string& strURIPart)
case RF_JSON: {
UniValue objTx(UniValue::VOBJ);
- TxToJSON(tx, hashBlock, objTx);
+ TxToJSON(*tx, hashBlock, objTx);
string strJSON = objTx.write() + "\n";
req->WriteHeader("Content-Type", "application/json");
req->WriteReply(HTTP_OK, strJSON);
diff --git a/src/reverselock.h b/src/reverselock.h
index fac1ccb793..1fd8de5d80 100644
--- a/src/reverselock.h
+++ b/src/reverselock.h
@@ -13,9 +13,9 @@ class reverse_lock
{
public:
- explicit reverse_lock(Lock& lock) : lock(lock) {
- lock.unlock();
- lock.swap(templock);
+ explicit reverse_lock(Lock& _lock) : lock(_lock) {
+ _lock.unlock();
+ _lock.swap(templock);
}
~reverse_lock() {
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index f05f8ff358..1139c2aa5c 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -9,7 +9,7 @@
#include "checkpoints.h"
#include "coins.h"
#include "consensus/validation.h"
-#include "main.h"
+#include "validation.h"
#include "policy/policy.h"
#include "primitives/transaction.h"
#include "rpc/server.h"
@@ -119,16 +119,16 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
result.push_back(Pair("versionHex", strprintf("%08x", block.nVersion)));
result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex()));
UniValue txs(UniValue::VARR);
- BOOST_FOREACH(const CTransaction&tx, block.vtx)
+ for(const auto& tx : block.vtx)
{
if(txDetails)
{
UniValue objTx(UniValue::VOBJ);
- TxToJSON(tx, uint256(), objTx);
+ TxToJSON(*tx, uint256(), objTx);
txs.push_back(objTx);
}
else
- txs.push_back(tx.GetHash().GetHex());
+ txs.push_back(tx->GetHash().GetHex());
}
result.push_back(Pair("tx", txs));
result.push_back(Pair("time", block.GetBlockTime()));
@@ -146,12 +146,12 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
return result;
}
-UniValue getblockcount(const UniValue& params, bool fHelp)
+UniValue getblockcount(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 0)
+ if (request.fHelp || request.params.size() != 0)
throw runtime_error(
"getblockcount\n"
- "\nReturns the number of blocks in the longest block chain.\n"
+ "\nReturns the number of blocks in the longest blockchain.\n"
"\nResult:\n"
"n (numeric) The current block count\n"
"\nExamples:\n"
@@ -163,12 +163,12 @@ UniValue getblockcount(const UniValue& params, bool fHelp)
return chainActive.Height();
}
-UniValue getbestblockhash(const UniValue& params, bool fHelp)
+UniValue getbestblockhash(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 0)
+ if (request.fHelp || request.params.size() != 0)
throw runtime_error(
"getbestblockhash\n"
- "\nReturns the hash of the best (tip) block in the longest block chain.\n"
+ "\nReturns the hash of the best (tip) block in the longest blockchain.\n"
"\nResult\n"
"\"hex\" (string) the block hash hex encoded\n"
"\nExamples\n"
@@ -190,16 +190,16 @@ void RPCNotifyBlockChange(bool ibd, const CBlockIndex * pindex)
cond_blockchange.notify_all();
}
-UniValue waitfornewblock(const UniValue& params, bool fHelp)
+UniValue waitfornewblock(const JSONRPCRequest& request)
{
- if (fHelp || params.size() > 1)
+ if (request.fHelp || request.params.size() > 1)
throw runtime_error(
- "waitfornewblock\n"
+ "waitfornewblock (timeout)\n"
"\nWaits for a specific new block and returns useful info about it.\n"
"\nReturns the current block on timeout or exit.\n"
"\nArguments:\n"
- "1. timeout (milliseconds) (int, optional, default=false)\n"
- "\nResult::\n"
+ "1. timeout (int, optional, default=0) Time in milliseconds to wait for a response. 0 indicates no timeout.\n"
+ "\nResult:\n"
"{ (json object)\n"
" \"hash\" : { (string) The blockhash\n"
" \"height\" : { (int) Block height\n"
@@ -209,8 +209,8 @@ UniValue waitfornewblock(const UniValue& params, bool fHelp)
+ HelpExampleRpc("waitfornewblock", "1000")
);
int timeout = 0;
- if (params.size() > 0)
- timeout = params[0].get_int();
+ if (request.params.size() > 0)
+ timeout = request.params[0].get_int();
CUpdatedBlock block;
{
@@ -228,17 +228,17 @@ UniValue waitfornewblock(const UniValue& params, bool fHelp)
return ret;
}
-UniValue waitforblock(const UniValue& params, bool fHelp)
+UniValue waitforblock(const JSONRPCRequest& request)
{
- if (fHelp || params.size() < 1 || params.size() > 2)
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw runtime_error(
- "waitforblock\n"
+ "waitforblock <blockhash> (timeout)\n"
"\nWaits for a specific new block and returns useful info about it.\n"
"\nReturns the current block on timeout or exit.\n"
"\nArguments:\n"
"1. blockhash to wait for (string)\n"
- "2. timeout (milliseconds) (int, optional, default=false)\n"
- "\nResult::\n"
+ "2. timeout (int, optional, default=0) Time in milliseconds to wait for a response. 0 indicates no timeout.\n"
+ "\nResult:\n"
"{ (json object)\n"
" \"hash\" : { (string) The blockhash\n"
" \"height\" : { (int) Block height\n"
@@ -249,10 +249,10 @@ UniValue waitforblock(const UniValue& params, bool fHelp)
);
int timeout = 0;
- uint256 hash = uint256S(params[0].get_str());
+ uint256 hash = uint256S(request.params[0].get_str());
- if (params.size() > 1)
- timeout = params[1].get_int();
+ if (request.params.size() > 1)
+ timeout = request.params[1].get_int();
CUpdatedBlock block;
{
@@ -270,18 +270,18 @@ UniValue waitforblock(const UniValue& params, bool fHelp)
return ret;
}
-UniValue waitforblockheight(const UniValue& params, bool fHelp)
+UniValue waitforblockheight(const JSONRPCRequest& request)
{
- if (fHelp || params.size() < 1 || params.size() > 2)
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw runtime_error(
- "waitforblock\n"
+ "waitforblockheight <blockheight> (timeout)\n"
"\nWaits for (at least) block height and returns the height and hash\n"
- "\nof the current tip.\n"
+ "of the current tip.\n"
"\nReturns the current block on timeout or exit.\n"
"\nArguments:\n"
"1. block height to wait for (int)\n"
- "2. timeout (milliseconds) (int, optional, default=false)\n"
- "\nResult::\n"
+ "2. timeout (int, optional, default=0) Time in milliseconds to wait for a response. 0 indicates no timeout.\n"
+ "\nResult:\n"
"{ (json object)\n"
" \"hash\" : { (string) The blockhash\n"
" \"height\" : { (int) Block height\n"
@@ -292,10 +292,10 @@ UniValue waitforblockheight(const UniValue& params, bool fHelp)
);
int timeout = 0;
- int height = params[0].get_int();
+ int height = request.params[0].get_int();
- if (params.size() > 1)
- timeout = params[1].get_int();
+ if (request.params.size() > 1)
+ timeout = request.params[1].get_int();
CUpdatedBlock block;
{
@@ -312,9 +312,9 @@ UniValue waitforblockheight(const UniValue& params, bool fHelp)
return ret;
}
-UniValue getdifficulty(const UniValue& params, bool fHelp)
+UniValue getdifficulty(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 0)
+ if (request.fHelp || request.params.size() != 0)
throw runtime_error(
"getdifficulty\n"
"\nReturns the proof-of-work difficulty as a multiple of the minimum difficulty.\n"
@@ -411,9 +411,9 @@ UniValue mempoolToJSON(bool fVerbose = false)
}
}
-UniValue getrawmempool(const UniValue& params, bool fHelp)
+UniValue getrawmempool(const JSONRPCRequest& request)
{
- if (fHelp || params.size() > 1)
+ if (request.fHelp || request.params.size() > 1)
throw runtime_error(
"getrawmempool ( verbose )\n"
"\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n"
@@ -436,15 +436,15 @@ UniValue getrawmempool(const UniValue& params, bool fHelp)
);
bool fVerbose = false;
- if (params.size() > 0)
- fVerbose = params[0].get_bool();
+ if (request.params.size() > 0)
+ fVerbose = request.params[0].get_bool();
return mempoolToJSON(fVerbose);
}
-UniValue getmempoolancestors(const UniValue& params, bool fHelp)
+UniValue getmempoolancestors(const JSONRPCRequest& request)
{
- if (fHelp || params.size() < 1 || params.size() > 2) {
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
throw runtime_error(
"getmempoolancestors txid (verbose)\n"
"\nIf txid is in the mempool, returns all in-mempool ancestors.\n"
@@ -469,10 +469,10 @@ UniValue getmempoolancestors(const UniValue& params, bool fHelp)
}
bool fVerbose = false;
- if (params.size() > 1)
- fVerbose = params[1].get_bool();
+ if (request.params.size() > 1)
+ fVerbose = request.params[1].get_bool();
- uint256 hash = ParseHashV(params[0], "parameter 1");
+ uint256 hash = ParseHashV(request.params[0], "parameter 1");
LOCK(mempool.cs);
@@ -497,18 +497,18 @@ UniValue getmempoolancestors(const UniValue& params, bool fHelp)
UniValue o(UniValue::VOBJ);
BOOST_FOREACH(CTxMemPool::txiter ancestorIt, setAncestors) {
const CTxMemPoolEntry &e = *ancestorIt;
- const uint256& hash = e.GetTx().GetHash();
+ const uint256& _hash = e.GetTx().GetHash();
UniValue info(UniValue::VOBJ);
entryToJSON(info, e);
- o.push_back(Pair(hash.ToString(), info));
+ o.push_back(Pair(_hash.ToString(), info));
}
return o;
}
}
-UniValue getmempooldescendants(const UniValue& params, bool fHelp)
+UniValue getmempooldescendants(const JSONRPCRequest& request)
{
- if (fHelp || params.size() < 1 || params.size() > 2) {
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
throw runtime_error(
"getmempooldescendants txid (verbose)\n"
"\nIf txid is in the mempool, returns all in-mempool descendants.\n"
@@ -533,10 +533,10 @@ UniValue getmempooldescendants(const UniValue& params, bool fHelp)
}
bool fVerbose = false;
- if (params.size() > 1)
- fVerbose = params[1].get_bool();
+ if (request.params.size() > 1)
+ fVerbose = request.params[1].get_bool();
- uint256 hash = ParseHashV(params[0], "parameter 1");
+ uint256 hash = ParseHashV(request.params[0], "parameter 1");
LOCK(mempool.cs);
@@ -561,18 +561,18 @@ UniValue getmempooldescendants(const UniValue& params, bool fHelp)
UniValue o(UniValue::VOBJ);
BOOST_FOREACH(CTxMemPool::txiter descendantIt, setDescendants) {
const CTxMemPoolEntry &e = *descendantIt;
- const uint256& hash = e.GetTx().GetHash();
+ const uint256& _hash = e.GetTx().GetHash();
UniValue info(UniValue::VOBJ);
entryToJSON(info, e);
- o.push_back(Pair(hash.ToString(), info));
+ o.push_back(Pair(_hash.ToString(), info));
}
return o;
}
}
-UniValue getmempoolentry(const UniValue& params, bool fHelp)
+UniValue getmempoolentry(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 1) {
+ if (request.fHelp || request.params.size() != 1) {
throw runtime_error(
"getmempoolentry txid\n"
"\nReturns mempool data for given transaction\n"
@@ -588,7 +588,7 @@ UniValue getmempoolentry(const UniValue& params, bool fHelp)
);
}
- uint256 hash = ParseHashV(params[0], "parameter 1");
+ uint256 hash = ParseHashV(request.params[0], "parameter 1");
LOCK(mempool.cs);
@@ -603,9 +603,9 @@ UniValue getmempoolentry(const UniValue& params, bool fHelp)
return info;
}
-UniValue getblockhash(const UniValue& params, bool fHelp)
+UniValue getblockhash(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 1)
+ if (request.fHelp || request.params.size() != 1)
throw runtime_error(
"getblockhash index\n"
"\nReturns hash of block in best-block-chain at index provided.\n"
@@ -620,7 +620,7 @@ UniValue getblockhash(const UniValue& params, bool fHelp)
LOCK(cs_main);
- int nHeight = params[0].get_int();
+ int nHeight = request.params[0].get_int();
if (nHeight < 0 || nHeight > chainActive.Height())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range");
@@ -628,9 +628,9 @@ UniValue getblockhash(const UniValue& params, bool fHelp)
return pblockindex->GetBlockHash().GetHex();
}
-UniValue getblockheader(const UniValue& params, bool fHelp)
+UniValue getblockheader(const JSONRPCRequest& request)
{
- if (fHelp || params.size() < 1 || params.size() > 2)
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw runtime_error(
"getblockheader \"hash\" ( verbose )\n"
"\nIf verbose is false, returns a string that is serialized, hex-encoded data for blockheader 'hash'.\n"
@@ -664,12 +664,12 @@ UniValue getblockheader(const UniValue& params, bool fHelp)
LOCK(cs_main);
- std::string strHash = params[0].get_str();
+ std::string strHash = request.params[0].get_str();
uint256 hash(uint256S(strHash));
bool fVerbose = true;
- if (params.size() > 1)
- fVerbose = params[1].get_bool();
+ if (request.params.size() > 1)
+ fVerbose = request.params[1].get_bool();
if (mapBlockIndex.count(hash) == 0)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
@@ -687,9 +687,9 @@ UniValue getblockheader(const UniValue& params, bool fHelp)
return blockheaderToJSON(pblockindex);
}
-UniValue getblock(const UniValue& params, bool fHelp)
+UniValue getblock(const JSONRPCRequest& request)
{
- if (fHelp || params.size() < 1 || params.size() > 2)
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw runtime_error(
"getblock \"hash\" ( verbose )\n"
"\nIf verbose is false, returns a string that is serialized, hex-encoded data for block 'hash'.\n"
@@ -730,12 +730,12 @@ UniValue getblock(const UniValue& params, bool fHelp)
LOCK(cs_main);
- std::string strHash = params[0].get_str();
+ std::string strHash = request.params[0].get_str();
uint256 hash(uint256S(strHash));
bool fVerbose = true;
- if (params.size() > 1)
- fVerbose = params[1].get_bool();
+ if (request.params.size() > 1)
+ fVerbose = request.params[1].get_bool();
if (mapBlockIndex.count(hash) == 0)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
@@ -751,7 +751,7 @@ UniValue getblock(const UniValue& params, bool fHelp)
if (!fVerbose)
{
- CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
+ CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
ssBlock << block;
std::string strHex = HexStr(ssBlock.begin(), ssBlock.end());
return strHex;
@@ -814,9 +814,9 @@ static bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats)
return true;
}
-UniValue gettxoutsetinfo(const UniValue& params, bool fHelp)
+UniValue gettxoutsetinfo(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 0)
+ if (request.fHelp || request.params.size() != 0)
throw runtime_error(
"gettxoutsetinfo\n"
"\nReturns statistics about the unspent transaction output set.\n"
@@ -848,20 +848,22 @@ UniValue gettxoutsetinfo(const UniValue& params, bool fHelp)
ret.push_back(Pair("bytes_serialized", (int64_t)stats.nSerializedSize));
ret.push_back(Pair("hash_serialized", stats.hashSerialized.GetHex()));
ret.push_back(Pair("total_amount", ValueFromAmount(stats.nTotalAmount)));
+ } else {
+ throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
}
return ret;
}
-UniValue gettxout(const UniValue& params, bool fHelp)
+UniValue gettxout(const JSONRPCRequest& request)
{
- if (fHelp || params.size() < 2 || params.size() > 3)
+ if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
throw runtime_error(
"gettxout \"txid\" n ( includemempool )\n"
"\nReturns details about an unspent transaction output.\n"
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id\n"
"2. n (numeric, required) vout number\n"
- "3. includemempool (boolean, optional) Whether to include the mem pool\n"
+ "3. includemempool (boolean, optional) Whether to include the mempool\n"
"\nResult:\n"
"{\n"
" \"bestblock\" : \"hash\", (string) the block hash\n"
@@ -894,12 +896,12 @@ UniValue gettxout(const UniValue& params, bool fHelp)
UniValue ret(UniValue::VOBJ);
- std::string strHash = params[0].get_str();
+ std::string strHash = request.params[0].get_str();
uint256 hash(uint256S(strHash));
- int n = params[1].get_int();
+ int n = request.params[1].get_int();
bool fMempool = true;
- if (params.size() > 2)
- fMempool = params[2].get_bool();
+ if (request.params.size() > 2)
+ fMempool = request.params[2].get_bool();
CCoins coins;
if (fMempool) {
@@ -932,11 +934,11 @@ UniValue gettxout(const UniValue& params, bool fHelp)
return ret;
}
-UniValue verifychain(const UniValue& params, bool fHelp)
+UniValue verifychain(const JSONRPCRequest& request)
{
int nCheckLevel = GetArg("-checklevel", DEFAULT_CHECKLEVEL);
int nCheckDepth = GetArg("-checkblocks", DEFAULT_CHECKBLOCKS);
- if (fHelp || params.size() > 2)
+ if (request.fHelp || request.params.size() > 2)
throw runtime_error(
"verifychain ( checklevel numblocks )\n"
"\nVerifies blockchain database.\n"
@@ -952,10 +954,10 @@ UniValue verifychain(const UniValue& params, bool fHelp)
LOCK(cs_main);
- if (params.size() > 0)
- nCheckLevel = params[0].get_int();
- if (params.size() > 1)
- nCheckDepth = params[1].get_int();
+ if (request.params.size() > 0)
+ nCheckLevel = request.params[0].get_int();
+ if (request.params.size() > 1)
+ nCheckDepth = request.params[1].get_int();
return CVerifyDB().VerifyDB(Params(), pcoinsTip, nCheckLevel, nCheckDepth);
}
@@ -1007,6 +1009,7 @@ static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Conse
}
rv.push_back(Pair("startTime", consensusParams.vDeployments[id].nStartTime));
rv.push_back(Pair("timeout", consensusParams.vDeployments[id].nTimeout));
+ rv.push_back(Pair("since", VersionBitsTipStateSinceHeight(consensusParams, id)));
return rv;
}
@@ -1019,12 +1022,12 @@ void BIP9SoftForkDescPushBack(UniValue& bip9_softforks, const std::string &name,
bip9_softforks.push_back(Pair(name, BIP9SoftForkDesc(consensusParams, id)));
}
-UniValue getblockchaininfo(const UniValue& params, bool fHelp)
+UniValue getblockchaininfo(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 0)
+ if (request.fHelp || request.params.size() != 0)
throw runtime_error(
"getblockchaininfo\n"
- "Returns an object containing various state info regarding block chain processing.\n"
+ "Returns an object containing various state info regarding blockchain processing.\n"
"\nResult:\n"
"{\n"
" \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n"
@@ -1036,7 +1039,7 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp)
" \"verificationprogress\": xxxx, (numeric) estimate of verification progress [0..1]\n"
" \"chainwork\": \"xxxx\" (string) total amount of work in active chain, in hexadecimal\n"
" \"pruned\": xx, (boolean) if the blocks are subject to pruning\n"
- " \"pruneheight\": xxxxxx, (numeric) heighest block available\n"
+ " \"pruneheight\": xxxxxx, (numeric) lowest-height complete block stored\n"
" \"softforks\": [ (array) status of softforks in progress\n"
" {\n"
" \"id\": \"xxxx\", (string) name of softfork\n"
@@ -1051,7 +1054,8 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp)
" \"status\": \"xxxx\", (string) one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\"\n"
" \"bit\": xx, (numeric) the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)\n"
" \"startTime\": xx, (numeric) the minimum median time past of a block at which the bit gains its meaning\n"
- " \"timeout\": xx (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n"
+ " \"timeout\": xx, (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n"
+ " \"since\": xx (numeric) height of the first block to which the status applies\n"
" }\n"
" }\n"
"}\n"
@@ -1111,9 +1115,9 @@ struct CompareBlocksByHeight
}
};
-UniValue getchaintips(const UniValue& params, bool fHelp)
+UniValue getchaintips(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 0)
+ if (request.fHelp || request.params.size() != 0)
throw runtime_error(
"getchaintips\n"
"Return information about all known tips in the block tree,"
@@ -1227,9 +1231,9 @@ UniValue mempoolInfoToJSON()
return ret;
}
-UniValue getmempoolinfo(const UniValue& params, bool fHelp)
+UniValue getmempoolinfo(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 0)
+ if (request.fHelp || request.params.size() != 0)
throw runtime_error(
"getmempoolinfo\n"
"\nReturns details on the active state of the TX memory pool.\n"
@@ -1249,9 +1253,47 @@ UniValue getmempoolinfo(const UniValue& params, bool fHelp)
return mempoolInfoToJSON();
}
-UniValue invalidateblock(const UniValue& params, bool fHelp)
+UniValue preciousblock(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 1)
+ if (request.fHelp || request.params.size() != 1)
+ throw runtime_error(
+ "preciousblock \"hash\"\n"
+ "\nTreats a block as if it were received before others with the same work.\n"
+ "\nA later preciousblock call can override the effect of an earlier one.\n"
+ "\nThe effects of preciousblock are not retained across restarts.\n"
+ "\nArguments:\n"
+ "1. hash (string, required) the hash of the block to mark as precious\n"
+ "\nResult:\n"
+ "\nExamples:\n"
+ + HelpExampleCli("preciousblock", "\"blockhash\"")
+ + HelpExampleRpc("preciousblock", "\"blockhash\"")
+ );
+
+ std::string strHash = request.params[0].get_str();
+ uint256 hash(uint256S(strHash));
+ CBlockIndex* pblockindex;
+
+ {
+ LOCK(cs_main);
+ if (mapBlockIndex.count(hash) == 0)
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
+
+ pblockindex = mapBlockIndex[hash];
+ }
+
+ CValidationState state;
+ PreciousBlock(state, Params(), pblockindex);
+
+ if (!state.IsValid()) {
+ throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason());
+ }
+
+ return NullUniValue;
+}
+
+UniValue invalidateblock(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1)
throw runtime_error(
"invalidateblock \"hash\"\n"
"\nPermanently marks a block as invalid, as if it violated a consensus rule.\n"
@@ -1263,7 +1305,7 @@ UniValue invalidateblock(const UniValue& params, bool fHelp)
+ HelpExampleRpc("invalidateblock", "\"blockhash\"")
);
- std::string strHash = params[0].get_str();
+ std::string strHash = request.params[0].get_str();
uint256 hash(uint256S(strHash));
CValidationState state;
@@ -1277,7 +1319,7 @@ UniValue invalidateblock(const UniValue& params, bool fHelp)
}
if (state.IsValid()) {
- ActivateBestChain(state, Params(), NULL, g_connman.get());
+ ActivateBestChain(state, Params());
}
if (!state.IsValid()) {
@@ -1287,9 +1329,9 @@ UniValue invalidateblock(const UniValue& params, bool fHelp)
return NullUniValue;
}
-UniValue reconsiderblock(const UniValue& params, bool fHelp)
+UniValue reconsiderblock(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 1)
+ if (request.fHelp || request.params.size() != 1)
throw runtime_error(
"reconsiderblock \"hash\"\n"
"\nRemoves invalidity status of a block and its descendants, reconsider them for activation.\n"
@@ -1302,7 +1344,7 @@ UniValue reconsiderblock(const UniValue& params, bool fHelp)
+ HelpExampleRpc("reconsiderblock", "\"blockhash\"")
);
- std::string strHash = params[0].get_str();
+ std::string strHash = request.params[0].get_str();
uint256 hash(uint256S(strHash));
{
@@ -1315,7 +1357,7 @@ UniValue reconsiderblock(const UniValue& params, bool fHelp)
}
CValidationState state;
- ActivateBestChain(state, Params(), NULL, g_connman.get());
+ ActivateBestChain(state, Params());
if (!state.IsValid()) {
throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason());
@@ -1344,6 +1386,8 @@ static const CRPCCommand commands[] =
{ "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true },
{ "blockchain", "verifychain", &verifychain, true },
+ { "blockchain", "preciousblock", &preciousblock, true },
+
/* Not shown in help */
{ "hidden", "invalidateblock", &invalidateblock, true },
{ "hidden", "reconsiderblock", &reconsiderblock, true },
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index c14d9d6747..03992a278d 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -95,6 +95,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "importaddress", 2 },
{ "importaddress", 3 },
{ "importpubkey", 2 },
+ { "importmulti", 0 },
+ { "importmulti", 1 },
{ "verifychain", 0 },
{ "verifychain", 1 },
{ "keypoolrefill", 0 },
@@ -107,6 +109,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "prioritisetransaction", 2 },
{ "setban", 2 },
{ "setban", 3 },
+ { "setnetworkactive", 0 },
{ "getmempoolancestors", 1 },
{ "getmempooldescendants", 1 },
};
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 7794ac619d..73797e2019 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -12,7 +12,7 @@
#include "consensus/validation.h"
#include "core_io.h"
#include "init.h"
-#include "main.h"
+#include "validation.h"
#include "miner.h"
#include "net.h"
#include "pow.h"
@@ -22,6 +22,7 @@
#include "utilstrencodings.h"
#include "validationinterface.h"
+#include <memory>
#include <stdint.h>
#include <boost/assign/list_of.hpp>
@@ -73,9 +74,9 @@ UniValue GetNetworkHashPS(int lookup, int height) {
return workDiff.getdouble() / timeDiff;
}
-UniValue getnetworkhashps(const UniValue& params, bool fHelp)
+UniValue getnetworkhashps(const JSONRPCRequest& request)
{
- if (fHelp || params.size() > 2)
+ if (request.fHelp || request.params.size() > 2)
throw runtime_error(
"getnetworkhashps ( blocks height )\n"
"\nReturns the estimated network hashes per second based on the last n blocks.\n"
@@ -92,7 +93,7 @@ UniValue getnetworkhashps(const UniValue& params, bool fHelp)
);
LOCK(cs_main);
- return GetNetworkHashPS(params.size() > 0 ? params[0].get_int() : 120, params.size() > 1 ? params[1].get_int() : -1);
+ return GetNetworkHashPS(request.params.size() > 0 ? request.params[0].get_int() : 120, request.params.size() > 1 ? request.params[1].get_int() : -1);
}
UniValue generateBlocks(boost::shared_ptr<CReserveScript> coinbaseScript, int nGenerate, uint64_t nMaxTries, bool keepScript)
@@ -130,8 +131,8 @@ UniValue generateBlocks(boost::shared_ptr<CReserveScript> coinbaseScript, int nG
if (pblock->nNonce == nInnerLoopCount) {
continue;
}
- CValidationState state;
- if (!ProcessNewBlock(state, Params(), NULL, pblock, true, NULL, g_connman.get()))
+ std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock);
+ if (!ProcessNewBlock(Params(), shared_pblock, true, NULL))
throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted");
++nHeight;
blockHashes.push_back(pblock->GetHash().GetHex());
@@ -145,9 +146,9 @@ UniValue generateBlocks(boost::shared_ptr<CReserveScript> coinbaseScript, int nG
return blockHashes;
}
-UniValue generate(const UniValue& params, bool fHelp)
+UniValue generate(const JSONRPCRequest& request)
{
- if (fHelp || params.size() < 1 || params.size() > 2)
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw runtime_error(
"generate numblocks ( maxtries )\n"
"\nMine up to numblocks blocks immediately (before the RPC call returns)\n"
@@ -161,10 +162,10 @@ UniValue generate(const UniValue& params, bool fHelp)
+ HelpExampleCli("generate", "11")
);
- int nGenerate = params[0].get_int();
+ int nGenerate = request.params[0].get_int();
uint64_t nMaxTries = 1000000;
- if (params.size() > 1) {
- nMaxTries = params[1].get_int();
+ if (request.params.size() > 1) {
+ nMaxTries = request.params[1].get_int();
}
boost::shared_ptr<CReserveScript> coinbaseScript;
@@ -181,9 +182,9 @@ UniValue generate(const UniValue& params, bool fHelp)
return generateBlocks(coinbaseScript, nGenerate, nMaxTries, true);
}
-UniValue generatetoaddress(const UniValue& params, bool fHelp)
+UniValue generatetoaddress(const JSONRPCRequest& request)
{
- if (fHelp || params.size() < 2 || params.size() > 3)
+ if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
throw runtime_error(
"generatetoaddress numblocks address (maxtries)\n"
"\nMine blocks immediately to a specified address (before the RPC call returns)\n"
@@ -198,13 +199,13 @@ UniValue generatetoaddress(const UniValue& params, bool fHelp)
+ HelpExampleCli("generatetoaddress", "11 \"myaddress\"")
);
- int nGenerate = params[0].get_int();
+ int nGenerate = request.params[0].get_int();
uint64_t nMaxTries = 1000000;
- if (params.size() > 2) {
- nMaxTries = params[2].get_int();
+ if (request.params.size() > 2) {
+ nMaxTries = request.params[2].get_int();
}
- CBitcoinAddress address(params[1].get_str());
+ CBitcoinAddress address(request.params[1].get_str());
if (!address.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Invalid address");
@@ -214,9 +215,9 @@ UniValue generatetoaddress(const UniValue& params, bool fHelp)
return generateBlocks(coinbaseScript, nGenerate, nMaxTries, false);
}
-UniValue getmininginfo(const UniValue& params, bool fHelp)
+UniValue getmininginfo(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 0)
+ if (request.fHelp || request.params.size() != 0)
throw runtime_error(
"getmininginfo\n"
"\nReturns a json object containing mining-related information."
@@ -229,8 +230,7 @@ UniValue getmininginfo(const UniValue& params, bool fHelp)
" \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n"
" \"errors\": \"...\" (string) Current errors\n"
" \"networkhashps\": nnn, (numeric) The network hashes per second\n"
- " \"pooledtx\": n (numeric) The size of the mem pool\n"
- " \"testnet\": true|false (boolean) If using testnet or not\n"
+ " \"pooledtx\": n (numeric) The size of the mempool\n"
" \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n"
"}\n"
"\nExamples:\n"
@@ -248,18 +248,17 @@ UniValue getmininginfo(const UniValue& params, bool fHelp)
obj.push_back(Pair("currentblocktx", (uint64_t)nLastBlockTx));
obj.push_back(Pair("difficulty", (double)GetDifficulty()));
obj.push_back(Pair("errors", GetWarnings("statusbar")));
- obj.push_back(Pair("networkhashps", getnetworkhashps(params, false)));
+ obj.push_back(Pair("networkhashps", getnetworkhashps(request)));
obj.push_back(Pair("pooledtx", (uint64_t)mempool.size()));
- obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC()));
obj.push_back(Pair("chain", Params().NetworkIDString()));
return obj;
}
// NOTE: Unlike wallet RPC (which use BTC values), mining RPCs follow GBT (BIP 22) in using satoshi amounts
-UniValue prioritisetransaction(const UniValue& params, bool fHelp)
+UniValue prioritisetransaction(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 3)
+ if (request.fHelp || request.params.size() != 3)
throw runtime_error(
"prioritisetransaction <txid> <priority delta> <fee delta>\n"
"Accepts the transaction into mined blocks at a higher (or lower) priority\n"
@@ -280,10 +279,10 @@ UniValue prioritisetransaction(const UniValue& params, bool fHelp)
LOCK(cs_main);
- uint256 hash = ParseHashStr(params[0].get_str(), "txid");
- CAmount nAmount = params[2].get_int64();
+ uint256 hash = ParseHashStr(request.params[0].get_str(), "txid");
+ CAmount nAmount = request.params[2].get_int64();
- mempool.PrioritiseTransaction(hash, params[0].get_str(), params[1].get_real(), nAmount);
+ mempool.PrioritiseTransaction(hash, request.params[0].get_str(), request.params[1].get_real(), nAmount);
return true;
}
@@ -316,71 +315,77 @@ std::string gbt_vb_name(const Consensus::DeploymentPos pos) {
return s;
}
-UniValue getblocktemplate(const UniValue& params, bool fHelp)
+UniValue getblocktemplate(const JSONRPCRequest& request)
{
- if (fHelp || params.size() > 1)
+ if (request.fHelp || request.params.size() > 1)
throw runtime_error(
- "getblocktemplate ( \"jsonrequestobject\" )\n"
+ "getblocktemplate ( TemplateRequest )\n"
"\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n"
"It returns data needed to construct a block to work on.\n"
- "For full specification, see BIPs 22 and 9:\n"
+ "For full specification, see BIPs 22, 23, 9, and 145:\n"
" https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki\n"
+ " https://github.com/bitcoin/bips/blob/master/bip-0023.mediawiki\n"
" https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#getblocktemplate_changes\n"
+ " https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki\n"
"\nArguments:\n"
- "1. \"jsonrequestobject\" (string, optional) A json object in the following spec\n"
+ "1. TemplateRequest (json object, optional) A json object in the following spec\n"
" {\n"
- " \"mode\":\"template\" (string, optional) This must be set to \"template\" or omitted\n"
- " \"capabilities\":[ (array, optional) A list of strings\n"
- " \"support\" (string) client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid'\n"
+ " \"mode\":\"template\" (string, optional) This must be set to \"template\", \"proposal\" (see BIP 23), or omitted\n"
+ " \"capabilities\":[ (array, optional) A list of strings\n"
+ " \"support\" (string) client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid'\n"
" ,...\n"
- " ]\n"
+ " ],\n"
+ " \"rules\":[ (array, optional) A list of strings\n"
+ " \"support\" (string) client side supported softfork deployment\n"
+ " ,...\n"
+ " ]\n"
" }\n"
"\n"
"\nResult:\n"
"{\n"
- " \"version\" : n, (numeric) The block version\n"
+ " \"version\" : n, (numeric) The preferred block version\n"
" \"rules\" : [ \"rulename\", ... ], (array of strings) specific block rules that are to be enforced\n"
" \"vbavailable\" : { (json object) set of pending, supported versionbit (BIP 9) softfork deployments\n"
- " \"rulename\" : bitnumber (numeric) identifies the bit number as indicating acceptance and readiness for the named softfork rule\n"
+ " \"rulename\" : bitnumber (numeric) identifies the bit number as indicating acceptance and readiness for the named softfork rule\n"
" ,...\n"
" },\n"
" \"vbrequired\" : n, (numeric) bit mask of versionbits the server requires set in submissions\n"
- " \"previousblockhash\" : \"xxxx\", (string) The hash of current highest block\n"
+ " \"previousblockhash\" : \"xxxx\", (string) The hash of current highest block\n"
" \"transactions\" : [ (array) contents of non-coinbase transactions that should be included in the next block\n"
" {\n"
- " \"data\" : \"xxxx\", (string) transaction data encoded in hexadecimal (byte-for-byte)\n"
- " \"txid\" : \"xxxx\", (string) transaction id encoded in little-endian hexadecimal\n"
- " \"hash\" : \"xxxx\", (string) hash encoded in little-endian hexadecimal (including witness data)\n"
- " \"depends\" : [ (array) array of numbers \n"
- " n (numeric) transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is\n"
+ " \"data\" : \"xxxx\", (string) transaction data encoded in hexadecimal (byte-for-byte)\n"
+ " \"txid\" : \"xxxx\", (string) transaction id encoded in little-endian hexadecimal\n"
+ " \"hash\" : \"xxxx\", (string) hash encoded in little-endian hexadecimal (including witness data)\n"
+ " \"depends\" : [ (array) array of numbers \n"
+ " n (numeric) transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is\n"
" ,...\n"
" ],\n"
- " \"fee\": n, (numeric) difference in value between transaction inputs and outputs (in Satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one\n"
- " \"sigops\" : n, (numeric) total SigOps cost, as counted for purposes of block limits; if key is not present, sigop cost is unknown and clients MUST NOT assume it is zero\n"
- " \"weight\" : n, (numeric) total transaction weight, as counted for purposes of block limits\n"
- " \"required\" : true|false (boolean) if provided and true, this transaction must be in the final block\n"
+ " \"fee\": n, (numeric) difference in value between transaction inputs and outputs (in Satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one\n"
+ " \"sigops\" : n, (numeric) total SigOps cost, as counted for purposes of block limits; if key is not present, sigop cost is unknown and clients MUST NOT assume it is zero\n"
+ " \"weight\" : n, (numeric) total transaction weight, as counted for purposes of block limits\n"
+ " \"required\" : true|false (boolean) if provided and true, this transaction must be in the final block\n"
" }\n"
" ,...\n"
" ],\n"
- " \"coinbaseaux\" : { (json object) data that should be included in the coinbase's scriptSig content\n"
- " \"flags\" : \"flags\" (string) \n"
+ " \"coinbaseaux\" : { (json object) data that should be included in the coinbase's scriptSig content\n"
+ " \"flags\" : \"xx\" (string) key name is to be ignored, and value included in scriptSig\n"
" },\n"
- " \"coinbasevalue\" : n, (numeric) maximum allowable input to coinbase transaction, including the generation award and transaction fees (in Satoshis)\n"
- " \"coinbasetxn\" : { ... }, (json object) information for coinbase transaction\n"
- " \"target\" : \"xxxx\", (string) The hash target\n"
- " \"mintime\" : xxx, (numeric) The minimum timestamp appropriate for next block time in seconds since epoch (Jan 1 1970 GMT)\n"
- " \"mutable\" : [ (array of string) list of ways the block template may be changed \n"
- " \"value\" (string) A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock'\n"
+ " \"coinbasevalue\" : n, (numeric) maximum allowable input to coinbase transaction, including the generation award and transaction fees (in Satoshis)\n"
+ " \"coinbasetxn\" : { ... }, (json object) information for coinbase transaction\n"
+ " \"target\" : \"xxxx\", (string) The hash target\n"
+ " \"mintime\" : xxx, (numeric) The minimum timestamp appropriate for next block time in seconds since epoch (Jan 1 1970 GMT)\n"
+ " \"mutable\" : [ (array of string) list of ways the block template may be changed \n"
+ " \"value\" (string) A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock'\n"
" ,...\n"
" ],\n"
- " \"noncerange\" : \"00000000ffffffff\", (string) A range of valid nonces\n"
- " \"sigoplimit\" : n, (numeric) cost limit of sigops in blocks\n"
+ " \"noncerange\" : \"00000000ffffffff\",(string) A range of valid nonces\n"
+ " \"sigoplimit\" : n, (numeric) limit of sigops in blocks\n"
" \"sizelimit\" : n, (numeric) limit of block size\n"
" \"weightlimit\" : n, (numeric) limit of block weight\n"
" \"curtime\" : ttt, (numeric) current timestamp in seconds since epoch (Jan 1 1970 GMT)\n"
- " \"bits\" : \"xxx\", (string) compressed target of next block\n"
+ " \"bits\" : \"xxxxxxxx\", (string) compressed target of next block\n"
" \"height\" : n (numeric) The height of the next block\n"
"}\n"
@@ -395,9 +400,9 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
UniValue lpval = NullUniValue;
std::set<std::string> setClientRules;
int64_t nMaxVersionPreVB = -1;
- if (params.size() > 0)
+ if (request.params.size() > 0)
{
- const UniValue& oparam = params[0].get_obj();
+ const UniValue& oparam = request.params[0].get_obj();
const UniValue& modeval = find_value(oparam, "mode");
if (modeval.isStr())
strMode = modeval.get_str();
@@ -517,12 +522,12 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
// Update block
static CBlockIndex* pindexPrev;
static int64_t nStart;
- static CBlockTemplate* pblocktemplate;
+ static std::unique_ptr<CBlockTemplate> pblocktemplate;
if (pindexPrev != chainActive.Tip() ||
(mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5))
{
// Clear pindexPrev so future calls make a new block, despite any failures from here on
- pindexPrev = NULL;
+ pindexPrev = nullptr;
// Store the pindexBest used before CreateNewBlock, to avoid races
nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
@@ -530,11 +535,6 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
nStart = GetTime();
// Create new block
- if(pblocktemplate)
- {
- delete pblocktemplate;
- pblocktemplate = NULL;
- }
CScript scriptDummy = CScript() << OP_TRUE;
pblocktemplate = BlockAssembler(Params()).CreateNewBlock(scriptDummy);
if (!pblocktemplate)
@@ -558,7 +558,8 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
UniValue transactions(UniValue::VARR);
map<uint256, int64_t> setTxIndex;
int i = 0;
- BOOST_FOREACH (CTransaction& tx, pblock->vtx) {
+ for (const auto& it : pblock->vtx) {
+ const CTransaction& tx = *it;
uint256 txHash = tx.GetHash();
setTxIndex[txHash] = i++;
@@ -607,8 +608,8 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
UniValue aRules(UniValue::VARR);
UniValue vbavailable(UniValue::VOBJ);
- for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++i) {
- Consensus::DeploymentPos pos = Consensus::DeploymentPos(i);
+ for (int j = 0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) {
+ Consensus::DeploymentPos pos = Consensus::DeploymentPos(j);
ThresholdState state = VersionBitsState(pindexPrev, consensusParams, pos, versionbitscache);
switch (state) {
case THRESHOLD_DEFINED:
@@ -663,7 +664,7 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
result.push_back(Pair("transactions", transactions));
result.push_back(Pair("coinbaseaux", aux));
- result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
+ result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0]->vout[0].nValue));
result.push_back(Pair("longpollid", chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast)));
result.push_back(Pair("target", hashTarget.GetHex()));
result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1));
@@ -680,7 +681,9 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
result.push_back(Pair("curtime", pblock->GetBlockTime()));
result.push_back(Pair("bits", strprintf("%08x", pblock->nBits)));
result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight+1)));
- if (!pblocktemplate->vchCoinbaseCommitment.empty()) {
+
+ const struct BIP9DeploymentInfo& segwit_info = VersionBitsDeploymentInfo[Consensus::DEPLOYMENT_SEGWIT];
+ if (!pblocktemplate->vchCoinbaseCommitment.empty() && setClientRules.find(segwit_info.name) != setClientRules.end()) {
result.push_back(Pair("default_witness_commitment", HexStr(pblocktemplate->vchCoinbaseCommitment.begin(), pblocktemplate->vchCoinbaseCommitment.end())));
}
@@ -705,9 +708,9 @@ protected:
};
};
-UniValue submitblock(const UniValue& params, bool fHelp)
+UniValue submitblock(const JSONRPCRequest& request)
{
- if (fHelp || params.size() < 1 || params.size() > 2)
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw runtime_error(
"submitblock \"hexdata\" ( \"jsonparametersobject\" )\n"
"\nAttempts to submit new block to network.\n"
@@ -726,8 +729,9 @@ UniValue submitblock(const UniValue& params, bool fHelp)
+ HelpExampleRpc("submitblock", "\"mydata\"")
);
- CBlock block;
- if (!DecodeHexBlk(block, params[0].get_str()))
+ std::shared_ptr<CBlock> blockptr = std::make_shared<CBlock>();
+ CBlock& block = *blockptr;
+ if (!DecodeHexBlk(block, request.params[0].get_str()))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
uint256 hash = block.GetHash();
@@ -754,10 +758,9 @@ UniValue submitblock(const UniValue& params, bool fHelp)
}
}
- CValidationState state;
submitblock_StateCatcher sc(block.GetHash());
RegisterValidationInterface(&sc);
- bool fAccepted = ProcessNewBlock(state, Params(), NULL, &block, true, NULL, g_connman.get());
+ bool fAccepted = ProcessNewBlock(Params(), blockptr, true, NULL);
UnregisterValidationInterface(&sc);
if (fBlockPresent)
{
@@ -765,18 +768,14 @@ UniValue submitblock(const UniValue& params, bool fHelp)
return "duplicate-inconclusive";
return "duplicate";
}
- if (fAccepted)
- {
- if (!sc.found)
- return "inconclusive";
- state = sc.state;
- }
- return BIP22ValidationResult(state);
+ if (!sc.found)
+ return "inconclusive";
+ return BIP22ValidationResult(sc.state);
}
-UniValue estimatefee(const UniValue& params, bool fHelp)
+UniValue estimatefee(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 1)
+ if (request.fHelp || request.params.size() != 1)
throw runtime_error(
"estimatefee nblocks\n"
"\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n"
@@ -788,13 +787,15 @@ UniValue estimatefee(const UniValue& params, bool fHelp)
"\n"
"A negative value is returned if not enough transactions and blocks\n"
"have been observed to make an estimate.\n"
+ "-1 is always returned for nblocks == 1 as it is impossible to calculate\n"
+ "a fee that is high enough to get reliably included in the next block.\n"
"\nExample:\n"
+ HelpExampleCli("estimatefee", "6")
);
- RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM));
+ RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VNUM));
- int nBlocks = params[0].get_int();
+ int nBlocks = request.params[0].get_int();
if (nBlocks < 1)
nBlocks = 1;
@@ -805,12 +806,12 @@ UniValue estimatefee(const UniValue& params, bool fHelp)
return ValueFromAmount(feeRate.GetFeePerK());
}
-UniValue estimatepriority(const UniValue& params, bool fHelp)
+UniValue estimatepriority(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 1)
+ if (request.fHelp || request.params.size() != 1)
throw runtime_error(
"estimatepriority nblocks\n"
- "\nEstimates the approximate priority a zero-fee transaction needs to begin\n"
+ "\nDEPRECATED. Estimates the approximate priority a zero-fee transaction needs to begin\n"
"confirmation within nblocks blocks.\n"
"\nArguments:\n"
"1. nblocks (numeric)\n"
@@ -823,18 +824,18 @@ UniValue estimatepriority(const UniValue& params, bool fHelp)
+ HelpExampleCli("estimatepriority", "6")
);
- RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM));
+ RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VNUM));
- int nBlocks = params[0].get_int();
+ int nBlocks = request.params[0].get_int();
if (nBlocks < 1)
nBlocks = 1;
return mempool.estimatePriority(nBlocks);
}
-UniValue estimatesmartfee(const UniValue& params, bool fHelp)
+UniValue estimatesmartfee(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 1)
+ if (request.fHelp || request.params.size() != 1)
throw runtime_error(
"estimatesmartfee nblocks\n"
"\nWARNING: This interface is unstable and may disappear or change!\n"
@@ -856,9 +857,9 @@ UniValue estimatesmartfee(const UniValue& params, bool fHelp)
+ HelpExampleCli("estimatesmartfee", "6")
);
- RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM));
+ RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VNUM));
- int nBlocks = params[0].get_int();
+ int nBlocks = request.params[0].get_int();
UniValue result(UniValue::VOBJ);
int answerFound;
@@ -868,12 +869,12 @@ UniValue estimatesmartfee(const UniValue& params, bool fHelp)
return result;
}
-UniValue estimatesmartpriority(const UniValue& params, bool fHelp)
+UniValue estimatesmartpriority(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 1)
+ if (request.fHelp || request.params.size() != 1)
throw runtime_error(
"estimatesmartpriority nblocks\n"
- "\nWARNING: This interface is unstable and may disappear or change!\n"
+ "\nDEPRECATED. WARNING: This interface is unstable and may disappear or change!\n"
"\nEstimates the approximate priority a zero-fee transaction needs to begin\n"
"confirmation within nblocks blocks if possible and return the number of blocks\n"
"for which the estimate is valid.\n"
@@ -892,9 +893,9 @@ UniValue estimatesmartpriority(const UniValue& params, bool fHelp)
+ HelpExampleCli("estimatesmartpriority", "6")
);
- RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM));
+ RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VNUM));
- int nBlocks = params[0].get_int();
+ int nBlocks = request.params[0].get_int();
UniValue result(UniValue::VOBJ);
int answerFound;
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 5afcf6353c..2aaee7f3f5 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -6,7 +6,7 @@
#include "base58.h"
#include "clientversion.h"
#include "init.h"
-#include "main.h"
+#include "validation.h"
#include "net.h"
#include "netbase.h"
#include "rpc/server.h"
@@ -39,12 +39,12 @@ using namespace std;
*
* Or alternatively, create a specific query method for the information.
**/
-UniValue getinfo(const UniValue& params, bool fHelp)
+UniValue getinfo(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 0)
+ if (request.fHelp || request.params.size() != 0)
throw runtime_error(
"getinfo\n"
- "Returns an object containing various state info.\n"
+ "\nDEPRECATED. Returns an object containing various state info.\n"
"\nResult:\n"
"{\n"
" \"version\": xxxxx, (numeric) the server version\n"
@@ -57,7 +57,7 @@ UniValue getinfo(const UniValue& params, bool fHelp)
" \"proxy\": \"host:port\", (string, optional) the proxy used by the server\n"
" \"difficulty\": xxxxxx, (numeric) the current difficulty\n"
" \"testnet\": true|false, (boolean) if the server is using testnet or not\n"
- " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n"
+ " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since Unix epoch) of the oldest pre-generated key in the key pool\n"
" \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n"
" \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n"
" \"paytxfee\": x.xxxx, (numeric) the transaction fee set in " + CURRENCY_UNIT + "/kB\n"
@@ -93,7 +93,7 @@ UniValue getinfo(const UniValue& params, bool fHelp)
obj.push_back(Pair("connections", (int)g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL)));
obj.push_back(Pair("proxy", (proxy.IsValid() ? proxy.proxy.ToStringIPPort() : string())));
obj.push_back(Pair("difficulty", (double)GetDifficulty()));
- obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC()));
+ obj.push_back(Pair("testnet", Params().NetworkIDString() == CBaseChainParams::TESTNET));
#ifdef ENABLE_WALLET
if (pwalletMain) {
obj.push_back(Pair("keypoololdest", pwalletMain->GetOldestKeyPoolTime()));
@@ -148,9 +148,9 @@ public:
};
#endif
-UniValue validateaddress(const UniValue& params, bool fHelp)
+UniValue validateaddress(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 1)
+ if (request.fHelp || request.params.size() != 1)
throw runtime_error(
"validateaddress \"bitcoinaddress\"\n"
"\nReturn information about the given bitcoin address.\n"
@@ -181,7 +181,7 @@ UniValue validateaddress(const UniValue& params, bool fHelp)
LOCK(cs_main);
#endif
- CBitcoinAddress address(params[0].get_str());
+ CBitcoinAddress address(request.params[0].get_str());
bool isValid = address.IsValid();
UniValue ret(UniValue::VOBJ);
@@ -278,9 +278,9 @@ CScript _createmultisig_redeemScript(const UniValue& params)
return result;
}
-UniValue createmultisig(const UniValue& params, bool fHelp)
+UniValue createmultisig(const JSONRPCRequest& request)
{
- if (fHelp || params.size() < 2 || params.size() > 2)
+ if (request.fHelp || request.params.size() < 2 || request.params.size() > 2)
{
string msg = "createmultisig nrequired [\"key\",...]\n"
"\nCreates a multi-signature address with n signature of m keys required.\n"
@@ -310,7 +310,7 @@ UniValue createmultisig(const UniValue& params, bool fHelp)
}
// Construct using pay-to-script-hash:
- CScript inner = _createmultisig_redeemScript(params);
+ CScript inner = _createmultisig_redeemScript(request.params);
CScriptID innerID(inner);
CBitcoinAddress address(innerID);
@@ -321,9 +321,9 @@ UniValue createmultisig(const UniValue& params, bool fHelp)
return result;
}
-UniValue verifymessage(const UniValue& params, bool fHelp)
+UniValue verifymessage(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 3)
+ if (request.fHelp || request.params.size() != 3)
throw runtime_error(
"verifymessage \"bitcoinaddress\" \"signature\" \"message\"\n"
"\nVerify a signed message\n"
@@ -346,9 +346,9 @@ UniValue verifymessage(const UniValue& params, bool fHelp)
LOCK(cs_main);
- string strAddress = params[0].get_str();
- string strSign = params[1].get_str();
- string strMessage = params[2].get_str();
+ string strAddress = request.params[0].get_str();
+ string strSign = request.params[1].get_str();
+ string strMessage = request.params[2].get_str();
CBitcoinAddress addr(strAddress);
if (!addr.IsValid())
@@ -375,9 +375,9 @@ UniValue verifymessage(const UniValue& params, bool fHelp)
return (pubkey.GetID() == keyID);
}
-UniValue signmessagewithprivkey(const UniValue& params, bool fHelp)
+UniValue signmessagewithprivkey(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 2)
+ if (request.fHelp || request.params.size() != 2)
throw runtime_error(
"signmessagewithprivkey \"privkey\" \"message\"\n"
"\nSign a message with the private key of an address\n"
@@ -395,8 +395,8 @@ UniValue signmessagewithprivkey(const UniValue& params, bool fHelp)
+ HelpExampleRpc("signmessagewithprivkey", "\"privkey\", \"my message\"")
);
- string strPrivkey = params[0].get_str();
- string strMessage = params[1].get_str();
+ string strPrivkey = request.params[0].get_str();
+ string strMessage = request.params[1].get_str();
CBitcoinSecret vchSecret;
bool fGood = vchSecret.SetString(strPrivkey);
@@ -417,9 +417,9 @@ UniValue signmessagewithprivkey(const UniValue& params, bool fHelp)
return EncodeBase64(&vchSig[0], vchSig.size());
}
-UniValue setmocktime(const UniValue& params, bool fHelp)
+UniValue setmocktime(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 1)
+ if (request.fHelp || request.params.size() != 1)
throw runtime_error(
"setmocktime timestamp\n"
"\nSet the local time to given timestamp (-regtest only)\n"
@@ -437,8 +437,8 @@ UniValue setmocktime(const UniValue& params, bool fHelp)
// in a long time.
LOCK(cs_main);
- RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM));
- SetMockTime(params[0].get_int64());
+ RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VNUM));
+ SetMockTime(request.params[0].get_int64());
uint64_t t = GetTime();
if(g_connman) {
@@ -450,10 +450,53 @@ UniValue setmocktime(const UniValue& params, bool fHelp)
return NullUniValue;
}
+static UniValue RPCLockedMemoryInfo()
+{
+ LockedPool::Stats stats = LockedPoolManager::Instance().stats();
+ UniValue obj(UniValue::VOBJ);
+ obj.push_back(Pair("used", uint64_t(stats.used)));
+ obj.push_back(Pair("free", uint64_t(stats.free)));
+ obj.push_back(Pair("total", uint64_t(stats.total)));
+ obj.push_back(Pair("locked", uint64_t(stats.locked)));
+ obj.push_back(Pair("chunks_used", uint64_t(stats.chunks_used)));
+ obj.push_back(Pair("chunks_free", uint64_t(stats.chunks_free)));
+ return obj;
+}
+
+UniValue getmemoryinfo(const JSONRPCRequest& request)
+{
+ /* Please, avoid using the word "pool" here in the RPC interface or help,
+ * as users will undoubtedly confuse it with the other "memory pool"
+ */
+ if (request.fHelp || request.params.size() != 0)
+ throw runtime_error(
+ "getmemoryinfo\n"
+ "Returns an object containing information about memory usage.\n"
+ "\nResult:\n"
+ "{\n"
+ " \"locked\": { (json object) Information about locked memory manager\n"
+ " \"used\": xxxxx, (numeric) Number of bytes used\n"
+ " \"free\": xxxxx, (numeric) Number of bytes available in current arenas\n"
+ " \"total\": xxxxxxx, (numeric) Total number of bytes managed\n"
+ " \"locked\": xxxxxx, (numeric) Amount of bytes that succeeded locking. If this number is smaller than total, locking pages failed at some point and key data could be swapped to disk.\n"
+ " \"chunks_used\": xxxxx, (numeric) Number allocated chunks\n"
+ " \"chunks_free\": xxxxx, (numeric) Number unused chunks\n"
+ " }\n"
+ "}\n"
+ "\nExamples:\n"
+ + HelpExampleCli("getmemoryinfo", "")
+ + HelpExampleRpc("getmemoryinfo", "")
+ );
+ UniValue obj(UniValue::VOBJ);
+ obj.push_back(Pair("locked", RPCLockedMemoryInfo()));
+ return obj;
+}
+
static const CRPCCommand commands[] =
{ // category name actor (function) okSafeMode
// --------------------- ------------------------ ----------------------- ----------
{ "control", "getinfo", &getinfo, true }, /* uses wallet if enabled */
+ { "control", "getmemoryinfo", &getmemoryinfo, true },
{ "util", "validateaddress", &validateaddress, true }, /* uses wallet if enabled */
{ "util", "createmultisig", &createmultisig, true },
{ "util", "verifymessage", &verifymessage, true },
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index b011029f51..53c0f993dc 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -6,8 +6,9 @@
#include "chainparams.h"
#include "clientversion.h"
-#include "main.h"
+#include "validation.h"
#include "net.h"
+#include "net_processing.h"
#include "netbase.h"
#include "protocol.h"
#include "sync.h"
@@ -23,9 +24,9 @@
using namespace std;
-UniValue getconnectioncount(const UniValue& params, bool fHelp)
+UniValue getconnectioncount(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 0)
+ if (request.fHelp || request.params.size() != 0)
throw runtime_error(
"getconnectioncount\n"
"\nReturns the number of connections to other nodes.\n"
@@ -42,9 +43,9 @@ UniValue getconnectioncount(const UniValue& params, bool fHelp)
return (int)g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL);
}
-UniValue ping(const UniValue& params, bool fHelp)
+UniValue ping(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 0)
+ if (request.fHelp || request.params.size() != 0)
throw runtime_error(
"ping\n"
"\nRequests that a ping be sent to all other nodes, to measure ping time.\n"
@@ -65,9 +66,9 @@ UniValue ping(const UniValue& params, bool fHelp)
return NullUniValue;
}
-UniValue getpeerinfo(const UniValue& params, bool fHelp)
+UniValue getpeerinfo(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 0)
+ if (request.fHelp || request.params.size() != 0)
throw runtime_error(
"getpeerinfo\n"
"\nReturns data about each connected network node as a json array of objects.\n"
@@ -141,8 +142,8 @@ UniValue getpeerinfo(const UniValue& params, bool fHelp)
obj.push_back(Pair("timeoffset", stats.nTimeOffset));
if (stats.dPingTime > 0.0)
obj.push_back(Pair("pingtime", stats.dPingTime));
- if (stats.dPingMin < std::numeric_limits<int64_t>::max()/1e6)
- obj.push_back(Pair("minping", stats.dPingMin));
+ if (stats.dMinPing < std::numeric_limits<int64_t>::max()/1e6)
+ obj.push_back(Pair("minping", stats.dMinPing));
if (stats.dPingWait > 0.0)
obj.push_back(Pair("pingwait", stats.dPingWait));
obj.push_back(Pair("version", stats.nVersion));
@@ -184,12 +185,12 @@ UniValue getpeerinfo(const UniValue& params, bool fHelp)
return ret;
}
-UniValue addnode(const UniValue& params, bool fHelp)
+UniValue addnode(const JSONRPCRequest& request)
{
string strCommand;
- if (params.size() == 2)
- strCommand = params[1].get_str();
- if (fHelp || params.size() != 2 ||
+ if (request.params.size() == 2)
+ strCommand = request.params[1].get_str();
+ if (request.fHelp || request.params.size() != 2 ||
(strCommand != "onetry" && strCommand != "add" && strCommand != "remove"))
throw runtime_error(
"addnode \"node\" \"add|remove|onetry\"\n"
@@ -206,7 +207,7 @@ UniValue addnode(const UniValue& params, bool fHelp)
if(!g_connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
- string strNode = params[0].get_str();
+ string strNode = request.params[0].get_str();
if (strCommand == "onetry")
{
@@ -229,9 +230,9 @@ UniValue addnode(const UniValue& params, bool fHelp)
return NullUniValue;
}
-UniValue disconnectnode(const UniValue& params, bool fHelp)
+UniValue disconnectnode(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 1)
+ if (request.fHelp || request.params.size() != 1)
throw runtime_error(
"disconnectnode \"node\" \n"
"\nImmediately disconnects from the specified node.\n"
@@ -245,16 +246,16 @@ UniValue disconnectnode(const UniValue& params, bool fHelp)
if(!g_connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
- bool ret = g_connman->DisconnectNode(params[0].get_str());
+ bool ret = g_connman->DisconnectNode(request.params[0].get_str());
if (!ret)
throw JSONRPCError(RPC_CLIENT_NODE_NOT_CONNECTED, "Node not found in connected nodes");
return NullUniValue;
}
-UniValue getaddednodeinfo(const UniValue& params, bool fHelp)
+UniValue getaddednodeinfo(const JSONRPCRequest& request)
{
- if (fHelp || params.size() > 1)
+ if (request.fHelp || request.params.size() > 1)
throw runtime_error(
"getaddednodeinfo ( \"node\" )\n"
"\nReturns information about the given added node, or all added nodes\n"
@@ -286,10 +287,10 @@ UniValue getaddednodeinfo(const UniValue& params, bool fHelp)
std::vector<AddedNodeInfo> vInfo = g_connman->GetAddedNodeInfo();
- if (params.size() == 1) {
+ if (request.params.size() == 1) {
bool found = false;
for (const AddedNodeInfo& info : vInfo) {
- if (info.strAddedNode == params[0].get_str()) {
+ if (info.strAddedNode == request.params[0].get_str()) {
vInfo.assign(1, info);
found = true;
break;
@@ -320,9 +321,9 @@ UniValue getaddednodeinfo(const UniValue& params, bool fHelp)
return ret;
}
-UniValue getnettotals(const UniValue& params, bool fHelp)
+UniValue getnettotals(const JSONRPCRequest& request)
{
- if (fHelp || params.size() > 0)
+ if (request.fHelp || request.params.size() > 0)
throw runtime_error(
"getnettotals\n"
"\nReturns information about network traffic, including bytes in, bytes out,\n"
@@ -331,7 +332,7 @@ UniValue getnettotals(const UniValue& params, bool fHelp)
"{\n"
" \"totalbytesrecv\": n, (numeric) Total bytes received\n"
" \"totalbytessent\": n, (numeric) Total bytes sent\n"
- " \"timemillis\": t, (numeric) Total cpu time\n"
+ " \"timemillis\": t, (numeric) Current UNIX time in milliseconds\n"
" \"uploadtarget\":\n"
" {\n"
" \"timeframe\": n, (numeric) Length of the measuring timeframe in seconds\n"
@@ -386,9 +387,9 @@ static UniValue GetNetworksInfo()
return networks;
}
-UniValue getnetworkinfo(const UniValue& params, bool fHelp)
+UniValue getnetworkinfo(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 0)
+ if (request.fHelp || request.params.size() != 0)
throw runtime_error(
"getnetworkinfo\n"
"Returns an object containing various state info regarding P2P networking.\n"
@@ -401,6 +402,7 @@ UniValue getnetworkinfo(const UniValue& params, bool fHelp)
" \"localrelay\": true|false, (bool) true if transaction relay is requested from peers\n"
" \"timeoffset\": xxxxx, (numeric) the time offset\n"
" \"connections\": xxxxx, (numeric) the number of connections\n"
+ " \"networkactive\": true|false, (bool) whether p2p networking is enabled\n"
" \"networks\": [ (array) information per network\n"
" {\n"
" \"name\": \"xxx\", (string) network (ipv4, ipv6 or onion)\n"
@@ -435,8 +437,10 @@ UniValue getnetworkinfo(const UniValue& params, bool fHelp)
obj.push_back(Pair("localservices", strprintf("%016x", g_connman->GetLocalServices())));
obj.push_back(Pair("localrelay", fRelayTxes));
obj.push_back(Pair("timeoffset", GetTimeOffset()));
- if(g_connman)
+ if (g_connman) {
+ obj.push_back(Pair("networkactive", g_connman->GetNetworkActive()));
obj.push_back(Pair("connections", (int)g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL)));
+ }
obj.push_back(Pair("networks", GetNetworksInfo()));
obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK())));
UniValue localAddresses(UniValue::VARR);
@@ -456,12 +460,12 @@ UniValue getnetworkinfo(const UniValue& params, bool fHelp)
return obj;
}
-UniValue setban(const UniValue& params, bool fHelp)
+UniValue setban(const JSONRPCRequest& request)
{
string strCommand;
- if (params.size() >= 2)
- strCommand = params[1].get_str();
- if (fHelp || params.size() < 2 ||
+ if (request.params.size() >= 2)
+ strCommand = request.params[1].get_str();
+ if (request.fHelp || request.params.size() < 2 ||
(strCommand != "add" && strCommand != "remove"))
throw runtime_error(
"setban \"ip(/netmask)\" \"add|remove\" (bantime) (absolute)\n"
@@ -483,16 +487,16 @@ UniValue setban(const UniValue& params, bool fHelp)
CNetAddr netAddr;
bool isSubnet = false;
- if (params[0].get_str().find("/") != string::npos)
+ if (request.params[0].get_str().find("/") != string::npos)
isSubnet = true;
if (!isSubnet) {
CNetAddr resolved;
- LookupHost(params[0].get_str().c_str(), resolved, false);
+ LookupHost(request.params[0].get_str().c_str(), resolved, false);
netAddr = resolved;
}
else
- LookupSubNet(params[0].get_str().c_str(), subNet);
+ LookupSubNet(request.params[0].get_str().c_str(), subNet);
if (! (isSubnet ? subNet.IsValid() : netAddr.IsValid()) )
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Invalid IP/Subnet");
@@ -503,11 +507,11 @@ UniValue setban(const UniValue& params, bool fHelp)
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: IP/Subnet already banned");
int64_t banTime = 0; //use standard bantime if not specified
- if (params.size() >= 3 && !params[2].isNull())
- banTime = params[2].get_int64();
+ if (request.params.size() >= 3 && !request.params[2].isNull())
+ banTime = request.params[2].get_int64();
bool absolute = false;
- if (params.size() == 4 && params[3].isTrue())
+ if (request.params.size() == 4 && request.params[3].isTrue())
absolute = true;
isSubnet ? g_connman->Ban(subNet, BanReasonManuallyAdded, banTime, absolute) : g_connman->Ban(netAddr, BanReasonManuallyAdded, banTime, absolute);
@@ -520,9 +524,9 @@ UniValue setban(const UniValue& params, bool fHelp)
return NullUniValue;
}
-UniValue listbanned(const UniValue& params, bool fHelp)
+UniValue listbanned(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 0)
+ if (request.fHelp || request.params.size() != 0)
throw runtime_error(
"listbanned\n"
"\nList all banned IPs/Subnets.\n"
@@ -553,9 +557,9 @@ UniValue listbanned(const UniValue& params, bool fHelp)
return bannedAddresses;
}
-UniValue clearbanned(const UniValue& params, bool fHelp)
+UniValue clearbanned(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 0)
+ if (request.fHelp || request.params.size() != 0)
throw runtime_error(
"clearbanned\n"
"\nClear all banned IPs.\n"
@@ -571,6 +575,24 @@ UniValue clearbanned(const UniValue& params, bool fHelp)
return NullUniValue;
}
+UniValue setnetworkactive(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1) {
+ throw runtime_error(
+ "setnetworkactive true|false\n"
+ "Disable/enable all p2p network activity."
+ );
+ }
+
+ if (!g_connman) {
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+ }
+
+ g_connman->SetNetworkActive(request.params[0].get_bool());
+
+ return g_connman->GetNetworkActive();
+}
+
static const CRPCCommand commands[] =
{ // category name actor (function) okSafeMode
// --------------------- ------------------------ ----------------------- ----------
@@ -585,6 +607,7 @@ static const CRPCCommand commands[] =
{ "network", "setban", &setban, true },
{ "network", "listbanned", &listbanned, true },
{ "network", "clearbanned", &clearbanned, true },
+ { "network", "setnetworkactive", &setnetworkactive, true, },
};
void RegisterNetRPCCommands(CRPCTable &t)
diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp
index f5275062a2..ec186f4fc5 100644
--- a/src/rpc/protocol.cpp
+++ b/src/rpc/protocol.cpp
@@ -26,13 +26,13 @@ using namespace std;
* 1.2 spec: http://jsonrpc.org/historical/json-rpc-over-http.html
*/
-string JSONRPCRequest(const string& strMethod, const UniValue& params, const UniValue& id)
+UniValue JSONRPCRequestObj(const string& strMethod, const UniValue& params, const UniValue& id)
{
UniValue request(UniValue::VOBJ);
request.push_back(Pair("method", strMethod));
request.push_back(Pair("params", params));
request.push_back(Pair("id", id));
- return request.write() + "\n";
+ return request;
}
UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const UniValue& id)
@@ -77,9 +77,10 @@ boost::filesystem::path GetAuthCookieFile()
bool GenerateAuthCookie(std::string *cookie_out)
{
- unsigned char rand_pwd[32];
- GetRandBytes(rand_pwd, 32);
- std::string cookie = COOKIEAUTH_USER + ":" + EncodeBase64(&rand_pwd[0],32);
+ const size_t COOKIE_SIZE = 32;
+ unsigned char rand_pwd[COOKIE_SIZE];
+ GetRandBytes(rand_pwd, COOKIE_SIZE);
+ std::string cookie = COOKIEAUTH_USER + ":" + HexStr(rand_pwd, rand_pwd+COOKIE_SIZE);
/** the umask determines what permissions are used to create this file -
* these are set to 077 in init.cpp unless overridden with -sysperms.
diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h
index 1d2ef0e41e..c74fa0070f 100644
--- a/src/rpc/protocol.h
+++ b/src/rpc/protocol.h
@@ -77,7 +77,7 @@ enum RPCErrorCode
RPC_WALLET_ALREADY_UNLOCKED = -17, //!< Wallet is already unlocked
};
-std::string JSONRPCRequest(const std::string& strMethod, const UniValue& params, const UniValue& id);
+UniValue JSONRPCRequestObj(const std::string& strMethod, const UniValue& params, const UniValue& id);
UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const UniValue& id);
std::string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id);
UniValue JSONRPCError(int code, const std::string& message);
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index d2ad0a52b7..1229a00c5f 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -10,7 +10,7 @@
#include "core_io.h"
#include "init.h"
#include "keystore.h"
-#include "main.h"
+#include "validation.h"
#include "merkleblock.h"
#include "net.h"
#include "policy/policy.h"
@@ -126,26 +126,26 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
}
}
-UniValue getrawtransaction(const UniValue& params, bool fHelp)
+UniValue getrawtransaction(const JSONRPCRequest& request)
{
- if (fHelp || params.size() < 1 || params.size() > 2)
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw runtime_error(
"getrawtransaction \"txid\" ( verbose )\n"
"\nNOTE: By default this function only works sometimes. This is when the tx is in the mempool\n"
"or there is an unspent output in the utxo for this transaction. To make it always work,\n"
"you need to maintain a transaction index, using the -txindex command line option.\n"
"\nReturn the raw transaction data.\n"
- "\nIf verbose=0, returns a string that is serialized, hex-encoded data for 'txid'.\n"
- "If verbose is non-zero, returns an Object with information about 'txid'.\n"
+ "\nIf verbose is 'true', returns an Object with information about 'txid'.\n"
+ "If verbose is 'false' or omitted, returns a string that is serialized, hex-encoded data for 'txid'.\n"
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id\n"
- "2. verbose (numeric, optional, default=0) If 0, return a string, other return a json object\n"
+ "2. verbose (bool, optional, default=false) If true, return a string, other return a json object\n"
- "\nResult (if verbose is not set or set to 0):\n"
+ "\nResult (if verbose is not set or set to false):\n"
"\"data\" (string) The serialized, hex-encoded data for 'txid'\n"
- "\nResult (if verbose > 0):\n"
+ "\nResult (if verbose is set to true):\n"
"{\n"
" \"hex\" : \"data\", (string) The serialized, hex-encoded data for 'txid'\n"
" \"txid\" : \"id\", (string) The transaction id (same as provided)\n"
@@ -192,37 +192,51 @@ UniValue getrawtransaction(const UniValue& params, bool fHelp)
"\nExamples:\n"
+ HelpExampleCli("getrawtransaction", "\"mytxid\"")
- + HelpExampleCli("getrawtransaction", "\"mytxid\" 1")
- + HelpExampleRpc("getrawtransaction", "\"mytxid\", 1")
+ + HelpExampleCli("getrawtransaction", "\"mytxid\" true")
+ + HelpExampleRpc("getrawtransaction", "\"mytxid\", true")
);
LOCK(cs_main);
- uint256 hash = ParseHashV(params[0], "parameter 1");
+ uint256 hash = ParseHashV(request.params[0], "parameter 1");
+ // Accept either a bool (true) or a num (>=1) to indicate verbose output.
bool fVerbose = false;
- if (params.size() > 1)
- fVerbose = (params[1].get_int() != 0);
+ if (request.params.size() > 1) {
+ if (request.params[1].isNum()) {
+ if (request.params[1].get_int() != 0) {
+ fVerbose = true;
+ }
+ }
+ else if(request.params[1].isBool()) {
+ if(request.params[1].isTrue()) {
+ fVerbose = true;
+ }
+ }
+ else {
+ throw JSONRPCError(RPC_TYPE_ERROR, "Invalid type provided. Verbose parameter must be a boolean.");
+ }
+ }
- CTransaction tx;
+ CTransactionRef tx;
uint256 hashBlock;
if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true))
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
- string strHex = EncodeHexTx(tx);
+ string strHex = EncodeHexTx(*tx, RPCSerializationFlags());
if (!fVerbose)
return strHex;
UniValue result(UniValue::VOBJ);
result.push_back(Pair("hex", strHex));
- TxToJSON(tx, hashBlock, result);
+ TxToJSON(*tx, hashBlock, result);
return result;
}
-UniValue gettxoutproof(const UniValue& params, bool fHelp)
+UniValue gettxoutproof(const JSONRPCRequest& request)
{
- if (fHelp || (params.size() != 1 && params.size() != 2))
+ if (request.fHelp || (request.params.size() != 1 && request.params.size() != 2))
throw runtime_error(
"gettxoutproof [\"txid\",...] ( blockhash )\n"
"\nReturns a hex-encoded proof that \"txid\" was included in a block.\n"
@@ -244,7 +258,7 @@ UniValue gettxoutproof(const UniValue& params, bool fHelp)
set<uint256> setTxids;
uint256 oneTxid;
- UniValue txids = params[0].get_array();
+ UniValue txids = request.params[0].get_array();
for (unsigned int idx = 0; idx < txids.size(); idx++) {
const UniValue& txid = txids[idx];
if (txid.get_str().length() != 64 || !IsHex(txid.get_str()))
@@ -261,9 +275,9 @@ UniValue gettxoutproof(const UniValue& params, bool fHelp)
CBlockIndex* pblockindex = NULL;
uint256 hashBlock;
- if (params.size() > 1)
+ if (request.params.size() > 1)
{
- hashBlock = uint256S(params[1].get_str());
+ hashBlock = uint256S(request.params[1].get_str());
if (!mapBlockIndex.count(hashBlock))
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
pblockindex = mapBlockIndex[hashBlock];
@@ -275,7 +289,7 @@ UniValue gettxoutproof(const UniValue& params, bool fHelp)
if (pblockindex == NULL)
{
- CTransaction tx;
+ CTransactionRef tx;
if (!GetTransaction(oneTxid, tx, Params().GetConsensus(), hashBlock, false) || hashBlock.IsNull())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not yet in block");
if (!mapBlockIndex.count(hashBlock))
@@ -288,8 +302,8 @@ UniValue gettxoutproof(const UniValue& params, bool fHelp)
throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
unsigned int ntxFound = 0;
- BOOST_FOREACH(const CTransaction&tx, block.vtx)
- if (setTxids.count(tx.GetHash()))
+ for (const auto& tx : block.vtx)
+ if (setTxids.count(tx->GetHash()))
ntxFound++;
if (ntxFound != setTxids.size())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "(Not all) transactions not found in specified block");
@@ -301,9 +315,9 @@ UniValue gettxoutproof(const UniValue& params, bool fHelp)
return strHex;
}
-UniValue verifytxoutproof(const UniValue& params, bool fHelp)
+UniValue verifytxoutproof(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 1)
+ if (request.fHelp || request.params.size() != 1)
throw runtime_error(
"verifytxoutproof \"proof\"\n"
"\nVerifies that a proof points to a transaction in a block, returning the transaction it commits to\n"
@@ -314,7 +328,7 @@ UniValue verifytxoutproof(const UniValue& params, bool fHelp)
"[\"txid\"] (array, strings) The txid(s) which the proof commits to, or empty array if the proof is invalid\n"
);
- CDataStream ssMB(ParseHexV(params[0], "proof"), SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
+ CDataStream ssMB(ParseHexV(request.params[0], "proof"), SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
CMerkleBlock merkleBlock;
ssMB >> merkleBlock;
@@ -335,9 +349,9 @@ UniValue verifytxoutproof(const UniValue& params, bool fHelp)
return res;
}
-UniValue createrawtransaction(const UniValue& params, bool fHelp)
+UniValue createrawtransaction(const JSONRPCRequest& request)
{
- if (fHelp || params.size() < 2 || params.size() > 3)
+ if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
throw runtime_error(
"createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,\"data\":\"hex\",...} ( locktime )\n"
"\nCreate a transaction spending the given inputs and creating new outputs.\n"
@@ -373,17 +387,17 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp)
+ HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"data\\\":\\\"00010203\\\"}\"")
);
- RPCTypeCheck(params, boost::assign::list_of(UniValue::VARR)(UniValue::VOBJ)(UniValue::VNUM), true);
- if (params[0].isNull() || params[1].isNull())
+ RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VARR)(UniValue::VOBJ)(UniValue::VNUM), true);
+ if (request.params[0].isNull() || request.params[1].isNull())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null");
- UniValue inputs = params[0].get_array();
- UniValue sendTo = params[1].get_obj();
+ UniValue inputs = request.params[0].get_array();
+ UniValue sendTo = request.params[1].get_obj();
CMutableTransaction rawTx;
- if (params.size() > 2 && !params[2].isNull()) {
- int64_t nLockTime = params[2].get_int64();
+ if (request.params.size() > 2 && !request.params[2].isNull()) {
+ int64_t nLockTime = request.params[2].get_int64();
if (nLockTime < 0 || nLockTime > std::numeric_limits<uint32_t>::max())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range");
rawTx.nLockTime = nLockTime;
@@ -448,9 +462,9 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp)
return EncodeHexTx(rawTx);
}
-UniValue decoderawtransaction(const UniValue& params, bool fHelp)
+UniValue decoderawtransaction(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 1)
+ if (request.fHelp || request.params.size() != 1)
throw runtime_error(
"decoderawtransaction \"hexstring\"\n"
"\nReturn a JSON object representing the serialized, hex-encoded transaction.\n"
@@ -504,22 +518,22 @@ UniValue decoderawtransaction(const UniValue& params, bool fHelp)
);
LOCK(cs_main);
- RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR));
+ RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR));
- CTransaction tx;
+ CMutableTransaction mtx;
- if (!DecodeHexTx(tx, params[0].get_str(), true))
+ if (!DecodeHexTx(mtx, request.params[0].get_str(), true))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
UniValue result(UniValue::VOBJ);
- TxToJSON(tx, uint256(), result);
+ TxToJSON(CTransaction(std::move(mtx)), uint256(), result);
return result;
}
-UniValue decodescript(const UniValue& params, bool fHelp)
+UniValue decodescript(const JSONRPCRequest& request)
{
- if (fHelp || params.size() != 1)
+ if (request.fHelp || request.params.size() != 1)
throw runtime_error(
"decodescript \"hex\"\n"
"\nDecode a hex-encoded script.\n"
@@ -535,26 +549,34 @@ UniValue decodescript(const UniValue& params, bool fHelp)
" \"address\" (string) bitcoin address\n"
" ,...\n"
" ],\n"
- " \"p2sh\",\"address\" (string) script address\n"
+ " \"p2sh\",\"address\" (string) address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH).\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("decodescript", "\"hexstring\"")
+ HelpExampleRpc("decodescript", "\"hexstring\"")
);
- RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR));
+ RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR));
UniValue r(UniValue::VOBJ);
CScript script;
- if (params[0].get_str().size() > 0){
- vector<unsigned char> scriptData(ParseHexV(params[0], "argument"));
+ if (request.params[0].get_str().size() > 0){
+ vector<unsigned char> scriptData(ParseHexV(request.params[0], "argument"));
script = CScript(scriptData.begin(), scriptData.end());
} else {
// Empty scripts are valid
}
ScriptPubKeyToJSON(script, r, false);
- r.push_back(Pair("p2sh", CBitcoinAddress(CScriptID(script)).ToString()));
+ UniValue type;
+ type = find_value(r, "type");
+
+ if (type.isStr() && type.get_str() != "scripthash") {
+ // P2SH cannot be wrapped in a P2SH. If this script is already a P2SH,
+ // don't return the address for a P2SH of the P2SH.
+ r.push_back(Pair("p2sh", CBitcoinAddress(CScriptID(script)).ToString()));
+ }
+
return r;
}
@@ -570,9 +592,9 @@ static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::
vErrorsRet.push_back(entry);
}
-UniValue signrawtransaction(const UniValue& params, bool fHelp)
+UniValue signrawtransaction(const JSONRPCRequest& request)
{
- if (fHelp || params.size() < 1 || params.size() > 4)
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 4)
throw runtime_error(
"signrawtransaction \"hexstring\" ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] [\"privatekey1\",...] sighashtype )\n"
"\nSign inputs for raw transaction (serialized, hex-encoded).\n"
@@ -636,9 +658,9 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp)
#else
LOCK(cs_main);
#endif
- RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VARR)(UniValue::VARR)(UniValue::VSTR), true);
+ RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VARR)(UniValue::VARR)(UniValue::VSTR), true);
- vector<unsigned char> txData(ParseHexV(params[0], "argument 1"));
+ vector<unsigned char> txData(ParseHexV(request.params[0], "argument 1"));
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
vector<CMutableTransaction> txVariants;
while (!ssData.empty()) {
@@ -679,9 +701,9 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp)
bool fGivenKeys = false;
CBasicKeyStore tempKeystore;
- if (params.size() > 2 && !params[2].isNull()) {
+ if (request.params.size() > 2 && !request.params[2].isNull()) {
fGivenKeys = true;
- UniValue keys = params[2].get_array();
+ UniValue keys = request.params[2].get_array();
for (unsigned int idx = 0; idx < keys.size(); idx++) {
UniValue k = keys[idx];
CBitcoinSecret vchSecret;
@@ -700,8 +722,8 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp)
#endif
// Add previous txouts given in the RPC call:
- if (params.size() > 1 && !params[1].isNull()) {
- UniValue prevTxs = params[1].get_array();
+ if (request.params.size() > 1 && !request.params[1].isNull()) {
+ UniValue prevTxs = request.params[1].get_array();
for (unsigned int idx = 0; idx < prevTxs.size(); idx++) {
const UniValue& p = prevTxs[idx];
if (!p.isObject())
@@ -769,7 +791,7 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp)
#endif
int nHashType = SIGHASH_ALL;
- if (params.size() > 3 && !params[3].isNull()) {
+ if (request.params.size() > 3 && !request.params[3].isNull()) {
static map<string, int> mapSigHashValues =
boost::assign::map_list_of
(string("ALL"), int(SIGHASH_ALL))
@@ -779,7 +801,7 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp)
(string("SINGLE"), int(SIGHASH_SINGLE))
(string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY))
;
- string strHashType = params[3].get_str();
+ string strHashType = request.params[3].get_str();
if (mapSigHashValues.count(strHashType))
nHashType = mapSigHashValues[strHashType];
else
@@ -834,9 +856,9 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp)
return result;
}
-UniValue sendrawtransaction(const UniValue& params, bool fHelp)
+UniValue sendrawtransaction(const JSONRPCRequest& request)
{
- if (fHelp || params.size() < 1 || params.size() > 2)
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw runtime_error(
"sendrawtransaction \"hexstring\" ( allowhighfees )\n"
"\nSubmits raw transaction (serialized, hex-encoded) to local node and network.\n"
@@ -858,16 +880,18 @@ UniValue sendrawtransaction(const UniValue& params, bool fHelp)
);
LOCK(cs_main);
- RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VBOOL));
+ RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VBOOL));
// parse hex string from parameter
- CTransaction tx;
- if (!DecodeHexTx(tx, params[0].get_str()))
+ CMutableTransaction mtx;
+ if (!DecodeHexTx(mtx, request.params[0].get_str()))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
+ CTransaction tx(std::move(mtx));
uint256 hashTx = tx.GetHash();
+ bool fLimitFree = false;
CAmount nMaxRawTxFee = maxTxFee;
- if (params.size() > 1 && params[1].get_bool())
+ if (request.params.size() > 1 && request.params[1].get_bool())
nMaxRawTxFee = 0;
CCoinsViewCache &view = *pcoinsTip;
@@ -878,7 +902,7 @@ UniValue sendrawtransaction(const UniValue& params, bool fHelp)
// push to local node and sync with wallets
CValidationState state;
bool fMissingInputs;
- if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, false, nMaxRawTxFee)) {
+ if (!AcceptToMemoryPool(mempool, state, tx, fLimitFree, &fMissingInputs, false, nMaxRawTxFee)) {
if (state.IsInvalid()) {
throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason()));
} else {
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index 5fb97f7496..2122819398 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -147,6 +147,8 @@ uint256 ParseHashV(const UniValue& v, string strName)
strHex = v.get_str();
if (!IsHex(strHex)) // Note: IsHex("") is false
throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
+ if (64 != strHex.length())
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be of length %d (not %d)", strName, 64, strHex.length()));
uint256 result;
result.SetHex(strHex);
return result;
@@ -195,10 +197,11 @@ std::string CRPCTable::help(const std::string& strCommand) const
continue;
try
{
- UniValue params;
+ JSONRPCRequest jreq;
+ jreq.fHelp = true;
rpcfn_type pfn = pcmd->actor;
if (setDone.insert(pfn).second)
- (*pfn)(params, true);
+ (*pfn)(jreq);
}
catch (const std::exception& e)
{
@@ -228,9 +231,9 @@ std::string CRPCTable::help(const std::string& strCommand) const
return strRet;
}
-UniValue help(const UniValue& params, bool fHelp)
+UniValue help(const JSONRPCRequest& jsonRequest)
{
- if (fHelp || params.size() > 1)
+ if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
throw runtime_error(
"help ( \"command\" )\n"
"\nList all commands, or get help for a specified command.\n"
@@ -241,17 +244,17 @@ UniValue help(const UniValue& params, bool fHelp)
);
string strCommand;
- if (params.size() > 0)
- strCommand = params[0].get_str();
+ if (jsonRequest.params.size() > 0)
+ strCommand = jsonRequest.params[0].get_str();
return tableRPC.help(strCommand);
}
-UniValue stop(const UniValue& params, bool fHelp)
+UniValue stop(const JSONRPCRequest& jsonRequest)
{
// Accept the deprecated and ignored 'detach' boolean argument
- if (fHelp || params.size() > 1)
+ if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
throw runtime_error(
"stop\n"
"\nStop Bitcoin server.");
@@ -354,7 +357,7 @@ bool RPCIsInWarmup(std::string *outStatus)
return fRPCInWarmup;
}
-void JSONRequest::parse(const UniValue& valRequest)
+void JSONRPCRequest::parse(const UniValue& valRequest)
{
// Parse request
if (!valRequest.isObject())
@@ -388,11 +391,11 @@ static UniValue JSONRPCExecOne(const UniValue& req)
{
UniValue rpc_result(UniValue::VOBJ);
- JSONRequest jreq;
+ JSONRPCRequest jreq;
try {
jreq.parse(req);
- UniValue result = tableRPC.execute(jreq.strMethod, jreq.params);
+ UniValue result = tableRPC.execute(jreq);
rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id);
}
catch (const UniValue& objError)
@@ -417,7 +420,7 @@ std::string JSONRPCExecBatch(const UniValue& vReq)
return ret.write() + "\n";
}
-UniValue CRPCTable::execute(const std::string &strMethod, const UniValue &params) const
+UniValue CRPCTable::execute(const JSONRPCRequest &request) const
{
// Return immediately if in warmup
{
@@ -427,7 +430,7 @@ UniValue CRPCTable::execute(const std::string &strMethod, const UniValue &params
}
// Find method
- const CRPCCommand *pcmd = tableRPC[strMethod];
+ const CRPCCommand *pcmd = tableRPC[request.strMethod];
if (!pcmd)
throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
@@ -436,7 +439,7 @@ UniValue CRPCTable::execute(const std::string &strMethod, const UniValue &params
try
{
// Execute
- return pcmd->actor(params, false);
+ return pcmd->actor(request);
}
catch (const std::exception& e)
{
@@ -494,4 +497,12 @@ void RPCRunLater(const std::string& name, boost::function<void(void)> func, int6
deadlineTimers.emplace(name, std::unique_ptr<RPCTimerBase>(timerInterface->NewTimer(func, nSeconds*1000)));
}
+int RPCSerializationFlags()
+{
+ int flag = 0;
+ if (GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) == 0)
+ flag |= SERIALIZE_TRANSACTION_NO_WITNESS;
+ return flag;
+}
+
CRPCTable tableRPC;
diff --git a/src/rpc/server.h b/src/rpc/server.h
index 4e0aa2c6d6..4fac68a51f 100644
--- a/src/rpc/server.h
+++ b/src/rpc/server.h
@@ -19,6 +19,8 @@
#include <univalue.h>
+static const unsigned int DEFAULT_RPC_SERIALIZE_VERSION = 1;
+
class CRPCCommand;
namespace RPCServer
@@ -41,14 +43,17 @@ struct UniValueType {
UniValue::VType type;
};
-class JSONRequest
+class JSONRPCRequest
{
public:
UniValue id;
std::string strMethod;
UniValue params;
+ bool fHelp;
+ std::string URI;
+ std::string authUser;
- JSONRequest() { id = NullUniValue; }
+ JSONRPCRequest() { id = NullUniValue; params = NullUniValue; fHelp = false; }
void parse(const UniValue& valRequest);
};
@@ -122,7 +127,7 @@ void RPCUnsetTimerInterface(RPCTimerInterface *iface);
*/
void RPCRunLater(const std::string& name, boost::function<void(void)> func, int64_t nSeconds);
-typedef UniValue(*rpcfn_type)(const UniValue& params, bool fHelp);
+typedef UniValue(*rpcfn_type)(const JSONRPCRequest& jsonRequest);
class CRPCCommand
{
@@ -147,12 +152,11 @@ public:
/**
* Execute a method.
- * @param method Method to execute
- * @param params UniValue Array of arguments (JSON objects)
+ * @param request The JSONRPCRequest to execute
* @returns Result of the call.
* @throws an exception (UniValue) when an error happens.
*/
- UniValue execute(const std::string &method, const UniValue &params) const;
+ UniValue execute(const JSONRPCRequest &request) const;
/**
* Returns a list of registered commands
@@ -196,4 +200,7 @@ void StopRPC();
std::string JSONRPCExecBatch(const UniValue& vReq);
void RPCNotifyBlockChange(bool ibd, const CBlockIndex *);
+// Retrieves any serialization flags requested in command line argument
+int RPCSerializationFlags();
+
#endif // BITCOIN_RPCSERVER_H
diff --git a/src/scheduler.cpp b/src/scheduler.cpp
index 52777b61f9..27c03f154c 100644
--- a/src/scheduler.cpp
+++ b/src/scheduler.cpp
@@ -54,9 +54,10 @@ void CScheduler::serviceQueue()
#else
// Some boost versions have a conflicting overload of wait_until that returns void.
// Explicitly use a template here to avoid hitting that overload.
- while (!shouldStop() && !taskQueue.empty() &&
- newTaskScheduled.wait_until<>(lock, taskQueue.begin()->first) != boost::cv_status::timeout) {
- // Keep waiting until timeout
+ while (!shouldStop() && !taskQueue.empty()) {
+ boost::chrono::system_clock::time_point timeToWaitFor = taskQueue.begin()->first;
+ if (newTaskScheduled.wait_until<>(lock, timeToWaitFor) == boost::cv_status::timeout)
+ break; // Exit loop after timeout, it means we reached the time of the event
}
#endif
// If there are multiple threads, the queue can empty while we're waiting (another
diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp
index b629f4278b..036d6ca7bf 100644
--- a/src/script/bitcoinconsensus.cpp
+++ b/src/script/bitcoinconsensus.cpp
@@ -23,7 +23,7 @@ public:
m_remaining(txToLen)
{}
- TxInputStream& read(char* pch, size_t nSize)
+ void read(char* pch, size_t nSize)
{
if (nSize > m_remaining)
throw std::ios_base::failure(std::string(__func__) + ": end of data");
@@ -37,16 +37,17 @@ public:
memcpy(pch, m_data, nSize);
m_remaining -= nSize;
m_data += nSize;
- return *this;
}
template<typename T>
TxInputStream& operator>>(T& obj)
{
- ::Unserialize(*this, obj, m_type, m_version);
+ ::Unserialize(*this, obj);
return *this;
}
+ int GetVersion() const { return m_version; }
+ int GetType() const { return m_type; }
private:
const int m_type;
const int m_version;
@@ -69,17 +70,25 @@ struct ECCryptoClosure
ECCryptoClosure instance_of_eccryptoclosure;
}
+/** Check that all specified flags are part of the libconsensus interface. */
+static bool verify_flags(unsigned int flags)
+{
+ return (flags & ~(bitcoinconsensus_SCRIPT_FLAGS_VERIFY_ALL)) == 0;
+}
+
static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, CAmount amount,
const unsigned char *txTo , unsigned int txToLen,
unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err)
{
+ if (!verify_flags(flags)) {
+ return bitcoinconsensus_ERR_INVALID_FLAGS;
+ }
try {
TxInputStream stream(SER_NETWORK, PROTOCOL_VERSION, txTo, txToLen);
- CTransaction tx;
- stream >> tx;
+ CTransaction tx(deserialize, stream);
if (nIn >= tx.vin.size())
return set_error(err, bitcoinconsensus_ERR_TX_INDEX);
- if (tx.GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION) != txToLen)
+ if (GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) != txToLen)
return set_error(err, bitcoinconsensus_ERR_TX_SIZE_MISMATCH);
// Regardless of the verification result, the tx did not error.
diff --git a/src/script/bitcoinconsensus.h b/src/script/bitcoinconsensus.h
index f73a8e30bc..1bef4fe9e9 100644
--- a/src/script/bitcoinconsensus.h
+++ b/src/script/bitcoinconsensus.h
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2015 The Bitcoin Core developers
+// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -42,6 +42,7 @@ typedef enum bitcoinconsensus_error_t
bitcoinconsensus_ERR_TX_SIZE_MISMATCH,
bitcoinconsensus_ERR_TX_DESERIALIZE,
bitcoinconsensus_ERR_AMOUNT_REQUIRED,
+ bitcoinconsensus_ERR_INVALID_FLAGS,
} bitcoinconsensus_error;
/** Script verification flags */
@@ -50,9 +51,13 @@ enum
bitcoinconsensus_SCRIPT_FLAGS_VERIFY_NONE = 0,
bitcoinconsensus_SCRIPT_FLAGS_VERIFY_P2SH = (1U << 0), // evaluate P2SH (BIP16) subscripts
bitcoinconsensus_SCRIPT_FLAGS_VERIFY_DERSIG = (1U << 2), // enforce strict DER (BIP66) compliance
+ bitcoinconsensus_SCRIPT_FLAGS_VERIFY_NULLDUMMY = (1U << 4), // enforce NULLDUMMY (BIP147)
bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), // enable CHECKLOCKTIMEVERIFY (BIP65)
bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKSEQUENCEVERIFY = (1U << 10), // enable CHECKSEQUENCEVERIFY (BIP112)
bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS = (1U << 11), // enable WITNESS (BIP141)
+ bitcoinconsensus_SCRIPT_FLAGS_VERIFY_ALL = bitcoinconsensus_SCRIPT_FLAGS_VERIFY_P2SH | bitcoinconsensus_SCRIPT_FLAGS_VERIFY_DERSIG |
+ bitcoinconsensus_SCRIPT_FLAGS_VERIFY_NULLDUMMY | bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY |
+ bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKSEQUENCEVERIFY | bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS
};
/// Returns 1 if the input nIn of the serialized transaction pointed to by
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index 47ea261e31..1410d0b73f 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -79,8 +79,20 @@ bool static IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) {
return false;
}
} else {
- // Non-canonical public key: neither compressed nor uncompressed
- return false;
+ // Non-canonical public key: neither compressed nor uncompressed
+ return false;
+ }
+ return true;
+}
+
+bool static IsCompressedPubKey(const valtype &vchPubKey) {
+ if (vchPubKey.size() != 33) {
+ // Non-canonical public key: invalid length for compressed key
+ return false;
+ }
+ if (vchPubKey[0] != 0x02 && vchPubKey[0] != 0x03) {
+ // Non-canonical public key: invalid prefix for compressed key
+ return false;
}
return true;
}
@@ -199,10 +211,14 @@ bool CheckSignatureEncoding(const vector<unsigned char> &vchSig, unsigned int fl
return true;
}
-bool static CheckPubKeyEncoding(const valtype &vchSig, unsigned int flags, ScriptError* serror) {
- if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsCompressedOrUncompressedPubKey(vchSig)) {
+bool static CheckPubKeyEncoding(const valtype &vchPubKey, unsigned int flags, const SigVersion &sigversion, ScriptError* serror) {
+ if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsCompressedOrUncompressedPubKey(vchPubKey)) {
return set_error(serror, SCRIPT_ERR_PUBKEYTYPE);
}
+ // Only compressed keys are accepted in segwit
+ if ((flags & SCRIPT_VERIFY_WITNESS_PUBKEYTYPE) != 0 && sigversion == SIGVERSION_WITNESS_V0 && !IsCompressedPubKey(vchPubKey)) {
+ return set_error(serror, SCRIPT_ERR_WITNESS_PUBKEYTYPE);
+ }
return true;
}
@@ -428,6 +444,12 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
if (stack.size() < 1)
return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL);
valtype& vch = stacktop(-1);
+ if (sigversion == SIGVERSION_WITNESS_V0 && (flags & SCRIPT_VERIFY_MINIMALIF)) {
+ if (vch.size() > 1)
+ return set_error(serror, SCRIPT_ERR_MINIMALIF);
+ if (vch.size() == 1 && vch[0] != 1)
+ return set_error(serror, SCRIPT_ERR_MINIMALIF);
+ }
fValue = CastToBool(vch);
if (opcode == OP_NOTIF)
fValue = !fValue;
@@ -834,15 +856,15 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
valtype& vch = stacktop(-1);
valtype vchHash((opcode == OP_RIPEMD160 || opcode == OP_SHA1 || opcode == OP_HASH160) ? 20 : 32);
if (opcode == OP_RIPEMD160)
- CRIPEMD160().Write(begin_ptr(vch), vch.size()).Finalize(begin_ptr(vchHash));
+ CRIPEMD160().Write(vch.data(), vch.size()).Finalize(vchHash.data());
else if (opcode == OP_SHA1)
- CSHA1().Write(begin_ptr(vch), vch.size()).Finalize(begin_ptr(vchHash));
+ CSHA1().Write(vch.data(), vch.size()).Finalize(vchHash.data());
else if (opcode == OP_SHA256)
- CSHA256().Write(begin_ptr(vch), vch.size()).Finalize(begin_ptr(vchHash));
+ CSHA256().Write(vch.data(), vch.size()).Finalize(vchHash.data());
else if (opcode == OP_HASH160)
- CHash160().Write(begin_ptr(vch), vch.size()).Finalize(begin_ptr(vchHash));
+ CHash160().Write(vch.data(), vch.size()).Finalize(vchHash.data());
else if (opcode == OP_HASH256)
- CHash256().Write(begin_ptr(vch), vch.size()).Finalize(begin_ptr(vchHash));
+ CHash256().Write(vch.data(), vch.size()).Finalize(vchHash.data());
popstack(stack);
stack.push_back(vchHash);
}
@@ -868,17 +890,20 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
// Subset of script starting at the most recent codeseparator
CScript scriptCode(pbegincodehash, pend);
- // Drop the signature, since there's no way for a signature to sign itself
+ // Drop the signature in pre-segwit scripts but not segwit scripts
if (sigversion == SIGVERSION_BASE) {
scriptCode.FindAndDelete(CScript(vchSig));
}
- if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) {
+ if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) {
//serror is set
return false;
}
bool fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion);
+ if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL) && vchSig.size())
+ return set_error(serror, SCRIPT_ERR_SIG_NULLFAIL);
+
popstack(stack);
popstack(stack);
stack.push_back(fSuccess ? vchTrue : vchFalse);
@@ -908,6 +933,9 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
if (nOpCount > MAX_OPS_PER_SCRIPT)
return set_error(serror, SCRIPT_ERR_OP_COUNT);
int ikey = ++i;
+ // ikey2 is the position of last non-signature item in the stack. Top stack item = 1.
+ // With SCRIPT_VERIFY_NULLFAIL, this is used for cleanup if operation fails.
+ int ikey2 = nKeysCount + 2;
i += nKeysCount;
if ((int)stack.size() < i)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
@@ -923,7 +951,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
// Subset of script starting at the most recent codeseparator
CScript scriptCode(pbegincodehash, pend);
- // Drop the signatures, since there's no way for a signature to sign itself
+ // Drop the signature in pre-segwit scripts but not segwit scripts
for (int k = 0; k < nSigsCount; k++)
{
valtype& vchSig = stacktop(-isig-k);
@@ -941,7 +969,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
// Note how this makes the exact order of pubkey/signature evaluation
// distinguishable by CHECKMULTISIG NOT if the STRICTENC flag is set.
// See the script_(in)valid tests for details.
- if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) {
+ if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) {
// serror is set
return false;
}
@@ -964,8 +992,14 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
}
// Clean up stack of actual arguments
- while (i-- > 1)
+ while (i-- > 1) {
+ // If the operation failed, we require that all signatures must be empty vector
+ if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL) && !ikey2 && stacktop(-1).size())
+ return set_error(serror, SCRIPT_ERR_SIG_NULLFAIL);
+ if (ikey2 > 0)
+ ikey2--;
popstack(stack);
+ }
// A bug causes CHECKMULTISIG to consume one extra argument
// whose contents were not checked in any way.
@@ -1035,7 +1069,7 @@ public:
/** Serialize the passed scriptCode, skipping OP_CODESEPARATORs */
template<typename S>
- void SerializeScriptCode(S &s, int nType, int nVersion) const {
+ void SerializeScriptCode(S &s) const {
CScript::const_iterator it = scriptCode.begin();
CScript::const_iterator itBegin = it;
opcodetype opcode;
@@ -1058,53 +1092,53 @@ public:
/** Serialize an input of txTo */
template<typename S>
- void SerializeInput(S &s, unsigned int nInput, int nType, int nVersion) const {
+ void SerializeInput(S &s, unsigned int nInput) const {
// In case of SIGHASH_ANYONECANPAY, only the input being signed is serialized
if (fAnyoneCanPay)
nInput = nIn;
// Serialize the prevout
- ::Serialize(s, txTo.vin[nInput].prevout, nType, nVersion);
+ ::Serialize(s, txTo.vin[nInput].prevout);
// Serialize the script
if (nInput != nIn)
// Blank out other inputs' signatures
- ::Serialize(s, CScriptBase(), nType, nVersion);
+ ::Serialize(s, CScriptBase());
else
- SerializeScriptCode(s, nType, nVersion);
+ SerializeScriptCode(s);
// Serialize the nSequence
if (nInput != nIn && (fHashSingle || fHashNone))
// let the others update at will
- ::Serialize(s, (int)0, nType, nVersion);
+ ::Serialize(s, (int)0);
else
- ::Serialize(s, txTo.vin[nInput].nSequence, nType, nVersion);
+ ::Serialize(s, txTo.vin[nInput].nSequence);
}
/** Serialize an output of txTo */
template<typename S>
- void SerializeOutput(S &s, unsigned int nOutput, int nType, int nVersion) const {
+ void SerializeOutput(S &s, unsigned int nOutput) const {
if (fHashSingle && nOutput != nIn)
// Do not lock-in the txout payee at other indices as txin
- ::Serialize(s, CTxOut(), nType, nVersion);
+ ::Serialize(s, CTxOut());
else
- ::Serialize(s, txTo.vout[nOutput], nType, nVersion);
+ ::Serialize(s, txTo.vout[nOutput]);
}
/** Serialize txTo */
template<typename S>
- void Serialize(S &s, int nType, int nVersion) const {
+ void Serialize(S &s) const {
// Serialize nVersion
- ::Serialize(s, txTo.nVersion, nType, nVersion);
+ ::Serialize(s, txTo.nVersion);
// Serialize vin
unsigned int nInputs = fAnyoneCanPay ? 1 : txTo.vin.size();
::WriteCompactSize(s, nInputs);
for (unsigned int nInput = 0; nInput < nInputs; nInput++)
- SerializeInput(s, nInput, nType, nVersion);
+ SerializeInput(s, nInput);
// Serialize vout
unsigned int nOutputs = fHashNone ? 0 : (fHashSingle ? nIn+1 : txTo.vout.size());
::WriteCompactSize(s, nOutputs);
for (unsigned int nOutput = 0; nOutput < nOutputs; nOutput++)
- SerializeOutput(s, nOutput, nType, nVersion);
+ SerializeOutput(s, nOutput);
// Serialize nLockTime
- ::Serialize(s, txTo.nLockTime, nType, nVersion);
+ ::Serialize(s, txTo.nLockTime);
}
};
diff --git a/src/script/interpreter.h b/src/script/interpreter.h
index e5d7865cd3..79894c5300 100644
--- a/src/script/interpreter.h
+++ b/src/script/interpreter.h
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2015 The Bitcoin Core developers
+// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -94,6 +94,18 @@ enum
// Making v1-v16 witness program non-standard
//
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM = (1U << 12),
+
+ // Segwit script only: Require the argument of OP_IF/NOTIF to be exactly 0x01 or empty vector
+ //
+ SCRIPT_VERIFY_MINIMALIF = (1U << 13),
+
+ // Signature(s) must be empty vector if an CHECK(MULTI)SIG operation failed
+ //
+ SCRIPT_VERIFY_NULLFAIL = (1U << 14),
+
+ // Public keys in segregated witness scripts must be compressed
+ //
+ SCRIPT_VERIFY_WITNESS_PUBKEYTYPE = (1U << 15),
};
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror);
diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp
index 0bf180341e..7467d23b2d 100644
--- a/src/script/ismine.cpp
+++ b/src/script/ismine.cpp
@@ -29,13 +29,25 @@ unsigned int HaveKeys(const vector<valtype>& pubkeys, const CKeyStore& keystore)
return nResult;
}
-isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest)
+isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, SigVersion sigversion)
+{
+ bool isInvalid = false;
+ return IsMine(keystore, scriptPubKey, isInvalid, sigversion);
+}
+
+isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest, SigVersion sigversion)
+{
+ bool isInvalid = false;
+ return IsMine(keystore, dest, isInvalid, sigversion);
+}
+
+isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest, bool& isInvalid, SigVersion sigversion)
{
CScript script = GetScriptForDestination(dest);
- return IsMine(keystore, script);
+ return IsMine(keystore, script, isInvalid, sigversion);
}
-isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
+isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& isInvalid, SigVersion sigversion)
{
vector<valtype> vSolutions;
txnouttype whichType;
@@ -53,12 +65,35 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
break;
case TX_PUBKEY:
keyID = CPubKey(vSolutions[0]).GetID();
+ if (sigversion != SIGVERSION_BASE && vSolutions[0].size() != 33) {
+ isInvalid = true;
+ return ISMINE_NO;
+ }
if (keystore.HaveKey(keyID))
return ISMINE_SPENDABLE;
break;
- case TX_PUBKEYHASH:
case TX_WITNESS_V0_KEYHASH:
+ {
+ if (!keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) {
+ // We do not support bare witness outputs unless the P2SH version of it would be
+ // acceptable as well. This protects against matching before segwit activates.
+ // This also applies to the P2WSH case.
+ break;
+ }
+ isminetype ret = ::IsMine(keystore, GetScriptForDestination(CKeyID(uint160(vSolutions[0]))), isInvalid, SIGVERSION_WITNESS_V0);
+ if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid))
+ return ret;
+ break;
+ }
+ case TX_PUBKEYHASH:
keyID = CKeyID(uint160(vSolutions[0]));
+ if (sigversion != SIGVERSION_BASE) {
+ CPubKey pubkey;
+ if (keystore.GetPubKey(keyID, pubkey) && !pubkey.IsCompressed()) {
+ isInvalid = true;
+ return ISMINE_NO;
+ }
+ }
if (keystore.HaveKey(keyID))
return ISMINE_SPENDABLE;
break;
@@ -67,21 +102,24 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
CScriptID scriptID = CScriptID(uint160(vSolutions[0]));
CScript subscript;
if (keystore.GetCScript(scriptID, subscript)) {
- isminetype ret = IsMine(keystore, subscript);
- if (ret == ISMINE_SPENDABLE)
+ isminetype ret = IsMine(keystore, subscript, isInvalid);
+ if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid))
return ret;
}
break;
}
case TX_WITNESS_V0_SCRIPTHASH:
{
+ if (!keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) {
+ break;
+ }
uint160 hash;
CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(hash.begin());
CScriptID scriptID = CScriptID(hash);
CScript subscript;
if (keystore.GetCScript(scriptID, subscript)) {
- isminetype ret = IsMine(keystore, subscript);
- if (ret == ISMINE_SPENDABLE)
+ isminetype ret = IsMine(keystore, subscript, isInvalid, SIGVERSION_WITNESS_V0);
+ if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid))
return ret;
}
break;
@@ -95,6 +133,14 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
// them) enable spend-out-from-under-you attacks, especially
// in shared-wallet situations.
vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1);
+ if (sigversion != SIGVERSION_BASE) {
+ for (size_t i = 0; i < keys.size(); i++) {
+ if (keys[i].size() != 33) {
+ isInvalid = true;
+ return ISMINE_NO;
+ }
+ }
+ }
if (HaveKeys(keys, keystore) == keys.size())
return ISMINE_SPENDABLE;
break;
diff --git a/src/script/ismine.h b/src/script/ismine.h
index 4b7db8802b..ec7a620e33 100644
--- a/src/script/ismine.h
+++ b/src/script/ismine.h
@@ -28,7 +28,14 @@ enum isminetype
/** used for bitflags of isminetype */
typedef uint8_t isminefilter;
-isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
-isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest);
+/* isInvalid becomes true when the script is found invalid by consensus or policy. This will terminate the recursion
+ * and return a ISMINE_NO immediately, as an invalid script should never be considered as "mine". This is needed as
+ * different SIGVERSION may have different network rules. Currently the only use of isInvalid is indicate uncompressed
+ * keys in SIGVERSION_WITNESS_V0 script, but could also be used in similar cases in the future
+ */
+isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, bool& isInvalid, SigVersion = SIGVERSION_BASE);
+isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, SigVersion = SIGVERSION_BASE);
+isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest, bool& isInvalid, SigVersion = SIGVERSION_BASE);
+isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest, SigVersion = SIGVERSION_BASE);
#endif // BITCOIN_SCRIPT_ISMINE_H
diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp
index cef807edcf..2c5359fe8a 100644
--- a/src/script/script_error.cpp
+++ b/src/script/script_error.cpp
@@ -63,6 +63,10 @@ const char* ScriptErrorString(const ScriptError serror)
return "Non-canonical signature: S value is unnecessarily high";
case SCRIPT_ERR_SIG_NULLDUMMY:
return "Dummy CHECKMULTISIG argument must be zero";
+ case SCRIPT_ERR_MINIMALIF:
+ return "OP_IF/NOTIF argument must be minimal";
+ case SCRIPT_ERR_SIG_NULLFAIL:
+ return "Signature must be zero for failed CHECK(MULTI)SIG operation";
case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS:
return "NOPx reserved for soft-fork upgrades";
case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM:
@@ -81,6 +85,8 @@ const char* ScriptErrorString(const ScriptError serror)
return "Witness requires only-redeemscript scriptSig";
case SCRIPT_ERR_WITNESS_UNEXPECTED:
return "Witness provided for non-witness script";
+ case SCRIPT_ERR_WITNESS_PUBKEYTYPE:
+ return "Using non-compressed keys in segwit";
case SCRIPT_ERR_UNKNOWN_ERROR:
case SCRIPT_ERR_ERROR_COUNT:
default: break;
diff --git a/src/script/script_error.h b/src/script/script_error.h
index 09dc6945ad..430836991b 100644
--- a/src/script/script_error.h
+++ b/src/script/script_error.h
@@ -39,7 +39,7 @@ typedef enum ScriptError_t
SCRIPT_ERR_NEGATIVE_LOCKTIME,
SCRIPT_ERR_UNSATISFIED_LOCKTIME,
- /* BIP62 */
+ /* Malleability */
SCRIPT_ERR_SIG_HASHTYPE,
SCRIPT_ERR_SIG_DER,
SCRIPT_ERR_MINIMALDATA,
@@ -48,6 +48,8 @@ typedef enum ScriptError_t
SCRIPT_ERR_SIG_NULLDUMMY,
SCRIPT_ERR_PUBKEYTYPE,
SCRIPT_ERR_CLEANSTACK,
+ SCRIPT_ERR_MINIMALIF,
+ SCRIPT_ERR_SIG_NULLFAIL,
/* softfork safeness */
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS,
@@ -60,6 +62,7 @@ typedef enum ScriptError_t
SCRIPT_ERR_WITNESS_MALLEATED,
SCRIPT_ERR_WITNESS_MALLEATED_P2SH,
SCRIPT_ERR_WITNESS_UNEXPECTED,
+ SCRIPT_ERR_WITNESS_PUBKEYTYPE,
SCRIPT_ERR_ERROR_COUNT
} ScriptError;
diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp
index bdc0bfdc1c..b78d7b607f 100644
--- a/src/script/sigcache.cpp
+++ b/src/script/sigcache.cpp
@@ -11,20 +11,29 @@
#include "uint256.h"
#include "util.h"
+#include "cuckoocache.h"
#include <boost/thread.hpp>
-#include <boost/unordered_set.hpp>
namespace {
/**
* We're hashing a nonce into the entries themselves, so we don't need extra
* blinding in the set hash computation.
+ *
+ * This may exhibit platform endian dependent behavior but because these are
+ * nonced hashes (random) and this state is only ever used locally it is safe.
+ * All that matters is local consistency.
*/
-class CSignatureCacheHasher
+class SignatureCacheHasher
{
public:
- size_t operator()(const uint256& key) const {
- return key.GetCheapHash();
+ template <uint8_t hash_select>
+ uint32_t operator()(const uint256& key) const
+ {
+ static_assert(hash_select <8, "SignatureCacheHasher only has 8 hashes available.");
+ uint32_t u;
+ std::memcpy(&u, key.begin()+4*hash_select, 4);
+ return u;
}
};
@@ -38,11 +47,10 @@ class CSignatureCache
private:
//! Entries are SHA256(nonce || signature hash || public key || signature):
uint256 nonce;
- typedef boost::unordered_set<uint256, CSignatureCacheHasher> map_type;
+ typedef CuckooCache::cache<uint256, SignatureCacheHasher> map_type;
map_type setValid;
boost::shared_mutex cs_sigcache;
-
public:
CSignatureCache()
{
@@ -56,58 +64,51 @@ public:
}
bool
- Get(const uint256& entry)
+ Get(const uint256& entry, const bool erase)
{
boost::shared_lock<boost::shared_mutex> lock(cs_sigcache);
- return setValid.count(entry);
+ return setValid.contains(entry, erase);
}
- void Erase(const uint256& entry)
+ void Set(uint256& entry)
{
boost::unique_lock<boost::shared_mutex> lock(cs_sigcache);
- setValid.erase(entry);
+ setValid.insert(entry);
}
-
- void Set(const uint256& entry)
+ uint32_t setup_bytes(size_t n)
{
- size_t nMaxCacheSize = GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20);
- if (nMaxCacheSize <= 0) return;
-
- boost::unique_lock<boost::shared_mutex> lock(cs_sigcache);
- while (memusage::DynamicUsage(setValid) > nMaxCacheSize)
- {
- map_type::size_type s = GetRand(setValid.bucket_count());
- map_type::local_iterator it = setValid.begin(s);
- if (it != setValid.end(s)) {
- setValid.erase(*it);
- }
- }
-
- setValid.insert(entry);
+ return setValid.setup_bytes(n);
}
};
+/* In previous versions of this code, signatureCache was a local static variable
+ * in CachingTransactionSignatureChecker::VerifySignature. We initialize
+ * signatureCache outside of VerifySignature to avoid the atomic operation per
+ * call overhead associated with local static variables even though
+ * signatureCache could be made local to VerifySignature.
+*/
+static CSignatureCache signatureCache;
}
-bool CachingTransactionSignatureChecker::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
+// To be called once in AppInit2/TestingSetup to initialize the signatureCache
+void InitSignatureCache()
{
- static CSignatureCache signatureCache;
+ size_t nMaxCacheSize = GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20);
+ if (nMaxCacheSize <= 0) return;
+ size_t nElems = signatureCache.setup_bytes(nMaxCacheSize);
+ LogPrintf("Using %zu MiB out of %zu requested for signature cache, able to store %zu elements\n",
+ (nElems*sizeof(uint256)) >>20, nMaxCacheSize>>20, nElems);
+}
+bool CachingTransactionSignatureChecker::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
+{
uint256 entry;
signatureCache.ComputeEntry(entry, sighash, vchSig, pubkey);
-
- if (signatureCache.Get(entry)) {
- if (!store) {
- signatureCache.Erase(entry);
- }
+ if (signatureCache.Get(entry, !store))
return true;
- }
-
if (!TransactionSignatureChecker::VerifySignature(vchSig, pubkey, sighash))
return false;
-
- if (store) {
+ if (store)
signatureCache.Set(entry);
- }
return true;
}
diff --git a/src/script/sigcache.h b/src/script/sigcache.h
index 44551ec2bc..5243fc0a42 100644
--- a/src/script/sigcache.h
+++ b/src/script/sigcache.h
@@ -10,9 +10,10 @@
#include <vector>
-// DoS prevention: limit cache size to less than 40MB (over 500000
-// entries on 64-bit systems).
-static const unsigned int DEFAULT_MAX_SIG_CACHE_SIZE = 40;
+// DoS prevention: limit cache size to 32MB (over 1000000 entries on 64-bit
+// systems). Due to how we count cache size, actual memory usage is slightly
+// more (~32.25 MB)
+static const unsigned int DEFAULT_MAX_SIG_CACHE_SIZE = 32;
class CPubKey;
@@ -27,4 +28,6 @@ public:
bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
};
+void InitSignatureCache();
+
#endif // BITCOIN_SCRIPT_SIGCACHE_H
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 87f38d9c72..f552ad5bba 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2015 The Bitcoin Core developers
+// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -26,6 +26,10 @@ bool TransactionSignatureCreator::CreateSig(std::vector<unsigned char>& vchSig,
if (!keystore->GetKey(address, key))
return false;
+ // Signing with uncompressed keys is disabled in witness scripts
+ if (sigversion == SIGVERSION_WITNESS_V0 && !key.IsCompressed())
+ return false;
+
uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion);
if (!key.Sign(hash, vchSig))
return false;
diff --git a/src/secp256k1/.gitignore b/src/secp256k1/.gitignore
index efb277d347..87fea161ba 100644
--- a/src/secp256k1/.gitignore
+++ b/src/secp256k1/.gitignore
@@ -6,6 +6,7 @@ bench_schnorr_verify
bench_recover
bench_internal
tests
+exhaustive_tests
gen_context
*.exe
*.so
diff --git a/src/secp256k1/.travis.yml b/src/secp256k1/.travis.yml
index 2c5c63adad..2439529242 100644
--- a/src/secp256k1/.travis.yml
+++ b/src/secp256k1/.travis.yml
@@ -11,7 +11,7 @@ cache:
- src/java/guava/
env:
global:
- - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no schnorr=no RECOVERY=no EXPERIMENTAL=no
+ - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no
- GUAVA_URL=https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar GUAVA_JAR=src/java/guava/guava-18.0.jar
matrix:
- SCALAR=32bit RECOVERY=yes
@@ -22,15 +22,14 @@ env:
- FIELD=64bit ENDOMORPHISM=yes ECDH=yes EXPERIMENTAL=yes
- FIELD=64bit ASM=x86_64
- FIELD=64bit ENDOMORPHISM=yes ASM=x86_64
- - FIELD=32bit SCHNORR=yes EXPERIMENTAL=yes
- FIELD=32bit ENDOMORPHISM=yes
- BIGNUM=no
- - BIGNUM=no ENDOMORPHISM=yes SCHNORR=yes RECOVERY=yes EXPERIMENTAL=yes
+ - BIGNUM=no ENDOMORPHISM=yes RECOVERY=yes EXPERIMENTAL=yes
- BIGNUM=no STATICPRECOMPUTATION=no
- BUILD=distcheck
- EXTRAFLAGS=CPPFLAGS=-DDETERMINISTIC
- EXTRAFLAGS=CFLAGS=-O0
- - BUILD=check-java ECDH=yes SCHNORR=yes EXPERIMENTAL=yes
+ - BUILD=check-java ECDH=yes EXPERIMENTAL=yes
matrix:
fast_finish: true
include:
@@ -66,5 +65,5 @@ before_script: ./autogen.sh
script:
- if [ -n "$HOST" ]; then export USE_HOST="--host=$HOST"; fi
- if [ "x$HOST" = "xi686-linux-gnu" ]; then export CC="$CC -m32"; fi
- - ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-schnorr=$SCHNORR --enable-module-recovery=$RECOVERY $EXTRAFLAGS $USE_HOST && make -j2 $BUILD
+ - ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-recovery=$RECOVERY $EXTRAFLAGS $USE_HOST && make -j2 $BUILD
os: linux
diff --git a/src/secp256k1/Makefile.am b/src/secp256k1/Makefile.am
index 3d130bdcbd..e5657f7f31 100644
--- a/src/secp256k1/Makefile.am
+++ b/src/secp256k1/Makefile.am
@@ -12,9 +12,11 @@ noinst_HEADERS =
noinst_HEADERS += src/scalar.h
noinst_HEADERS += src/scalar_4x64.h
noinst_HEADERS += src/scalar_8x32.h
+noinst_HEADERS += src/scalar_low.h
noinst_HEADERS += src/scalar_impl.h
noinst_HEADERS += src/scalar_4x64_impl.h
noinst_HEADERS += src/scalar_8x32_impl.h
+noinst_HEADERS += src/scalar_low_impl.h
noinst_HEADERS += src/group.h
noinst_HEADERS += src/group_impl.h
noinst_HEADERS += src/num_gmp.h
@@ -87,13 +89,23 @@ bench_internal_LDADD = $(SECP_LIBS) $(COMMON_LIB)
bench_internal_CPPFLAGS = -DSECP256K1_BUILD $(SECP_INCLUDES)
endif
+TESTS =
if USE_TESTS
noinst_PROGRAMS += tests
tests_SOURCES = src/tests.c
tests_CPPFLAGS = -DSECP256K1_BUILD -DVERIFY -I$(top_srcdir)/src -I$(top_srcdir)/include $(SECP_INCLUDES) $(SECP_TEST_INCLUDES)
tests_LDADD = $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
tests_LDFLAGS = -static
-TESTS = tests
+TESTS += tests
+endif
+
+if USE_EXHAUSTIVE_TESTS
+noinst_PROGRAMS += exhaustive_tests
+exhaustive_tests_SOURCES = src/tests_exhaustive.c
+exhaustive_tests_CPPFLAGS = -DSECP256K1_BUILD -DVERIFY -I$(top_srcdir)/src $(SECP_INCLUDES)
+exhaustive_tests_LDADD = $(SECP_LIBS)
+exhaustive_tests_LDFLAGS = -static
+TESTS += exhaustive_tests
endif
JAVAROOT=src/java
@@ -154,10 +166,6 @@ if ENABLE_MODULE_ECDH
include src/modules/ecdh/Makefile.am.include
endif
-if ENABLE_MODULE_SCHNORR
-include src/modules/schnorr/Makefile.am.include
-endif
-
if ENABLE_MODULE_RECOVERY
include src/modules/recovery/Makefile.am.include
endif
diff --git a/src/secp256k1/build-aux/m4/bitcoin_secp.m4 b/src/secp256k1/build-aux/m4/bitcoin_secp.m4
index b25d8adb92..b74acb8c13 100644
--- a/src/secp256k1/build-aux/m4/bitcoin_secp.m4
+++ b/src/secp256k1/build-aux/m4/bitcoin_secp.m4
@@ -46,6 +46,10 @@ if test x"$has_libcrypto" = x"yes" && test x"$has_openssl_ec" = x; then
ECDSA_sign(0, NULL, 0, NULL, NULL, eckey);
ECDSA_verify(0, NULL, 0, NULL, 0, eckey);
EC_KEY_free(eckey);
+ ECDSA_SIG *sig_openssl;
+ sig_openssl = ECDSA_SIG_new();
+ (void)sig_openssl->r;
+ ECDSA_SIG_free(sig_openssl);
]])],[has_openssl_ec=yes],[has_openssl_ec=no])
AC_MSG_RESULT([$has_openssl_ec])
fi
diff --git a/src/secp256k1/configure.ac b/src/secp256k1/configure.ac
index 0743c36690..ec50ffe3a2 100644
--- a/src/secp256k1/configure.ac
+++ b/src/secp256k1/configure.ac
@@ -104,6 +104,11 @@ AC_ARG_ENABLE(experimental,
[use_experimental=$enableval],
[use_experimental=no])
+AC_ARG_ENABLE(exhaustive_tests,
+ AS_HELP_STRING([--enable-exhaustive-tests],[compile exhaustive tests (default is yes)]),
+ [use_exhaustive_tests=$enableval],
+ [use_exhaustive_tests=yes])
+
AC_ARG_ENABLE(endomorphism,
AS_HELP_STRING([--enable-endomorphism],[enable endomorphism (default is no)]),
[use_endomorphism=$enableval],
@@ -119,11 +124,6 @@ AC_ARG_ENABLE(module_ecdh,
[enable_module_ecdh=$enableval],
[enable_module_ecdh=no])
-AC_ARG_ENABLE(module_schnorr,
- AS_HELP_STRING([--enable-module-schnorr],[enable Schnorr signature module (experimental)]),
- [enable_module_schnorr=$enableval],
- [enable_module_schnorr=no])
-
AC_ARG_ENABLE(module_recovery,
AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module (default is no)]),
[enable_module_recovery=$enableval],
@@ -381,9 +381,6 @@ fi
if test x"$use_jni" != x"no"; then
AX_JNI_INCLUDE_DIR
have_jni_dependencies=yes
- if test x"$enable_module_schnorr" = x"no"; then
- have_jni_dependencies=no
- fi
if test x"$enable_module_ecdh" = x"no"; then
have_jni_dependencies=no
fi
@@ -392,7 +389,7 @@ if test x"$use_jni" != x"no"; then
fi
if test "x$have_jni_dependencies" = "xno"; then
if test x"$use_jni" = x"yes"; then
- AC_MSG_ERROR([jni support explicitly requested but headers/dependencies were not found. Enable ECDH and Schnorr and try again.])
+ AC_MSG_ERROR([jni support explicitly requested but headers/dependencies were not found. Enable ECDH and try again.])
fi
AC_MSG_WARN([jni headers/dependencies not found. jni support disabled])
use_jni=no
@@ -413,7 +410,7 @@ if test x"$use_endomorphism" = x"yes"; then
AC_DEFINE(USE_ENDOMORPHISM, 1, [Define this symbol to use endomorphism optimization])
fi
-if test x"$use_ecmult_static_precomputation" = x"yes"; then
+if test x"$set_precomp" = x"yes"; then
AC_DEFINE(USE_ECMULT_STATIC_PRECOMPUTATION, 1, [Define this symbol to use a statically generated ecmult table])
fi
@@ -421,10 +418,6 @@ if test x"$enable_module_ecdh" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_ECDH, 1, [Define this symbol to enable the ECDH module])
fi
-if test x"$enable_module_schnorr" = x"yes"; then
- AC_DEFINE(ENABLE_MODULE_SCHNORR, 1, [Define this symbol to enable the Schnorr signature module])
-fi
-
if test x"$enable_module_recovery" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module])
fi
@@ -442,7 +435,6 @@ AC_MSG_NOTICE([Using bignum implementation: $set_bignum])
AC_MSG_NOTICE([Using scalar implementation: $set_scalar])
AC_MSG_NOTICE([Using endomorphism optimizations: $use_endomorphism])
AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh])
-AC_MSG_NOTICE([Building Schnorr signatures module: $enable_module_schnorr])
AC_MSG_NOTICE([Building ECDSA pubkey recovery module: $enable_module_recovery])
AC_MSG_NOTICE([Using jni: $use_jni])
@@ -451,12 +443,8 @@ if test x"$enable_experimental" = x"yes"; then
AC_MSG_NOTICE([WARNING: experimental build])
AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.])
AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh])
- AC_MSG_NOTICE([Building Schnorr signatures module: $enable_module_schnorr])
AC_MSG_NOTICE([******])
else
- if test x"$enable_module_schnorr" = x"yes"; then
- AC_MSG_ERROR([Schnorr signature module is experimental. Use --enable-experimental to allow.])
- fi
if test x"$enable_module_ecdh" = x"yes"; then
AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.])
fi
@@ -473,10 +461,10 @@ AC_SUBST(SECP_LIBS)
AC_SUBST(SECP_TEST_LIBS)
AC_SUBST(SECP_TEST_INCLUDES)
AM_CONDITIONAL([USE_TESTS], [test x"$use_tests" != x"no"])
+AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$use_exhaustive_tests" != x"no"])
AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"])
-AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$use_ecmult_static_precomputation" = x"yes"])
+AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
-AM_CONDITIONAL([ENABLE_MODULE_SCHNORR], [test x"$enable_module_schnorr" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"])
AM_CONDITIONAL([USE_JNI], [test x"$use_jni" == x"yes"])
AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"])
diff --git a/src/secp256k1/include/secp256k1.h b/src/secp256k1/include/secp256k1.h
index 7145dbcc54..f268e309d0 100644
--- a/src/secp256k1/include/secp256k1.h
+++ b/src/secp256k1/include/secp256k1.h
@@ -47,11 +47,8 @@ typedef struct secp256k1_context_struct secp256k1_context;
* The exact representation of data inside is implementation defined and not
* guaranteed to be portable between different platforms or versions. It is
* however guaranteed to be 64 bytes in size, and can be safely copied/moved.
- * If you need to convert to a format suitable for storage or transmission, use
- * secp256k1_ec_pubkey_serialize and secp256k1_ec_pubkey_parse.
- *
- * Furthermore, it is guaranteed that identical public keys (ignoring
- * compression) will have identical representation, so they can be memcmp'ed.
+ * If you need to convert to a format suitable for storage, transmission, or
+ * comparison, use secp256k1_ec_pubkey_serialize and secp256k1_ec_pubkey_parse.
*/
typedef struct {
unsigned char data[64];
@@ -62,12 +59,9 @@ typedef struct {
* The exact representation of data inside is implementation defined and not
* guaranteed to be portable between different platforms or versions. It is
* however guaranteed to be 64 bytes in size, and can be safely copied/moved.
- * If you need to convert to a format suitable for storage or transmission, use
- * the secp256k1_ecdsa_signature_serialize_* and
+ * If you need to convert to a format suitable for storage, transmission, or
+ * comparison, use the secp256k1_ecdsa_signature_serialize_* and
* secp256k1_ecdsa_signature_serialize_* functions.
- *
- * Furthermore, it is guaranteed to identical signatures will have identical
- * representation, so they can be memcmp'ed.
*/
typedef struct {
unsigned char data[64];
diff --git a/src/secp256k1/include/secp256k1_schnorr.h b/src/secp256k1/include/secp256k1_schnorr.h
deleted file mode 100644
index dc32fec1ea..0000000000
--- a/src/secp256k1/include/secp256k1_schnorr.h
+++ /dev/null
@@ -1,173 +0,0 @@
-#ifndef _SECP256K1_SCHNORR_
-# define _SECP256K1_SCHNORR_
-
-# include "secp256k1.h"
-
-# ifdef __cplusplus
-extern "C" {
-# endif
-
-/** Create a signature using a custom EC-Schnorr-SHA256 construction. It
- * produces non-malleable 64-byte signatures which support public key recovery
- * batch validation, and multiparty signing.
- * Returns: 1: signature created
- * 0: the nonce generation function failed, or the private key was
- * invalid.
- * Args: ctx: pointer to a context object, initialized for signing
- * (cannot be NULL)
- * Out: sig64: pointer to a 64-byte array where the signature will be
- * placed (cannot be NULL)
- * In: msg32: the 32-byte message hash being signed (cannot be NULL)
- * seckey: pointer to a 32-byte secret key (cannot be NULL)
- * noncefp:pointer to a nonce generation function. If NULL,
- * secp256k1_nonce_function_default is used
- * ndata: pointer to arbitrary data used by the nonce generation
- * function (can be NULL)
- */
-SECP256K1_API int secp256k1_schnorr_sign(
- const secp256k1_context* ctx,
- unsigned char *sig64,
- const unsigned char *msg32,
- const unsigned char *seckey,
- secp256k1_nonce_function noncefp,
- const void *ndata
-) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
-
-/** Verify a signature created by secp256k1_schnorr_sign.
- * Returns: 1: correct signature
- * 0: incorrect signature
- * Args: ctx: a secp256k1 context object, initialized for verification.
- * In: sig64: the 64-byte signature being verified (cannot be NULL)
- * msg32: the 32-byte message hash being verified (cannot be NULL)
- * pubkey: the public key to verify with (cannot be NULL)
- */
-SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_verify(
- const secp256k1_context* ctx,
- const unsigned char *sig64,
- const unsigned char *msg32,
- const secp256k1_pubkey *pubkey
-) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
-
-/** Recover an EC public key from a Schnorr signature created using
- * secp256k1_schnorr_sign.
- * Returns: 1: public key successfully recovered (which guarantees a correct
- * signature).
- * 0: otherwise.
- * Args: ctx: pointer to a context object, initialized for
- * verification (cannot be NULL)
- * Out: pubkey: pointer to a pubkey to set to the recovered public key
- * (cannot be NULL).
- * In: sig64: signature as 64 byte array (cannot be NULL)
- * msg32: the 32-byte message hash assumed to be signed (cannot
- * be NULL)
- */
-SECP256K1_API int secp256k1_schnorr_recover(
- const secp256k1_context* ctx,
- secp256k1_pubkey *pubkey,
- const unsigned char *sig64,
- const unsigned char *msg32
-) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
-
-/** Generate a nonce pair deterministically for use with
- * secp256k1_schnorr_partial_sign.
- * Returns: 1: valid nonce pair was generated.
- * 0: otherwise (nonce generation function failed)
- * Args: ctx: pointer to a context object, initialized for signing
- * (cannot be NULL)
- * Out: pubnonce: public side of the nonce (cannot be NULL)
- * privnonce32: private side of the nonce (32 byte) (cannot be NULL)
- * In: msg32: the 32-byte message hash assumed to be signed (cannot
- * be NULL)
- * sec32: the 32-byte private key (cannot be NULL)
- * noncefp: pointer to a nonce generation function. If NULL,
- * secp256k1_nonce_function_default is used
- * noncedata: pointer to arbitrary data used by the nonce generation
- * function (can be NULL)
- *
- * Do not use the output as a private/public key pair for signing/validation.
- */
-SECP256K1_API int secp256k1_schnorr_generate_nonce_pair(
- const secp256k1_context* ctx,
- secp256k1_pubkey *pubnonce,
- unsigned char *privnonce32,
- const unsigned char *msg32,
- const unsigned char *sec32,
- secp256k1_nonce_function noncefp,
- const void* noncedata
-) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
-
-/** Produce a partial Schnorr signature, which can be combined using
- * secp256k1_schnorr_partial_combine, to end up with a full signature that is
- * verifiable using secp256k1_schnorr_verify.
- * Returns: 1: signature created successfully.
- * 0: no valid signature exists with this combination of keys, nonces
- * and message (chance around 1 in 2^128)
- * -1: invalid private key, nonce, or public nonces.
- * Args: ctx: pointer to context object, initialized for signing (cannot
- * be NULL)
- * Out: sig64: pointer to 64-byte array to put partial signature in
- * In: msg32: pointer to 32-byte message to sign
- * sec32: pointer to 32-byte private key
- * pubnonce_others: pointer to pubkey containing the sum of the other's
- * nonces (see secp256k1_ec_pubkey_combine)
- * secnonce32: pointer to 32-byte array containing our nonce
- *
- * The intended procedure for creating a multiparty signature is:
- * - Each signer S[i] with private key x[i] and public key Q[i] runs
- * secp256k1_schnorr_generate_nonce_pair to produce a pair (k[i],R[i]) of
- * private/public nonces.
- * - All signers communicate their public nonces to each other (revealing your
- * private nonce can lead to discovery of your private key, so it should be
- * considered secret).
- * - All signers combine all the public nonces they received (excluding their
- * own) using secp256k1_ec_pubkey_combine to obtain an
- * Rall[i] = sum(R[0..i-1,i+1..n]).
- * - All signers produce a partial signature using
- * secp256k1_schnorr_partial_sign, passing in their own private key x[i],
- * their own private nonce k[i], and the sum of the others' public nonces
- * Rall[i].
- * - All signers communicate their partial signatures to each other.
- * - Someone combines all partial signatures using
- * secp256k1_schnorr_partial_combine, to obtain a full signature.
- * - The resulting signature is validatable using secp256k1_schnorr_verify, with
- * public key equal to the result of secp256k1_ec_pubkey_combine of the
- * signers' public keys (sum(Q[0..n])).
- *
- * Note that secp256k1_schnorr_partial_combine and secp256k1_ec_pubkey_combine
- * function take their arguments in any order, and it is possible to
- * pre-combine several inputs already with one call, and add more inputs later
- * by calling the function again (they are commutative and associative).
- */
-SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_partial_sign(
- const secp256k1_context* ctx,
- unsigned char *sig64,
- const unsigned char *msg32,
- const unsigned char *sec32,
- const secp256k1_pubkey *pubnonce_others,
- const unsigned char *secnonce32
-) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6);
-
-/** Combine multiple Schnorr partial signatures.
- * Returns: 1: the passed signatures were successfully combined.
- * 0: the resulting signature is not valid (chance of 1 in 2^256)
- * -1: some inputs were invalid, or the signatures were not created
- * using the same set of nonces
- * Args: ctx: pointer to a context object
- * Out: sig64: pointer to a 64-byte array to place the combined signature
- * (cannot be NULL)
- * In: sig64sin: pointer to an array of n pointers to 64-byte input
- * signatures
- * n: the number of signatures to combine (at least 1)
- */
-SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_partial_combine(
- const secp256k1_context* ctx,
- unsigned char *sig64,
- const unsigned char * const * sig64sin,
- size_t n
-) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
-
-# ifdef __cplusplus
-}
-# endif
-
-#endif
diff --git a/src/secp256k1/src/ecdsa_impl.h b/src/secp256k1/src/ecdsa_impl.h
index d110b4bb1d..9a42e519bd 100644
--- a/src/secp256k1/src/ecdsa_impl.h
+++ b/src/secp256k1/src/ecdsa_impl.h
@@ -203,7 +203,9 @@ static int secp256k1_ecdsa_sig_serialize(unsigned char *sig, size_t *size, const
static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const secp256k1_scalar *sigr, const secp256k1_scalar *sigs, const secp256k1_ge *pubkey, const secp256k1_scalar *message) {
unsigned char c[32];
secp256k1_scalar sn, u1, u2;
+#if !defined(EXHAUSTIVE_TEST_ORDER)
secp256k1_fe xr;
+#endif
secp256k1_gej pubkeyj;
secp256k1_gej pr;
@@ -219,6 +221,21 @@ static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const
if (secp256k1_gej_is_infinity(&pr)) {
return 0;
}
+
+#if defined(EXHAUSTIVE_TEST_ORDER)
+{
+ secp256k1_scalar computed_r;
+ int overflow = 0;
+ secp256k1_ge pr_ge;
+ secp256k1_ge_set_gej(&pr_ge, &pr);
+ secp256k1_fe_normalize(&pr_ge.x);
+
+ secp256k1_fe_get_b32(c, &pr_ge.x);
+ secp256k1_scalar_set_b32(&computed_r, c, &overflow);
+ /* we fully expect overflow */
+ return secp256k1_scalar_eq(sigr, &computed_r);
+}
+#else
secp256k1_scalar_get_b32(c, sigr);
secp256k1_fe_set_b32(&xr, c);
@@ -252,6 +269,7 @@ static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const
return 1;
}
return 0;
+#endif
}
static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, secp256k1_scalar *sigr, secp256k1_scalar *sigs, const secp256k1_scalar *seckey, const secp256k1_scalar *message, const secp256k1_scalar *nonce, int *recid) {
diff --git a/src/secp256k1/src/ecmult_const_impl.h b/src/secp256k1/src/ecmult_const_impl.h
index 7a6a25318c..0db314c48e 100644
--- a/src/secp256k1/src/ecmult_const_impl.h
+++ b/src/secp256k1/src/ecmult_const_impl.h
@@ -78,7 +78,7 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w) {
/* Negative numbers will be negated to keep their bit representation below the maximum width */
flip = secp256k1_scalar_is_high(&s);
/* We add 1 to even numbers, 2 to odd ones, noting that negation flips parity */
- bit = flip ^ (s.d[0] & 1);
+ bit = flip ^ !secp256k1_scalar_is_even(&s);
/* We check for negative one, since adding 2 to it will cause an overflow */
secp256k1_scalar_negate(&neg_s, &s);
not_neg_one = !secp256k1_scalar_is_one(&neg_s);
diff --git a/src/secp256k1/src/ecmult_gen_impl.h b/src/secp256k1/src/ecmult_gen_impl.h
index b63c4d8662..35f2546077 100644
--- a/src/secp256k1/src/ecmult_gen_impl.h
+++ b/src/secp256k1/src/ecmult_gen_impl.h
@@ -77,7 +77,7 @@ static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx
secp256k1_gej_add_var(&numsbase, &numsbase, &nums_gej, NULL);
}
}
- secp256k1_ge_set_all_gej_var(1024, prec, precj, cb);
+ secp256k1_ge_set_all_gej_var(prec, precj, 1024, cb);
}
for (j = 0; j < 64; j++) {
for (i = 0; i < 16; i++) {
diff --git a/src/secp256k1/src/ecmult_impl.h b/src/secp256k1/src/ecmult_impl.h
index 81ae08e100..4e40104ad4 100644
--- a/src/secp256k1/src/ecmult_impl.h
+++ b/src/secp256k1/src/ecmult_impl.h
@@ -7,15 +7,29 @@
#ifndef _SECP256K1_ECMULT_IMPL_H_
#define _SECP256K1_ECMULT_IMPL_H_
+#include <string.h>
+
#include "group.h"
#include "scalar.h"
#include "ecmult.h"
-#include <string.h>
-
+#if defined(EXHAUSTIVE_TEST_ORDER)
+/* We need to lower these values for exhaustive tests because
+ * the tables cannot have infinities in them (this breaks the
+ * affine-isomorphism stuff which tracks z-ratios) */
+# if EXHAUSTIVE_TEST_ORDER > 128
+# define WINDOW_A 5
+# define WINDOW_G 8
+# elif EXHAUSTIVE_TEST_ORDER > 8
+# define WINDOW_A 4
+# define WINDOW_G 4
+# else
+# define WINDOW_A 2
+# define WINDOW_G 2
+# endif
+#else
/* optimal for 128-bit and 256-bit exponents. */
#define WINDOW_A 5
-
/** larger numbers may result in slightly better performance, at the cost of
exponentially larger precomputed tables. */
#ifdef USE_ENDOMORPHISM
@@ -25,6 +39,7 @@
/** One table for window size 16: 1.375 MiB. */
#define WINDOW_G 16
#endif
+#endif
/** The number of entries a table with precomputed multiples needs to have. */
#define ECMULT_TABLE_SIZE(w) (1 << ((w)-2))
@@ -103,7 +118,7 @@ static void secp256k1_ecmult_odd_multiples_table_storage_var(int n, secp256k1_ge
/* Compute the odd multiples in Jacobian form. */
secp256k1_ecmult_odd_multiples_table(n, prej, zr, a);
/* Convert them in batch to affine coordinates. */
- secp256k1_ge_set_table_gej_var(n, prea, prej, zr);
+ secp256k1_ge_set_table_gej_var(prea, prej, zr, n);
/* Convert them to compact storage form. */
for (i = 0; i < n; i++) {
secp256k1_ge_to_storage(&pre[i], &prea[i]);
diff --git a/src/secp256k1/src/field.h b/src/secp256k1/src/field.h
index c5ba074244..bbb1ee866c 100644
--- a/src/secp256k1/src/field.h
+++ b/src/secp256k1/src/field.h
@@ -30,6 +30,8 @@
#error "Please select field implementation"
#endif
+#include "util.h"
+
/** Normalize a field element. */
static void secp256k1_fe_normalize(secp256k1_fe *r);
@@ -50,6 +52,9 @@ static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r);
/** Set a field element equal to a small integer. Resulting field element is normalized. */
static void secp256k1_fe_set_int(secp256k1_fe *r, int a);
+/** Sets a field element equal to zero, initializing all fields. */
+static void secp256k1_fe_clear(secp256k1_fe *a);
+
/** Verify whether a field element is zero. Requires the input to be normalized. */
static int secp256k1_fe_is_zero(const secp256k1_fe *a);
@@ -110,7 +115,7 @@ static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *a);
/** Calculate the (modular) inverses of a batch of field elements. Requires the inputs' magnitudes to be
* at most 8. The output magnitudes are 1 (but not guaranteed to be normalized). The inputs and
* outputs must not overlap in memory. */
-static void secp256k1_fe_inv_all_var(size_t len, secp256k1_fe *r, const secp256k1_fe *a);
+static void secp256k1_fe_inv_all_var(secp256k1_fe *r, const secp256k1_fe *a, size_t len);
/** Convert a field element to the storage type. */
static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a);
diff --git a/src/secp256k1/src/field_impl.h b/src/secp256k1/src/field_impl.h
index 52cd902eb3..5127b279bc 100644
--- a/src/secp256k1/src/field_impl.h
+++ b/src/secp256k1/src/field_impl.h
@@ -260,7 +260,7 @@ static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *a) {
#endif
}
-static void secp256k1_fe_inv_all_var(size_t len, secp256k1_fe *r, const secp256k1_fe *a) {
+static void secp256k1_fe_inv_all_var(secp256k1_fe *r, const secp256k1_fe *a, size_t len) {
secp256k1_fe u;
size_t i;
if (len < 1) {
diff --git a/src/secp256k1/src/group.h b/src/secp256k1/src/group.h
index d515716744..4957b248fe 100644
--- a/src/secp256k1/src/group.h
+++ b/src/secp256k1/src/group.h
@@ -65,12 +65,12 @@ static void secp256k1_ge_neg(secp256k1_ge *r, const secp256k1_ge *a);
static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a);
/** Set a batch of group elements equal to the inputs given in jacobian coordinates */
-static void secp256k1_ge_set_all_gej_var(size_t len, secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_callback *cb);
+static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len, const secp256k1_callback *cb);
/** Set a batch of group elements equal to the inputs given in jacobian
* coordinates (with known z-ratios). zr must contain the known z-ratios such
* that mul(a[i].z, zr[i+1]) == a[i+1].z. zr[0] is ignored. */
-static void secp256k1_ge_set_table_gej_var(size_t len, secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr);
+static void secp256k1_ge_set_table_gej_var(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr, size_t len);
/** Bring a batch inputs given in jacobian coordinates (with known z-ratios) to
* the same global z "denominator". zr must contain the known z-ratios such
diff --git a/src/secp256k1/src/group_impl.h b/src/secp256k1/src/group_impl.h
index 3e9c4c410d..2e192b62fd 100644
--- a/src/secp256k1/src/group_impl.h
+++ b/src/secp256k1/src/group_impl.h
@@ -11,6 +11,53 @@
#include "field.h"
#include "group.h"
+/* These points can be generated in sage as follows:
+ *
+ * 0. Setup a worksheet with the following parameters.
+ * b = 4 # whatever CURVE_B will be set to
+ * F = FiniteField (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F)
+ * C = EllipticCurve ([F (0), F (b)])
+ *
+ * 1. Determine all the small orders available to you. (If there are
+ * no satisfactory ones, go back and change b.)
+ * print C.order().factor(limit=1000)
+ *
+ * 2. Choose an order as one of the prime factors listed in the above step.
+ * (You can also multiply some to get a composite order, though the
+ * tests will crash trying to invert scalars during signing.) We take a
+ * random point and scale it to drop its order to the desired value.
+ * There is some probability this won't work; just try again.
+ * order = 199
+ * P = C.random_point()
+ * P = (int(P.order()) / int(order)) * P
+ * assert(P.order() == order)
+ *
+ * 3. Print the values. You'll need to use a vim macro or something to
+ * split the hex output into 4-byte chunks.
+ * print "%x %x" % P.xy()
+ */
+#if defined(EXHAUSTIVE_TEST_ORDER)
+# if EXHAUSTIVE_TEST_ORDER == 199
+const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
+ 0xFA7CC9A7, 0x0737F2DB, 0xA749DD39, 0x2B4FB069,
+ 0x3B017A7D, 0xA808C2F1, 0xFB12940C, 0x9EA66C18,
+ 0x78AC123A, 0x5ED8AEF3, 0x8732BC91, 0x1F3A2868,
+ 0x48DF246C, 0x808DAE72, 0xCFE52572, 0x7F0501ED
+);
+
+const int CURVE_B = 4;
+# elif EXHAUSTIVE_TEST_ORDER == 13
+const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
+ 0xedc60018, 0xa51a786b, 0x2ea91f4d, 0x4c9416c0,
+ 0x9de54c3b, 0xa1316554, 0x6cf4345c, 0x7277ef15,
+ 0x54cb1b6b, 0xdc8c1273, 0x087844ea, 0x43f4603e,
+ 0x0eaf9a43, 0xf6effe55, 0x939f806d, 0x37adf8ac
+);
+const int CURVE_B = 2;
+# else
+# error No known generator for the specified exhaustive test group order.
+# endif
+#else
/** Generator for secp256k1, value 'g' defined in
* "Standards for Efficient Cryptography" (SEC2) 2.7.1.
*/
@@ -21,8 +68,11 @@ static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
0xFD17B448UL, 0xA6855419UL, 0x9C47D08FUL, 0xFB10D4B8UL
);
+const int CURVE_B = 7;
+#endif
+
static void secp256k1_ge_set_gej_zinv(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zi) {
- secp256k1_fe zi2;
+ secp256k1_fe zi2;
secp256k1_fe zi3;
secp256k1_fe_sqr(&zi2, zi);
secp256k1_fe_mul(&zi3, &zi2, zi);
@@ -76,7 +126,7 @@ static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a) {
r->y = a->y;
}
-static void secp256k1_ge_set_all_gej_var(size_t len, secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_callback *cb) {
+static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len, const secp256k1_callback *cb) {
secp256k1_fe *az;
secp256k1_fe *azi;
size_t i;
@@ -89,7 +139,7 @@ static void secp256k1_ge_set_all_gej_var(size_t len, secp256k1_ge *r, const secp
}
azi = (secp256k1_fe *)checked_malloc(cb, sizeof(secp256k1_fe) * count);
- secp256k1_fe_inv_all_var(count, azi, az);
+ secp256k1_fe_inv_all_var(azi, az, count);
free(az);
count = 0;
@@ -102,7 +152,7 @@ static void secp256k1_ge_set_all_gej_var(size_t len, secp256k1_ge *r, const secp
free(azi);
}
-static void secp256k1_ge_set_table_gej_var(size_t len, secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr) {
+static void secp256k1_ge_set_table_gej_var(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr, size_t len) {
size_t i = len - 1;
secp256k1_fe zi;
@@ -145,9 +195,15 @@ static void secp256k1_ge_globalz_set_table_gej(size_t len, secp256k1_ge *r, secp
static void secp256k1_gej_set_infinity(secp256k1_gej *r) {
r->infinity = 1;
- secp256k1_fe_set_int(&r->x, 0);
- secp256k1_fe_set_int(&r->y, 0);
- secp256k1_fe_set_int(&r->z, 0);
+ secp256k1_fe_clear(&r->x);
+ secp256k1_fe_clear(&r->y);
+ secp256k1_fe_clear(&r->z);
+}
+
+static void secp256k1_ge_set_infinity(secp256k1_ge *r) {
+ r->infinity = 1;
+ secp256k1_fe_clear(&r->x);
+ secp256k1_fe_clear(&r->y);
}
static void secp256k1_gej_clear(secp256k1_gej *r) {
@@ -169,7 +225,7 @@ static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x) {
secp256k1_fe_sqr(&x2, x);
secp256k1_fe_mul(&x3, x, &x2);
r->infinity = 0;
- secp256k1_fe_set_int(&c, 7);
+ secp256k1_fe_set_int(&c, CURVE_B);
secp256k1_fe_add(&c, &x3);
return secp256k1_fe_sqrt(&r->y, &c);
}
@@ -228,7 +284,7 @@ static int secp256k1_gej_is_valid_var(const secp256k1_gej *a) {
secp256k1_fe_sqr(&x3, &a->x); secp256k1_fe_mul(&x3, &x3, &a->x);
secp256k1_fe_sqr(&z2, &a->z);
secp256k1_fe_sqr(&z6, &z2); secp256k1_fe_mul(&z6, &z6, &z2);
- secp256k1_fe_mul_int(&z6, 7);
+ secp256k1_fe_mul_int(&z6, CURVE_B);
secp256k1_fe_add(&x3, &z6);
secp256k1_fe_normalize_weak(&x3);
return secp256k1_fe_equal_var(&y2, &x3);
@@ -242,7 +298,7 @@ static int secp256k1_ge_is_valid_var(const secp256k1_ge *a) {
/* y^2 = x^3 + 7 */
secp256k1_fe_sqr(&y2, &a->y);
secp256k1_fe_sqr(&x3, &a->x); secp256k1_fe_mul(&x3, &x3, &a->x);
- secp256k1_fe_set_int(&c, 7);
+ secp256k1_fe_set_int(&c, CURVE_B);
secp256k1_fe_add(&x3, &c);
secp256k1_fe_normalize_weak(&x3);
return secp256k1_fe_equal_var(&y2, &x3);
@@ -260,7 +316,7 @@ static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, s
/** For secp256k1, 2Q is infinity if and only if Q is infinity. This is because if 2Q = infinity,
* Q must equal -Q, or that Q.y == -(Q.y), or Q.y is 0. For a point on y^2 = x^3 + 7 to have
* y=0, x^3 must be -7 mod p. However, -7 has no cube root mod p.
- *
+ *
* Having said this, if this function receives a point on a sextic twist, e.g. by
* a fault attack, it is possible for y to be 0. This happens for y^2 = x^3 + 6,
* since -6 does have a cube root mod p. For this point, this function will not set
diff --git a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java
index be67048fbe..1c67802fba 100644
--- a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java
+++ b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java
@@ -32,7 +32,7 @@ import static org.bitcoin.NativeSecp256k1Util.*;
* <p>You can find an example library that can be used for this at https://github.com/bitcoin/secp256k1</p>
*
* <p>To build secp256k1 for use with bitcoinj, run
- * `./configure --enable-jni --enable-experimental --enable-module-schnorr --enable-module-ecdh`
+ * `./configure --enable-jni --enable-experimental --enable-module-ecdh`
* and `make` then copy `.libs/libsecp256k1.so` to your system library path
* or point the JVM to the folder containing it with -Djava.library.path
* </p>
@@ -417,36 +417,6 @@ public class NativeSecp256k1 {
}
}
- public static byte[] schnorrSign(byte[] data, byte[] sec) throws AssertFailException {
- Preconditions.checkArgument(data.length == 32 && sec.length <= 32);
-
- ByteBuffer byteBuff = nativeECDSABuffer.get();
- if (byteBuff == null) {
- byteBuff = ByteBuffer.allocateDirect(32 + 32);
- byteBuff.order(ByteOrder.nativeOrder());
- nativeECDSABuffer.set(byteBuff);
- }
- byteBuff.rewind();
- byteBuff.put(data);
- byteBuff.put(sec);
-
- byte[][] retByteArray;
-
- r.lock();
- try {
- retByteArray = secp256k1_schnorr_sign(byteBuff, Secp256k1Context.getContext());
- } finally {
- r.unlock();
- }
-
- byte[] sigArr = retByteArray[0];
- int retVal = new BigInteger(new byte[] { retByteArray[1][0] }).intValue();
-
- assertEquals(sigArr.length, 64, "Got bad signature length.");
-
- return retVal == 0 ? new byte[0] : sigArr;
- }
-
private static native long secp256k1_ctx_clone(long context);
private static native int secp256k1_context_randomize(ByteBuffer byteBuff, long context);
@@ -471,8 +441,6 @@ public class NativeSecp256k1 {
private static native byte[][] secp256k1_ec_pubkey_parse(ByteBuffer byteBuff, long context, int inputLen);
- private static native byte[][] secp256k1_schnorr_sign(ByteBuffer byteBuff, long context);
-
private static native byte[][] secp256k1_ecdh(ByteBuffer byteBuff, long context, int inputLen);
}
diff --git a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java
index f18ce95810..c00d08899b 100644
--- a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java
+++ b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java
@@ -167,22 +167,6 @@ public class NativeSecp256k1Test {
assertEquals( result, true, "testRandomize");
}
- /**
- * This tests signSchnorr() for a valid secretkey
- */
- public static void testSchnorrSign() throws AssertFailException{
-
- byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing"
- byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
-
- byte[] resultArr = NativeSecp256k1.schnorrSign(data, sec);
- String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
- assertEquals( sigString, "C5E929AA058B982048760422D3B563749B7D0E50C5EBD8CD2FFC23214BD6A2F1B072C13880997EBA847CF20F2F90FCE07C1CA33A890A4127095A351127F8D95F" , "testSchnorrSign");
- }
-
- /**
- * This tests signSchnorr() for a valid secretkey
- */
public static void testCreateECDHSecret() throws AssertFailException{
byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
@@ -216,11 +200,6 @@ public class NativeSecp256k1Test {
testSignPos();
testSignNeg();
- //Test Schnorr (partial support) //TODO
- testSchnorrSign();
- //testSchnorrVerify
- //testSchnorrRecovery
-
//Test privKeyTweakAdd() 1
testPrivKeyTweakAdd_1();
diff --git a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c b/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c
index dba9524dd4..bcef7b32ce 100644
--- a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c
+++ b/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c
@@ -5,7 +5,6 @@
#include "include/secp256k1.h"
#include "include/secp256k1_ecdh.h"
#include "include/secp256k1_recovery.h"
-#include "include/secp256k1_schnorr.h"
SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ctx_1clone
@@ -333,39 +332,6 @@ SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1p
return 0;
}
-SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1schnorr_1sign
- (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
-{
- secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
- unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
- unsigned char* secKey = (unsigned char*) (data + 32);
-
- jobjectArray retArray;
- jbyteArray sigArray, intsByteArray;
- unsigned char intsarray[1];
- unsigned char sig[64];
-
- int ret = secp256k1_schnorr_sign(ctx, sig, data, secKey, NULL, NULL);
-
- intsarray[0] = ret;
-
- retArray = (*env)->NewObjectArray(env, 2,
- (*env)->FindClass(env, "[B"),
- (*env)->NewByteArray(env, 1));
-
- sigArray = (*env)->NewByteArray(env, 64);
- (*env)->SetByteArrayRegion(env, sigArray, 0, 64, (jbyte*)sig);
- (*env)->SetObjectArrayElement(env, retArray, 0, sigArray);
-
- intsByteArray = (*env)->NewByteArray(env, 1);
- (*env)->SetByteArrayRegion(env, intsByteArray, 0, 1, (jbyte*)intsarray);
- (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
-
- (void)classObject;
-
- return retArray;
-}
-
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdh
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen)
{
diff --git a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h b/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h
index 4125a1f523..fe613c9e9e 100644
--- a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h
+++ b/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h
@@ -106,14 +106,6 @@ SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1e
/*
* Class: org_bitcoin_NativeSecp256k1
- * Method: secp256k1_schnorr_sign
- * Signature: (Ljava/nio/ByteBuffer;JI)[[B
- */
-SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1schnorr_1sign
- (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l);
-
-/*
- * Class: org_bitcoin_NativeSecp256k1
* Method: secp256k1_ecdh
* Signature: (Ljava/nio/ByteBuffer;JI)[[B
*/
diff --git a/src/secp256k1/src/modules/recovery/main_impl.h b/src/secp256k1/src/modules/recovery/main_impl.h
index ec42f4bb6c..86f2f0cb2b 100644..100755
--- a/src/secp256k1/src/modules/recovery/main_impl.h
+++ b/src/secp256k1/src/modules/recovery/main_impl.h
@@ -138,16 +138,15 @@ int secp256k1_ecdsa_sign_recoverable(const secp256k1_context* ctx, secp256k1_ecd
secp256k1_scalar_set_b32(&sec, seckey, &overflow);
/* Fail if the secret key is invalid. */
if (!overflow && !secp256k1_scalar_is_zero(&sec)) {
+ unsigned char nonce32[32];
unsigned int count = 0;
secp256k1_scalar_set_b32(&msg, msg32, NULL);
while (1) {
- unsigned char nonce32[32];
ret = noncefp(nonce32, msg32, seckey, NULL, (void*)noncedata, count);
if (!ret) {
break;
}
secp256k1_scalar_set_b32(&non, nonce32, &overflow);
- memset(nonce32, 0, 32);
if (!secp256k1_scalar_is_zero(&non) && !overflow) {
if (secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, &recid)) {
break;
@@ -155,6 +154,7 @@ int secp256k1_ecdsa_sign_recoverable(const secp256k1_context* ctx, secp256k1_ecd
}
count++;
}
+ memset(nonce32, 0, 32);
secp256k1_scalar_clear(&msg);
secp256k1_scalar_clear(&non);
secp256k1_scalar_clear(&sec);
diff --git a/src/secp256k1/src/modules/schnorr/Makefile.am.include b/src/secp256k1/src/modules/schnorr/Makefile.am.include
deleted file mode 100644
index f1af8e8325..0000000000
--- a/src/secp256k1/src/modules/schnorr/Makefile.am.include
+++ /dev/null
@@ -1,10 +0,0 @@
-include_HEADERS += include/secp256k1_schnorr.h
-noinst_HEADERS += src/modules/schnorr/main_impl.h
-noinst_HEADERS += src/modules/schnorr/schnorr.h
-noinst_HEADERS += src/modules/schnorr/schnorr_impl.h
-noinst_HEADERS += src/modules/schnorr/tests_impl.h
-if USE_BENCHMARK
-noinst_PROGRAMS += bench_schnorr_verify
-bench_schnorr_verify_SOURCES = src/bench_schnorr_verify.c
-bench_schnorr_verify_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB)
-endif
diff --git a/src/secp256k1/src/modules/schnorr/main_impl.h b/src/secp256k1/src/modules/schnorr/main_impl.h
deleted file mode 100644
index fa176a1767..0000000000
--- a/src/secp256k1/src/modules/schnorr/main_impl.h
+++ /dev/null
@@ -1,164 +0,0 @@
-/**********************************************************************
- * Copyright (c) 2014-2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
-
-#ifndef SECP256K1_MODULE_SCHNORR_MAIN
-#define SECP256K1_MODULE_SCHNORR_MAIN
-
-#include "include/secp256k1_schnorr.h"
-#include "modules/schnorr/schnorr_impl.h"
-
-static void secp256k1_schnorr_msghash_sha256(unsigned char *h32, const unsigned char *r32, const unsigned char *msg32) {
- secp256k1_sha256_t sha;
- secp256k1_sha256_initialize(&sha);
- secp256k1_sha256_write(&sha, r32, 32);
- secp256k1_sha256_write(&sha, msg32, 32);
- secp256k1_sha256_finalize(&sha, h32);
-}
-
-static const unsigned char secp256k1_schnorr_algo16[17] = "Schnorr+SHA256 ";
-
-int secp256k1_schnorr_sign(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) {
- secp256k1_scalar sec, non;
- int ret = 0;
- int overflow = 0;
- unsigned int count = 0;
- VERIFY_CHECK(ctx != NULL);
- ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
- ARG_CHECK(msg32 != NULL);
- ARG_CHECK(sig64 != NULL);
- ARG_CHECK(seckey != NULL);
- if (noncefp == NULL) {
- noncefp = secp256k1_nonce_function_default;
- }
-
- secp256k1_scalar_set_b32(&sec, seckey, NULL);
- while (1) {
- unsigned char nonce32[32];
- ret = noncefp(nonce32, msg32, seckey, secp256k1_schnorr_algo16, (void*)noncedata, count);
- if (!ret) {
- break;
- }
- secp256k1_scalar_set_b32(&non, nonce32, &overflow);
- memset(nonce32, 0, 32);
- if (!secp256k1_scalar_is_zero(&non) && !overflow) {
- if (secp256k1_schnorr_sig_sign(&ctx->ecmult_gen_ctx, sig64, &sec, &non, NULL, secp256k1_schnorr_msghash_sha256, msg32)) {
- break;
- }
- }
- count++;
- }
- if (!ret) {
- memset(sig64, 0, 64);
- }
- secp256k1_scalar_clear(&non);
- secp256k1_scalar_clear(&sec);
- return ret;
-}
-
-int secp256k1_schnorr_verify(const secp256k1_context* ctx, const unsigned char *sig64, const unsigned char *msg32, const secp256k1_pubkey *pubkey) {
- secp256k1_ge q;
- VERIFY_CHECK(ctx != NULL);
- ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
- ARG_CHECK(msg32 != NULL);
- ARG_CHECK(sig64 != NULL);
- ARG_CHECK(pubkey != NULL);
-
- secp256k1_pubkey_load(ctx, &q, pubkey);
- return secp256k1_schnorr_sig_verify(&ctx->ecmult_ctx, sig64, &q, secp256k1_schnorr_msghash_sha256, msg32);
-}
-
-int secp256k1_schnorr_recover(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *sig64, const unsigned char *msg32) {
- secp256k1_ge q;
-
- VERIFY_CHECK(ctx != NULL);
- ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
- ARG_CHECK(msg32 != NULL);
- ARG_CHECK(sig64 != NULL);
- ARG_CHECK(pubkey != NULL);
-
- if (secp256k1_schnorr_sig_recover(&ctx->ecmult_ctx, sig64, &q, secp256k1_schnorr_msghash_sha256, msg32)) {
- secp256k1_pubkey_save(pubkey, &q);
- return 1;
- } else {
- memset(pubkey, 0, sizeof(*pubkey));
- return 0;
- }
-}
-
-int secp256k1_schnorr_generate_nonce_pair(const secp256k1_context* ctx, secp256k1_pubkey *pubnonce, unsigned char *privnonce32, const unsigned char *sec32, const unsigned char *msg32, secp256k1_nonce_function noncefp, const void* noncedata) {
- int count = 0;
- int ret = 1;
- secp256k1_gej Qj;
- secp256k1_ge Q;
- secp256k1_scalar sec;
-
- VERIFY_CHECK(ctx != NULL);
- ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
- ARG_CHECK(msg32 != NULL);
- ARG_CHECK(sec32 != NULL);
- ARG_CHECK(pubnonce != NULL);
- ARG_CHECK(privnonce32 != NULL);
-
- if (noncefp == NULL) {
- noncefp = secp256k1_nonce_function_default;
- }
-
- do {
- int overflow;
- ret = noncefp(privnonce32, sec32, msg32, secp256k1_schnorr_algo16, (void*)noncedata, count++);
- if (!ret) {
- break;
- }
- secp256k1_scalar_set_b32(&sec, privnonce32, &overflow);
- if (overflow || secp256k1_scalar_is_zero(&sec)) {
- continue;
- }
- secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &Qj, &sec);
- secp256k1_ge_set_gej(&Q, &Qj);
-
- secp256k1_pubkey_save(pubnonce, &Q);
- break;
- } while(1);
-
- secp256k1_scalar_clear(&sec);
- if (!ret) {
- memset(pubnonce, 0, sizeof(*pubnonce));
- }
- return ret;
-}
-
-int secp256k1_schnorr_partial_sign(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, const unsigned char *sec32, const secp256k1_pubkey *pubnonce_others, const unsigned char *secnonce32) {
- int overflow = 0;
- secp256k1_scalar sec, non;
- secp256k1_ge pubnon;
- VERIFY_CHECK(ctx != NULL);
- ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
- ARG_CHECK(msg32 != NULL);
- ARG_CHECK(sig64 != NULL);
- ARG_CHECK(sec32 != NULL);
- ARG_CHECK(secnonce32 != NULL);
- ARG_CHECK(pubnonce_others != NULL);
-
- secp256k1_scalar_set_b32(&sec, sec32, &overflow);
- if (overflow || secp256k1_scalar_is_zero(&sec)) {
- return -1;
- }
- secp256k1_scalar_set_b32(&non, secnonce32, &overflow);
- if (overflow || secp256k1_scalar_is_zero(&non)) {
- return -1;
- }
- secp256k1_pubkey_load(ctx, &pubnon, pubnonce_others);
- return secp256k1_schnorr_sig_sign(&ctx->ecmult_gen_ctx, sig64, &sec, &non, &pubnon, secp256k1_schnorr_msghash_sha256, msg32);
-}
-
-int secp256k1_schnorr_partial_combine(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char * const *sig64sin, size_t n) {
- ARG_CHECK(sig64 != NULL);
- ARG_CHECK(n >= 1);
- ARG_CHECK(sig64sin != NULL);
- return secp256k1_schnorr_sig_combine(sig64, n, sig64sin);
-}
-
-#endif
diff --git a/src/secp256k1/src/modules/schnorr/schnorr.h b/src/secp256k1/src/modules/schnorr/schnorr.h
deleted file mode 100644
index de18147bd5..0000000000
--- a/src/secp256k1/src/modules/schnorr/schnorr.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/***********************************************************************
- * Copyright (c) 2014-2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php. *
- ***********************************************************************/
-
-#ifndef _SECP256K1_MODULE_SCHNORR_H_
-#define _SECP256K1_MODULE_SCHNORR_H_
-
-#include "scalar.h"
-#include "group.h"
-
-typedef void (*secp256k1_schnorr_msghash)(unsigned char *h32, const unsigned char *r32, const unsigned char *msg32);
-
-static int secp256k1_schnorr_sig_sign(const secp256k1_ecmult_gen_context* ctx, unsigned char *sig64, const secp256k1_scalar *key, const secp256k1_scalar *nonce, const secp256k1_ge *pubnonce, secp256k1_schnorr_msghash hash, const unsigned char *msg32);
-static int secp256k1_schnorr_sig_verify(const secp256k1_ecmult_context* ctx, const unsigned char *sig64, const secp256k1_ge *pubkey, secp256k1_schnorr_msghash hash, const unsigned char *msg32);
-static int secp256k1_schnorr_sig_recover(const secp256k1_ecmult_context* ctx, const unsigned char *sig64, secp256k1_ge *pubkey, secp256k1_schnorr_msghash hash, const unsigned char *msg32);
-static int secp256k1_schnorr_sig_combine(unsigned char *sig64, size_t n, const unsigned char * const *sig64ins);
-
-#endif
diff --git a/src/secp256k1/src/modules/schnorr/schnorr_impl.h b/src/secp256k1/src/modules/schnorr/schnorr_impl.h
deleted file mode 100644
index e13ab6db7c..0000000000
--- a/src/secp256k1/src/modules/schnorr/schnorr_impl.h
+++ /dev/null
@@ -1,207 +0,0 @@
-/***********************************************************************
- * Copyright (c) 2014-2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php. *
- ***********************************************************************/
-
-#ifndef _SECP256K1_SCHNORR_IMPL_H_
-#define _SECP256K1_SCHNORR_IMPL_H_
-
-#include <string.h>
-
-#include "schnorr.h"
-#include "num.h"
-#include "field.h"
-#include "group.h"
-#include "ecmult.h"
-#include "ecmult_gen.h"
-
-/**
- * Custom Schnorr-based signature scheme. They support multiparty signing, public key
- * recovery and batch validation.
- *
- * Rationale for verifying R's y coordinate:
- * In order to support batch validation and public key recovery, the full R point must
- * be known to verifiers, rather than just its x coordinate. In order to not risk
- * being more strict in batch validation than normal validation, validators must be
- * required to reject signatures with incorrect y coordinate. This is only possible
- * by including a (relatively slow) field inverse, or a field square root. However,
- * batch validation offers potentially much higher benefits than this cost.
- *
- * Rationale for having an implicit y coordinate oddness:
- * If we commit to having the full R point known to verifiers, there are two mechanism.
- * Either include its oddness in the signature, or give it an implicit fixed value.
- * As the R y coordinate can be flipped by a simple negation of the nonce, we choose the
- * latter, as it comes with nearly zero impact on signing or validation performance, and
- * saves a byte in the signature.
- *
- * Signing:
- * Inputs: 32-byte message m, 32-byte scalar key x (!=0), 32-byte scalar nonce k (!=0)
- *
- * Compute point R = k * G. Reject nonce if R's y coordinate is odd (or negate nonce).
- * Compute 32-byte r, the serialization of R's x coordinate.
- * Compute scalar h = Hash(r || m). Reject nonce if h == 0 or h >= order.
- * Compute scalar s = k - h * x.
- * The signature is (r, s).
- *
- *
- * Verification:
- * Inputs: 32-byte message m, public key point Q, signature: (32-byte r, scalar s)
- *
- * Signature is invalid if s >= order.
- * Signature is invalid if r >= p.
- * Compute scalar h = Hash(r || m). Signature is invalid if h == 0 or h >= order.
- * Option 1 (faster for single verification):
- * Compute point R = h * Q + s * G. Signature is invalid if R is infinity or R's y coordinate is odd.
- * Signature is valid if the serialization of R's x coordinate equals r.
- * Option 2 (allows batch validation and pubkey recovery):
- * Decompress x coordinate r into point R, with odd y coordinate. Fail if R is not on the curve.
- * Signature is valid if R + h * Q + s * G == 0.
- */
-
-static int secp256k1_schnorr_sig_sign(const secp256k1_ecmult_gen_context* ctx, unsigned char *sig64, const secp256k1_scalar *key, const secp256k1_scalar *nonce, const secp256k1_ge *pubnonce, secp256k1_schnorr_msghash hash, const unsigned char *msg32) {
- secp256k1_gej Rj;
- secp256k1_ge Ra;
- unsigned char h32[32];
- secp256k1_scalar h, s;
- int overflow;
- secp256k1_scalar n;
-
- if (secp256k1_scalar_is_zero(key) || secp256k1_scalar_is_zero(nonce)) {
- return 0;
- }
- n = *nonce;
-
- secp256k1_ecmult_gen(ctx, &Rj, &n);
- if (pubnonce != NULL) {
- secp256k1_gej_add_ge(&Rj, &Rj, pubnonce);
- }
- secp256k1_ge_set_gej(&Ra, &Rj);
- secp256k1_fe_normalize(&Ra.y);
- if (secp256k1_fe_is_odd(&Ra.y)) {
- /* R's y coordinate is odd, which is not allowed (see rationale above).
- Force it to be even by negating the nonce. Note that this even works
- for multiparty signing, as the R point is known to all participants,
- which can all decide to flip the sign in unison, resulting in the
- overall R point to be negated too. */
- secp256k1_scalar_negate(&n, &n);
- }
- secp256k1_fe_normalize(&Ra.x);
- secp256k1_fe_get_b32(sig64, &Ra.x);
- hash(h32, sig64, msg32);
- overflow = 0;
- secp256k1_scalar_set_b32(&h, h32, &overflow);
- if (overflow || secp256k1_scalar_is_zero(&h)) {
- secp256k1_scalar_clear(&n);
- return 0;
- }
- secp256k1_scalar_mul(&s, &h, key);
- secp256k1_scalar_negate(&s, &s);
- secp256k1_scalar_add(&s, &s, &n);
- secp256k1_scalar_clear(&n);
- secp256k1_scalar_get_b32(sig64 + 32, &s);
- return 1;
-}
-
-static int secp256k1_schnorr_sig_verify(const secp256k1_ecmult_context* ctx, const unsigned char *sig64, const secp256k1_ge *pubkey, secp256k1_schnorr_msghash hash, const unsigned char *msg32) {
- secp256k1_gej Qj, Rj;
- secp256k1_ge Ra;
- secp256k1_fe Rx;
- secp256k1_scalar h, s;
- unsigned char hh[32];
- int overflow;
-
- if (secp256k1_ge_is_infinity(pubkey)) {
- return 0;
- }
- hash(hh, sig64, msg32);
- overflow = 0;
- secp256k1_scalar_set_b32(&h, hh, &overflow);
- if (overflow || secp256k1_scalar_is_zero(&h)) {
- return 0;
- }
- overflow = 0;
- secp256k1_scalar_set_b32(&s, sig64 + 32, &overflow);
- if (overflow) {
- return 0;
- }
- if (!secp256k1_fe_set_b32(&Rx, sig64)) {
- return 0;
- }
- secp256k1_gej_set_ge(&Qj, pubkey);
- secp256k1_ecmult(ctx, &Rj, &Qj, &h, &s);
- if (secp256k1_gej_is_infinity(&Rj)) {
- return 0;
- }
- secp256k1_ge_set_gej_var(&Ra, &Rj);
- secp256k1_fe_normalize_var(&Ra.y);
- if (secp256k1_fe_is_odd(&Ra.y)) {
- return 0;
- }
- return secp256k1_fe_equal_var(&Rx, &Ra.x);
-}
-
-static int secp256k1_schnorr_sig_recover(const secp256k1_ecmult_context* ctx, const unsigned char *sig64, secp256k1_ge *pubkey, secp256k1_schnorr_msghash hash, const unsigned char *msg32) {
- secp256k1_gej Qj, Rj;
- secp256k1_ge Ra;
- secp256k1_fe Rx;
- secp256k1_scalar h, s;
- unsigned char hh[32];
- int overflow;
-
- hash(hh, sig64, msg32);
- overflow = 0;
- secp256k1_scalar_set_b32(&h, hh, &overflow);
- if (overflow || secp256k1_scalar_is_zero(&h)) {
- return 0;
- }
- overflow = 0;
- secp256k1_scalar_set_b32(&s, sig64 + 32, &overflow);
- if (overflow) {
- return 0;
- }
- if (!secp256k1_fe_set_b32(&Rx, sig64)) {
- return 0;
- }
- if (!secp256k1_ge_set_xo_var(&Ra, &Rx, 0)) {
- return 0;
- }
- secp256k1_gej_set_ge(&Rj, &Ra);
- secp256k1_scalar_inverse_var(&h, &h);
- secp256k1_scalar_negate(&s, &s);
- secp256k1_scalar_mul(&s, &s, &h);
- secp256k1_ecmult(ctx, &Qj, &Rj, &h, &s);
- if (secp256k1_gej_is_infinity(&Qj)) {
- return 0;
- }
- secp256k1_ge_set_gej(pubkey, &Qj);
- return 1;
-}
-
-static int secp256k1_schnorr_sig_combine(unsigned char *sig64, size_t n, const unsigned char * const *sig64ins) {
- secp256k1_scalar s = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0);
- size_t i;
- for (i = 0; i < n; i++) {
- secp256k1_scalar si;
- int overflow;
- secp256k1_scalar_set_b32(&si, sig64ins[i] + 32, &overflow);
- if (overflow) {
- return -1;
- }
- if (i) {
- if (memcmp(sig64ins[i - 1], sig64ins[i], 32) != 0) {
- return -1;
- }
- }
- secp256k1_scalar_add(&s, &s, &si);
- }
- if (secp256k1_scalar_is_zero(&s)) {
- return 0;
- }
- memcpy(sig64, sig64ins[0], 32);
- secp256k1_scalar_get_b32(sig64 + 32, &s);
- secp256k1_scalar_clear(&s);
- return 1;
-}
-
-#endif
diff --git a/src/secp256k1/src/modules/schnorr/tests_impl.h b/src/secp256k1/src/modules/schnorr/tests_impl.h
deleted file mode 100644
index 5bd14a03e3..0000000000
--- a/src/secp256k1/src/modules/schnorr/tests_impl.h
+++ /dev/null
@@ -1,175 +0,0 @@
-/**********************************************************************
- * Copyright (c) 2014-2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
-
-#ifndef SECP256K1_MODULE_SCHNORR_TESTS
-#define SECP256K1_MODULE_SCHNORR_TESTS
-
-#include "include/secp256k1_schnorr.h"
-
-void test_schnorr_end_to_end(void) {
- unsigned char privkey[32];
- unsigned char message[32];
- unsigned char schnorr_signature[64];
- secp256k1_pubkey pubkey, recpubkey;
-
- /* Generate a random key and message. */
- {
- secp256k1_scalar key;
- random_scalar_order_test(&key);
- secp256k1_scalar_get_b32(privkey, &key);
- secp256k1_rand256_test(message);
- }
-
- /* Construct and verify corresponding public key. */
- CHECK(secp256k1_ec_seckey_verify(ctx, privkey) == 1);
- CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, privkey) == 1);
-
- /* Schnorr sign. */
- CHECK(secp256k1_schnorr_sign(ctx, schnorr_signature, message, privkey, NULL, NULL) == 1);
- CHECK(secp256k1_schnorr_verify(ctx, schnorr_signature, message, &pubkey) == 1);
- CHECK(secp256k1_schnorr_recover(ctx, &recpubkey, schnorr_signature, message) == 1);
- CHECK(memcmp(&pubkey, &recpubkey, sizeof(pubkey)) == 0);
- /* Destroy signature and verify again. */
- schnorr_signature[secp256k1_rand_bits(6)] += 1 + secp256k1_rand_int(255);
- CHECK(secp256k1_schnorr_verify(ctx, schnorr_signature, message, &pubkey) == 0);
- CHECK(secp256k1_schnorr_recover(ctx, &recpubkey, schnorr_signature, message) != 1 ||
- memcmp(&pubkey, &recpubkey, sizeof(pubkey)) != 0);
-}
-
-/** Horribly broken hash function. Do not use for anything but tests. */
-void test_schnorr_hash(unsigned char *h32, const unsigned char *r32, const unsigned char *msg32) {
- int i;
- for (i = 0; i < 32; i++) {
- h32[i] = r32[i] ^ msg32[i];
- }
-}
-
-void test_schnorr_sign_verify(void) {
- unsigned char msg32[32];
- unsigned char sig64[3][64];
- secp256k1_gej pubkeyj[3];
- secp256k1_ge pubkey[3];
- secp256k1_scalar nonce[3], key[3];
- int i = 0;
- int k;
-
- secp256k1_rand256_test(msg32);
-
- for (k = 0; k < 3; k++) {
- random_scalar_order_test(&key[k]);
-
- do {
- random_scalar_order_test(&nonce[k]);
- if (secp256k1_schnorr_sig_sign(&ctx->ecmult_gen_ctx, sig64[k], &key[k], &nonce[k], NULL, &test_schnorr_hash, msg32)) {
- break;
- }
- } while(1);
-
- secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pubkeyj[k], &key[k]);
- secp256k1_ge_set_gej_var(&pubkey[k], &pubkeyj[k]);
- CHECK(secp256k1_schnorr_sig_verify(&ctx->ecmult_ctx, sig64[k], &pubkey[k], &test_schnorr_hash, msg32));
-
- for (i = 0; i < 4; i++) {
- int pos = secp256k1_rand_bits(6);
- int mod = 1 + secp256k1_rand_int(255);
- sig64[k][pos] ^= mod;
- CHECK(secp256k1_schnorr_sig_verify(&ctx->ecmult_ctx, sig64[k], &pubkey[k], &test_schnorr_hash, msg32) == 0);
- sig64[k][pos] ^= mod;
- }
- }
-}
-
-void test_schnorr_threshold(void) {
- unsigned char msg[32];
- unsigned char sec[5][32];
- secp256k1_pubkey pub[5];
- unsigned char nonce[5][32];
- secp256k1_pubkey pubnonce[5];
- unsigned char sig[5][64];
- const unsigned char* sigs[5];
- unsigned char allsig[64];
- const secp256k1_pubkey* pubs[5];
- secp256k1_pubkey allpub;
- int n, i;
- int damage;
- int ret = 0;
-
- damage = secp256k1_rand_bits(1) ? (1 + secp256k1_rand_int(4)) : 0;
- secp256k1_rand256_test(msg);
- n = 2 + secp256k1_rand_int(4);
- for (i = 0; i < n; i++) {
- do {
- secp256k1_rand256_test(sec[i]);
- } while (!secp256k1_ec_seckey_verify(ctx, sec[i]));
- CHECK(secp256k1_ec_pubkey_create(ctx, &pub[i], sec[i]));
- CHECK(secp256k1_schnorr_generate_nonce_pair(ctx, &pubnonce[i], nonce[i], msg, sec[i], NULL, NULL));
- pubs[i] = &pub[i];
- }
- if (damage == 1) {
- nonce[secp256k1_rand_int(n)][secp256k1_rand_int(32)] ^= 1 + secp256k1_rand_int(255);
- } else if (damage == 2) {
- sec[secp256k1_rand_int(n)][secp256k1_rand_int(32)] ^= 1 + secp256k1_rand_int(255);
- }
- for (i = 0; i < n; i++) {
- secp256k1_pubkey allpubnonce;
- const secp256k1_pubkey *pubnonces[4];
- int j;
- for (j = 0; j < i; j++) {
- pubnonces[j] = &pubnonce[j];
- }
- for (j = i + 1; j < n; j++) {
- pubnonces[j - 1] = &pubnonce[j];
- }
- CHECK(secp256k1_ec_pubkey_combine(ctx, &allpubnonce, pubnonces, n - 1));
- ret |= (secp256k1_schnorr_partial_sign(ctx, sig[i], msg, sec[i], &allpubnonce, nonce[i]) != 1) * 1;
- sigs[i] = sig[i];
- }
- if (damage == 3) {
- sig[secp256k1_rand_int(n)][secp256k1_rand_bits(6)] ^= 1 + secp256k1_rand_int(255);
- }
- ret |= (secp256k1_ec_pubkey_combine(ctx, &allpub, pubs, n) != 1) * 2;
- if ((ret & 1) == 0) {
- ret |= (secp256k1_schnorr_partial_combine(ctx, allsig, sigs, n) != 1) * 4;
- }
- if (damage == 4) {
- allsig[secp256k1_rand_int(32)] ^= 1 + secp256k1_rand_int(255);
- }
- if ((ret & 7) == 0) {
- ret |= (secp256k1_schnorr_verify(ctx, allsig, msg, &allpub) != 1) * 8;
- }
- CHECK((ret == 0) == (damage == 0));
-}
-
-void test_schnorr_recovery(void) {
- unsigned char msg32[32];
- unsigned char sig64[64];
- secp256k1_ge Q;
-
- secp256k1_rand256_test(msg32);
- secp256k1_rand256_test(sig64);
- secp256k1_rand256_test(sig64 + 32);
- if (secp256k1_schnorr_sig_recover(&ctx->ecmult_ctx, sig64, &Q, &test_schnorr_hash, msg32) == 1) {
- CHECK(secp256k1_schnorr_sig_verify(&ctx->ecmult_ctx, sig64, &Q, &test_schnorr_hash, msg32) == 1);
- }
-}
-
-void run_schnorr_tests(void) {
- int i;
- for (i = 0; i < 32*count; i++) {
- test_schnorr_end_to_end();
- }
- for (i = 0; i < 32 * count; i++) {
- test_schnorr_sign_verify();
- }
- for (i = 0; i < 16 * count; i++) {
- test_schnorr_recovery();
- }
- for (i = 0; i < 10 * count; i++) {
- test_schnorr_threshold();
- }
-}
-
-#endif
diff --git a/src/secp256k1/src/scalar.h b/src/secp256k1/src/scalar.h
index b590ccd6dd..27e9d8375e 100644
--- a/src/secp256k1/src/scalar.h
+++ b/src/secp256k1/src/scalar.h
@@ -13,7 +13,9 @@
#include "libsecp256k1-config.h"
#endif
-#if defined(USE_SCALAR_4X64)
+#if defined(EXHAUSTIVE_TEST_ORDER)
+#include "scalar_low.h"
+#elif defined(USE_SCALAR_4X64)
#include "scalar_4x64.h"
#elif defined(USE_SCALAR_8X32)
#include "scalar_8x32.h"
diff --git a/src/secp256k1/src/scalar_4x64_impl.h b/src/secp256k1/src/scalar_4x64_impl.h
index aa2703dd23..56e7bd82af 100644
--- a/src/secp256k1/src/scalar_4x64_impl.h
+++ b/src/secp256k1/src/scalar_4x64_impl.h
@@ -282,8 +282,8 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"movq 56(%%rsi), %%r14\n"
/* Initialize r8,r9,r10 */
"movq 0(%%rsi), %%r8\n"
- "movq $0, %%r9\n"
- "movq $0, %%r10\n"
+ "xorq %%r9, %%r9\n"
+ "xorq %%r10, %%r10\n"
/* (r8,r9) += n0 * c0 */
"movq %8, %%rax\n"
"mulq %%r11\n"
@@ -291,7 +291,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"adcq %%rdx, %%r9\n"
/* extract m0 */
"movq %%r8, %q0\n"
- "movq $0, %%r8\n"
+ "xorq %%r8, %%r8\n"
/* (r9,r10) += l1 */
"addq 8(%%rsi), %%r9\n"
"adcq $0, %%r10\n"
@@ -309,7 +309,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"adcq $0, %%r8\n"
/* extract m1 */
"movq %%r9, %q1\n"
- "movq $0, %%r9\n"
+ "xorq %%r9, %%r9\n"
/* (r10,r8,r9) += l2 */
"addq 16(%%rsi), %%r10\n"
"adcq $0, %%r8\n"
@@ -332,7 +332,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"adcq $0, %%r9\n"
/* extract m2 */
"movq %%r10, %q2\n"
- "movq $0, %%r10\n"
+ "xorq %%r10, %%r10\n"
/* (r8,r9,r10) += l3 */
"addq 24(%%rsi), %%r8\n"
"adcq $0, %%r9\n"
@@ -355,7 +355,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"adcq $0, %%r10\n"
/* extract m3 */
"movq %%r8, %q3\n"
- "movq $0, %%r8\n"
+ "xorq %%r8, %%r8\n"
/* (r9,r10,r8) += n3 * c1 */
"movq %9, %%rax\n"
"mulq %%r14\n"
@@ -387,8 +387,8 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"movq %q11, %%r13\n"
/* Initialize (r8,r9,r10) */
"movq %q5, %%r8\n"
- "movq $0, %%r9\n"
- "movq $0, %%r10\n"
+ "xorq %%r9, %%r9\n"
+ "xorq %%r10, %%r10\n"
/* (r8,r9) += m4 * c0 */
"movq %12, %%rax\n"
"mulq %%r11\n"
@@ -396,7 +396,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"adcq %%rdx, %%r9\n"
/* extract p0 */
"movq %%r8, %q0\n"
- "movq $0, %%r8\n"
+ "xorq %%r8, %%r8\n"
/* (r9,r10) += m1 */
"addq %q6, %%r9\n"
"adcq $0, %%r10\n"
@@ -414,7 +414,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"adcq $0, %%r8\n"
/* extract p1 */
"movq %%r9, %q1\n"
- "movq $0, %%r9\n"
+ "xorq %%r9, %%r9\n"
/* (r10,r8,r9) += m2 */
"addq %q7, %%r10\n"
"adcq $0, %%r8\n"
@@ -472,7 +472,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"movq %%rax, 0(%q6)\n"
/* Move to (r8,r9) */
"movq %%rdx, %%r8\n"
- "movq $0, %%r9\n"
+ "xorq %%r9, %%r9\n"
/* (r8,r9) += p1 */
"addq %q2, %%r8\n"
"adcq $0, %%r9\n"
@@ -483,7 +483,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"adcq %%rdx, %%r9\n"
/* Extract r1 */
"movq %%r8, 8(%q6)\n"
- "movq $0, %%r8\n"
+ "xorq %%r8, %%r8\n"
/* (r9,r8) += p4 */
"addq %%r10, %%r9\n"
"adcq $0, %%r8\n"
@@ -492,7 +492,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"adcq $0, %%r8\n"
/* Extract r2 */
"movq %%r9, 16(%q6)\n"
- "movq $0, %%r9\n"
+ "xorq %%r9, %%r9\n"
/* (r8,r9) += p3 */
"addq %q4, %%r8\n"
"adcq $0, %%r9\n"
diff --git a/src/secp256k1/src/scalar_impl.h b/src/secp256k1/src/scalar_impl.h
index c5baf4df41..f5b2376407 100644
--- a/src/secp256k1/src/scalar_impl.h
+++ b/src/secp256k1/src/scalar_impl.h
@@ -14,7 +14,9 @@
#include "libsecp256k1-config.h"
#endif
-#if defined(USE_SCALAR_4X64)
+#if defined(EXHAUSTIVE_TEST_ORDER)
+#include "scalar_low_impl.h"
+#elif defined(USE_SCALAR_4X64)
#include "scalar_4x64_impl.h"
#elif defined(USE_SCALAR_8X32)
#include "scalar_8x32_impl.h"
@@ -31,17 +33,37 @@ static void secp256k1_scalar_get_num(secp256k1_num *r, const secp256k1_scalar *a
/** secp256k1 curve order, see secp256k1_ecdsa_const_order_as_fe in ecdsa_impl.h */
static void secp256k1_scalar_order_get_num(secp256k1_num *r) {
+#if defined(EXHAUSTIVE_TEST_ORDER)
+ static const unsigned char order[32] = {
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,EXHAUSTIVE_TEST_ORDER
+ };
+#else
static const unsigned char order[32] = {
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,
0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,
0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x41
};
+#endif
secp256k1_num_set_bin(r, order, 32);
}
#endif
static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) {
+#if defined(EXHAUSTIVE_TEST_ORDER)
+ int i;
+ *r = 0;
+ for (i = 0; i < EXHAUSTIVE_TEST_ORDER; i++)
+ if ((i * *x) % EXHAUSTIVE_TEST_ORDER == 1)
+ *r = i;
+ /* If this VERIFY_CHECK triggers we were given a noninvertible scalar (and thus
+ * have a composite group order; fix it in exhaustive_tests.c). */
+ VERIFY_CHECK(*r != 0);
+}
+#else
secp256k1_scalar *t;
int i;
/* First compute x ^ (2^N - 1) for some values of N. */
@@ -233,9 +255,9 @@ static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar
}
SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) {
- /* d[0] is present and is the lowest word for all representations */
return !(a->d[0] & 1);
}
+#endif
static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *x) {
#if defined(USE_SCALAR_INV_BUILTIN)
@@ -259,6 +281,18 @@ static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_sc
}
#ifdef USE_ENDOMORPHISM
+#if defined(EXHAUSTIVE_TEST_ORDER)
+/**
+ * Find k1 and k2 given k, such that k1 + k2 * lambda == k mod n; unlike in the
+ * full case we don't bother making k1 and k2 be small, we just want them to be
+ * nontrivial to get full test coverage for the exhaustive tests. We therefore
+ * (arbitrarily) set k2 = k + 5 and k1 = k - k2 * lambda.
+ */
+static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) {
+ *r2 = (*a + 5) % EXHAUSTIVE_TEST_ORDER;
+ *r1 = (*a + (EXHAUSTIVE_TEST_ORDER - *r2) * EXHAUSTIVE_TEST_LAMBDA) % EXHAUSTIVE_TEST_ORDER;
+}
+#else
/**
* The Secp256k1 curve has an endomorphism, where lambda * (x, y) = (beta * x, y), where
* lambda is {0x53,0x63,0xad,0x4c,0xc0,0x5c,0x30,0xe0,0xa5,0x26,0x1c,0x02,0x88,0x12,0x64,0x5a,
@@ -331,5 +365,6 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar
secp256k1_scalar_add(r1, r1, a);
}
#endif
+#endif
#endif
diff --git a/src/secp256k1/src/scalar_low.h b/src/secp256k1/src/scalar_low.h
new file mode 100644
index 0000000000..5574c44c7a
--- /dev/null
+++ b/src/secp256k1/src/scalar_low.h
@@ -0,0 +1,15 @@
+/**********************************************************************
+ * Copyright (c) 2015 Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef _SECP256K1_SCALAR_REPR_
+#define _SECP256K1_SCALAR_REPR_
+
+#include <stdint.h>
+
+/** A scalar modulo the group order of the secp256k1 curve. */
+typedef uint32_t secp256k1_scalar;
+
+#endif
diff --git a/src/secp256k1/src/scalar_low_impl.h b/src/secp256k1/src/scalar_low_impl.h
new file mode 100644
index 0000000000..4f94441f49
--- /dev/null
+++ b/src/secp256k1/src/scalar_low_impl.h
@@ -0,0 +1,114 @@
+/**********************************************************************
+ * Copyright (c) 2015 Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef _SECP256K1_SCALAR_REPR_IMPL_H_
+#define _SECP256K1_SCALAR_REPR_IMPL_H_
+
+#include "scalar.h"
+
+#include <string.h>
+
+SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) {
+ return !(*a & 1);
+}
+
+SECP256K1_INLINE static void secp256k1_scalar_clear(secp256k1_scalar *r) { *r = 0; }
+SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v) { *r = v; }
+
+SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) {
+ if (offset < 32)
+ return ((*a >> offset) & ((((uint32_t)1) << count) - 1));
+ else
+ return 0;
+}
+
+SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count) {
+ return secp256k1_scalar_get_bits(a, offset, count);
+}
+
+SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scalar *a) { return *a >= EXHAUSTIVE_TEST_ORDER; }
+
+static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) {
+ *r = (*a + *b) % EXHAUSTIVE_TEST_ORDER;
+ return *r < *b;
+}
+
+static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) {
+ if (flag && bit < 32)
+ *r += (1 << bit);
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0);
+#endif
+}
+
+static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b32, int *overflow) {
+ const int base = 0x100 % EXHAUSTIVE_TEST_ORDER;
+ int i;
+ *r = 0;
+ for (i = 0; i < 32; i++) {
+ *r = ((*r * base) + b32[i]) % EXHAUSTIVE_TEST_ORDER;
+ }
+ /* just deny overflow, it basically always happens */
+ if (overflow) *overflow = 0;
+}
+
+static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a) {
+ memset(bin, 0, 32);
+ bin[28] = *a >> 24; bin[29] = *a >> 16; bin[30] = *a >> 8; bin[31] = *a;
+}
+
+SECP256K1_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar *a) {
+ return *a == 0;
+}
+
+static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a) {
+ if (*a == 0) {
+ *r = 0;
+ } else {
+ *r = EXHAUSTIVE_TEST_ORDER - *a;
+ }
+}
+
+SECP256K1_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar *a) {
+ return *a == 1;
+}
+
+static int secp256k1_scalar_is_high(const secp256k1_scalar *a) {
+ return *a > EXHAUSTIVE_TEST_ORDER / 2;
+}
+
+static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
+ if (flag) secp256k1_scalar_negate(r, r);
+ return flag ? -1 : 1;
+}
+
+static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) {
+ *r = (*a * *b) % EXHAUSTIVE_TEST_ORDER;
+}
+
+static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) {
+ int ret;
+ VERIFY_CHECK(n > 0);
+ VERIFY_CHECK(n < 16);
+ ret = *r & ((1 << n) - 1);
+ *r >>= n;
+ return ret;
+}
+
+static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) {
+ *r = (*a * *a) % EXHAUSTIVE_TEST_ORDER;
+}
+
+static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) {
+ *r1 = *a;
+ *r2 = 0;
+}
+
+SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) {
+ return *a == *b;
+}
+
+#endif
diff --git a/src/secp256k1/src/secp256k1.c b/src/secp256k1/src/secp256k1.c
index 7973d60c36..fb8b882faa 100644..100755
--- a/src/secp256k1/src/secp256k1.c
+++ b/src/secp256k1/src/secp256k1.c
@@ -359,16 +359,15 @@ int secp256k1_ecdsa_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature
secp256k1_scalar_set_b32(&sec, seckey, &overflow);
/* Fail if the secret key is invalid. */
if (!overflow && !secp256k1_scalar_is_zero(&sec)) {
+ unsigned char nonce32[32];
unsigned int count = 0;
secp256k1_scalar_set_b32(&msg, msg32, NULL);
while (1) {
- unsigned char nonce32[32];
ret = noncefp(nonce32, msg32, seckey, NULL, (void*)noncedata, count);
if (!ret) {
break;
}
secp256k1_scalar_set_b32(&non, nonce32, &overflow);
- memset(nonce32, 0, 32);
if (!overflow && !secp256k1_scalar_is_zero(&non)) {
if (secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, NULL)) {
break;
@@ -376,6 +375,7 @@ int secp256k1_ecdsa_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature
}
count++;
}
+ memset(nonce32, 0, 32);
secp256k1_scalar_clear(&msg);
secp256k1_scalar_clear(&non);
secp256k1_scalar_clear(&sec);
diff --git a/src/secp256k1/src/tests.c b/src/secp256k1/src/tests.c
index b32cb90813..9ae7d30281 100644
--- a/src/secp256k1/src/tests.c
+++ b/src/secp256k1/src/tests.c
@@ -520,7 +520,7 @@ void test_num_mod(void) {
secp256k1_num order, n;
/* check that 0 mod anything is 0 */
- random_scalar_order_test(&s);
+ random_scalar_order_test(&s);
secp256k1_scalar_get_num(&order, &s);
secp256k1_scalar_set_int(&s, 0);
secp256k1_scalar_get_num(&n, &s);
@@ -535,7 +535,7 @@ void test_num_mod(void) {
CHECK(secp256k1_num_is_zero(&n));
/* check that increasing the number past 2^256 does not break this */
- random_scalar_order_test(&s);
+ random_scalar_order_test(&s);
secp256k1_scalar_get_num(&n, &s);
/* multiply by 2^8, which'll test this case with high probability */
for (i = 0; i < 8; ++i) {
@@ -568,7 +568,7 @@ void test_num_jacobi(void) {
/* we first need a scalar which is not a multiple of 5 */
do {
secp256k1_num fiven;
- random_scalar_order_test(&sqr);
+ random_scalar_order_test(&sqr);
secp256k1_scalar_get_num(&fiven, &five);
secp256k1_scalar_get_num(&n, &sqr);
secp256k1_num_mod(&n, &fiven);
@@ -587,7 +587,7 @@ void test_num_jacobi(void) {
/** test with secp group order as order */
secp256k1_scalar_order_get_num(&order);
- random_scalar_order_test(&sqr);
+ random_scalar_order_test(&sqr);
secp256k1_scalar_sqr(&sqr, &sqr);
/* test residue */
secp256k1_scalar_get_num(&n, &sqr);
@@ -1733,18 +1733,18 @@ void run_field_inv_all_var(void) {
secp256k1_fe x[16], xi[16], xii[16];
int i;
/* Check it's safe to call for 0 elements */
- secp256k1_fe_inv_all_var(0, xi, x);
+ secp256k1_fe_inv_all_var(xi, x, 0);
for (i = 0; i < count; i++) {
size_t j;
size_t len = secp256k1_rand_int(15) + 1;
for (j = 0; j < len; j++) {
random_fe_non_zero(&x[j]);
}
- secp256k1_fe_inv_all_var(len, xi, x);
+ secp256k1_fe_inv_all_var(xi, x, len);
for (j = 0; j < len; j++) {
CHECK(check_fe_inverse(&x[j], &xi[j]));
}
- secp256k1_fe_inv_all_var(len, xii, xi);
+ secp256k1_fe_inv_all_var(xii, xi, len);
for (j = 0; j < len; j++) {
CHECK(check_fe_equal(&x[j], &xii[j]));
}
@@ -1930,7 +1930,7 @@ void test_ge(void) {
zs[i] = gej[i].z;
}
}
- secp256k1_fe_inv_all_var(4 * runs + 1, zinv, zs);
+ secp256k1_fe_inv_all_var(zinv, zs, 4 * runs + 1);
free(zs);
}
@@ -2050,8 +2050,8 @@ void test_ge(void) {
secp256k1_fe_mul(&zr[i + 1], &zinv[i], &gej[i + 1].z);
}
}
- secp256k1_ge_set_table_gej_var(4 * runs + 1, ge_set_table, gej, zr);
- secp256k1_ge_set_all_gej_var(4 * runs + 1, ge_set_all, gej, &ctx->error_callback);
+ secp256k1_ge_set_table_gej_var(ge_set_table, gej, zr, 4 * runs + 1);
+ secp256k1_ge_set_all_gej_var(ge_set_all, gej, 4 * runs + 1, &ctx->error_callback);
for (i = 0; i < 4 * runs + 1; i++) {
secp256k1_fe s;
random_fe_non_zero(&s);
diff --git a/src/secp256k1/src/tests_exhaustive.c b/src/secp256k1/src/tests_exhaustive.c
new file mode 100644
index 0000000000..bda6ee475c
--- /dev/null
+++ b/src/secp256k1/src/tests_exhaustive.c
@@ -0,0 +1,329 @@
+/***********************************************************************
+ * Copyright (c) 2016 Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#if defined HAVE_CONFIG_H
+#include "libsecp256k1-config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <time.h>
+
+#undef USE_ECMULT_STATIC_PRECOMPUTATION
+
+#ifndef EXHAUSTIVE_TEST_ORDER
+/* see group_impl.h for allowable values */
+#define EXHAUSTIVE_TEST_ORDER 13
+#define EXHAUSTIVE_TEST_LAMBDA 9 /* cube root of 1 mod 13 */
+#endif
+
+#include "include/secp256k1.h"
+#include "group.h"
+#include "secp256k1.c"
+#include "testrand_impl.h"
+
+/** stolen from tests.c */
+void ge_equals_ge(const secp256k1_ge *a, const secp256k1_ge *b) {
+ CHECK(a->infinity == b->infinity);
+ if (a->infinity) {
+ return;
+ }
+ CHECK(secp256k1_fe_equal_var(&a->x, &b->x));
+ CHECK(secp256k1_fe_equal_var(&a->y, &b->y));
+}
+
+void ge_equals_gej(const secp256k1_ge *a, const secp256k1_gej *b) {
+ secp256k1_fe z2s;
+ secp256k1_fe u1, u2, s1, s2;
+ CHECK(a->infinity == b->infinity);
+ if (a->infinity) {
+ return;
+ }
+ /* Check a.x * b.z^2 == b.x && a.y * b.z^3 == b.y, to avoid inverses. */
+ secp256k1_fe_sqr(&z2s, &b->z);
+ secp256k1_fe_mul(&u1, &a->x, &z2s);
+ u2 = b->x; secp256k1_fe_normalize_weak(&u2);
+ secp256k1_fe_mul(&s1, &a->y, &z2s); secp256k1_fe_mul(&s1, &s1, &b->z);
+ s2 = b->y; secp256k1_fe_normalize_weak(&s2);
+ CHECK(secp256k1_fe_equal_var(&u1, &u2));
+ CHECK(secp256k1_fe_equal_var(&s1, &s2));
+}
+
+void random_fe(secp256k1_fe *x) {
+ unsigned char bin[32];
+ do {
+ secp256k1_rand256(bin);
+ if (secp256k1_fe_set_b32(x, bin)) {
+ return;
+ }
+ } while(1);
+}
+/** END stolen from tests.c */
+
+int secp256k1_nonce_function_smallint(unsigned char *nonce32, const unsigned char *msg32,
+ const unsigned char *key32, const unsigned char *algo16,
+ void *data, unsigned int attempt) {
+ secp256k1_scalar s;
+ int *idata = data;
+ (void)msg32;
+ (void)key32;
+ (void)algo16;
+ /* Some nonces cannot be used because they'd cause s and/or r to be zero.
+ * The signing function has retry logic here that just re-calls the nonce
+ * function with an increased `attempt`. So if attempt > 0 this means we
+ * need to change the nonce to avoid an infinite loop. */
+ if (attempt > 0) {
+ (*idata)++;
+ }
+ secp256k1_scalar_set_int(&s, *idata);
+ secp256k1_scalar_get_b32(nonce32, &s);
+ return 1;
+}
+
+#ifdef USE_ENDOMORPHISM
+void test_exhaustive_endomorphism(const secp256k1_ge *group, int order) {
+ int i;
+ for (i = 0; i < order; i++) {
+ secp256k1_ge res;
+ secp256k1_ge_mul_lambda(&res, &group[i]);
+ ge_equals_ge(&group[i * EXHAUSTIVE_TEST_LAMBDA % EXHAUSTIVE_TEST_ORDER], &res);
+ }
+}
+#endif
+
+void test_exhaustive_addition(const secp256k1_ge *group, const secp256k1_gej *groupj, int order) {
+ int i, j;
+
+ /* Sanity-check (and check infinity functions) */
+ CHECK(secp256k1_ge_is_infinity(&group[0]));
+ CHECK(secp256k1_gej_is_infinity(&groupj[0]));
+ for (i = 1; i < order; i++) {
+ CHECK(!secp256k1_ge_is_infinity(&group[i]));
+ CHECK(!secp256k1_gej_is_infinity(&groupj[i]));
+ }
+
+ /* Check all addition formulae */
+ for (j = 0; j < order; j++) {
+ secp256k1_fe fe_inv;
+ secp256k1_fe_inv(&fe_inv, &groupj[j].z);
+ for (i = 0; i < order; i++) {
+ secp256k1_ge zless_gej;
+ secp256k1_gej tmp;
+ /* add_var */
+ secp256k1_gej_add_var(&tmp, &groupj[i], &groupj[j], NULL);
+ ge_equals_gej(&group[(i + j) % order], &tmp);
+ /* add_ge */
+ if (j > 0) {
+ secp256k1_gej_add_ge(&tmp, &groupj[i], &group[j]);
+ ge_equals_gej(&group[(i + j) % order], &tmp);
+ }
+ /* add_ge_var */
+ secp256k1_gej_add_ge_var(&tmp, &groupj[i], &group[j], NULL);
+ ge_equals_gej(&group[(i + j) % order], &tmp);
+ /* add_zinv_var */
+ zless_gej.infinity = groupj[j].infinity;
+ zless_gej.x = groupj[j].x;
+ zless_gej.y = groupj[j].y;
+ secp256k1_gej_add_zinv_var(&tmp, &groupj[i], &zless_gej, &fe_inv);
+ ge_equals_gej(&group[(i + j) % order], &tmp);
+ }
+ }
+
+ /* Check doubling */
+ for (i = 0; i < order; i++) {
+ secp256k1_gej tmp;
+ if (i > 0) {
+ secp256k1_gej_double_nonzero(&tmp, &groupj[i], NULL);
+ ge_equals_gej(&group[(2 * i) % order], &tmp);
+ }
+ secp256k1_gej_double_var(&tmp, &groupj[i], NULL);
+ ge_equals_gej(&group[(2 * i) % order], &tmp);
+ }
+
+ /* Check negation */
+ for (i = 1; i < order; i++) {
+ secp256k1_ge tmp;
+ secp256k1_gej tmpj;
+ secp256k1_ge_neg(&tmp, &group[i]);
+ ge_equals_ge(&group[order - i], &tmp);
+ secp256k1_gej_neg(&tmpj, &groupj[i]);
+ ge_equals_gej(&group[order - i], &tmpj);
+ }
+}
+
+void test_exhaustive_ecmult(const secp256k1_context *ctx, const secp256k1_ge *group, const secp256k1_gej *groupj, int order) {
+ int i, j, r_log;
+ for (r_log = 1; r_log < order; r_log++) {
+ for (j = 0; j < order; j++) {
+ for (i = 0; i < order; i++) {
+ secp256k1_gej tmp;
+ secp256k1_scalar na, ng;
+ secp256k1_scalar_set_int(&na, i);
+ secp256k1_scalar_set_int(&ng, j);
+
+ secp256k1_ecmult(&ctx->ecmult_ctx, &tmp, &groupj[r_log], &na, &ng);
+ ge_equals_gej(&group[(i * r_log + j) % order], &tmp);
+
+ if (i > 0) {
+ secp256k1_ecmult_const(&tmp, &group[i], &ng);
+ ge_equals_gej(&group[(i * j) % order], &tmp);
+ }
+ }
+ }
+ }
+}
+
+void r_from_k(secp256k1_scalar *r, const secp256k1_ge *group, int k) {
+ secp256k1_fe x;
+ unsigned char x_bin[32];
+ k %= EXHAUSTIVE_TEST_ORDER;
+ x = group[k].x;
+ secp256k1_fe_normalize(&x);
+ secp256k1_fe_get_b32(x_bin, &x);
+ secp256k1_scalar_set_b32(r, x_bin, NULL);
+}
+
+void test_exhaustive_verify(const secp256k1_context *ctx, const secp256k1_ge *group, int order) {
+ int s, r, msg, key;
+ for (s = 1; s < order; s++) {
+ for (r = 1; r < order; r++) {
+ for (msg = 1; msg < order; msg++) {
+ for (key = 1; key < order; key++) {
+ secp256k1_ge nonconst_ge;
+ secp256k1_ecdsa_signature sig;
+ secp256k1_pubkey pk;
+ secp256k1_scalar sk_s, msg_s, r_s, s_s;
+ secp256k1_scalar s_times_k_s, msg_plus_r_times_sk_s;
+ int k, should_verify;
+ unsigned char msg32[32];
+
+ secp256k1_scalar_set_int(&s_s, s);
+ secp256k1_scalar_set_int(&r_s, r);
+ secp256k1_scalar_set_int(&msg_s, msg);
+ secp256k1_scalar_set_int(&sk_s, key);
+
+ /* Verify by hand */
+ /* Run through every k value that gives us this r and check that *one* works.
+ * Note there could be none, there could be multiple, ECDSA is weird. */
+ should_verify = 0;
+ for (k = 0; k < order; k++) {
+ secp256k1_scalar check_x_s;
+ r_from_k(&check_x_s, group, k);
+ if (r_s == check_x_s) {
+ secp256k1_scalar_set_int(&s_times_k_s, k);
+ secp256k1_scalar_mul(&s_times_k_s, &s_times_k_s, &s_s);
+ secp256k1_scalar_mul(&msg_plus_r_times_sk_s, &r_s, &sk_s);
+ secp256k1_scalar_add(&msg_plus_r_times_sk_s, &msg_plus_r_times_sk_s, &msg_s);
+ should_verify |= secp256k1_scalar_eq(&s_times_k_s, &msg_plus_r_times_sk_s);
+ }
+ }
+ /* nb we have a "high s" rule */
+ should_verify &= !secp256k1_scalar_is_high(&s_s);
+
+ /* Verify by calling verify */
+ secp256k1_ecdsa_signature_save(&sig, &r_s, &s_s);
+ memcpy(&nonconst_ge, &group[sk_s], sizeof(nonconst_ge));
+ secp256k1_pubkey_save(&pk, &nonconst_ge);
+ secp256k1_scalar_get_b32(msg32, &msg_s);
+ CHECK(should_verify ==
+ secp256k1_ecdsa_verify(ctx, &sig, msg32, &pk));
+ }
+ }
+ }
+ }
+}
+
+void test_exhaustive_sign(const secp256k1_context *ctx, const secp256k1_ge *group, int order) {
+ int i, j, k;
+
+ /* Loop */
+ for (i = 1; i < order; i++) { /* message */
+ for (j = 1; j < order; j++) { /* key */
+ for (k = 1; k < order; k++) { /* nonce */
+ secp256k1_ecdsa_signature sig;
+ secp256k1_scalar sk, msg, r, s, expected_r;
+ unsigned char sk32[32], msg32[32];
+ secp256k1_scalar_set_int(&msg, i);
+ secp256k1_scalar_set_int(&sk, j);
+ secp256k1_scalar_get_b32(sk32, &sk);
+ secp256k1_scalar_get_b32(msg32, &msg);
+
+ secp256k1_ecdsa_sign(ctx, &sig, msg32, sk32, secp256k1_nonce_function_smallint, &k);
+
+ secp256k1_ecdsa_signature_load(ctx, &r, &s, &sig);
+ /* Note that we compute expected_r *after* signing -- this is important
+ * because our nonce-computing function function might change k during
+ * signing. */
+ r_from_k(&expected_r, group, k);
+ CHECK(r == expected_r);
+ CHECK((k * s) % order == (i + r * j) % order ||
+ (k * (EXHAUSTIVE_TEST_ORDER - s)) % order == (i + r * j) % order);
+ }
+ }
+ }
+
+ /* We would like to verify zero-knowledge here by counting how often every
+ * possible (s, r) tuple appears, but because the group order is larger
+ * than the field order, when coercing the x-values to scalar values, some
+ * appear more often than others, so we are actually not zero-knowledge.
+ * (This effect also appears in the real code, but the difference is on the
+ * order of 1/2^128th the field order, so the deviation is not useful to a
+ * computationally bounded attacker.)
+ */
+}
+
+int main(void) {
+ int i;
+ secp256k1_gej groupj[EXHAUSTIVE_TEST_ORDER];
+ secp256k1_ge group[EXHAUSTIVE_TEST_ORDER];
+
+ /* Build context */
+ secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
+
+ /* TODO set z = 1, then do num_tests runs with random z values */
+
+ /* Generate the entire group */
+ secp256k1_gej_set_infinity(&groupj[0]);
+ secp256k1_ge_set_gej(&group[0], &groupj[0]);
+ for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) {
+ /* Set a different random z-value for each Jacobian point */
+ secp256k1_fe z;
+ random_fe(&z);
+
+ secp256k1_gej_add_ge(&groupj[i], &groupj[i - 1], &secp256k1_ge_const_g);
+ secp256k1_ge_set_gej(&group[i], &groupj[i]);
+ secp256k1_gej_rescale(&groupj[i], &z);
+
+ /* Verify against ecmult_gen */
+ {
+ secp256k1_scalar scalar_i;
+ secp256k1_gej generatedj;
+ secp256k1_ge generated;
+
+ secp256k1_scalar_set_int(&scalar_i, i);
+ secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &generatedj, &scalar_i);
+ secp256k1_ge_set_gej(&generated, &generatedj);
+
+ CHECK(group[i].infinity == 0);
+ CHECK(generated.infinity == 0);
+ CHECK(secp256k1_fe_equal_var(&generated.x, &group[i].x));
+ CHECK(secp256k1_fe_equal_var(&generated.y, &group[i].y));
+ }
+ }
+
+ /* Run the tests */
+#ifdef USE_ENDOMORPHISM
+ test_exhaustive_endomorphism(group, EXHAUSTIVE_TEST_ORDER);
+#endif
+ test_exhaustive_addition(group, groupj, EXHAUSTIVE_TEST_ORDER);
+ test_exhaustive_ecmult(ctx, group, groupj, EXHAUSTIVE_TEST_ORDER);
+ test_exhaustive_sign(ctx, group, EXHAUSTIVE_TEST_ORDER);
+ test_exhaustive_verify(ctx, group, EXHAUSTIVE_TEST_ORDER);
+
+ return 0;
+}
+
diff --git a/src/serialize.h b/src/serialize.h
index 04ab9aa2e7..2be6f005e3 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -13,6 +13,7 @@
#include <ios>
#include <limits>
#include <map>
+#include <memory>
#include <set>
#include <stdint.h>
#include <string>
@@ -25,6 +26,20 @@
static const unsigned int MAX_SIZE = 0x02000000;
/**
+ * Dummy data type to identify deserializing constructors.
+ *
+ * By convention, a constructor of a type T with signature
+ *
+ * template <typename Stream> T::T(deserialize_type, Stream& s)
+ *
+ * is a deserializing constructor, which builds the type by
+ * deserializing it from s. If T contains const fields, this
+ * is likely the only way to do so.
+ */
+struct deserialize_type {};
+constexpr deserialize_type deserialize {};
+
+/**
* Used to bypass the rule against non-const reference to temporary
* where it makes sense with wrappers such as CFlatData or CTxDB
*/
@@ -44,35 +59,6 @@ inline T* NCONST_PTR(const T* val)
return const_cast<T*>(val);
}
-/**
- * Get begin pointer of vector (non-const version).
- * @note These functions avoid the undefined case of indexing into an empty
- * vector, as well as that of indexing after the end of the vector.
- */
-template <typename V>
-inline typename V::value_type* begin_ptr(V& v)
-{
- return v.empty() ? NULL : &v[0];
-}
-/** Get begin pointer of vector (const version) */
-template <typename V>
-inline const typename V::value_type* begin_ptr(const V& v)
-{
- return v.empty() ? NULL : &v[0];
-}
-/** Get end pointer of vector (non-const version) */
-template <typename V>
-inline typename V::value_type* end_ptr(V& v)
-{
- return v.empty() ? NULL : (&v[0] + v.size());
-}
-/** Get end pointer of vector (const version) */
-template <typename V>
-inline const typename V::value_type* end_ptr(const V& v)
-{
- return v.empty() ? NULL : (&v[0] + v.size());
-}
-
/*
* Lowest-level serialization and conversion.
* @note Sizes of these types are verified in the tests
@@ -152,6 +138,8 @@ inline float ser_uint32_to_float(uint32_t y)
// i.e. anything that supports .read(char*, size_t) and .write(char*, size_t)
//
+class CSizeComputer;
+
enum
{
// primary actions
@@ -160,7 +148,8 @@ enum
SER_GETHASH = (1 << 2),
};
-#define READWRITE(obj) (::SerReadWrite(s, (obj), nType, nVersion, ser_action))
+#define READWRITE(obj) (::SerReadWrite(s, (obj), ser_action))
+#define READWRITEMANY(...) (::SerReadWriteMany(s, ser_action, __VA_ARGS__))
/**
* Implement three methods for serializable objects. These are actually wrappers over
@@ -168,63 +157,42 @@ enum
* code. Adding "ADD_SERIALIZE_METHODS" in the body of the class causes these wrappers to be
* added as members.
*/
-#define ADD_SERIALIZE_METHODS \
- size_t GetSerializeSize(int nType, int nVersion) const { \
- CSizeComputer s(nType, nVersion); \
- NCONST_PTR(this)->SerializationOp(s, CSerActionSerialize(), nType, nVersion);\
- return s.size(); \
- } \
- template<typename Stream> \
- void Serialize(Stream& s, int nType, int nVersion) const { \
- NCONST_PTR(this)->SerializationOp(s, CSerActionSerialize(), nType, nVersion);\
- } \
- template<typename Stream> \
- void Unserialize(Stream& s, int nType, int nVersion) { \
- SerializationOp(s, CSerActionUnserialize(), nType, nVersion); \
+#define ADD_SERIALIZE_METHODS \
+ template<typename Stream> \
+ void Serialize(Stream& s) const { \
+ NCONST_PTR(this)->SerializationOp(s, CSerActionSerialize()); \
+ } \
+ template<typename Stream> \
+ void Unserialize(Stream& s) { \
+ SerializationOp(s, CSerActionUnserialize()); \
}
-/*
- * Basic Types
- */
-inline unsigned int GetSerializeSize(char a, int, int=0) { return 1; }
-inline unsigned int GetSerializeSize(int8_t a, int, int=0) { return 1; }
-inline unsigned int GetSerializeSize(uint8_t a, int, int=0) { return 1; }
-inline unsigned int GetSerializeSize(int16_t a, int, int=0) { return 2; }
-inline unsigned int GetSerializeSize(uint16_t a, int, int=0) { return 2; }
-inline unsigned int GetSerializeSize(int32_t a, int, int=0) { return 4; }
-inline unsigned int GetSerializeSize(uint32_t a, int, int=0) { return 4; }
-inline unsigned int GetSerializeSize(int64_t a, int, int=0) { return 8; }
-inline unsigned int GetSerializeSize(uint64_t a, int, int=0) { return 8; }
-inline unsigned int GetSerializeSize(float a, int, int=0) { return 4; }
-inline unsigned int GetSerializeSize(double a, int, int=0) { return 8; }
-
-template<typename Stream> inline void Serialize(Stream& s, char a, int, int=0) { ser_writedata8(s, a); } // TODO Get rid of bare char
-template<typename Stream> inline void Serialize(Stream& s, int8_t a, int, int=0) { ser_writedata8(s, a); }
-template<typename Stream> inline void Serialize(Stream& s, uint8_t a, int, int=0) { ser_writedata8(s, a); }
-template<typename Stream> inline void Serialize(Stream& s, int16_t a, int, int=0) { ser_writedata16(s, a); }
-template<typename Stream> inline void Serialize(Stream& s, uint16_t a, int, int=0) { ser_writedata16(s, a); }
-template<typename Stream> inline void Serialize(Stream& s, int32_t a, int, int=0) { ser_writedata32(s, a); }
-template<typename Stream> inline void Serialize(Stream& s, uint32_t a, int, int=0) { ser_writedata32(s, a); }
-template<typename Stream> inline void Serialize(Stream& s, int64_t a, int, int=0) { ser_writedata64(s, a); }
-template<typename Stream> inline void Serialize(Stream& s, uint64_t a, int, int=0) { ser_writedata64(s, a); }
-template<typename Stream> inline void Serialize(Stream& s, float a, int, int=0) { ser_writedata32(s, ser_float_to_uint32(a)); }
-template<typename Stream> inline void Serialize(Stream& s, double a, int, int=0) { ser_writedata64(s, ser_double_to_uint64(a)); }
-
-template<typename Stream> inline void Unserialize(Stream& s, char& a, int, int=0) { a = ser_readdata8(s); } // TODO Get rid of bare char
-template<typename Stream> inline void Unserialize(Stream& s, int8_t& a, int, int=0) { a = ser_readdata8(s); }
-template<typename Stream> inline void Unserialize(Stream& s, uint8_t& a, int, int=0) { a = ser_readdata8(s); }
-template<typename Stream> inline void Unserialize(Stream& s, int16_t& a, int, int=0) { a = ser_readdata16(s); }
-template<typename Stream> inline void Unserialize(Stream& s, uint16_t& a, int, int=0) { a = ser_readdata16(s); }
-template<typename Stream> inline void Unserialize(Stream& s, int32_t& a, int, int=0) { a = ser_readdata32(s); }
-template<typename Stream> inline void Unserialize(Stream& s, uint32_t& a, int, int=0) { a = ser_readdata32(s); }
-template<typename Stream> inline void Unserialize(Stream& s, int64_t& a, int, int=0) { a = ser_readdata64(s); }
-template<typename Stream> inline void Unserialize(Stream& s, uint64_t& a, int, int=0) { a = ser_readdata64(s); }
-template<typename Stream> inline void Unserialize(Stream& s, float& a, int, int=0) { a = ser_uint32_to_float(ser_readdata32(s)); }
-template<typename Stream> inline void Unserialize(Stream& s, double& a, int, int=0) { a = ser_uint64_to_double(ser_readdata64(s)); }
-
-inline unsigned int GetSerializeSize(bool a, int, int=0) { return sizeof(char); }
-template<typename Stream> inline void Serialize(Stream& s, bool a, int, int=0) { char f=a; ser_writedata8(s, f); }
-template<typename Stream> inline void Unserialize(Stream& s, bool& a, int, int=0) { char f=ser_readdata8(s); a=f; }
+template<typename Stream> inline void Serialize(Stream& s, char a ) { ser_writedata8(s, a); } // TODO Get rid of bare char
+template<typename Stream> inline void Serialize(Stream& s, int8_t a ) { ser_writedata8(s, a); }
+template<typename Stream> inline void Serialize(Stream& s, uint8_t a ) { ser_writedata8(s, a); }
+template<typename Stream> inline void Serialize(Stream& s, int16_t a ) { ser_writedata16(s, a); }
+template<typename Stream> inline void Serialize(Stream& s, uint16_t a) { ser_writedata16(s, a); }
+template<typename Stream> inline void Serialize(Stream& s, int32_t a ) { ser_writedata32(s, a); }
+template<typename Stream> inline void Serialize(Stream& s, uint32_t a) { ser_writedata32(s, a); }
+template<typename Stream> inline void Serialize(Stream& s, int64_t a ) { ser_writedata64(s, a); }
+template<typename Stream> inline void Serialize(Stream& s, uint64_t a) { ser_writedata64(s, a); }
+template<typename Stream> inline void Serialize(Stream& s, float a ) { ser_writedata32(s, ser_float_to_uint32(a)); }
+template<typename Stream> inline void Serialize(Stream& s, double a ) { ser_writedata64(s, ser_double_to_uint64(a)); }
+
+template<typename Stream> inline void Unserialize(Stream& s, char& a ) { a = ser_readdata8(s); } // TODO Get rid of bare char
+template<typename Stream> inline void Unserialize(Stream& s, int8_t& a ) { a = ser_readdata8(s); }
+template<typename Stream> inline void Unserialize(Stream& s, uint8_t& a ) { a = ser_readdata8(s); }
+template<typename Stream> inline void Unserialize(Stream& s, int16_t& a ) { a = ser_readdata16(s); }
+template<typename Stream> inline void Unserialize(Stream& s, uint16_t& a) { a = ser_readdata16(s); }
+template<typename Stream> inline void Unserialize(Stream& s, int32_t& a ) { a = ser_readdata32(s); }
+template<typename Stream> inline void Unserialize(Stream& s, uint32_t& a) { a = ser_readdata32(s); }
+template<typename Stream> inline void Unserialize(Stream& s, int64_t& a ) { a = ser_readdata64(s); }
+template<typename Stream> inline void Unserialize(Stream& s, uint64_t& a) { a = ser_readdata64(s); }
+template<typename Stream> inline void Unserialize(Stream& s, float& a ) { a = ser_uint32_to_float(ser_readdata32(s)); }
+template<typename Stream> inline void Unserialize(Stream& s, double& a ) { a = ser_uint64_to_double(ser_readdata64(s)); }
+
+template<typename Stream> inline void Serialize(Stream& s, bool a) { char f=a; ser_writedata8(s, f); }
+template<typename Stream> inline void Unserialize(Stream& s, bool& a) { char f=ser_readdata8(s); a=f; }
@@ -246,6 +214,8 @@ inline unsigned int GetSizeOfCompactSize(uint64_t nSize)
else return sizeof(unsigned char) + sizeof(uint64_t);
}
+inline void WriteCompactSize(CSizeComputer& os, uint64_t nSize);
+
template<typename Stream>
void WriteCompactSize(Stream& os, uint64_t nSize)
{
@@ -340,6 +310,9 @@ inline unsigned int GetSizeOfVarInt(I n)
return nRet;
}
+template<typename I>
+inline void WriteVarInt(CSizeComputer& os, I n);
+
template<typename Stream, typename I>
void WriteVarInt(Stream& os, I n)
{
@@ -389,33 +362,28 @@ public:
template <class T, class TAl>
explicit CFlatData(std::vector<T,TAl> &v)
{
- pbegin = (char*)begin_ptr(v);
- pend = (char*)end_ptr(v);
+ pbegin = (char*)v.data();
+ pend = (char*)(v.data() + v.size());
}
template <unsigned int N, typename T, typename S, typename D>
explicit CFlatData(prevector<N, T, S, D> &v)
{
- pbegin = (char*)begin_ptr(v);
- pend = (char*)end_ptr(v);
+ pbegin = (char*)v.data();
+ pend = (char*)(v.data() + v.size());
}
char* begin() { return pbegin; }
const char* begin() const { return pbegin; }
char* end() { return pend; }
const char* end() const { return pend; }
- unsigned int GetSerializeSize(int, int=0) const
- {
- return pend - pbegin;
- }
-
template<typename Stream>
- void Serialize(Stream& s, int, int=0) const
+ void Serialize(Stream& s) const
{
s.write(pbegin, pend - pbegin);
}
template<typename Stream>
- void Unserialize(Stream& s, int, int=0)
+ void Unserialize(Stream& s)
{
s.read(pbegin, pend - pbegin);
}
@@ -429,17 +397,13 @@ protected:
public:
CVarInt(I& nIn) : n(nIn) { }
- unsigned int GetSerializeSize(int, int) const {
- return GetSizeOfVarInt<I>(n);
- }
-
template<typename Stream>
- void Serialize(Stream &s, int, int) const {
+ void Serialize(Stream &s) const {
WriteVarInt<Stream,I>(s, n);
}
template<typename Stream>
- void Unserialize(Stream& s, int, int) {
+ void Unserialize(Stream& s) {
n = ReadVarInt<Stream,I>(s);
}
};
@@ -451,17 +415,13 @@ protected:
public:
CCompactSize(uint64_t& nIn) : n(nIn) { }
- unsigned int GetSerializeSize(int, int) const {
- return GetSizeOfCompactSize(n);
- }
-
template<typename Stream>
- void Serialize(Stream &s, int, int) const {
+ void Serialize(Stream &s) const {
WriteCompactSize<Stream>(s, n);
}
template<typename Stream>
- void Unserialize(Stream& s, int, int) {
+ void Unserialize(Stream& s) {
n = ReadCompactSize<Stream>(s);
}
};
@@ -472,10 +432,10 @@ class LimitedString
protected:
std::string& string;
public:
- LimitedString(std::string& string) : string(string) {}
+ LimitedString(std::string& _string) : string(_string) {}
template<typename Stream>
- void Unserialize(Stream& s, int, int=0)
+ void Unserialize(Stream& s)
{
size_t size = ReadCompactSize(s);
if (size > Limit) {
@@ -487,17 +447,12 @@ public:
}
template<typename Stream>
- void Serialize(Stream& s, int, int=0) const
+ void Serialize(Stream& s) const
{
WriteCompactSize(s, string.size());
if (!string.empty())
s.write((char*)&string[0], string.size());
}
-
- unsigned int GetSerializeSize(int, int=0) const
- {
- return GetSizeOfCompactSize(string.size()) + string.size();
- }
};
template<typename I>
@@ -510,85 +465,76 @@ CVarInt<I> WrapVarInt(I& n) { return CVarInt<I>(n); }
/**
* string
*/
-template<typename C> unsigned int GetSerializeSize(const std::basic_string<C>& str, int, int=0);
-template<typename Stream, typename C> void Serialize(Stream& os, const std::basic_string<C>& str, int, int=0);
-template<typename Stream, typename C> void Unserialize(Stream& is, std::basic_string<C>& str, int, int=0);
+template<typename Stream, typename C> void Serialize(Stream& os, const std::basic_string<C>& str);
+template<typename Stream, typename C> void Unserialize(Stream& is, std::basic_string<C>& str);
/**
* prevector
* prevectors of unsigned char are a special case and are intended to be serialized as a single opaque blob.
*/
-template<unsigned int N, typename T> unsigned int GetSerializeSize_impl(const prevector<N, T>& v, int nType, int nVersion, const unsigned char&);
-template<unsigned int N, typename T, typename V> unsigned int GetSerializeSize_impl(const prevector<N, T>& v, int nType, int nVersion, const V&);
-template<unsigned int N, typename T> inline unsigned int GetSerializeSize(const prevector<N, T>& v, int nType, int nVersion);
-template<typename Stream, unsigned int N, typename T> void Serialize_impl(Stream& os, const prevector<N, T>& v, int nType, int nVersion, const unsigned char&);
-template<typename Stream, unsigned int N, typename T, typename V> void Serialize_impl(Stream& os, const prevector<N, T>& v, int nType, int nVersion, const V&);
-template<typename Stream, unsigned int N, typename T> inline void Serialize(Stream& os, const prevector<N, T>& v, int nType, int nVersion);
-template<typename Stream, unsigned int N, typename T> void Unserialize_impl(Stream& is, prevector<N, T>& v, int nType, int nVersion, const unsigned char&);
-template<typename Stream, unsigned int N, typename T, typename V> void Unserialize_impl(Stream& is, prevector<N, T>& v, int nType, int nVersion, const V&);
-template<typename Stream, unsigned int N, typename T> inline void Unserialize(Stream& is, prevector<N, T>& v, int nType, int nVersion);
+template<typename Stream, unsigned int N, typename T> void Serialize_impl(Stream& os, const prevector<N, T>& v, const unsigned char&);
+template<typename Stream, unsigned int N, typename T, typename V> void Serialize_impl(Stream& os, const prevector<N, T>& v, const V&);
+template<typename Stream, unsigned int N, typename T> inline void Serialize(Stream& os, const prevector<N, T>& v);
+template<typename Stream, unsigned int N, typename T> void Unserialize_impl(Stream& is, prevector<N, T>& v, const unsigned char&);
+template<typename Stream, unsigned int N, typename T, typename V> void Unserialize_impl(Stream& is, prevector<N, T>& v, const V&);
+template<typename Stream, unsigned int N, typename T> inline void Unserialize(Stream& is, prevector<N, T>& v);
/**
* vector
* vectors of unsigned char are a special case and are intended to be serialized as a single opaque blob.
*/
-template<typename T, typename A> unsigned int GetSerializeSize_impl(const std::vector<T, A>& v, int nType, int nVersion, const unsigned char&);
-template<typename T, typename A, typename V> unsigned int GetSerializeSize_impl(const std::vector<T, A>& v, int nType, int nVersion, const V&);
-template<typename T, typename A> inline unsigned int GetSerializeSize(const std::vector<T, A>& v, int nType, int nVersion);
-template<typename Stream, typename T, typename A> void Serialize_impl(Stream& os, const std::vector<T, A>& v, int nType, int nVersion, const unsigned char&);
-template<typename Stream, typename T, typename A, typename V> void Serialize_impl(Stream& os, const std::vector<T, A>& v, int nType, int nVersion, const V&);
-template<typename Stream, typename T, typename A> inline void Serialize(Stream& os, const std::vector<T, A>& v, int nType, int nVersion);
-template<typename Stream, typename T, typename A> void Unserialize_impl(Stream& is, std::vector<T, A>& v, int nType, int nVersion, const unsigned char&);
-template<typename Stream, typename T, typename A, typename V> void Unserialize_impl(Stream& is, std::vector<T, A>& v, int nType, int nVersion, const V&);
-template<typename Stream, typename T, typename A> inline void Unserialize(Stream& is, std::vector<T, A>& v, int nType, int nVersion);
+template<typename Stream, typename T, typename A> void Serialize_impl(Stream& os, const std::vector<T, A>& v, const unsigned char&);
+template<typename Stream, typename T, typename A, typename V> void Serialize_impl(Stream& os, const std::vector<T, A>& v, const V&);
+template<typename Stream, typename T, typename A> inline void Serialize(Stream& os, const std::vector<T, A>& v);
+template<typename Stream, typename T, typename A> void Unserialize_impl(Stream& is, std::vector<T, A>& v, const unsigned char&);
+template<typename Stream, typename T, typename A, typename V> void Unserialize_impl(Stream& is, std::vector<T, A>& v, const V&);
+template<typename Stream, typename T, typename A> inline void Unserialize(Stream& is, std::vector<T, A>& v);
/**
* pair
*/
-template<typename K, typename T> unsigned int GetSerializeSize(const std::pair<K, T>& item, int nType, int nVersion);
-template<typename Stream, typename K, typename T> void Serialize(Stream& os, const std::pair<K, T>& item, int nType, int nVersion);
-template<typename Stream, typename K, typename T> void Unserialize(Stream& is, std::pair<K, T>& item, int nType, int nVersion);
+template<typename Stream, typename K, typename T> void Serialize(Stream& os, const std::pair<K, T>& item);
+template<typename Stream, typename K, typename T> void Unserialize(Stream& is, std::pair<K, T>& item);
/**
* map
*/
-template<typename K, typename T, typename Pred, typename A> unsigned int GetSerializeSize(const std::map<K, T, Pred, A>& m, int nType, int nVersion);
-template<typename Stream, typename K, typename T, typename Pred, typename A> void Serialize(Stream& os, const std::map<K, T, Pred, A>& m, int nType, int nVersion);
-template<typename Stream, typename K, typename T, typename Pred, typename A> void Unserialize(Stream& is, std::map<K, T, Pred, A>& m, int nType, int nVersion);
+template<typename Stream, typename K, typename T, typename Pred, typename A> void Serialize(Stream& os, const std::map<K, T, Pred, A>& m);
+template<typename Stream, typename K, typename T, typename Pred, typename A> void Unserialize(Stream& is, std::map<K, T, Pred, A>& m);
/**
* set
*/
-template<typename K, typename Pred, typename A> unsigned int GetSerializeSize(const std::set<K, Pred, A>& m, int nType, int nVersion);
-template<typename Stream, typename K, typename Pred, typename A> void Serialize(Stream& os, const std::set<K, Pred, A>& m, int nType, int nVersion);
-template<typename Stream, typename K, typename Pred, typename A> void Unserialize(Stream& is, std::set<K, Pred, A>& m, int nType, int nVersion);
+template<typename Stream, typename K, typename Pred, typename A> void Serialize(Stream& os, const std::set<K, Pred, A>& m);
+template<typename Stream, typename K, typename Pred, typename A> void Unserialize(Stream& is, std::set<K, Pred, A>& m);
+/**
+ * shared_ptr
+ */
+template<typename Stream, typename T> void Serialize(Stream& os, const std::shared_ptr<const T>& p);
+template<typename Stream, typename T> void Unserialize(Stream& os, std::shared_ptr<const T>& p);
+/**
+ * unique_ptr
+ */
+template<typename Stream, typename T> void Serialize(Stream& os, const std::unique_ptr<const T>& p);
+template<typename Stream, typename T> void Unserialize(Stream& os, std::unique_ptr<const T>& p);
/**
* If none of the specialized versions above matched, default to calling member function.
- * "int nType" is changed to "long nType" to keep from getting an ambiguous overload error.
- * The compiler will only cast int to long if none of the other templates matched.
- * Thanks to Boost serialization for this idea.
*/
-template<typename T>
-inline unsigned int GetSerializeSize(const T& a, long nType, int nVersion)
-{
- return a.GetSerializeSize((int)nType, nVersion);
-}
-
template<typename Stream, typename T>
-inline void Serialize(Stream& os, const T& a, long nType, int nVersion)
+inline void Serialize(Stream& os, const T& a)
{
- a.Serialize(os, (int)nType, nVersion);
+ a.Serialize(os);
}
template<typename Stream, typename T>
-inline void Unserialize(Stream& is, T& a, long nType, int nVersion)
+inline void Unserialize(Stream& is, T& a)
{
- a.Unserialize(is, (int)nType, nVersion);
+ a.Unserialize(is);
}
@@ -598,14 +544,8 @@ inline void Unserialize(Stream& is, T& a, long nType, int nVersion)
/**
* string
*/
-template<typename C>
-unsigned int GetSerializeSize(const std::basic_string<C>& str, int, int)
-{
- return GetSizeOfCompactSize(str.size()) + str.size() * sizeof(str[0]);
-}
-
template<typename Stream, typename C>
-void Serialize(Stream& os, const std::basic_string<C>& str, int, int)
+void Serialize(Stream& os, const std::basic_string<C>& str)
{
WriteCompactSize(os, str.size());
if (!str.empty())
@@ -613,7 +553,7 @@ void Serialize(Stream& os, const std::basic_string<C>& str, int, int)
}
template<typename Stream, typename C>
-void Unserialize(Stream& is, std::basic_string<C>& str, int, int)
+void Unserialize(Stream& is, std::basic_string<C>& str)
{
unsigned int nSize = ReadCompactSize(is);
str.resize(nSize);
@@ -626,30 +566,8 @@ void Unserialize(Stream& is, std::basic_string<C>& str, int, int)
/**
* prevector
*/
-template<unsigned int N, typename T>
-unsigned int GetSerializeSize_impl(const prevector<N, T>& v, int nType, int nVersion, const unsigned char&)
-{
- return (GetSizeOfCompactSize(v.size()) + v.size() * sizeof(T));
-}
-
-template<unsigned int N, typename T, typename V>
-unsigned int GetSerializeSize_impl(const prevector<N, T>& v, int nType, int nVersion, const V&)
-{
- unsigned int nSize = GetSizeOfCompactSize(v.size());
- for (typename prevector<N, T>::const_iterator vi = v.begin(); vi != v.end(); ++vi)
- nSize += GetSerializeSize((*vi), nType, nVersion);
- return nSize;
-}
-
-template<unsigned int N, typename T>
-inline unsigned int GetSerializeSize(const prevector<N, T>& v, int nType, int nVersion)
-{
- return GetSerializeSize_impl(v, nType, nVersion, T());
-}
-
-
template<typename Stream, unsigned int N, typename T>
-void Serialize_impl(Stream& os, const prevector<N, T>& v, int nType, int nVersion, const unsigned char&)
+void Serialize_impl(Stream& os, const prevector<N, T>& v, const unsigned char&)
{
WriteCompactSize(os, v.size());
if (!v.empty())
@@ -657,22 +575,22 @@ void Serialize_impl(Stream& os, const prevector<N, T>& v, int nType, int nVersio
}
template<typename Stream, unsigned int N, typename T, typename V>
-void Serialize_impl(Stream& os, const prevector<N, T>& v, int nType, int nVersion, const V&)
+void Serialize_impl(Stream& os, const prevector<N, T>& v, const V&)
{
WriteCompactSize(os, v.size());
for (typename prevector<N, T>::const_iterator vi = v.begin(); vi != v.end(); ++vi)
- ::Serialize(os, (*vi), nType, nVersion);
+ ::Serialize(os, (*vi));
}
template<typename Stream, unsigned int N, typename T>
-inline void Serialize(Stream& os, const prevector<N, T>& v, int nType, int nVersion)
+inline void Serialize(Stream& os, const prevector<N, T>& v)
{
- Serialize_impl(os, v, nType, nVersion, T());
+ Serialize_impl(os, v, T());
}
template<typename Stream, unsigned int N, typename T>
-void Unserialize_impl(Stream& is, prevector<N, T>& v, int nType, int nVersion, const unsigned char&)
+void Unserialize_impl(Stream& is, prevector<N, T>& v, const unsigned char&)
{
// Limit size per read so bogus size value won't cause out of memory
v.clear();
@@ -688,7 +606,7 @@ void Unserialize_impl(Stream& is, prevector<N, T>& v, int nType, int nVersion, c
}
template<typename Stream, unsigned int N, typename T, typename V>
-void Unserialize_impl(Stream& is, prevector<N, T>& v, int nType, int nVersion, const V&)
+void Unserialize_impl(Stream& is, prevector<N, T>& v, const V&)
{
v.clear();
unsigned int nSize = ReadCompactSize(is);
@@ -701,14 +619,14 @@ void Unserialize_impl(Stream& is, prevector<N, T>& v, int nType, int nVersion, c
nMid = nSize;
v.resize(nMid);
for (; i < nMid; i++)
- Unserialize(is, v[i], nType, nVersion);
+ Unserialize(is, v[i]);
}
}
template<typename Stream, unsigned int N, typename T>
-inline void Unserialize(Stream& is, prevector<N, T>& v, int nType, int nVersion)
+inline void Unserialize(Stream& is, prevector<N, T>& v)
{
- Unserialize_impl(is, v, nType, nVersion, T());
+ Unserialize_impl(is, v, T());
}
@@ -716,30 +634,8 @@ inline void Unserialize(Stream& is, prevector<N, T>& v, int nType, int nVersion)
/**
* vector
*/
-template<typename T, typename A>
-unsigned int GetSerializeSize_impl(const std::vector<T, A>& v, int nType, int nVersion, const unsigned char&)
-{
- return (GetSizeOfCompactSize(v.size()) + v.size() * sizeof(T));
-}
-
-template<typename T, typename A, typename V>
-unsigned int GetSerializeSize_impl(const std::vector<T, A>& v, int nType, int nVersion, const V&)
-{
- unsigned int nSize = GetSizeOfCompactSize(v.size());
- for (typename std::vector<T, A>::const_iterator vi = v.begin(); vi != v.end(); ++vi)
- nSize += GetSerializeSize((*vi), nType, nVersion);
- return nSize;
-}
-
-template<typename T, typename A>
-inline unsigned int GetSerializeSize(const std::vector<T, A>& v, int nType, int nVersion)
-{
- return GetSerializeSize_impl(v, nType, nVersion, T());
-}
-
-
template<typename Stream, typename T, typename A>
-void Serialize_impl(Stream& os, const std::vector<T, A>& v, int nType, int nVersion, const unsigned char&)
+void Serialize_impl(Stream& os, const std::vector<T, A>& v, const unsigned char&)
{
WriteCompactSize(os, v.size());
if (!v.empty())
@@ -747,22 +643,22 @@ void Serialize_impl(Stream& os, const std::vector<T, A>& v, int nType, int nVers
}
template<typename Stream, typename T, typename A, typename V>
-void Serialize_impl(Stream& os, const std::vector<T, A>& v, int nType, int nVersion, const V&)
+void Serialize_impl(Stream& os, const std::vector<T, A>& v, const V&)
{
WriteCompactSize(os, v.size());
for (typename std::vector<T, A>::const_iterator vi = v.begin(); vi != v.end(); ++vi)
- ::Serialize(os, (*vi), nType, nVersion);
+ ::Serialize(os, (*vi));
}
template<typename Stream, typename T, typename A>
-inline void Serialize(Stream& os, const std::vector<T, A>& v, int nType, int nVersion)
+inline void Serialize(Stream& os, const std::vector<T, A>& v)
{
- Serialize_impl(os, v, nType, nVersion, T());
+ Serialize_impl(os, v, T());
}
template<typename Stream, typename T, typename A>
-void Unserialize_impl(Stream& is, std::vector<T, A>& v, int nType, int nVersion, const unsigned char&)
+void Unserialize_impl(Stream& is, std::vector<T, A>& v, const unsigned char&)
{
// Limit size per read so bogus size value won't cause out of memory
v.clear();
@@ -778,7 +674,7 @@ void Unserialize_impl(Stream& is, std::vector<T, A>& v, int nType, int nVersion,
}
template<typename Stream, typename T, typename A, typename V>
-void Unserialize_impl(Stream& is, std::vector<T, A>& v, int nType, int nVersion, const V&)
+void Unserialize_impl(Stream& is, std::vector<T, A>& v, const V&)
{
v.clear();
unsigned int nSize = ReadCompactSize(is);
@@ -791,14 +687,14 @@ void Unserialize_impl(Stream& is, std::vector<T, A>& v, int nType, int nVersion,
nMid = nSize;
v.resize(nMid);
for (; i < nMid; i++)
- Unserialize(is, v[i], nType, nVersion);
+ Unserialize(is, v[i]);
}
}
template<typename Stream, typename T, typename A>
-inline void Unserialize(Stream& is, std::vector<T, A>& v, int nType, int nVersion)
+inline void Unserialize(Stream& is, std::vector<T, A>& v)
{
- Unserialize_impl(is, v, nType, nVersion, T());
+ Unserialize_impl(is, v, T());
}
@@ -806,24 +702,18 @@ inline void Unserialize(Stream& is, std::vector<T, A>& v, int nType, int nVersio
/**
* pair
*/
-template<typename K, typename T>
-unsigned int GetSerializeSize(const std::pair<K, T>& item, int nType, int nVersion)
-{
- return GetSerializeSize(item.first, nType, nVersion) + GetSerializeSize(item.second, nType, nVersion);
-}
-
template<typename Stream, typename K, typename T>
-void Serialize(Stream& os, const std::pair<K, T>& item, int nType, int nVersion)
+void Serialize(Stream& os, const std::pair<K, T>& item)
{
- Serialize(os, item.first, nType, nVersion);
- Serialize(os, item.second, nType, nVersion);
+ Serialize(os, item.first);
+ Serialize(os, item.second);
}
template<typename Stream, typename K, typename T>
-void Unserialize(Stream& is, std::pair<K, T>& item, int nType, int nVersion)
+void Unserialize(Stream& is, std::pair<K, T>& item)
{
- Unserialize(is, item.first, nType, nVersion);
- Unserialize(is, item.second, nType, nVersion);
+ Unserialize(is, item.first);
+ Unserialize(is, item.second);
}
@@ -831,25 +721,16 @@ void Unserialize(Stream& is, std::pair<K, T>& item, int nType, int nVersion)
/**
* map
*/
-template<typename K, typename T, typename Pred, typename A>
-unsigned int GetSerializeSize(const std::map<K, T, Pred, A>& m, int nType, int nVersion)
-{
- unsigned int nSize = GetSizeOfCompactSize(m.size());
- for (typename std::map<K, T, Pred, A>::const_iterator mi = m.begin(); mi != m.end(); ++mi)
- nSize += GetSerializeSize((*mi), nType, nVersion);
- return nSize;
-}
-
template<typename Stream, typename K, typename T, typename Pred, typename A>
-void Serialize(Stream& os, const std::map<K, T, Pred, A>& m, int nType, int nVersion)
+void Serialize(Stream& os, const std::map<K, T, Pred, A>& m)
{
WriteCompactSize(os, m.size());
for (typename std::map<K, T, Pred, A>::const_iterator mi = m.begin(); mi != m.end(); ++mi)
- Serialize(os, (*mi), nType, nVersion);
+ Serialize(os, (*mi));
}
template<typename Stream, typename K, typename T, typename Pred, typename A>
-void Unserialize(Stream& is, std::map<K, T, Pred, A>& m, int nType, int nVersion)
+void Unserialize(Stream& is, std::map<K, T, Pred, A>& m)
{
m.clear();
unsigned int nSize = ReadCompactSize(is);
@@ -857,7 +738,7 @@ void Unserialize(Stream& is, std::map<K, T, Pred, A>& m, int nType, int nVersion
for (unsigned int i = 0; i < nSize; i++)
{
std::pair<K, T> item;
- Unserialize(is, item, nType, nVersion);
+ Unserialize(is, item);
mi = m.insert(mi, item);
}
}
@@ -867,25 +748,16 @@ void Unserialize(Stream& is, std::map<K, T, Pred, A>& m, int nType, int nVersion
/**
* set
*/
-template<typename K, typename Pred, typename A>
-unsigned int GetSerializeSize(const std::set<K, Pred, A>& m, int nType, int nVersion)
-{
- unsigned int nSize = GetSizeOfCompactSize(m.size());
- for (typename std::set<K, Pred, A>::const_iterator it = m.begin(); it != m.end(); ++it)
- nSize += GetSerializeSize((*it), nType, nVersion);
- return nSize;
-}
-
template<typename Stream, typename K, typename Pred, typename A>
-void Serialize(Stream& os, const std::set<K, Pred, A>& m, int nType, int nVersion)
+void Serialize(Stream& os, const std::set<K, Pred, A>& m)
{
WriteCompactSize(os, m.size());
for (typename std::set<K, Pred, A>::const_iterator it = m.begin(); it != m.end(); ++it)
- Serialize(os, (*it), nType, nVersion);
+ Serialize(os, (*it));
}
template<typename Stream, typename K, typename Pred, typename A>
-void Unserialize(Stream& is, std::set<K, Pred, A>& m, int nType, int nVersion)
+void Unserialize(Stream& is, std::set<K, Pred, A>& m)
{
m.clear();
unsigned int nSize = ReadCompactSize(is);
@@ -893,7 +765,7 @@ void Unserialize(Stream& is, std::set<K, Pred, A>& m, int nType, int nVersion)
for (unsigned int i = 0; i < nSize; i++)
{
K key;
- Unserialize(is, key, nType, nVersion);
+ Unserialize(is, key);
it = m.insert(it, key);
}
}
@@ -901,27 +773,61 @@ void Unserialize(Stream& is, std::set<K, Pred, A>& m, int nType, int nVersion)
/**
+ * unique_ptr
+ */
+template<typename Stream, typename T> void
+Serialize(Stream& os, const std::unique_ptr<const T>& p)
+{
+ Serialize(os, *p);
+}
+
+template<typename Stream, typename T>
+void Unserialize(Stream& is, std::unique_ptr<const T>& p)
+{
+ p.reset(new T(deserialize, is));
+}
+
+
+
+/**
+ * shared_ptr
+ */
+template<typename Stream, typename T> void
+Serialize(Stream& os, const std::shared_ptr<const T>& p)
+{
+ Serialize(os, *p);
+}
+
+template<typename Stream, typename T>
+void Unserialize(Stream& is, std::shared_ptr<const T>& p)
+{
+ p = std::make_shared<const T>(deserialize, is);
+}
+
+
+
+/**
* Support for ADD_SERIALIZE_METHODS and READWRITE macro
*/
struct CSerActionSerialize
{
- bool ForRead() const { return false; }
+ constexpr bool ForRead() const { return false; }
};
struct CSerActionUnserialize
{
- bool ForRead() const { return true; }
+ constexpr bool ForRead() const { return true; }
};
template<typename Stream, typename T>
-inline void SerReadWrite(Stream& s, const T& obj, int nType, int nVersion, CSerActionSerialize ser_action)
+inline void SerReadWrite(Stream& s, const T& obj, CSerActionSerialize ser_action)
{
- ::Serialize(s, obj, nType, nVersion);
+ ::Serialize(s, obj);
}
template<typename Stream, typename T>
-inline void SerReadWrite(Stream& s, T& obj, int nType, int nVersion, CSerActionUnserialize ser_action)
+inline void SerReadWrite(Stream& s, T& obj, CSerActionUnserialize ser_action)
{
- ::Unserialize(s, obj, nType, nVersion);
+ ::Unserialize(s, obj);
}
@@ -932,33 +838,122 @@ inline void SerReadWrite(Stream& s, T& obj, int nType, int nVersion, CSerActionU
+/* ::GetSerializeSize implementations
+ *
+ * Computing the serialized size of objects is done through a special stream
+ * object of type CSizeComputer, which only records the number of bytes written
+ * to it.
+ *
+ * If your Serialize or SerializationOp method has non-trivial overhead for
+ * serialization, it may be worthwhile to implement a specialized version for
+ * CSizeComputer, which uses the s.seek() method to record bytes that would
+ * be written instead.
+ */
class CSizeComputer
{
protected:
size_t nSize;
+ const int nType;
+ const int nVersion;
public:
- int nType;
- int nVersion;
-
CSizeComputer(int nTypeIn, int nVersionIn) : nSize(0), nType(nTypeIn), nVersion(nVersionIn) {}
- CSizeComputer& write(const char *psz, size_t nSize)
+ void write(const char *psz, size_t _nSize)
{
- this->nSize += nSize;
- return *this;
+ this->nSize += _nSize;
+ }
+
+ /** Pretend _nSize bytes are written, without specifying them. */
+ void seek(size_t _nSize)
+ {
+ this->nSize += _nSize;
}
template<typename T>
CSizeComputer& operator<<(const T& obj)
{
- ::Serialize(*this, obj, nType, nVersion);
+ ::Serialize(*this, obj);
return (*this);
}
size_t size() const {
return nSize;
}
+
+ int GetVersion() const { return nVersion; }
+ int GetType() const { return nType; }
};
+template<typename Stream>
+void SerializeMany(Stream& s)
+{
+}
+
+template<typename Stream, typename Arg>
+void SerializeMany(Stream& s, Arg&& arg)
+{
+ ::Serialize(s, std::forward<Arg>(arg));
+}
+
+template<typename Stream, typename Arg, typename... Args>
+void SerializeMany(Stream& s, Arg&& arg, Args&&... args)
+{
+ ::Serialize(s, std::forward<Arg>(arg));
+ ::SerializeMany(s, std::forward<Args>(args)...);
+}
+
+template<typename Stream>
+inline void UnserializeMany(Stream& s)
+{
+}
+
+template<typename Stream, typename Arg>
+inline void UnserializeMany(Stream& s, Arg& arg)
+{
+ ::Unserialize(s, arg);
+}
+
+template<typename Stream, typename Arg, typename... Args>
+inline void UnserializeMany(Stream& s, Arg& arg, Args&... args)
+{
+ ::Unserialize(s, arg);
+ ::UnserializeMany(s, args...);
+}
+
+template<typename Stream, typename... Args>
+inline void SerReadWriteMany(Stream& s, CSerActionSerialize ser_action, Args&&... args)
+{
+ ::SerializeMany(s, std::forward<Args>(args)...);
+}
+
+template<typename Stream, typename... Args>
+inline void SerReadWriteMany(Stream& s, CSerActionUnserialize ser_action, Args&... args)
+{
+ ::UnserializeMany(s, args...);
+}
+
+template<typename I>
+inline void WriteVarInt(CSizeComputer &s, I n)
+{
+ s.seek(GetSizeOfVarInt<I>(n));
+}
+
+inline void WriteCompactSize(CSizeComputer &s, uint64_t nSize)
+{
+ s.seek(GetSizeOfCompactSize(nSize));
+}
+
+template <typename T>
+size_t GetSerializeSize(const T& t, int nType, int nVersion = 0)
+{
+ return (CSizeComputer(nType, nVersion) << t).size();
+}
+
+template <typename S, typename T>
+size_t GetSerializeSize(const S& s, const T& t)
+{
+ return (CSizeComputer(s.GetType(), s.GetVersion()) << t).size();
+}
+
#endif // BITCOIN_SERIALIZE_H
diff --git a/src/streams.h b/src/streams.h
index 7132364eb1..b508784238 100644
--- a/src/streams.h
+++ b/src/streams.h
@@ -26,17 +26,18 @@ template<typename Stream>
class OverrideStream
{
Stream* stream;
-public:
+
const int nType;
const int nVersion;
+public:
OverrideStream(Stream* stream_, int nType_, int nVersion_) : stream(stream_), nType(nType_), nVersion(nVersion_) {}
template<typename T>
OverrideStream<Stream>& operator<<(const T& obj)
{
// Serialize to this stream
- ::Serialize(*this->stream, obj, nType, nVersion);
+ ::Serialize(*this, obj);
return (*this);
}
@@ -44,9 +45,22 @@ public:
OverrideStream<Stream>& operator>>(T& obj)
{
// Unserialize from this stream
- ::Unserialize(*this->stream, obj, nType, nVersion);
+ ::Unserialize(*this, obj);
return (*this);
}
+
+ void write(const char* pch, size_t nSize)
+ {
+ stream->write(pch, nSize);
+ }
+
+ void read(char* pch, size_t nSize)
+ {
+ stream->read(pch, nSize);
+ }
+
+ int GetVersion() const { return nVersion; }
+ int GetType() const { return nType; }
};
template<typename S>
@@ -55,6 +69,75 @@ OverrideStream<S> WithOrVersion(S* s, int nVersionFlag)
return OverrideStream<S>(s, s->GetType(), s->GetVersion() | nVersionFlag);
}
+/* Minimal stream for overwriting and/or appending to an existing byte vector
+ *
+ * The referenced vector will grow as necessary
+ */
+class CVectorWriter
+{
+ public:
+
+/*
+ * @param[in] nTypeIn Serialization Type
+ * @param[in] nVersionIn Serialization Version (including any flags)
+ * @param[in] vchDataIn Referenced byte vector to overwrite/append
+ * @param[in] nPosIn Starting position. Vector index where writes should start. The vector will initially
+ * grow as necessary to max(index, vec.size()). So to append, use vec.size().
+*/
+ CVectorWriter(int nTypeIn, int nVersionIn, std::vector<unsigned char>& vchDataIn, size_t nPosIn) : nType(nTypeIn), nVersion(nVersionIn), vchData(vchDataIn), nPos(nPosIn)
+ {
+ if(nPos > vchData.size())
+ vchData.resize(nPos);
+ }
+/*
+ * (other params same as above)
+ * @param[in] args A list of items to serialize starting at nPos.
+*/
+ template <typename... Args>
+ CVectorWriter(int nTypeIn, int nVersionIn, std::vector<unsigned char>& vchDataIn, size_t nPosIn, Args&&... args) : CVectorWriter(nTypeIn, nVersionIn, vchDataIn, nPosIn)
+ {
+ ::SerializeMany(*this, std::forward<Args>(args)...);
+ }
+ void write(const char* pch, size_t nSize)
+ {
+ assert(nPos <= vchData.size());
+ size_t nOverwrite = std::min(nSize, vchData.size() - nPos);
+ if (nOverwrite) {
+ memcpy(vchData.data() + nPos, reinterpret_cast<const unsigned char*>(pch), nOverwrite);
+ }
+ if (nOverwrite < nSize) {
+ vchData.insert(vchData.end(), reinterpret_cast<const unsigned char*>(pch) + nOverwrite, reinterpret_cast<const unsigned char*>(pch) + nSize);
+ }
+ nPos += nSize;
+ }
+ template<typename T>
+ CVectorWriter& operator<<(const T& obj)
+ {
+ // Serialize to this stream
+ ::Serialize(*this, obj);
+ return (*this);
+ }
+ int GetVersion() const
+ {
+ return nVersion;
+ }
+ int GetType() const
+ {
+ return nType;
+ }
+ void seek(size_t nSize)
+ {
+ nPos += nSize;
+ if(nPos > vchData.size())
+ vchData.resize(nPos);
+ }
+private:
+ const int nType;
+ const int nVersion;
+ std::vector<unsigned char>& vchData;
+ size_t nPos;
+};
+
/** Double ended buffer combining vector and stream-like interfaces.
*
* >> and << read and write unformatted data using the above serialization templates.
@@ -66,9 +149,10 @@ protected:
typedef CSerializeData vector_type;
vector_type vch;
unsigned int nReadPos;
-public:
+
int nType;
int nVersion;
+public:
typedef vector_type::allocator_type allocator_type;
typedef vector_type::size_type size_type;
@@ -112,6 +196,13 @@ public:
Init(nTypeIn, nVersionIn);
}
+ template <typename... Args>
+ CDataStream(int nTypeIn, int nVersionIn, Args&&... args)
+ {
+ Init(nTypeIn, nVersionIn);
+ ::SerializeMany(*this, std::forward<Args>(args)...);
+ }
+
void Init(int nTypeIn, int nVersionIn)
{
nReadPos = 0;
@@ -244,13 +335,11 @@ public:
int in_avail() { return size(); }
void SetType(int n) { nType = n; }
- int GetType() { return nType; }
+ int GetType() const { return nType; }
void SetVersion(int n) { nVersion = n; }
- int GetVersion() { return nVersion; }
- void ReadVersion() { *this >> nVersion; }
- void WriteVersion() { *this << nVersion; }
+ int GetVersion() const { return nVersion; }
- CDataStream& read(char* pch, size_t nSize)
+ void read(char* pch, size_t nSize)
{
// Read from the beginning of the buffer
unsigned int nReadPosNext = nReadPos + nSize;
@@ -263,14 +352,13 @@ public:
memcpy(pch, &vch[nReadPos], nSize);
nReadPos = 0;
vch.clear();
- return (*this);
+ return;
}
memcpy(pch, &vch[nReadPos], nSize);
nReadPos = nReadPosNext;
- return (*this);
}
- CDataStream& ignore(int nSize)
+ void ignore(int nSize)
{
// Ignore from the beginning of the buffer
if (nSize < 0) {
@@ -283,21 +371,19 @@ public:
throw std::ios_base::failure("CDataStream::ignore(): end of data");
nReadPos = 0;
vch.clear();
- return (*this);
+ return;
}
nReadPos = nReadPosNext;
- return (*this);
}
- CDataStream& write(const char* pch, size_t nSize)
+ void write(const char* pch, size_t nSize)
{
// Write to the end of the buffer
vch.insert(vch.end(), pch, pch + nSize);
- return (*this);
}
template<typename Stream>
- void Serialize(Stream& s, int nType, int nVersion) const
+ void Serialize(Stream& s) const
{
// Special case: stream << stream concatenates like stream += stream
if (!vch.empty())
@@ -305,17 +391,10 @@ public:
}
template<typename T>
- unsigned int GetSerializeSize(const T& obj)
- {
- // Tells the size of the object if serialized to this stream
- return ::GetSerializeSize(obj, nType, nVersion);
- }
-
- template<typename T>
CDataStream& operator<<(const T& obj)
{
// Serialize to this stream
- ::Serialize(*this, obj, nType, nVersion);
+ ::Serialize(*this, obj);
return (*this);
}
@@ -323,7 +402,7 @@ public:
CDataStream& operator>>(T& obj)
{
// Unserialize from this stream
- ::Unserialize(*this, obj, nType, nVersion);
+ ::Unserialize(*this, obj);
return (*this);
}
@@ -378,17 +457,15 @@ private:
CAutoFile(const CAutoFile&);
CAutoFile& operator=(const CAutoFile&);
- int nType;
- int nVersion;
-
+ const int nType;
+ const int nVersion;
+
FILE* file;
public:
- CAutoFile(FILE* filenew, int nTypeIn, int nVersionIn)
+ CAutoFile(FILE* filenew, int nTypeIn, int nVersionIn) : nType(nTypeIn), nVersion(nVersionIn)
{
file = filenew;
- nType = nTypeIn;
- nVersion = nVersionIn;
}
~CAutoFile()
@@ -423,23 +500,18 @@ public:
//
// Stream subset
//
- void SetType(int n) { nType = n; }
- int GetType() { return nType; }
- void SetVersion(int n) { nVersion = n; }
- int GetVersion() { return nVersion; }
- void ReadVersion() { *this >> nVersion; }
- void WriteVersion() { *this << nVersion; }
+ int GetType() const { return nType; }
+ int GetVersion() const { return nVersion; }
- CAutoFile& read(char* pch, size_t nSize)
+ void read(char* pch, size_t nSize)
{
if (!file)
throw std::ios_base::failure("CAutoFile::read: file handle is NULL");
if (fread(pch, 1, nSize, file) != nSize)
throw std::ios_base::failure(feof(file) ? "CAutoFile::read: end of file" : "CAutoFile::read: fread failed");
- return (*this);
}
- CAutoFile& ignore(size_t nSize)
+ void ignore(size_t nSize)
{
if (!file)
throw std::ios_base::failure("CAutoFile::ignore: file handle is NULL");
@@ -450,23 +522,14 @@ public:
throw std::ios_base::failure(feof(file) ? "CAutoFile::ignore: end of file" : "CAutoFile::read: fread failed");
nSize -= nNow;
}
- return (*this);
}
- CAutoFile& write(const char* pch, size_t nSize)
+ void write(const char* pch, size_t nSize)
{
if (!file)
throw std::ios_base::failure("CAutoFile::write: file handle is NULL");
if (fwrite(pch, 1, nSize, file) != nSize)
throw std::ios_base::failure("CAutoFile::write: write failed");
- return (*this);
- }
-
- template<typename T>
- unsigned int GetSerializeSize(const T& obj)
- {
- // Tells the size of the object if serialized to this stream
- return ::GetSerializeSize(obj, nType, nVersion);
}
template<typename T>
@@ -475,7 +538,7 @@ public:
// Serialize to this stream
if (!file)
throw std::ios_base::failure("CAutoFile::operator<<: file handle is NULL");
- ::Serialize(*this, obj, nType, nVersion);
+ ::Serialize(*this, obj);
return (*this);
}
@@ -485,7 +548,7 @@ public:
// Unserialize from this stream
if (!file)
throw std::ios_base::failure("CAutoFile::operator>>: file handle is NULL");
- ::Unserialize(*this, obj, nType, nVersion);
+ ::Unserialize(*this, obj);
return (*this);
}
};
@@ -503,8 +566,8 @@ private:
CBufferedFile(const CBufferedFile&);
CBufferedFile& operator=(const CBufferedFile&);
- int nType;
- int nVersion;
+ const int nType;
+ const int nVersion;
FILE *src; // source file
uint64_t nSrcPos; // how many bytes have been read from source
@@ -534,11 +597,9 @@ protected:
public:
CBufferedFile(FILE *fileIn, uint64_t nBufSize, uint64_t nRewindIn, int nTypeIn, int nVersionIn) :
- nSrcPos(0), nReadPos(0), nReadLimit((uint64_t)(-1)), nRewind(nRewindIn), vchBuf(nBufSize, 0)
+ nType(nTypeIn), nVersion(nVersionIn), nSrcPos(0), nReadPos(0), nReadLimit((uint64_t)(-1)), nRewind(nRewindIn), vchBuf(nBufSize, 0)
{
src = fileIn;
- nType = nTypeIn;
- nVersion = nVersionIn;
}
~CBufferedFile()
@@ -546,6 +607,9 @@ public:
fclose();
}
+ int GetVersion() const { return nVersion; }
+ int GetType() const { return nType; }
+
void fclose()
{
if (src) {
@@ -560,7 +624,7 @@ public:
}
// read a number of bytes
- CBufferedFile& read(char *pch, size_t nSize) {
+ void read(char *pch, size_t nSize) {
if (nSize + nReadPos > nReadLimit)
throw std::ios_base::failure("Read attempted past buffer limit");
if (nSize + nRewind > vchBuf.size())
@@ -579,7 +643,6 @@ public:
pch += nNow;
nSize -= nNow;
}
- return (*this);
}
// return the current reading position
@@ -625,7 +688,7 @@ public:
template<typename T>
CBufferedFile& operator>>(T& obj) {
// Unserialize from this stream
- ::Unserialize(*this, obj, nType, nVersion);
+ ::Unserialize(*this, obj);
return (*this);
}
diff --git a/src/support/allocators/secure.h b/src/support/allocators/secure.h
index 1ec40fe830..67064314ef 100644
--- a/src/support/allocators/secure.h
+++ b/src/support/allocators/secure.h
@@ -6,7 +6,8 @@
#ifndef BITCOIN_SUPPORT_ALLOCATORS_SECURE_H
#define BITCOIN_SUPPORT_ALLOCATORS_SECURE_H
-#include "support/pagelocker.h"
+#include "support/lockedpool.h"
+#include "support/cleanse.h"
#include <string>
@@ -39,20 +40,15 @@ struct secure_allocator : public std::allocator<T> {
T* allocate(std::size_t n, const void* hint = 0)
{
- T* p;
- p = std::allocator<T>::allocate(n, hint);
- if (p != NULL)
- LockedPageManager::Instance().LockRange(p, sizeof(T) * n);
- return p;
+ return static_cast<T*>(LockedPoolManager::Instance().alloc(sizeof(T) * n));
}
void deallocate(T* p, std::size_t n)
{
if (p != NULL) {
memory_cleanse(p, sizeof(T) * n);
- LockedPageManager::Instance().UnlockRange(p, sizeof(T) * n);
}
- std::allocator<T>::deallocate(p, n);
+ LockedPoolManager::Instance().free(p);
}
};
diff --git a/src/support/lockedpool.cpp b/src/support/lockedpool.cpp
new file mode 100644
index 0000000000..01273c9791
--- /dev/null
+++ b/src/support/lockedpool.cpp
@@ -0,0 +1,385 @@
+// Copyright (c) 2016 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "support/lockedpool.h"
+#include "support/cleanse.h"
+
+#if defined(HAVE_CONFIG_H)
+#include "config/bitcoin-config.h"
+#endif
+
+#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 <windows.h>
+#else
+#include <sys/mman.h> // for mmap
+#include <sys/resource.h> // for getrlimit
+#include <limits.h> // for PAGESIZE
+#include <unistd.h> // for sysconf
+#endif
+
+#include <algorithm>
+
+LockedPoolManager* LockedPoolManager::_instance = NULL;
+std::once_flag LockedPoolManager::init_flag;
+
+/*******************************************************************************/
+// Utilities
+//
+/** Align up to power of 2 */
+static inline size_t align_up(size_t x, size_t align)
+{
+ return (x + align - 1) & ~(align - 1);
+}
+
+/*******************************************************************************/
+// Implementation: Arena
+
+Arena::Arena(void *base_in, size_t size_in, size_t alignment_in):
+ base(static_cast<char*>(base_in)), end(static_cast<char*>(base_in) + size_in), alignment(alignment_in)
+{
+ // Start with one free chunk that covers the entire arena
+ chunks_free.emplace(base, size_in);
+}
+
+Arena::~Arena()
+{
+}
+
+void* Arena::alloc(size_t size)
+{
+ // Round to next multiple of alignment
+ size = align_up(size, alignment);
+
+ // Don't handle zero-sized chunks
+ if (size == 0)
+ return nullptr;
+
+ // Pick a large enough free-chunk
+ auto it = std::find_if(chunks_free.begin(), chunks_free.end(),
+ [=](const std::map<char*, size_t>::value_type& chunk){ return chunk.second >= size; });
+ if (it == chunks_free.end())
+ return nullptr;
+
+ // Create the used-chunk, taking its space from the end of the free-chunk
+ auto alloced = chunks_used.emplace(it->first + it->second - size, size).first;
+ if (!(it->second -= size))
+ chunks_free.erase(it);
+ return reinterpret_cast<void*>(alloced->first);
+}
+
+/* extend the Iterator if other begins at its end */
+template <class Iterator, class Pair> bool extend(Iterator it, const Pair& other) {
+ if (it->first + it->second == other.first) {
+ it->second += other.second;
+ return true;
+ }
+ return false;
+}
+
+void Arena::free(void *ptr)
+{
+ // Freeing the NULL pointer is OK.
+ if (ptr == nullptr) {
+ return;
+ }
+
+ // Remove chunk from used map
+ auto i = chunks_used.find(static_cast<char*>(ptr));
+ if (i == chunks_used.end()) {
+ throw std::runtime_error("Arena: invalid or double free");
+ }
+ auto freed = *i;
+ chunks_used.erase(i);
+
+ // Add space to free map, coalescing contiguous chunks
+ auto next = chunks_free.upper_bound(freed.first);
+ auto prev = (next == chunks_free.begin()) ? chunks_free.end() : std::prev(next);
+ if (prev == chunks_free.end() || !extend(prev, freed))
+ prev = chunks_free.emplace_hint(next, freed);
+ if (next != chunks_free.end() && extend(prev, *next))
+ chunks_free.erase(next);
+}
+
+Arena::Stats Arena::stats() const
+{
+ Arena::Stats r{ 0, 0, 0, chunks_used.size(), chunks_free.size() };
+ for (const auto& chunk: chunks_used)
+ r.used += chunk.second;
+ for (const auto& chunk: chunks_free)
+ r.free += chunk.second;
+ r.total = r.used + r.free;
+ return r;
+}
+
+#ifdef ARENA_DEBUG
+void printchunk(char* base, size_t sz, bool used) {
+ std::cout <<
+ "0x" << std::hex << std::setw(16) << std::setfill('0') << base <<
+ " 0x" << std::hex << std::setw(16) << std::setfill('0') << sz <<
+ " 0x" << used << std::endl;
+}
+void Arena::walk() const
+{
+ for (const auto& chunk: chunks_used)
+ printchunk(chunk.first, chunk.second, true);
+ std::cout << std::endl;
+ for (const auto& chunk: chunks_free)
+ printchunk(chunk.first, chunk.second, false);
+ std::cout << std::endl;
+}
+#endif
+
+/*******************************************************************************/
+// Implementation: Win32LockedPageAllocator
+
+#ifdef WIN32
+/** LockedPageAllocator specialized for Windows.
+ */
+class Win32LockedPageAllocator: public LockedPageAllocator
+{
+public:
+ Win32LockedPageAllocator();
+ void* AllocateLocked(size_t len, bool *lockingSuccess);
+ void FreeLocked(void* addr, size_t len);
+ size_t GetLimit();
+private:
+ size_t page_size;
+};
+
+Win32LockedPageAllocator::Win32LockedPageAllocator()
+{
+ // Determine system page size in bytes
+ SYSTEM_INFO sSysInfo;
+ GetSystemInfo(&sSysInfo);
+ page_size = sSysInfo.dwPageSize;
+}
+void *Win32LockedPageAllocator::AllocateLocked(size_t len, bool *lockingSuccess)
+{
+ len = align_up(len, page_size);
+ void *addr = VirtualAlloc(nullptr, len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+ if (addr) {
+ // VirtualLock is used to attempt to keep keying material out of swap. Note
+ // that it does not provide this as a guarantee, 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.
+ *lockingSuccess = VirtualLock(const_cast<void*>(addr), len) != 0;
+ }
+ return addr;
+}
+void Win32LockedPageAllocator::FreeLocked(void* addr, size_t len)
+{
+ len = align_up(len, page_size);
+ memory_cleanse(addr, len);
+ VirtualUnlock(const_cast<void*>(addr), len);
+}
+
+size_t Win32LockedPageAllocator::GetLimit()
+{
+ // TODO is there a limit on windows, how to get it?
+ return std::numeric_limits<size_t>::max();
+}
+#endif
+
+/*******************************************************************************/
+// Implementation: PosixLockedPageAllocator
+
+#ifndef WIN32
+/** LockedPageAllocator specialized for OSes that don't try to be
+ * special snowflakes.
+ */
+class PosixLockedPageAllocator: public LockedPageAllocator
+{
+public:
+ PosixLockedPageAllocator();
+ void* AllocateLocked(size_t len, bool *lockingSuccess);
+ void FreeLocked(void* addr, size_t len);
+ size_t GetLimit();
+private:
+ size_t page_size;
+};
+
+PosixLockedPageAllocator::PosixLockedPageAllocator()
+{
+ // Determine system page size in bytes
+#if defined(PAGESIZE) // defined in limits.h
+ page_size = PAGESIZE;
+#else // assume some POSIX OS
+ page_size = sysconf(_SC_PAGESIZE);
+#endif
+}
+
+// Some systems (at least OS X) do not define MAP_ANONYMOUS yet and define
+// MAP_ANON which is deprecated
+#ifndef MAP_ANONYMOUS
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+
+void *PosixLockedPageAllocator::AllocateLocked(size_t len, bool *lockingSuccess)
+{
+ void *addr;
+ len = align_up(len, page_size);
+ addr = mmap(nullptr, len, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+ if (addr) {
+ *lockingSuccess = mlock(addr, len) == 0;
+ }
+ return addr;
+}
+void PosixLockedPageAllocator::FreeLocked(void* addr, size_t len)
+{
+ len = align_up(len, page_size);
+ memory_cleanse(addr, len);
+ munlock(addr, len);
+ munmap(addr, len);
+}
+size_t PosixLockedPageAllocator::GetLimit()
+{
+#ifdef RLIMIT_MEMLOCK
+ struct rlimit rlim;
+ if (getrlimit(RLIMIT_MEMLOCK, &rlim) == 0) {
+ if (rlim.rlim_cur != RLIM_INFINITY) {
+ return rlim.rlim_cur;
+ }
+ }
+#endif
+ return std::numeric_limits<size_t>::max();
+}
+#endif
+
+/*******************************************************************************/
+// Implementation: LockedPool
+
+LockedPool::LockedPool(std::unique_ptr<LockedPageAllocator> allocator_in, LockingFailed_Callback lf_cb_in):
+ allocator(std::move(allocator_in)), lf_cb(lf_cb_in), cumulative_bytes_locked(0)
+{
+}
+
+LockedPool::~LockedPool()
+{
+}
+void* LockedPool::alloc(size_t size)
+{
+ std::lock_guard<std::mutex> lock(mutex);
+
+ // Don't handle impossible sizes
+ if (size == 0 || size > ARENA_SIZE)
+ return nullptr;
+
+ // Try allocating from each current arena
+ for (auto &arena: arenas) {
+ void *addr = arena.alloc(size);
+ if (addr) {
+ return addr;
+ }
+ }
+ // If that fails, create a new one
+ if (new_arena(ARENA_SIZE, ARENA_ALIGN)) {
+ return arenas.back().alloc(size);
+ }
+ return nullptr;
+}
+
+void LockedPool::free(void *ptr)
+{
+ std::lock_guard<std::mutex> lock(mutex);
+ // TODO we can do better than this linear search by keeping a map of arena
+ // extents to arena, and looking up the address.
+ for (auto &arena: arenas) {
+ if (arena.addressInArena(ptr)) {
+ arena.free(ptr);
+ return;
+ }
+ }
+ throw std::runtime_error("LockedPool: invalid address not pointing to any arena");
+}
+
+LockedPool::Stats LockedPool::stats() const
+{
+ std::lock_guard<std::mutex> lock(mutex);
+ LockedPool::Stats r{0, 0, 0, cumulative_bytes_locked, 0, 0};
+ for (const auto &arena: arenas) {
+ Arena::Stats i = arena.stats();
+ r.used += i.used;
+ r.free += i.free;
+ r.total += i.total;
+ r.chunks_used += i.chunks_used;
+ r.chunks_free += i.chunks_free;
+ }
+ return r;
+}
+
+bool LockedPool::new_arena(size_t size, size_t align)
+{
+ bool locked;
+ // If this is the first arena, handle this specially: Cap the upper size
+ // by the process limit. This makes sure that the first arena will at least
+ // be locked. An exception to this is if the process limit is 0:
+ // in this case no memory can be locked at all so we'll skip past this logic.
+ if (arenas.empty()) {
+ size_t limit = allocator->GetLimit();
+ if (limit > 0) {
+ size = std::min(size, limit);
+ }
+ }
+ void *addr = allocator->AllocateLocked(size, &locked);
+ if (!addr) {
+ return false;
+ }
+ if (locked) {
+ cumulative_bytes_locked += size;
+ } else if (lf_cb) { // Call the locking-failed callback if locking failed
+ if (!lf_cb()) { // If the callback returns false, free the memory and fail, otherwise consider the user warned and proceed.
+ allocator->FreeLocked(addr, size);
+ return false;
+ }
+ }
+ arenas.emplace_back(allocator.get(), addr, size, align);
+ return true;
+}
+
+LockedPool::LockedPageArena::LockedPageArena(LockedPageAllocator *allocator_in, void *base_in, size_t size_in, size_t align_in):
+ Arena(base_in, size_in, align_in), base(base_in), size(size_in), allocator(allocator_in)
+{
+}
+LockedPool::LockedPageArena::~LockedPageArena()
+{
+ allocator->FreeLocked(base, size);
+}
+
+/*******************************************************************************/
+// Implementation: LockedPoolManager
+//
+LockedPoolManager::LockedPoolManager(std::unique_ptr<LockedPageAllocator> allocator):
+ LockedPool(std::move(allocator), &LockedPoolManager::LockingFailed)
+{
+}
+
+bool LockedPoolManager::LockingFailed()
+{
+ // TODO: log something but how? without including util.h
+ return true;
+}
+
+void LockedPoolManager::CreateInstance()
+{
+ // Using a local static instance guarantees that the object is initialized
+ // when it's first needed and also deinitialized after all objects that use
+ // it are done with it. I can think of one unlikely scenario where we may
+ // have a static deinitialization order/problem, but the check in
+ // LockedPoolManagerBase's destructor helps us detect if that ever happens.
+#ifdef WIN32
+ std::unique_ptr<LockedPageAllocator> allocator(new Win32LockedPageAllocator());
+#else
+ std::unique_ptr<LockedPageAllocator> allocator(new PosixLockedPageAllocator());
+#endif
+ static LockedPoolManager instance(std::move(allocator));
+ LockedPoolManager::_instance = &instance;
+}
diff --git a/src/support/lockedpool.h b/src/support/lockedpool.h
new file mode 100644
index 0000000000..f5212bc266
--- /dev/null
+++ b/src/support/lockedpool.h
@@ -0,0 +1,231 @@
+// Copyright (c) 2016 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_SUPPORT_LOCKEDPOOL_H
+#define BITCOIN_SUPPORT_LOCKEDPOOL_H
+
+#include <stdint.h>
+#include <list>
+#include <map>
+#include <mutex>
+#include <memory>
+
+/**
+ * OS-dependent allocation and deallocation of locked/pinned memory pages.
+ * Abstract base class.
+ */
+class LockedPageAllocator
+{
+public:
+ virtual ~LockedPageAllocator() {}
+ /** Allocate and lock memory pages.
+ * If len is not a multiple of the system page size, it is rounded up.
+ * Returns 0 in case of allocation failure.
+ *
+ * If locking the memory pages could not be accomplished it will still
+ * return the memory, however the lockingSuccess flag will be false.
+ * lockingSuccess is undefined if the allocation fails.
+ */
+ virtual void* AllocateLocked(size_t len, bool *lockingSuccess) = 0;
+
+ /** Unlock and free memory pages.
+ * Clear the memory before unlocking.
+ */
+ virtual void FreeLocked(void* addr, size_t len) = 0;
+
+ /** Get the total limit on the amount of memory that may be locked by this
+ * process, in bytes. Return size_t max if there is no limit or the limit
+ * is unknown. Return 0 if no memory can be locked at all.
+ */
+ virtual size_t GetLimit() = 0;
+};
+
+/* An arena manages a contiguous region of memory by dividing it into
+ * chunks.
+ */
+class Arena
+{
+public:
+ Arena(void *base, size_t size, size_t alignment);
+ virtual ~Arena();
+
+ /** Memory statistics. */
+ struct Stats
+ {
+ size_t used;
+ size_t free;
+ size_t total;
+ size_t chunks_used;
+ size_t chunks_free;
+ };
+
+ /** Allocate size bytes from this arena.
+ * Returns pointer on success, or 0 if memory is full or
+ * the application tried to allocate 0 bytes.
+ */
+ void* alloc(size_t size);
+
+ /** Free a previously allocated chunk of memory.
+ * Freeing the zero pointer has no effect.
+ * Raises std::runtime_error in case of error.
+ */
+ void free(void *ptr);
+
+ /** Get arena usage statistics */
+ Stats stats() const;
+
+#ifdef ARENA_DEBUG
+ void walk() const;
+#endif
+
+ /** Return whether a pointer points inside this arena.
+ * This returns base <= ptr < (base+size) so only use it for (inclusive)
+ * chunk starting addresses.
+ */
+ bool addressInArena(void *ptr) const { return ptr >= base && ptr < end; }
+private:
+ Arena(const Arena& other) = delete; // non construction-copyable
+ Arena& operator=(const Arena&) = delete; // non copyable
+
+ /** Map of chunk address to chunk information. This class makes use of the
+ * sorted order to merge previous and next chunks during deallocation.
+ */
+ std::map<char*, size_t> chunks_free;
+ std::map<char*, size_t> chunks_used;
+ /** Base address of arena */
+ char* base;
+ /** End address of arena */
+ char* end;
+ /** Minimum chunk alignment */
+ size_t alignment;
+};
+
+/** Pool for locked memory chunks.
+ *
+ * To avoid sensitive key data from being swapped to disk, the memory in this pool
+ * is locked/pinned.
+ *
+ * An arena manages a contiguous region of memory. The pool starts out with one arena
+ * but can grow to multiple arenas if the need arises.
+ *
+ * Unlike a normal C heap, the administrative structures are separate from the managed
+ * memory. This has been done as the sizes and bases of objects are not in themselves sensitive
+ * information, as to conserve precious locked memory. In some operating systems
+ * the amount of memory that can be locked is small.
+ */
+class LockedPool
+{
+public:
+ /** Size of one arena of locked memory. This is a compromise.
+ * Do not set this too low, as managing many arenas will increase
+ * allocation and deallocation overhead. Setting it too high allocates
+ * more locked memory from the OS than strictly necessary.
+ */
+ static const size_t ARENA_SIZE = 256*1024;
+ /** Chunk alignment. Another compromise. Setting this too high will waste
+ * memory, setting it too low will facilitate fragmentation.
+ */
+ static const size_t ARENA_ALIGN = 16;
+
+ /** Callback when allocation succeeds but locking fails.
+ */
+ typedef bool (*LockingFailed_Callback)();
+
+ /** Memory statistics. */
+ struct Stats
+ {
+ size_t used;
+ size_t free;
+ size_t total;
+ size_t locked;
+ size_t chunks_used;
+ size_t chunks_free;
+ };
+
+ /** Create a new LockedPool. This takes ownership of the MemoryPageLocker,
+ * you can only instantiate this with LockedPool(std::move(...)).
+ *
+ * The second argument is an optional callback when locking a newly allocated arena failed.
+ * If this callback is provided and returns false, the allocation fails (hard fail), if
+ * it returns true the allocation proceeds, but it could warn.
+ */
+ LockedPool(std::unique_ptr<LockedPageAllocator> allocator, LockingFailed_Callback lf_cb_in = 0);
+ ~LockedPool();
+
+ /** Allocate size bytes from this arena.
+ * Returns pointer on success, or 0 if memory is full or
+ * the application tried to allocate 0 bytes.
+ */
+ void* alloc(size_t size);
+
+ /** Free a previously allocated chunk of memory.
+ * Freeing the zero pointer has no effect.
+ * Raises std::runtime_error in case of error.
+ */
+ void free(void *ptr);
+
+ /** Get pool usage statistics */
+ Stats stats() const;
+private:
+ LockedPool(const LockedPool& other) = delete; // non construction-copyable
+ LockedPool& operator=(const LockedPool&) = delete; // non copyable
+
+ std::unique_ptr<LockedPageAllocator> allocator;
+
+ /** Create an arena from locked pages */
+ class LockedPageArena: public Arena
+ {
+ public:
+ LockedPageArena(LockedPageAllocator *alloc_in, void *base_in, size_t size, size_t align);
+ ~LockedPageArena();
+ private:
+ void *base;
+ size_t size;
+ LockedPageAllocator *allocator;
+ };
+
+ bool new_arena(size_t size, size_t align);
+
+ std::list<LockedPageArena> arenas;
+ LockingFailed_Callback lf_cb;
+ size_t cumulative_bytes_locked;
+ /** Mutex protects access to this pool's data structures, including arenas.
+ */
+ mutable std::mutex mutex;
+};
+
+/**
+ * Singleton class to keep track of locked (ie, non-swappable) memory, for use in
+ * std::allocator templates.
+ *
+ * Some implementations of the STL allocate memory in some constructors (i.e., see
+ * MSVC's vector<T> implementation where it allocates 1 byte of memory in the allocator.)
+ * Due to the unpredictable order of static initializers, we have to make sure the
+ * LockedPoolManager instance exists before any other STL-based objects that use
+ * secure_allocator are created. So instead of having LockedPoolManager also be
+ * static-initialized, it is created on demand.
+ */
+class LockedPoolManager : public LockedPool
+{
+public:
+ /** Return the current instance, or create it once */
+ static LockedPoolManager& Instance()
+ {
+ std::call_once(LockedPoolManager::init_flag, LockedPoolManager::CreateInstance);
+ return *LockedPoolManager::_instance;
+ }
+
+private:
+ LockedPoolManager(std::unique_ptr<LockedPageAllocator> allocator);
+
+ /** Create a new LockedPoolManager specialized to the OS */
+ static void CreateInstance();
+ /** Called when locking fails, warn the user here */
+ static bool LockingFailed();
+
+ static LockedPoolManager* _instance;
+ static std::once_flag init_flag;
+};
+
+#endif // BITCOIN_SUPPORT_LOCKEDPOOL_H
diff --git a/src/support/pagelocker.cpp b/src/support/pagelocker.cpp
deleted file mode 100644
index 7cea2d88c5..0000000000
--- a/src/support/pagelocker.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright (c) 2009-2015 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#include "support/pagelocker.h"
-
-#if defined(HAVE_CONFIG_H)
-#include "config/bitcoin-config.h"
-#endif
-
-#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 <windows.h>
-// 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 <sys/mman.h>
-#include <limits.h> // for PAGESIZE
-#include <unistd.h> // for sysconf
-#endif
-
-LockedPageManager* LockedPageManager::_instance = NULL;
-boost::once_flag LockedPageManager::init_flag = BOOST_ONCE_INIT;
-
-/** 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;
-}
-
-bool MemoryPageLocker::Lock(const void* addr, size_t len)
-{
-#ifdef WIN32
- return VirtualLock(const_cast<void*>(addr), len) != 0;
-#else
- return mlock(addr, len) == 0;
-#endif
-}
-
-bool MemoryPageLocker::Unlock(const void* addr, size_t len)
-{
-#ifdef WIN32
- return VirtualUnlock(const_cast<void*>(addr), len) != 0;
-#else
- return munlock(addr, len) == 0;
-#endif
-}
-
-LockedPageManager::LockedPageManager() : LockedPageManagerBase<MemoryPageLocker>(GetSystemPageSize())
-{
-}
diff --git a/src/support/pagelocker.h b/src/support/pagelocker.h
deleted file mode 100644
index 6b3979e551..0000000000
--- a/src/support/pagelocker.h
+++ /dev/null
@@ -1,177 +0,0 @@
-// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2015 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#ifndef BITCOIN_SUPPORT_PAGELOCKER_H
-#define BITCOIN_SUPPORT_PAGELOCKER_H
-
-#include "support/cleanse.h"
-
-#include <map>
-
-#include <boost/thread/mutex.hpp>
-#include <boost/thread/once.hpp>
-
-/**
- * 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 Locker>
-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);
- }
-
- ~LockedPageManagerBase()
- {
- }
-
-
- // 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<size_t>(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<void*>(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<size_t>(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<void*>(page), page_size);
- histogram.erase(it);
- }
- }
- }
-
- // Get number of locked pages for diagnostics
- int GetLockedPageCount()
- {
- boost::mutex::scoped_lock lock(mutex);
- return histogram.size();
- }
-
-private:
- Locker locker;
- boost::mutex mutex;
- size_t page_size, page_mask;
- // map of page base address to lock count
- typedef std::map<size_t, int> Histogram;
- Histogram histogram;
-};
-
-
-/**
- * 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);
- /** Unlock memory pages.
- * addr and len must be a multiple of the system page size
- */
- bool Unlock(const void* addr, size_t len);
-};
-
-/**
- * Singleton class to keep track of locked (ie, non-swappable) memory pages, for use in
- * std::allocator templates.
- *
- * Some implementations of the STL allocate memory in some constructors (i.e., see
- * MSVC's vector<T> implementation where it allocates 1 byte of memory in the allocator.)
- * Due to the unpredictable order of static initializers, we have to make sure the
- * LockedPageManager instance exists before any other STL-based objects that use
- * secure_allocator are created. So instead of having LockedPageManager also be
- * static-initialized, it is created on demand.
- */
-class LockedPageManager : public LockedPageManagerBase<MemoryPageLocker>
-{
-public:
- static LockedPageManager& Instance()
- {
- boost::call_once(LockedPageManager::CreateInstance, LockedPageManager::init_flag);
- return *LockedPageManager::_instance;
- }
-
-private:
- LockedPageManager();
-
- static void CreateInstance()
- {
- // Using a local static instance guarantees that the object is initialized
- // when it's first needed and also deinitialized after all objects that use
- // it are done with it. I can think of one unlikely scenario where we may
- // have a static deinitialization order/problem, but the check in
- // LockedPageManagerBase's destructor helps us detect if that ever happens.
- static LockedPageManager instance;
- LockedPageManager::_instance = &instance;
- }
-
- static LockedPageManager* _instance;
- static boost::once_flag init_flag;
-};
-
-//
-// Functions for directly locking/unlocking memory objects.
-// Intended for non-dynamically allocated structures.
-//
-template <typename T>
-void LockObject(const T& t)
-{
- LockedPageManager::Instance().LockRange((void*)(&t), sizeof(T));
-}
-
-template <typename T>
-void UnlockObject(const T& t)
-{
- memory_cleanse((void*)(&t), sizeof(T));
- LockedPageManager::Instance().UnlockRange((void*)(&t), sizeof(T));
-}
-
-#endif // BITCOIN_SUPPORT_PAGELOCKER_H
diff --git a/src/test/Checkpoints_tests.cpp b/src/test/Checkpoints_tests.cpp
deleted file mode 100644
index 1b7d368e13..0000000000
--- a/src/test/Checkpoints_tests.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2011-2015 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-//
-// Unit tests for block-chain checkpoints
-//
-
-#include "checkpoints.h"
-
-#include "uint256.h"
-#include "test/test_bitcoin.h"
-#include "chainparams.h"
-
-#include <boost/test/unit_test.hpp>
-
-using namespace std;
-
-BOOST_FIXTURE_TEST_SUITE(Checkpoints_tests, BasicTestingSetup)
-
-BOOST_AUTO_TEST_CASE(sanity)
-{
- const CCheckpointData& checkpoints = Params(CBaseChainParams::MAIN).Checkpoints();
- BOOST_CHECK(Checkpoints::GetTotalBlocksEstimate(checkpoints) >= 134444);
-}
-
-BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp
index 33f107d84b..131d667e0b 100644
--- a/src/test/DoS_tests.cpp
+++ b/src/test/DoS_tests.cpp
@@ -6,8 +6,8 @@
#include "chainparams.h"
#include "keystore.h"
-#include "main.h"
#include "net.h"
+#include "net_processing.h"
#include "pow.h"
#include "script/sign.h"
#include "serialize.h"
@@ -29,9 +29,9 @@ extern unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans);
struct COrphanTx {
CTransaction tx;
NodeId fromPeer;
+ int64_t nTimeExpire;
};
extern std::map<uint256, COrphanTx> mapOrphanTransactions;
-extern std::map<uint256, std::set<uint256> > mapOrphanTransactionsByPrev;
CService ip(uint32_t i)
{
@@ -48,8 +48,9 @@ BOOST_AUTO_TEST_CASE(DoS_banning)
{
connman->ClearBanned();
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
- CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, "", true);
- GetNodeSignals().InitializeNode(dummyNode1.GetId(), &dummyNode1);
+ CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 0, 0, "", true);
+ dummyNode1.SetSendVersion(PROTOCOL_VERSION);
+ GetNodeSignals().InitializeNode(&dummyNode1, *connman);
dummyNode1.nVersion = 1;
Misbehaving(dummyNode1.GetId(), 100); // Should get banned
SendMessages(&dummyNode1, *connman);
@@ -57,8 +58,9 @@ BOOST_AUTO_TEST_CASE(DoS_banning)
BOOST_CHECK(!connman->IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different IP, not banned
CAddress addr2(ip(0xa0b0c002), NODE_NONE);
- CNode dummyNode2(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr2, "", true);
- GetNodeSignals().InitializeNode(dummyNode2.GetId(), &dummyNode2);
+ CNode dummyNode2(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr2, 1, 1, "", true);
+ dummyNode2.SetSendVersion(PROTOCOL_VERSION);
+ GetNodeSignals().InitializeNode(&dummyNode2, *connman);
dummyNode2.nVersion = 1;
Misbehaving(dummyNode2.GetId(), 50);
SendMessages(&dummyNode2, *connman);
@@ -74,8 +76,9 @@ BOOST_AUTO_TEST_CASE(DoS_banscore)
connman->ClearBanned();
mapArgs["-banscore"] = "111"; // because 11 is my favorite number
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
- CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, "", true);
- GetNodeSignals().InitializeNode(dummyNode1.GetId(), &dummyNode1);
+ CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 3, 1, "", true);
+ dummyNode1.SetSendVersion(PROTOCOL_VERSION);
+ GetNodeSignals().InitializeNode(&dummyNode1, *connman);
dummyNode1.nVersion = 1;
Misbehaving(dummyNode1.GetId(), 100);
SendMessages(&dummyNode1, *connman);
@@ -96,8 +99,9 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
SetMockTime(nStartTime); // Overrides future calls to GetTime()
CAddress addr(ip(0xa0b0c001), NODE_NONE);
- CNode dummyNode(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr, "", true);
- GetNodeSignals().InitializeNode(dummyNode.GetId(), &dummyNode);
+ CNode dummyNode(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr, 4, 4, "", true);
+ dummyNode.SetSendVersion(PROTOCOL_VERSION);
+ GetNodeSignals().InitializeNode(&dummyNode, *connman);
dummyNode.nVersion = 1;
Misbehaving(dummyNode.GetId(), 100);
@@ -198,7 +202,6 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
BOOST_CHECK(mapOrphanTransactions.size() <= 10);
LimitOrphanTxSize(0);
BOOST_CHECK(mapOrphanTransactions.empty());
- BOOST_CHECK(mapOrphanTransactionsByPrev.empty());
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/README.md b/src/test/README.md
index 61462642bf..8f99804e10 100644
--- a/src/test/README.md
+++ b/src/test/README.md
@@ -1,4 +1,36 @@
-# Notes
+### Compiling/running unit tests
+
+Unit tests will be automatically compiled if dependencies were met in `./configure`
+and tests weren't explicitly disabled.
+
+After configuring, they can be run with `make check`.
+
+To run the bitcoind tests manually, launch `src/test/test_bitcoin`.
+
+To add more bitcoind 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.
+
+To run the bitcoin-qt tests manually, launch `src/qt/test/test_bitcoin-qt`
+
+To add more bitcoin-qt tests, add them to the `src/qt/test/` directory and
+the `src/qt/test/test_main.cpp` file.
+
+### Running individual tests
+
+test_bitcoin has some built-in command-line arguments; for
+example, to run just the getarg_tests verbosely:
+
+ test_bitcoin --log_level=all --run_test=getarg_tests
+
+... or to run just the doubledash test:
+
+ test_bitcoin --run_test=getarg_tests/doubledash
+
+Run `test_bitcoin --help` for the full list.
+
+### Note on adding test cases
+
The sources in this directory are unit test cases. Boost includes a
unit testing framework, and since bitcoin already uses boost, it makes
sense to simply use this framework rather than require developers to
@@ -19,14 +51,11 @@ For further reading, I found the following website to be helpful in
explaining how the boost unit test framework works:
[http://www.alittlemadness.com/2009/03/31/c-unit-testing-with-boosttest/](http://www.alittlemadness.com/2009/03/31/c-unit-testing-with-boosttest/).
-test_bitcoin has some built-in command-line arguments; for
-example, to run just the getarg_tests verbosely:
-
- test_bitcoin --log_level=all --run_test=getarg_tests
+### bitcoin-util-test.py
-... or to run just the doubledash test:
+The test directory also contains the bitcoin-util-test.py tool, which tests bitcoin utils (currently just bitcoin-tx). This test gets run automatically during the `make check` build process. It is also possible to run the test manually from the src directory:
- test_bitcoin --run_test=getarg_tests/doubledash
-
-Run `test_bitcoin --help` for the full list.
+```
+test/bitcoin-util-test.py --srcdir=[current directory]
+```
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index 5f150e4812..adff09f754 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -26,7 +26,7 @@ public:
void MakeDeterministic()
{
nKey.SetNull();
- seed_insecure_rand(true);
+ insecure_rand = FastRandomContext(true);
}
int RandomInt(int nMax)
@@ -257,8 +257,7 @@ BOOST_AUTO_TEST_CASE(addrman_tried_collisions)
addrman.Good(CAddress(addr, NODE_NONE));
//Test 15: No collision in tried table yet.
- BOOST_TEST_MESSAGE(addrman.size());
- BOOST_CHECK(addrman.size() == i);
+ BOOST_CHECK_EQUAL(addrman.size(), i);
}
//Test 16: tried table collision!
@@ -543,4 +542,4 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
// than 64 buckets.
BOOST_CHECK(buckets.size() > 64);
}
-BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/allocator_tests.cpp b/src/test/allocator_tests.cpp
index 613f6c12d7..77e9df5d82 100644
--- a/src/test/allocator_tests.cpp
+++ b/src/test/allocator_tests.cpp
@@ -11,110 +11,224 @@
BOOST_FIXTURE_TEST_SUITE(allocator_tests, BasicTestingSetup)
-// Dummy memory page locker for platform independent tests
-static const void *last_lock_addr, *last_unlock_addr;
-static size_t last_lock_len, last_unlock_len;
-class TestLocker
+BOOST_AUTO_TEST_CASE(arena_tests)
{
-public:
- bool Lock(const void *addr, size_t len)
+ // Fake memory base address for testing
+ // without actually using memory.
+ void *synth_base = reinterpret_cast<void*>(0x08000000);
+ const size_t synth_size = 1024*1024;
+ Arena b(synth_base, synth_size, 16);
+ void *chunk = b.alloc(1000);
+#ifdef ARENA_DEBUG
+ b.walk();
+#endif
+ BOOST_CHECK(chunk != nullptr);
+ BOOST_CHECK(b.stats().used == 1008); // Aligned to 16
+ BOOST_CHECK(b.stats().total == synth_size); // Nothing has disappeared?
+ b.free(chunk);
+#ifdef ARENA_DEBUG
+ b.walk();
+#endif
+ BOOST_CHECK(b.stats().used == 0);
+ BOOST_CHECK(b.stats().free == synth_size);
+ try { // Test exception on double-free
+ b.free(chunk);
+ BOOST_CHECK(0);
+ } catch(std::runtime_error &)
{
- last_lock_addr = addr;
- last_lock_len = len;
- return true;
}
- bool Unlock(const void *addr, size_t len)
- {
- last_unlock_addr = addr;
- last_unlock_len = len;
- return true;
+
+ void *a0 = b.alloc(128);
+ void *a1 = b.alloc(256);
+ void *a2 = b.alloc(512);
+ BOOST_CHECK(b.stats().used == 896);
+ BOOST_CHECK(b.stats().total == synth_size);
+#ifdef ARENA_DEBUG
+ b.walk();
+#endif
+ b.free(a0);
+#ifdef ARENA_DEBUG
+ b.walk();
+#endif
+ BOOST_CHECK(b.stats().used == 768);
+ b.free(a1);
+ BOOST_CHECK(b.stats().used == 512);
+ void *a3 = b.alloc(128);
+#ifdef ARENA_DEBUG
+ b.walk();
+#endif
+ BOOST_CHECK(b.stats().used == 640);
+ b.free(a2);
+ BOOST_CHECK(b.stats().used == 128);
+ b.free(a3);
+ BOOST_CHECK(b.stats().used == 0);
+ BOOST_CHECK_EQUAL(b.stats().chunks_used, 0);
+ BOOST_CHECK(b.stats().total == synth_size);
+ BOOST_CHECK(b.stats().free == synth_size);
+ BOOST_CHECK_EQUAL(b.stats().chunks_free, 1);
+
+ std::vector<void*> addr;
+ BOOST_CHECK(b.alloc(0) == nullptr); // allocating 0 always returns nullptr
+#ifdef ARENA_DEBUG
+ b.walk();
+#endif
+ // Sweeping allocate all memory
+ for (int x=0; x<1024; ++x)
+ addr.push_back(b.alloc(1024));
+ BOOST_CHECK(b.stats().free == 0);
+ BOOST_CHECK(b.alloc(1024) == nullptr); // memory is full, this must return nullptr
+ BOOST_CHECK(b.alloc(0) == nullptr);
+ for (int x=0; x<1024; ++x)
+ b.free(addr[x]);
+ addr.clear();
+ BOOST_CHECK(b.stats().total == synth_size);
+ BOOST_CHECK(b.stats().free == synth_size);
+
+ // Now in the other direction...
+ for (int x=0; x<1024; ++x)
+ addr.push_back(b.alloc(1024));
+ for (int x=0; x<1024; ++x)
+ b.free(addr[1023-x]);
+ addr.clear();
+
+ // Now allocate in smaller unequal chunks, then deallocate haphazardly
+ // Not all the chunks will succeed allocating, but freeing nullptr is
+ // allowed so that is no problem.
+ for (int x=0; x<2048; ++x)
+ addr.push_back(b.alloc(x+1));
+ for (int x=0; x<2048; ++x)
+ b.free(addr[((x*23)%2048)^242]);
+ addr.clear();
+
+ // Go entirely wild: free and alloc interleaved,
+ // generate targets and sizes using pseudo-randomness.
+ for (int x=0; x<2048; ++x)
+ addr.push_back(0);
+ uint32_t s = 0x12345678;
+ for (int x=0; x<5000; ++x) {
+ int idx = s & (addr.size()-1);
+ if (s & 0x80000000) {
+ b.free(addr[idx]);
+ addr[idx] = 0;
+ } else if(!addr[idx]) {
+ addr[idx] = b.alloc((s >> 16) & 2047);
+ }
+ bool lsb = s & 1;
+ s >>= 1;
+ if (lsb)
+ s ^= 0xf00f00f0; // LFSR period 0xf7ffffe0
}
-};
+ for (void *ptr: addr)
+ b.free(ptr);
+ addr.clear();
-BOOST_AUTO_TEST_CASE(test_LockedPageManagerBase)
+ BOOST_CHECK(b.stats().total == synth_size);
+ BOOST_CHECK(b.stats().free == synth_size);
+}
+
+/** Mock LockedPageAllocator for testing */
+class TestLockedPageAllocator: public LockedPageAllocator
{
- const size_t test_page_size = 4096;
- LockedPageManagerBase<TestLocker> lpm(test_page_size);
- size_t addr;
- last_lock_addr = last_unlock_addr = 0;
- last_lock_len = last_unlock_len = 0;
-
- /* Try large number of small objects */
- addr = 0;
- for(int i=0; i<1000; ++i)
- {
- lpm.LockRange(reinterpret_cast<void*>(addr), 33);
- addr += 33;
- }
- /* Try small number of page-sized objects, straddling two pages */
- addr = test_page_size*100 + 53;
- for(int i=0; i<100; ++i)
- {
- lpm.LockRange(reinterpret_cast<void*>(addr), test_page_size);
- addr += test_page_size;
- }
- /* Try small number of page-sized objects aligned to exactly one page */
- addr = test_page_size*300;
- for(int i=0; i<100; ++i)
- {
- lpm.LockRange(reinterpret_cast<void*>(addr), test_page_size);
- addr += test_page_size;
- }
- /* one very large object, straddling pages */
- lpm.LockRange(reinterpret_cast<void*>(test_page_size*600+1), test_page_size*500);
- BOOST_CHECK(last_lock_addr == reinterpret_cast<void*>(test_page_size*(600+500)));
- /* one very large object, page aligned */
- lpm.LockRange(reinterpret_cast<void*>(test_page_size*1200), test_page_size*500-1);
- BOOST_CHECK(last_lock_addr == reinterpret_cast<void*>(test_page_size*(1200+500-1)));
-
- BOOST_CHECK(lpm.GetLockedPageCount() == (
- (1000*33+test_page_size-1)/test_page_size + // small objects
- 101 + 100 + // page-sized objects
- 501 + 500)); // large objects
- BOOST_CHECK((last_lock_len & (test_page_size-1)) == 0); // always lock entire pages
- BOOST_CHECK(last_unlock_len == 0); // nothing unlocked yet
-
- /* And unlock again */
- addr = 0;
- for(int i=0; i<1000; ++i)
+public:
+ TestLockedPageAllocator(int count_in, int lockedcount_in): count(count_in), lockedcount(lockedcount_in) {}
+ void* AllocateLocked(size_t len, bool *lockingSuccess)
{
- lpm.UnlockRange(reinterpret_cast<void*>(addr), 33);
- addr += 33;
+ *lockingSuccess = false;
+ if (count > 0) {
+ --count;
+
+ if (lockedcount > 0) {
+ --lockedcount;
+ *lockingSuccess = true;
+ }
+
+ return reinterpret_cast<void*>(0x08000000 + (count<<24)); // Fake address, do not actually use this memory
+ }
+ return 0;
}
- addr = test_page_size*100 + 53;
- for(int i=0; i<100; ++i)
+ void FreeLocked(void* addr, size_t len)
{
- lpm.UnlockRange(reinterpret_cast<void*>(addr), test_page_size);
- addr += test_page_size;
}
- addr = test_page_size*300;
- for(int i=0; i<100; ++i)
+ size_t GetLimit()
{
- lpm.UnlockRange(reinterpret_cast<void*>(addr), test_page_size);
- addr += test_page_size;
+ return std::numeric_limits<size_t>::max();
}
- lpm.UnlockRange(reinterpret_cast<void*>(test_page_size*600+1), test_page_size*500);
- lpm.UnlockRange(reinterpret_cast<void*>(test_page_size*1200), test_page_size*500-1);
+private:
+ int count;
+ int lockedcount;
+};
- /* Check that everything is released */
- BOOST_CHECK(lpm.GetLockedPageCount() == 0);
+BOOST_AUTO_TEST_CASE(lockedpool_tests_mock)
+{
+ // Test over three virtual arenas, of which one will succeed being locked
+ std::unique_ptr<LockedPageAllocator> x(new TestLockedPageAllocator(3, 1));
+ LockedPool pool(std::move(x));
+ BOOST_CHECK(pool.stats().total == 0);
+ BOOST_CHECK(pool.stats().locked == 0);
- /* A few and unlocks of size zero (should have no effect) */
- addr = 0;
- for(int i=0; i<1000; ++i)
- {
- lpm.LockRange(reinterpret_cast<void*>(addr), 0);
- addr += 1;
- }
- BOOST_CHECK(lpm.GetLockedPageCount() == 0);
- addr = 0;
- for(int i=0; i<1000; ++i)
+ // Ensure unreasonable requests are refused without allocating anything
+ void *invalid_toosmall = pool.alloc(0);
+ BOOST_CHECK(invalid_toosmall == nullptr);
+ BOOST_CHECK(pool.stats().used == 0);
+ BOOST_CHECK(pool.stats().free == 0);
+ void *invalid_toobig = pool.alloc(LockedPool::ARENA_SIZE+1);
+ BOOST_CHECK(invalid_toobig == nullptr);
+ BOOST_CHECK(pool.stats().used == 0);
+ BOOST_CHECK(pool.stats().free == 0);
+
+ void *a0 = pool.alloc(LockedPool::ARENA_SIZE / 2);
+ BOOST_CHECK(a0);
+ BOOST_CHECK(pool.stats().locked == LockedPool::ARENA_SIZE);
+ void *a1 = pool.alloc(LockedPool::ARENA_SIZE / 2);
+ BOOST_CHECK(a1);
+ void *a2 = pool.alloc(LockedPool::ARENA_SIZE / 2);
+ BOOST_CHECK(a2);
+ void *a3 = pool.alloc(LockedPool::ARENA_SIZE / 2);
+ BOOST_CHECK(a3);
+ void *a4 = pool.alloc(LockedPool::ARENA_SIZE / 2);
+ BOOST_CHECK(a4);
+ void *a5 = pool.alloc(LockedPool::ARENA_SIZE / 2);
+ BOOST_CHECK(a5);
+ // We've passed a count of three arenas, so this allocation should fail
+ void *a6 = pool.alloc(16);
+ BOOST_CHECK(!a6);
+
+ pool.free(a0);
+ pool.free(a2);
+ pool.free(a4);
+ pool.free(a1);
+ pool.free(a3);
+ pool.free(a5);
+ BOOST_CHECK(pool.stats().total == 3*LockedPool::ARENA_SIZE);
+ BOOST_CHECK(pool.stats().locked == LockedPool::ARENA_SIZE);
+ BOOST_CHECK(pool.stats().used == 0);
+}
+
+// These tests used the live LockedPoolManager object, this is also used
+// by other tests so the conditions are somewhat less controllable and thus the
+// tests are somewhat more error-prone.
+BOOST_AUTO_TEST_CASE(lockedpool_tests_live)
+{
+ LockedPoolManager &pool = LockedPoolManager::Instance();
+ LockedPool::Stats initial = pool.stats();
+
+ void *a0 = pool.alloc(16);
+ BOOST_CHECK(a0);
+ // Test reading and writing the allocated memory
+ *((uint32_t*)a0) = 0x1234;
+ BOOST_CHECK(*((uint32_t*)a0) == 0x1234);
+
+ pool.free(a0);
+ try { // Test exception on double-free
+ pool.free(a0);
+ BOOST_CHECK(0);
+ } catch(std::runtime_error &)
{
- lpm.UnlockRange(reinterpret_cast<void*>(addr), 0);
- addr += 1;
}
- BOOST_CHECK(lpm.GetLockedPageCount() == 0);
- BOOST_CHECK((last_unlock_len & (test_page_size-1)) == 0); // always unlock entire pages
+ // If more than one new arena was allocated for the above tests, something is wrong
+ BOOST_CHECK(pool.stats().total <= (initial.total + LockedPool::ARENA_SIZE));
+ // Usage must be back to where it started
+ BOOST_CHECK(pool.stats().used == initial.used);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp
index ac3ab4c83f..04a6506655 100644
--- a/src/test/base58_tests.cpp
+++ b/src/test/base58_tests.cpp
@@ -39,7 +39,7 @@ BOOST_AUTO_TEST_CASE(base58_EncodeBase58)
std::vector<unsigned char> sourcedata = ParseHex(test[0].get_str());
std::string base58string = test[1].get_str();
BOOST_CHECK_MESSAGE(
- EncodeBase58(begin_ptr(sourcedata), end_ptr(sourcedata)) == base58string,
+ EncodeBase58(sourcedata.data(), sourcedata.data() + sourcedata.size()) == base58string,
strTest);
}
}
diff --git a/src/test/bctest.py b/src/test/bctest.py
index 8105b87ffa..adc5d0e418 100644
--- a/src/test/bctest.py
+++ b/src/test/bctest.py
@@ -1,4 +1,5 @@
-# Copyright 2014 BitPay, Inc.
+# Copyright 2014 BitPay Inc.
+# Copyright 2016 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
from __future__ import division,print_function,unicode_literals
@@ -6,49 +7,115 @@ import subprocess
import os
import json
import sys
+import binascii
+import difflib
+import logging
+
+def parse_output(a, fmt):
+ """Parse the output according to specified format.
+
+ Raise an error if the output can't be parsed."""
+ if fmt == 'json': # json: compare parsed data
+ return json.loads(a)
+ elif fmt == 'hex': # hex: parse and compare binary data
+ return binascii.a2b_hex(a.strip())
+ else:
+ raise NotImplementedError("Don't know how to compare %s" % fmt)
def bctest(testDir, testObj, exeext):
+ """Runs a single test, comparing output and RC to expected output and RC.
+
+ Raises an error if input can't be read, executable fails, or output/RC
+ are not as expected. Error is caught by bctester() and reported.
+ """
+ # Get the exec names and arguments
+ execprog = testObj['exec'] + exeext
+ execargs = testObj['args']
+ execrun = [execprog] + execargs
+
+ # Read the input data (if there is any)
+ stdinCfg = None
+ inputData = None
+ if "input" in testObj:
+ filename = testDir + "/" + testObj['input']
+ inputData = open(filename).read()
+ stdinCfg = subprocess.PIPE
+
+ # Read the expected output data (if there is any)
+ outputFn = None
+ outputData = None
+ if "output_cmp" in testObj:
+ outputFn = testObj['output_cmp']
+ outputType = os.path.splitext(outputFn)[1][1:] # output type from file extension (determines how to compare)
+ try:
+ outputData = open(testDir + "/" + outputFn).read()
+ except:
+ logging.error("Output file " + outputFn + " can not be opened")
+ raise
+ if not outputData:
+ logging.error("Output data missing for " + outputFn)
+ raise Exception
+
+ # Run the test
+ proc = subprocess.Popen(execrun, stdin=stdinCfg, stdout=subprocess.PIPE, stderr=subprocess.PIPE,universal_newlines=True)
+ try:
+ outs = proc.communicate(input=inputData)
+ except OSError:
+ logging.error("OSError, Failed to execute " + execprog)
+ raise
+
+ if outputData:
+ # Parse command output and expected output
+ try:
+ a_parsed = parse_output(outs[0], outputType)
+ except Exception as e:
+ logging.error('Error parsing command output as %s: %s' % (outputType,e))
+ raise
+ try:
+ b_parsed = parse_output(outputData, outputType)
+ except Exception as e:
+ logging.error('Error parsing expected output %s as %s: %s' % (outputFn,outputType,e))
+ raise
+ # Compare data
+ if a_parsed != b_parsed:
+ logging.error("Output data mismatch for " + outputFn + " (format " + outputType + ")")
+ raise Exception
+ # Compare formatting
+ if outs[0] != outputData:
+ error_message = "Output formatting mismatch for " + outputFn + ":\n"
+ error_message += "".join(difflib.context_diff(outputData.splitlines(True),
+ outs[0].splitlines(True),
+ fromfile=outputFn,
+ tofile="returned"))
+ logging.error(error_message)
+ raise Exception
- execprog = testObj['exec'] + exeext
- execargs = testObj['args']
- execrun = [execprog] + execargs
- stdinCfg = None
- inputData = None
- if "input" in testObj:
- filename = testDir + "/" + testObj['input']
- inputData = open(filename).read()
- stdinCfg = subprocess.PIPE
-
- outputFn = None
- outputData = None
- if "output_cmp" in testObj:
- outputFn = testObj['output_cmp']
- outputData = open(testDir + "/" + outputFn).read()
- proc = subprocess.Popen(execrun, stdin=stdinCfg, stdout=subprocess.PIPE, stderr=subprocess.PIPE,universal_newlines=True)
- try:
- outs = proc.communicate(input=inputData)
- except OSError:
- print("OSError, Failed to execute " + execprog)
- sys.exit(1)
-
- if outputData and (outs[0] != outputData):
- print("Output data mismatch for " + outputFn)
- sys.exit(1)
-
- wantRC = 0
- if "return_code" in testObj:
- wantRC = testObj['return_code']
- if proc.returncode != wantRC:
- print("Return code mismatch for " + outputFn)
- sys.exit(1)
+ # Compare the return code to the expected return code
+ wantRC = 0
+ if "return_code" in testObj:
+ wantRC = testObj['return_code']
+ if proc.returncode != wantRC:
+ logging.error("Return code mismatch for " + outputFn)
+ raise Exception
def bctester(testDir, input_basename, buildenv):
- input_filename = testDir + "/" + input_basename
- raw_data = open(input_filename).read()
- input_data = json.loads(raw_data)
+ """ Loads and parses the input file, runs all tests and reports results"""
+ input_filename = testDir + "/" + input_basename
+ raw_data = open(input_filename).read()
+ input_data = json.loads(raw_data)
- for testObj in input_data:
- bctest(testDir, testObj, buildenv.exeext)
+ failed_testcases = []
- sys.exit(0)
+ for testObj in input_data:
+ try:
+ bctest(testDir, testObj, buildenv.exeext)
+ logging.info("PASSED: " + testObj["description"])
+ except:
+ logging.info("FAILED: " + testObj["description"])
+ failed_testcases.append(testObj["description"])
+ if failed_testcases:
+ logging.error("FAILED TESTCASES: [" + ", ".join(failed_testcases) + "]")
+ sys.exit(1)
+ else:
+ sys.exit(0)
diff --git a/src/test/bitcoin-util-test.py b/src/test/bitcoin-util-test.py
index 882b5c67b8..1c090b3f3f 100755
--- a/src/test/bitcoin-util-test.py
+++ b/src/test/bitcoin-util-test.py
@@ -1,13 +1,46 @@
#!/usr/bin/env python
-# Copyright 2014 BitPay, Inc.
+# Copyright 2014 BitPay Inc.
+# Copyright 2016 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
from __future__ import division,print_function,unicode_literals
import os
+import sys
import bctest
import buildenv
+import argparse
+import logging
+
+help_text="""Test framework for bitcoin utils.
+
+Runs automatically during `make check`.
+
+Can also be run manually from the src directory by specifying the source directory:
+
+test/bitcoin-util-test.py --srcdir='srcdir' [--verbose]
+"""
if __name__ == '__main__':
- bctest.bctester(os.environ["srcdir"] + "/test/data",
- "bitcoin-util-test.json",buildenv)
+ # Try to get the source directory from the environment variables. This will
+ # be set for `make check` automated runs. If environment variable is not set,
+ # then get the source directory from command line args.
+ try:
+ srcdir = os.environ["srcdir"]
+ verbose = False
+ except:
+ parser = argparse.ArgumentParser(description=help_text)
+ parser.add_argument('-s', '--srcdir')
+ parser.add_argument('-v', '--verbose', action='store_true')
+ args = parser.parse_args()
+ srcdir = args.srcdir
+ verbose = args.verbose
+
+ if verbose:
+ level = logging.DEBUG
+ else:
+ level = logging.ERROR
+ formatter = '%(asctime)s - %(levelname)s - %(message)s'
+ # Add the format/level to the logger
+ logging.basicConfig(format = formatter, level=level)
+ bctest.bctester(srcdir + "/test/data", "bitcoin-util-test.json", buildenv)
diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp
index d2392cfb22..7478758f73 100644
--- a/src/test/blockencodings_tests.cpp
+++ b/src/test/blockencodings_tests.cpp
@@ -26,21 +26,21 @@ static CBlock BuildBlockTestCase() {
tx.vout[0].nValue = 42;
block.vtx.resize(3);
- block.vtx[0] = tx;
+ block.vtx[0] = MakeTransactionRef(tx);
block.nVersion = 42;
block.hashPrevBlock = GetRandHash();
block.nBits = 0x207fffff;
tx.vin[0].prevout.hash = GetRandHash();
tx.vin[0].prevout.n = 0;
- block.vtx[1] = tx;
+ block.vtx[1] = MakeTransactionRef(tx);
tx.vin.resize(10);
for (size_t i = 0; i < tx.vin.size(); i++) {
tx.vin[i].prevout.hash = GetRandHash();
tx.vin[i].prevout.n = 0;
}
- block.vtx[2] = tx;
+ block.vtx[2] = MakeTransactionRef(tx);
bool mutated;
block.hashMerkleRoot = BlockMerkleRoot(block, &mutated);
@@ -59,12 +59,12 @@ BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
TestMemPoolEntryHelper entry;
CBlock block(BuildBlockTestCase());
- pool.addUnchecked(block.vtx[2].GetHash(), entry.FromTx(block.vtx[2]));
- BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
+ pool.addUnchecked(block.vtx[2]->GetHash(), entry.FromTx(*block.vtx[2]));
+ BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
// Do a simple ShortTxIDs RT
{
- CBlockHeaderAndShortTxIDs shortIDs(block);
+ CBlockHeaderAndShortTxIDs shortIDs(block, true);
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << shortIDs;
@@ -78,14 +78,14 @@ BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
BOOST_CHECK(!partialBlock.IsTxAvailable(1));
BOOST_CHECK( partialBlock.IsTxAvailable(2));
- BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
+ BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
- std::list<CTransaction> removed;
- pool.removeRecursive(block.vtx[2], removed);
- BOOST_CHECK_EQUAL(removed.size(), 1);
+ size_t poolSize = pool.size();
+ pool.removeRecursive(*block.vtx[2]);
+ BOOST_CHECK_EQUAL(pool.size(), poolSize - 1);
CBlock block2;
- std::vector<CTransaction> vtx_missing;
+ std::vector<CTransactionRef> vtx_missing;
BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing) == READ_STATUS_INVALID); // No transactions
vtx_missing.push_back(block.vtx[2]); // Wrong transaction
@@ -116,7 +116,7 @@ public:
stream >> *this;
}
TestHeaderAndShortIDs(const CBlock& block) :
- TestHeaderAndShortIDs(CBlockHeaderAndShortTxIDs(block)) {}
+ TestHeaderAndShortIDs(CBlockHeaderAndShortTxIDs(block, true)) {}
uint64_t GetShortID(const uint256& txhash) const {
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
@@ -129,7 +129,7 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(header);
READWRITE(nonce);
size_t shorttxids_size = shorttxids.size();
@@ -152,8 +152,10 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
TestMemPoolEntryHelper entry;
CBlock block(BuildBlockTestCase());
- pool.addUnchecked(block.vtx[2].GetHash(), entry.FromTx(block.vtx[2]));
- BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
+ pool.addUnchecked(block.vtx[2]->GetHash(), entry.FromTx(*block.vtx[2]));
+ BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
+
+ uint256 txhash;
// Test with pre-forwarding tx 1, but not coinbase
{
@@ -161,8 +163,8 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
shortIDs.prefilledtxn.resize(1);
shortIDs.prefilledtxn[0] = {1, block.vtx[1]};
shortIDs.shorttxids.resize(2);
- shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[0].GetHash());
- shortIDs.shorttxids[1] = shortIDs.GetShortID(block.vtx[2].GetHash());
+ shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[0]->GetHash());
+ shortIDs.shorttxids[1] = shortIDs.GetShortID(block.vtx[2]->GetHash());
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << shortIDs;
@@ -176,10 +178,10 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
BOOST_CHECK( partialBlock.IsTxAvailable(1));
BOOST_CHECK( partialBlock.IsTxAvailable(2));
- BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
+ BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
CBlock block2;
- std::vector<CTransaction> vtx_missing;
+ std::vector<CTransactionRef> vtx_missing;
BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing) == READ_STATUS_INVALID); // No transactions
vtx_missing.push_back(block.vtx[1]); // Wrong transaction
@@ -194,9 +196,13 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block3, &mutated).ToString());
BOOST_CHECK(!mutated);
- BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
+ txhash = block.vtx[2]->GetHash();
+ block.vtx.clear();
+ block2.vtx.clear();
+ block3.vtx.clear();
+ BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
}
- BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
+ BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
}
BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
@@ -205,8 +211,10 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
TestMemPoolEntryHelper entry;
CBlock block(BuildBlockTestCase());
- pool.addUnchecked(block.vtx[1].GetHash(), entry.FromTx(block.vtx[1]));
- BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
+ pool.addUnchecked(block.vtx[1]->GetHash(), entry.FromTx(*block.vtx[1]));
+ BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
+
+ uint256 txhash;
// Test with pre-forwarding coinbase + tx 2 with tx 1 in mempool
{
@@ -215,7 +223,7 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
shortIDs.prefilledtxn[0] = {0, block.vtx[0]};
shortIDs.prefilledtxn[1] = {1, block.vtx[2]}; // id == 1 as it is 1 after index 1
shortIDs.shorttxids.resize(1);
- shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[1].GetHash());
+ shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[1]->GetHash());
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << shortIDs;
@@ -229,19 +237,22 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
BOOST_CHECK( partialBlock.IsTxAvailable(1));
BOOST_CHECK( partialBlock.IsTxAvailable(2));
- BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
+ BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
CBlock block2;
- std::vector<CTransaction> vtx_missing;
+ std::vector<CTransactionRef> vtx_missing;
BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing) == READ_STATUS_OK);
BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
bool mutated;
BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block2, &mutated).ToString());
BOOST_CHECK(!mutated);
- BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
+ txhash = block.vtx[1]->GetHash();
+ block.vtx.clear();
+ block2.vtx.clear();
+ BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
}
- BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
+ BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
}
BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)
@@ -255,7 +266,7 @@ BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)
CBlock block;
block.vtx.resize(1);
- block.vtx[0] = coinbase;
+ block.vtx[0] = MakeTransactionRef(std::move(coinbase));
block.nVersion = 42;
block.hashPrevBlock = GetRandHash();
block.nBits = 0x207fffff;
@@ -267,7 +278,7 @@ BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)
// Test simple header round-trip with only coinbase
{
- CBlockHeaderAndShortTxIDs shortIDs(block);
+ CBlockHeaderAndShortTxIDs shortIDs(block, false);
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << shortIDs;
@@ -280,7 +291,7 @@ BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)
BOOST_CHECK(partialBlock.IsTxAvailable(0));
CBlock block2;
- std::vector<CTransaction> vtx_missing;
+ std::vector<CTransactionRef> vtx_missing;
BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing) == READ_STATUS_OK);
BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block2, &mutated).ToString());
diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp
index 042fad42da..b15ff9e44b 100644
--- a/src/test/bloom_tests.cpp
+++ b/src/test/bloom_tests.cpp
@@ -30,18 +30,18 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize)
CBloomFilter filter(3, 0.01, 0, BLOOM_UPDATE_ALL);
filter.insert(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8"));
- BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter doesn't contain just-inserted object!");
+ BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "Bloom filter doesn't contain just-inserted object!");
// One bit different in first byte
- BOOST_CHECK_MESSAGE(!filter.contains(ParseHex("19108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter contains something it shouldn't!");
+ BOOST_CHECK_MESSAGE(!filter.contains(ParseHex("19108ad8ed9bb6274d3980bab5a85c048f0950c8")), "Bloom filter contains something it shouldn't!");
filter.insert(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee"));
- BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee")), "BloomFilter doesn't contain just-inserted object (2)!");
+ BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee")), "Bloom filter doesn't contain just-inserted object (2)!");
filter.insert(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5"));
- BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5")), "BloomFilter doesn't contain just-inserted object (3)!");
+ BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5")), "Bloom filter doesn't contain just-inserted object (3)!");
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
- filter.Serialize(stream, SER_NETWORK, PROTOCOL_VERSION);
+ stream << filter;
vector<unsigned char> vch = ParseHex("03614e9b050000000000000001");
vector<char> expected(vch.size());
@@ -51,9 +51,9 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize)
BOOST_CHECK_EQUAL_COLLECTIONS(stream.begin(), stream.end(), expected.begin(), expected.end());
- BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter doesn't contain just-inserted object!");
+ BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "Bloom filter doesn't contain just-inserted object!");
filter.clear();
- BOOST_CHECK_MESSAGE( !filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter should be empty!");
+ BOOST_CHECK_MESSAGE( !filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "Bloom filter should be empty!");
}
BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize_with_tweak)
@@ -62,18 +62,18 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize_with_tweak)
CBloomFilter filter(3, 0.01, 2147483649UL, BLOOM_UPDATE_ALL);
filter.insert(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8"));
- BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter doesn't contain just-inserted object!");
+ BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "Bloom filter doesn't contain just-inserted object!");
// One bit different in first byte
- BOOST_CHECK_MESSAGE(!filter.contains(ParseHex("19108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter contains something it shouldn't!");
+ BOOST_CHECK_MESSAGE(!filter.contains(ParseHex("19108ad8ed9bb6274d3980bab5a85c048f0950c8")), "Bloom filter contains something it shouldn't!");
filter.insert(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee"));
- BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee")), "BloomFilter doesn't contain just-inserted object (2)!");
+ BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee")), "Bloom filter doesn't contain just-inserted object (2)!");
filter.insert(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5"));
- BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5")), "BloomFilter doesn't contain just-inserted object (3)!");
+ BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5")), "Bloom filter doesn't contain just-inserted object (3)!");
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
- filter.Serialize(stream, SER_NETWORK, PROTOCOL_VERSION);
+ stream << filter;
vector<unsigned char> vch = ParseHex("03ce4299050000000100008001");
vector<char> expected(vch.size());
@@ -100,7 +100,7 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_key)
filter.insert(vector<unsigned char>(hash.begin(), hash.end()));
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
- filter.Serialize(stream, SER_NETWORK, PROTOCOL_VERSION);
+ stream << filter;
vector<unsigned char> vch = ParseHex("038fc16b080000000000000001");
vector<char> expected(vch.size());
@@ -114,16 +114,14 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_key)
BOOST_AUTO_TEST_CASE(bloom_match)
{
// Random real transaction (b4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b)
- CTransaction tx;
CDataStream stream(ParseHex("01000000010b26e9b7735eb6aabdf358bab62f9816a21ba9ebdb719d5299e88607d722c190000000008b4830450220070aca44506c5cef3a16ed519d7c3c39f8aab192c4e1c90d065f37b8a4af6141022100a8e160b856c2d43d27d8fba71e5aef6405b8643ac4cb7cb3c462aced7f14711a0141046d11fee51b0e60666d5049a9101a72741df480b96ee26488a4d3466b95c9a40ac5eeef87e10a5cd336c19a84565f80fa6c547957b7700ff4dfbdefe76036c339ffffffff021bff3d11000000001976a91404943fdd508053c75000106d3bc6e2754dbcff1988ac2f15de00000000001976a914a266436d2965547608b9e15d9032a7b9d64fa43188ac00000000"), SER_DISK, CLIENT_VERSION);
- stream >> tx;
+ CTransaction tx(deserialize, stream);
// and one which spends it (e2769b09e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436)
unsigned char ch[] = {0x01, 0x00, 0x00, 0x00, 0x01, 0x6b, 0xff, 0x7f, 0xcd, 0x4f, 0x85, 0x65, 0xef, 0x40, 0x6d, 0xd5, 0xd6, 0x3d, 0x4f, 0xf9, 0x4f, 0x31, 0x8f, 0xe8, 0x20, 0x27, 0xfd, 0x4d, 0xc4, 0x51, 0xb0, 0x44, 0x74, 0x01, 0x9f, 0x74, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x49, 0x30, 0x46, 0x02, 0x21, 0x00, 0xda, 0x0d, 0xc6, 0xae, 0xce, 0xfe, 0x1e, 0x06, 0xef, 0xdf, 0x05, 0x77, 0x37, 0x57, 0xde, 0xb1, 0x68, 0x82, 0x09, 0x30, 0xe3, 0xb0, 0xd0, 0x3f, 0x46, 0xf5, 0xfc, 0xf1, 0x50, 0xbf, 0x99, 0x0c, 0x02, 0x21, 0x00, 0xd2, 0x5b, 0x5c, 0x87, 0x04, 0x00, 0x76, 0xe4, 0xf2, 0x53, 0xf8, 0x26, 0x2e, 0x76, 0x3e, 0x2d, 0xd5, 0x1e, 0x7f, 0xf0, 0xbe, 0x15, 0x77, 0x27, 0xc4, 0xbc, 0x42, 0x80, 0x7f, 0x17, 0xbd, 0x39, 0x01, 0x41, 0x04, 0xe6, 0xc2, 0x6e, 0xf6, 0x7d, 0xc6, 0x10, 0xd2, 0xcd, 0x19, 0x24, 0x84, 0x78, 0x9a, 0x6c, 0xf9, 0xae, 0xa9, 0x93, 0x0b, 0x94, 0x4b, 0x7e, 0x2d, 0xb5, 0x34, 0x2b, 0x9d, 0x9e, 0x5b, 0x9f, 0xf7, 0x9a, 0xff, 0x9a, 0x2e, 0xe1, 0x97, 0x8d, 0xd7, 0xfd, 0x01, 0xdf, 0xc5, 0x22, 0xee, 0x02, 0x28, 0x3d, 0x3b, 0x06, 0xa9, 0xd0, 0x3a, 0xcf, 0x80, 0x96, 0x96, 0x8d, 0x7d, 0xbb, 0x0f, 0x91, 0x78, 0xff, 0xff, 0xff, 0xff, 0x02, 0x8b, 0xa7, 0x94, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xba, 0xde, 0xec, 0xfd, 0xef, 0x05, 0x07, 0x24, 0x7f, 0xc8, 0xf7, 0x42, 0x41, 0xd7, 0x3b, 0xc0, 0x39, 0x97, 0x2d, 0x7b, 0x88, 0xac, 0x40, 0x94, 0xa8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xc1, 0x09, 0x32, 0x48, 0x3f, 0xec, 0x93, 0xed, 0x51, 0xf5, 0xfe, 0x95, 0xe7, 0x25, 0x59, 0xf2, 0xcc, 0x70, 0x43, 0xf9, 0x88, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00};
vector<unsigned char> vch(ch, ch + sizeof(ch) -1);
CDataStream spendStream(vch, SER_DISK, CLIENT_VERSION);
- CTransaction spendingTx;
- spendStream >> spendingTx;
+ CTransaction spendingTx(deserialize, spendStream);
CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
filter.insert(uint256S("0xb4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b"));
diff --git a/src/test/bswap_tests.cpp b/src/test/bswap_tests.cpp
new file mode 100644
index 0000000000..7b3134d327
--- /dev/null
+++ b/src/test/bswap_tests.cpp
@@ -0,0 +1,26 @@
+// Copyright (c) 2016 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "compat/byteswap.h"
+#include "test/test_bitcoin.h"
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_FIXTURE_TEST_SUITE(bswap_tests, BasicTestingSetup)
+
+BOOST_AUTO_TEST_CASE(bswap_tests)
+{
+ // Sibling in bitcoin/src/qt/test/compattests.cpp
+ uint16_t u1 = 0x1234;
+ uint32_t u2 = 0x56789abc;
+ uint64_t u3 = 0xdef0123456789abc;
+ uint16_t e1 = 0x3412;
+ uint32_t e2 = 0xbc9a7856;
+ uint64_t e3 = 0xbc9a78563412f0de;
+ BOOST_CHECK(bswap_16(u1) == e1);
+ BOOST_CHECK(bswap_32(u2) == e2);
+ BOOST_CHECK(bswap_64(u3) == e3);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
index e692326559..79dea00e46 100644
--- a/src/test/coins_tests.cpp
+++ b/src/test/coins_tests.cpp
@@ -3,12 +3,12 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "coins.h"
-#include "random.h"
#include "script/standard.h"
#include "uint256.h"
#include "utilstrencodings.h"
#include "test/test_bitcoin.h"
-#include "main.h"
+#include "test/test_random.h"
+#include "validation.h"
#include "consensus/validation.h"
#include <vector>
diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp
index 58a62ee022..7dcd548edf 100644
--- a/src/test/crypto_tests.cpp
+++ b/src/test/crypto_tests.cpp
@@ -9,9 +9,9 @@
#include "crypto/sha512.h"
#include "crypto/hmac_sha256.h"
#include "crypto/hmac_sha512.h"
-#include "random.h"
#include "utilstrencodings.h"
#include "test/test_bitcoin.h"
+#include "test/test_random.h"
#include <vector>
@@ -133,13 +133,13 @@ void TestAES128CBC(const std::string &hexkey, const std::string &hexiv, bool pad
{
std::vector<unsigned char> sub(i, in.end());
std::vector<unsigned char> subout(sub.size() + AES_BLOCKSIZE);
- int size = enc.Encrypt(&sub[0], sub.size(), &subout[0]);
- if (size != 0)
+ int _size = enc.Encrypt(&sub[0], sub.size(), &subout[0]);
+ if (_size != 0)
{
- subout.resize(size);
+ subout.resize(_size);
std::vector<unsigned char> subdecrypted(subout.size());
- size = dec.Decrypt(&subout[0], subout.size(), &subdecrypted[0]);
- subdecrypted.resize(size);
+ _size = dec.Decrypt(&subout[0], subout.size(), &subdecrypted[0]);
+ subdecrypted.resize(_size);
BOOST_CHECK(decrypted.size() == in.size());
BOOST_CHECK_MESSAGE(subdecrypted == sub, HexStr(subdecrypted) + std::string(" != ") + HexStr(sub));
}
@@ -174,13 +174,13 @@ void TestAES256CBC(const std::string &hexkey, const std::string &hexiv, bool pad
{
std::vector<unsigned char> sub(i, in.end());
std::vector<unsigned char> subout(sub.size() + AES_BLOCKSIZE);
- int size = enc.Encrypt(&sub[0], sub.size(), &subout[0]);
- if (size != 0)
+ int _size = enc.Encrypt(&sub[0], sub.size(), &subout[0]);
+ if (_size != 0)
{
- subout.resize(size);
+ subout.resize(_size);
std::vector<unsigned char> subdecrypted(subout.size());
- size = dec.Decrypt(&subout[0], subout.size(), &subdecrypted[0]);
- subdecrypted.resize(size);
+ _size = dec.Decrypt(&subout[0], subout.size(), &subdecrypted[0]);
+ subdecrypted.resize(_size);
BOOST_CHECK(decrypted.size() == in.size());
BOOST_CHECK_MESSAGE(subdecrypted == sub, HexStr(subdecrypted) + std::string(" != ") + HexStr(sub));
}
diff --git a/src/test/cuckoocache_tests.cpp b/src/test/cuckoocache_tests.cpp
new file mode 100644
index 0000000000..1bc50d5ea9
--- /dev/null
+++ b/src/test/cuckoocache_tests.cpp
@@ -0,0 +1,394 @@
+// Copyright (c) 2012-2016 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <boost/test/unit_test.hpp>
+#include "cuckoocache.h"
+#include "test/test_bitcoin.h"
+#include "random.h"
+#include <thread>
+#include <boost/thread.hpp>
+
+
+/** Test Suite for CuckooCache
+ *
+ * 1) All tests should have a deterministic result (using insecure rand
+ * with deterministic seeds)
+ * 2) Some test methods are templated to allow for easier testing
+ * against new versions / comparing
+ * 3) Results should be treated as a regression test, ie, did the behavior
+ * change significantly from what was expected. This can be OK, depending on
+ * the nature of the change, but requires updating the tests to reflect the new
+ * expected behavior. For example improving the hit rate may cause some tests
+ * using BOOST_CHECK_CLOSE to fail.
+ *
+ */
+FastRandomContext insecure_rand(true);
+
+BOOST_AUTO_TEST_SUITE(cuckoocache_tests);
+
+
+/** insecure_GetRandHash fills in a uint256 from insecure_rand
+ */
+void insecure_GetRandHash(uint256& t)
+{
+ uint32_t* ptr = (uint32_t*)t.begin();
+ for (uint8_t j = 0; j < 8; ++j)
+ *(ptr++) = insecure_rand.rand32();
+}
+
+/** Definition copied from /src/script/sigcache.cpp
+ */
+class uint256Hasher
+{
+public:
+ template <uint8_t hash_select>
+ uint32_t operator()(const uint256& key) const
+ {
+ static_assert(hash_select <8, "SignatureCacheHasher only has 8 hashes available.");
+ uint32_t u;
+ std::memcpy(&u, key.begin() + 4 * hash_select, 4);
+ return u;
+ }
+};
+
+
+/* Test that no values not inserted into the cache are read out of it.
+ *
+ * There are no repeats in the first 200000 insecure_GetRandHash calls
+ */
+BOOST_AUTO_TEST_CASE(test_cuckoocache_no_fakes)
+{
+ insecure_rand = FastRandomContext(true);
+ CuckooCache::cache<uint256, uint256Hasher> cc{};
+ cc.setup_bytes(32 << 20);
+ uint256 v;
+ for (int x = 0; x < 100000; ++x) {
+ insecure_GetRandHash(v);
+ cc.insert(v);
+ }
+ for (int x = 0; x < 100000; ++x) {
+ insecure_GetRandHash(v);
+ BOOST_CHECK(!cc.contains(v, false));
+ }
+};
+
+/** This helper returns the hit rate when megabytes*load worth of entries are
+ * inserted into a megabytes sized cache
+ */
+template <typename Cache>
+double test_cache(size_t megabytes, double load)
+{
+ insecure_rand = FastRandomContext(true);
+ std::vector<uint256> hashes;
+ Cache set{};
+ size_t bytes = megabytes * (1 << 20);
+ set.setup_bytes(bytes);
+ uint32_t n_insert = static_cast<uint32_t>(load * (bytes / sizeof(uint256)));
+ hashes.resize(n_insert);
+ for (uint32_t i = 0; i < n_insert; ++i) {
+ uint32_t* ptr = (uint32_t*)hashes[i].begin();
+ for (uint8_t j = 0; j < 8; ++j)
+ *(ptr++) = insecure_rand.rand32();
+ }
+ /** We make a copy of the hashes because future optimizations of the
+ * cuckoocache may overwrite the inserted element, so the test is
+ * "future proofed".
+ */
+ std::vector<uint256> hashes_insert_copy = hashes;
+ /** Do the insert */
+ for (uint256& h : hashes_insert_copy)
+ set.insert(h);
+ /** Count the hits */
+ uint32_t count = 0;
+ for (uint256& h : hashes)
+ count += set.contains(h, false);
+ double hit_rate = ((double)count) / ((double)n_insert);
+ return hit_rate;
+}
+
+/** The normalized hit rate for a given load.
+ *
+ * The semantics are a little confusing, so please see the below
+ * explanation.
+ *
+ * Examples:
+ *
+ * 1) at load 0.5, we expect a perfect hit rate, so we multiply by
+ * 1.0
+ * 2) at load 2.0, we expect to see half the entries, so a perfect hit rate
+ * would be 0.5. Therefore, if we see a hit rate of 0.4, 0.4*2.0 = 0.8 is the
+ * normalized hit rate.
+ *
+ * This is basically the right semantics, but has a bit of a glitch depending on
+ * how you measure around load 1.0 as after load 1.0 your normalized hit rate
+ * becomes effectively perfect, ignoring freshness.
+ */
+double normalize_hit_rate(double hits, double load)
+{
+ return hits * std::max(load, 1.0);
+}
+
+/** Check the hit rate on loads ranging from 0.1 to 2.0 */
+BOOST_AUTO_TEST_CASE(cuckoocache_hit_rate_ok)
+{
+ /** Arbitrarily selected Hit Rate threshold that happens to work for this test
+ * as a lower bound on performance.
+ */
+ double HitRateThresh = 0.98;
+ size_t megabytes = 32;
+ for (double load = 0.1; load < 2; load *= 2) {
+ double hits = test_cache<CuckooCache::cache<uint256, uint256Hasher>>(megabytes, load);
+ BOOST_CHECK(normalize_hit_rate(hits, load) > HitRateThresh);
+ }
+}
+
+
+/** This helper checks that erased elements are preferentially inserted onto and
+ * that the hit rate of "fresher" keys is reasonable*/
+template <typename Cache>
+void test_cache_erase(size_t megabytes)
+{
+ double load = 1;
+ insecure_rand = FastRandomContext(true);
+ std::vector<uint256> hashes;
+ Cache set{};
+ size_t bytes = megabytes * (1 << 20);
+ set.setup_bytes(bytes);
+ uint32_t n_insert = static_cast<uint32_t>(load * (bytes / sizeof(uint256)));
+ hashes.resize(n_insert);
+ for (uint32_t i = 0; i < n_insert; ++i) {
+ uint32_t* ptr = (uint32_t*)hashes[i].begin();
+ for (uint8_t j = 0; j < 8; ++j)
+ *(ptr++) = insecure_rand.rand32();
+ }
+ /** We make a copy of the hashes because future optimizations of the
+ * cuckoocache may overwrite the inserted element, so the test is
+ * "future proofed".
+ */
+ std::vector<uint256> hashes_insert_copy = hashes;
+
+ /** Insert the first half */
+ for (uint32_t i = 0; i < (n_insert / 2); ++i)
+ set.insert(hashes_insert_copy[i]);
+ /** Erase the first quarter */
+ for (uint32_t i = 0; i < (n_insert / 4); ++i)
+ set.contains(hashes[i], true);
+ /** Insert the second half */
+ for (uint32_t i = (n_insert / 2); i < n_insert; ++i)
+ set.insert(hashes_insert_copy[i]);
+
+ /** elements that we marked erased but that are still there */
+ size_t count_erased_but_contained = 0;
+ /** elements that we did not erase but are older */
+ size_t count_stale = 0;
+ /** elements that were most recently inserted */
+ size_t count_fresh = 0;
+
+ for (uint32_t i = 0; i < (n_insert / 4); ++i)
+ count_erased_but_contained += set.contains(hashes[i], false);
+ for (uint32_t i = (n_insert / 4); i < (n_insert / 2); ++i)
+ count_stale += set.contains(hashes[i], false);
+ for (uint32_t i = (n_insert / 2); i < n_insert; ++i)
+ count_fresh += set.contains(hashes[i], false);
+
+ double hit_rate_erased_but_contained = double(count_erased_but_contained) / (double(n_insert) / 4.0);
+ double hit_rate_stale = double(count_stale) / (double(n_insert) / 4.0);
+ double hit_rate_fresh = double(count_fresh) / (double(n_insert) / 2.0);
+
+ // Check that our hit_rate_fresh is perfect
+ BOOST_CHECK_EQUAL(hit_rate_fresh, 1.0);
+ // Check that we have a more than 2x better hit rate on stale elements than
+ // erased elements.
+ BOOST_CHECK(hit_rate_stale > 2 * hit_rate_erased_but_contained);
+}
+
+BOOST_AUTO_TEST_CASE(cuckoocache_erase_ok)
+{
+ size_t megabytes = 32;
+ test_cache_erase<CuckooCache::cache<uint256, uint256Hasher>>(megabytes);
+}
+
+template <typename Cache>
+void test_cache_erase_parallel(size_t megabytes)
+{
+ double load = 1;
+ insecure_rand = FastRandomContext(true);
+ std::vector<uint256> hashes;
+ Cache set{};
+ size_t bytes = megabytes * (1 << 20);
+ set.setup_bytes(bytes);
+ uint32_t n_insert = static_cast<uint32_t>(load * (bytes / sizeof(uint256)));
+ hashes.resize(n_insert);
+ for (uint32_t i = 0; i < n_insert; ++i) {
+ uint32_t* ptr = (uint32_t*)hashes[i].begin();
+ for (uint8_t j = 0; j < 8; ++j)
+ *(ptr++) = insecure_rand.rand32();
+ }
+ /** We make a copy of the hashes because future optimizations of the
+ * cuckoocache may overwrite the inserted element, so the test is
+ * "future proofed".
+ */
+ std::vector<uint256> hashes_insert_copy = hashes;
+ boost::shared_mutex mtx;
+
+ {
+ /** Grab lock to make sure we release inserts */
+ boost::unique_lock<boost::shared_mutex> l(mtx);
+ /** Insert the first half */
+ for (uint32_t i = 0; i < (n_insert / 2); ++i)
+ set.insert(hashes_insert_copy[i]);
+ }
+
+ /** Spin up 3 threads to run contains with erase.
+ */
+ std::vector<std::thread> threads;
+ /** Erase the first quarter */
+ for (uint32_t x = 0; x < 3; ++x)
+ /** Each thread is emplaced with x copy-by-value
+ */
+ threads.emplace_back([&, x] {
+ boost::shared_lock<boost::shared_mutex> l(mtx);
+ size_t ntodo = (n_insert/4)/3;
+ size_t start = ntodo*x;
+ size_t end = ntodo*(x+1);
+ for (uint32_t i = start; i < end; ++i)
+ set.contains(hashes[i], true);
+ });
+
+ /** Wait for all threads to finish
+ */
+ for (std::thread& t : threads)
+ t.join();
+ /** Grab lock to make sure we observe erases */
+ boost::unique_lock<boost::shared_mutex> l(mtx);
+ /** Insert the second half */
+ for (uint32_t i = (n_insert / 2); i < n_insert; ++i)
+ set.insert(hashes_insert_copy[i]);
+
+ /** elements that we marked erased but that are still there */
+ size_t count_erased_but_contained = 0;
+ /** elements that we did not erase but are older */
+ size_t count_stale = 0;
+ /** elements that were most recently inserted */
+ size_t count_fresh = 0;
+
+ for (uint32_t i = 0; i < (n_insert / 4); ++i)
+ count_erased_but_contained += set.contains(hashes[i], false);
+ for (uint32_t i = (n_insert / 4); i < (n_insert / 2); ++i)
+ count_stale += set.contains(hashes[i], false);
+ for (uint32_t i = (n_insert / 2); i < n_insert; ++i)
+ count_fresh += set.contains(hashes[i], false);
+
+ double hit_rate_erased_but_contained = double(count_erased_but_contained) / (double(n_insert) / 4.0);
+ double hit_rate_stale = double(count_stale) / (double(n_insert) / 4.0);
+ double hit_rate_fresh = double(count_fresh) / (double(n_insert) / 2.0);
+
+ // Check that our hit_rate_fresh is perfect
+ BOOST_CHECK_EQUAL(hit_rate_fresh, 1.0);
+ // Check that we have a more than 2x better hit rate on stale elements than
+ // erased elements.
+ BOOST_CHECK(hit_rate_stale > 2 * hit_rate_erased_but_contained);
+}
+BOOST_AUTO_TEST_CASE(cuckoocache_erase_parallel_ok)
+{
+ size_t megabytes = 32;
+ test_cache_erase_parallel<CuckooCache::cache<uint256, uint256Hasher>>(megabytes);
+}
+
+
+template <typename Cache>
+void test_cache_generations()
+{
+ // This test checks that for a simulation of network activity, the fresh hit
+ // rate is never below 99%, and the number of times that it is worse than
+ // 99.9% are less than 1% of the time.
+ double min_hit_rate = 0.99;
+ double tight_hit_rate = 0.999;
+ double max_rate_less_than_tight_hit_rate = 0.01;
+ // A cache that meets this specification is therefore shown to have a hit
+ // rate of at least tight_hit_rate * (1 - max_rate_less_than_tight_hit_rate) +
+ // min_hit_rate*max_rate_less_than_tight_hit_rate = 0.999*99%+0.99*1% == 99.89%
+ // hit rate with low variance.
+
+ // We use deterministic values, but this test has also passed on many
+ // iterations with non-deterministic values, so it isn't "overfit" to the
+ // specific entropy in FastRandomContext(true) and implementation of the
+ // cache.
+ insecure_rand = FastRandomContext(true);
+
+ // block_activity models a chunk of network activity. n_insert elements are
+ // adde to the cache. The first and last n/4 are stored for removal later
+ // and the middle n/2 are not stored. This models a network which uses half
+ // the signatures of recently (since the last block) added transactions
+ // immediately and never uses the other half.
+ struct block_activity {
+ std::vector<uint256> reads;
+ block_activity(uint32_t n_insert, Cache& c) : reads()
+ {
+ std::vector<uint256> inserts;
+ inserts.resize(n_insert);
+ reads.reserve(n_insert / 2);
+ for (uint32_t i = 0; i < n_insert; ++i) {
+ uint32_t* ptr = (uint32_t*)inserts[i].begin();
+ for (uint8_t j = 0; j < 8; ++j)
+ *(ptr++) = insecure_rand.rand32();
+ }
+ for (uint32_t i = 0; i < n_insert / 4; ++i)
+ reads.push_back(inserts[i]);
+ for (uint32_t i = n_insert - (n_insert / 4); i < n_insert; ++i)
+ reads.push_back(inserts[i]);
+ for (auto h : inserts)
+ c.insert(h);
+ }
+ };
+
+ const uint32_t BLOCK_SIZE = 10000;
+ // We expect window size 60 to perform reasonably given that each epoch
+ // stores 45% of the cache size (~472k).
+ const uint32_t WINDOW_SIZE = 60;
+ const uint32_t POP_AMOUNT = (BLOCK_SIZE / WINDOW_SIZE) / 2;
+ const double load = 10;
+ const size_t megabytes = 32;
+ const size_t bytes = megabytes * (1 << 20);
+ const uint32_t n_insert = static_cast<uint32_t>(load * (bytes / sizeof(uint256)));
+
+ std::vector<block_activity> hashes;
+ Cache set{};
+ set.setup_bytes(bytes);
+ hashes.reserve(n_insert / BLOCK_SIZE);
+ std::deque<block_activity> last_few;
+ uint32_t out_of_tight_tolerance = 0;
+ uint32_t total = n_insert / BLOCK_SIZE;
+ // we use the deque last_few to model a sliding window of blocks. at each
+ // step, each of the last WINDOW_SIZE block_activities checks the cache for
+ // POP_AMOUNT of the hashes that they inserted, and marks these erased.
+ for (uint32_t i = 0; i < total; ++i) {
+ if (last_few.size() == WINDOW_SIZE)
+ last_few.pop_front();
+ last_few.emplace_back(BLOCK_SIZE, set);
+ uint32_t count = 0;
+ for (auto& act : last_few)
+ for (uint32_t k = 0; k < POP_AMOUNT; ++k) {
+ count += set.contains(act.reads.back(), true);
+ act.reads.pop_back();
+ }
+ // We use last_few.size() rather than WINDOW_SIZE for the correct
+ // behavior on the first WINDOW_SIZE iterations where the deque is not
+ // full yet.
+ double hit = (double(count)) / (last_few.size() * POP_AMOUNT);
+ // Loose Check that hit rate is above min_hit_rate
+ BOOST_CHECK(hit > min_hit_rate);
+ // Tighter check, count number of times we are less than tight_hit_rate
+ // (and implicityly, greater than min_hit_rate)
+ out_of_tight_tolerance += hit < tight_hit_rate;
+ }
+ // Check that being out of tolerance happens less than
+ // max_rate_less_than_tight_hit_rate of the time
+ BOOST_CHECK(double(out_of_tight_tolerance) / double(total) < max_rate_less_than_tight_hit_rate);
+}
+BOOST_AUTO_TEST_CASE(cuckoocache_generations)
+{
+ test_cache_generations<CuckooCache::cache<uint256, uint256Hasher>>();
+}
+
+BOOST_AUTO_TEST_SUITE_END();
diff --git a/src/test/data/bitcoin-util-test.json b/src/test/data/bitcoin-util-test.json
index 5cb383de85..ab1a1385a3 100644
--- a/src/test/data/bitcoin-util-test.json
+++ b/src/test/data/bitcoin-util-test.json
@@ -1,37 +1,73 @@
[
- { "exec": "././bitcoin-tx",
- "args": ["-create"],
- "output_cmp": "blanktx.hex"
+ { "exec": "./bitcoin-tx",
+ "args": ["-create", "nversion=1"],
+ "output_cmp": "blanktxv1.hex",
+ "description": "Creates a blank v1 transaction"
+ },
+ { "exec": "./bitcoin-tx",
+ "args": ["-json","-create"],
+ "output_cmp": "blanktxv2.json",
+ "description": "Creates a blank transaction (output in json)"
},
{ "exec": "./bitcoin-tx",
"args": ["-"],
- "input": "blanktx.hex",
- "output_cmp": "blanktx.hex"
+ "input": "blanktxv2.hex",
+ "output_cmp": "blanktxv2.hex",
+ "description": "Creates a blank transaction when nothing is piped into bitcoin-tx"
+ },
+ { "exec": "./bitcoin-tx",
+ "args": ["-json","-"],
+ "input": "blanktxv2.hex",
+ "output_cmp": "blanktxv2.json",
+ "description": "Creates a blank transaction when nothing is piped into bitcoin-tx (output in json)"
},
{ "exec": "./bitcoin-tx",
"args": ["-", "delin=1"],
"input": "tx394b54bb.hex",
- "output_cmp": "tt-delin1-out.hex"
+ "output_cmp": "tt-delin1-out.hex",
+ "description": "Deletes a single input from a transaction"
+ },
+ { "exec": "./bitcoin-tx",
+ "args": ["-json", "-", "delin=1"],
+ "input": "tx394b54bb.hex",
+ "output_cmp": "tt-delin1-out.json",
+ "description": "Deletes a single input from a transaction (output in json)"
},
{ "exec": "./bitcoin-tx",
"args": ["-", "delin=31"],
"input": "tx394b54bb.hex",
- "return_code": 1
+ "return_code": 1,
+ "description": "Attempts to delete an input with a bad index from a transaction. Expected to fail."
},
{ "exec": "./bitcoin-tx",
"args": ["-", "delout=1"],
"input": "tx394b54bb.hex",
- "output_cmp": "tt-delout1-out.hex"
+ "output_cmp": "tt-delout1-out.hex",
+ "description": "Deletes a single output from a transaction"
+ },
+ { "exec": "./bitcoin-tx",
+ "args": ["-json", "-", "delout=1"],
+ "input": "tx394b54bb.hex",
+ "output_cmp": "tt-delout1-out.json",
+ "description": "Deletes a single output from a transaction (output in json)"
},
{ "exec": "./bitcoin-tx",
"args": ["-", "delout=2"],
"input": "tx394b54bb.hex",
- "return_code": 1
+ "return_code": 1,
+ "description": "Attempts to delete an output with a bad index from a transaction. Expected to fail."
},
{ "exec": "./bitcoin-tx",
"args": ["-", "locktime=317000"],
"input": "tx394b54bb.hex",
- "output_cmp": "tt-locktime317000-out.hex"
+ "output_cmp": "tt-locktime317000-out.hex",
+ "description": "Adds an nlocktime to a transaction"
+ },
+ { "exec": "./bitcoin-tx",
+ "args": ["-json", "-", "locktime=317000"],
+ "input": "tx394b54bb.hex",
+ "output_cmp": "tt-locktime317000-out.json",
+ "description": "Adds an nlocktime to a transaction (output in json)"
},
{ "exec": "./bitcoin-tx",
"args":
@@ -41,35 +77,90 @@
"in=22a6f904655d53ae2ff70e701a0bbd90aa3975c0f40bfc6cc996a9049e31cdfc:1",
"outaddr=0.18:13tuJJDR2RgArmgfv6JScSdreahzgc4T6o",
"outaddr=4:1P8yWvZW8jVihP1bzHeqfE4aoXNX8AVa46"],
- "output_cmp": "txcreate1.hex"
+ "output_cmp": "txcreate1.hex",
+ "description": "Creates a new transaction with three inputs and two outputs"
+ },
+ { "exec": "./bitcoin-tx",
+ "args":
+ ["-json",
+ "-create",
+ "in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0",
+ "in=bf829c6bcf84579331337659d31f89dfd138f7f7785802d5501c92333145ca7c:18",
+ "in=22a6f904655d53ae2ff70e701a0bbd90aa3975c0f40bfc6cc996a9049e31cdfc:1",
+ "outaddr=0.18:13tuJJDR2RgArmgfv6JScSdreahzgc4T6o",
+ "outaddr=4:1P8yWvZW8jVihP1bzHeqfE4aoXNX8AVa46"],
+ "output_cmp": "txcreate1.json",
+ "description": "Creates a new transaction with three inputs and two outputs (output in json)"
},
{ "exec": "./bitcoin-tx",
"args": ["-create", "outscript=0:"],
- "output_cmp": "txcreate2.hex"
+ "output_cmp": "txcreate2.hex",
+ "description": "Creates a new transaction with a single empty output script"
+ },
+ { "exec": "./bitcoin-tx",
+ "args": ["-json", "-create", "outscript=0:"],
+ "output_cmp": "txcreate2.json",
+ "description": "Creates a new transaction with a single empty output script (output in json)"
+ },
+ { "exec": "./bitcoin-tx",
+ "args": ["02000000000100000000000000000000000000"],
+ "output_cmp": "txcreate2.hex",
+ "description": "Parses a transation with no inputs and a single output script"
+ },
+ { "exec": "./bitcoin-tx",
+ "args": ["-json", "02000000000100000000000000000000000000"],
+ "output_cmp": "txcreate2.json",
+ "description": "Parses a transation with no inputs and a single output script (output in json)"
},
{ "exec": "./bitcoin-tx",
"args":
- ["-create",
+ ["-create", "nversion=1",
+ "in=4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485:0",
+ "set=privatekeys:[\"5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf\"]",
+ "set=prevtxs:[{\"txid\":\"4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485\",\"vout\":0,\"scriptPubKey\":\"76a91491b24bf9f5288532960ac687abb035127b1d28a588ac\"}]",
+ "sign=ALL",
+ "outaddr=0.001:193P6LtvS4nCnkDvM9uXn1gsSRqh4aDAz7"],
+ "output_cmp": "txcreatesignv1.hex",
+ "description": "Creates a new v1 transaction with a single input and a single output, and then signs the transaction"
+ },
+ { "exec": "./bitcoin-tx",
+ "args":
+ ["-json",
+ "-create", "nversion=1",
"in=4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485:0",
"set=privatekeys:[\"5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf\"]",
"set=prevtxs:[{\"txid\":\"4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485\",\"vout\":0,\"scriptPubKey\":\"76a91491b24bf9f5288532960ac687abb035127b1d28a588ac\"}]",
"sign=ALL",
"outaddr=0.001:193P6LtvS4nCnkDvM9uXn1gsSRqh4aDAz7"],
- "output_cmp": "txcreatesign.hex"
+ "output_cmp": "txcreatesignv1.json",
+ "description": "Creates a new v1 transaction with a single input and a single output, and then signs the transaction (output in json)"
+ },
+ { "exec": "./bitcoin-tx",
+ "args":
+ ["-create",
+ "in=4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485:0",
+ "set=privatekeys:[\"5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf\"]",
+ "set=prevtxs:[{\"txid\":\"4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485\",\"vout\":0,\"scriptPubKey\":\"76a91491b24bf9f5288532960ac687abb035127b1d28a588ac\"}]",
+ "sign=ALL",
+ "outaddr=0.001:193P6LtvS4nCnkDvM9uXn1gsSRqh4aDAz7"],
+ "output_cmp": "txcreatesignv2.hex",
+ "description": "Creates a new transaction with a single input and a single output, and then signs the transaction"
},
{ "exec": "./bitcoin-tx",
"args":
["-create",
"in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0",
"outdata=4:badhexdata"],
- "return_code": 1
+ "return_code": 1,
+ "description": "Attempts to create a new transaction with one input and an output with malformed hex data. Expected to fail"
},
{ "exec": "./bitcoin-tx",
"args":
["-create",
"in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0",
"outdata=badhexdata"],
- "return_code": 1
+ "return_code": 1,
+ "description": "Attempts to create a new transaction with one input and an output with no value and malformed hex data. Expected to fail"
},
{ "exec": "./bitcoin-tx",
"args":
@@ -77,7 +168,18 @@
"in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0",
"outaddr=0.18:13tuJJDR2RgArmgfv6JScSdreahzgc4T6o",
"outdata=4:54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e"],
- "output_cmp": "txcreatedata1.hex"
+ "output_cmp": "txcreatedata1.hex",
+ "description": "Creates a new transaction with one input, one address output and one data output"
+ },
+ { "exec": "./bitcoin-tx",
+ "args":
+ ["-json",
+ "-create", "nversion=1",
+ "in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0",
+ "outaddr=0.18:13tuJJDR2RgArmgfv6JScSdreahzgc4T6o",
+ "outdata=4:54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e"],
+ "output_cmp": "txcreatedata1.json",
+ "description": "Creates a new v1 transaction with one input, one address output and one data output (output in json)"
},
{ "exec": "./bitcoin-tx",
"args":
@@ -85,19 +187,49 @@
"in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0",
"outaddr=0.18:13tuJJDR2RgArmgfv6JScSdreahzgc4T6o",
"outdata=54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e"],
- "output_cmp": "txcreatedata2.hex"
+ "output_cmp": "txcreatedata2.hex",
+ "description": "Creates a new transaction with one input, one address output and one data (zero value) output"
+ },
+ { "exec": "./bitcoin-tx",
+ "args":
+ ["-json",
+ "-create",
+ "in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0",
+ "outaddr=0.18:13tuJJDR2RgArmgfv6JScSdreahzgc4T6o",
+ "outdata=54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e"],
+ "output_cmp": "txcreatedata2.json",
+ "description": "Creates a new transaction with one input, one address output and one data (zero value) output (output in json)"
},
{ "exec": "./bitcoin-tx",
"args":
["-create",
"in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0:4294967293",
"outaddr=0.18:13tuJJDR2RgArmgfv6JScSdreahzgc4T6o"],
- "output_cmp": "txcreatedata_seq0.hex"
+ "output_cmp": "txcreatedata_seq0.hex",
+ "description": "Creates a new transaction with one input with sequence number and one address output"
+ },
+ { "exec": "./bitcoin-tx",
+ "args":
+ ["-json",
+ "-create",
+ "in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0:4294967293",
+ "outaddr=0.18:13tuJJDR2RgArmgfv6JScSdreahzgc4T6o"],
+ "output_cmp": "txcreatedata_seq0.json",
+ "description": "Creates a new transaction with one input with sequence number and one address output (output in json)"
},
{ "exec": "./bitcoin-tx",
"args":
["01000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000fdffffff0180a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac00000000",
"in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0:1"],
- "output_cmp": "txcreatedata_seq1.hex"
+ "output_cmp": "txcreatedata_seq1.hex",
+ "description": "Adds a new input with sequence number to a transaction"
+ },
+ { "exec": "./bitcoin-tx",
+ "args":
+ ["-json",
+ "01000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000fdffffff0180a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac00000000",
+ "in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0:1"],
+ "output_cmp": "txcreatedata_seq1.json",
+ "description": "Adds a new input with sequence number to a transaction (output in json)"
}
]
diff --git a/src/test/data/blanktx.json b/src/test/data/blanktx.json
new file mode 100644
index 0000000000..51c25a5a98
--- /dev/null
+++ b/src/test/data/blanktx.json
@@ -0,0 +1,11 @@
+{
+ "txid": "d21633ba23f70118185227be58a63527675641ad37967e2aa461559f577aec43",
+ "hash": "d21633ba23f70118185227be58a63527675641ad37967e2aa461559f577aec43",
+ "version": 1,
+ "locktime": 0,
+ "vin": [
+ ],
+ "vout": [
+ ],
+ "hex": "01000000000000000000"
+}
diff --git a/src/test/data/blanktx.hex b/src/test/data/blanktxv1.hex
index 36b6f00fb6..36b6f00fb6 100644
--- a/src/test/data/blanktx.hex
+++ b/src/test/data/blanktxv1.hex
diff --git a/src/test/data/blanktxv2.hex b/src/test/data/blanktxv2.hex
new file mode 100644
index 0000000000..22d830eda1
--- /dev/null
+++ b/src/test/data/blanktxv2.hex
@@ -0,0 +1 @@
+02000000000000000000
diff --git a/src/test/data/blanktxv2.json b/src/test/data/blanktxv2.json
new file mode 100644
index 0000000000..266919f445
--- /dev/null
+++ b/src/test/data/blanktxv2.json
@@ -0,0 +1,11 @@
+{
+ "txid": "4ebd325a4b394cff8c57e8317ccf5a8d0e2bdf1b8526f8aad6c8e43d8240621a",
+ "hash": "4ebd325a4b394cff8c57e8317ccf5a8d0e2bdf1b8526f8aad6c8e43d8240621a",
+ "version": 2,
+ "locktime": 0,
+ "vin": [
+ ],
+ "vout": [
+ ],
+ "hex": "02000000000000000000"
+}
diff --git a/src/test/data/script_tests.json b/src/test/data/script_tests.json
index fcd5457386..5c054ed3e8 100644
--- a/src/test/data/script_tests.json
+++ b/src/test/data/script_tests.json
@@ -1492,6 +1492,27 @@
"BIP66 example 4, with DERSIG"
],
[
+ "0x09 0x300602010102010101",
+ "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT",
+ "DERSIG",
+ "OK",
+ "BIP66 example 4, with DERSIG, non-null DER-compliant signature"
+],
+[
+ "0",
+ "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT",
+ "DERSIG,NULLFAIL",
+ "OK",
+ "BIP66 example 4, with DERSIG and NULLFAIL"
+],
+[
+ "0x09 0x300602010102010101",
+ "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT",
+ "DERSIG,NULLFAIL",
+ "NULLFAIL",
+ "BIP66 example 4, with DERSIG and NULLFAIL, non-null DER-compliant signature"
+],
+[
"1",
"0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG",
"",
@@ -1834,6 +1855,8 @@
"OK",
"P2SH with CLEANSTACK"
],
+
+["Testing with uncompressed keys in witness v0 without WITNESS_PUBKEYTYPE"],
[
[
"304402200d461c140cfdfcf36b94961db57ae8c18d1cb80e9d95a9e47ac22470c1bf125502201c8dc1cbfef6a3ef90acbbb992ca22fe9466ee6f9d4898eda277a7ac3ab4b25101",
@@ -2118,12 +2141,469 @@
"P2PK with witness"
],
-["CHECKSEQUENCEVERIFY tests"],
+["Testing with compressed keys in witness v0 with WITNESS_PUBKEYTYPE"],
+[
+ [
+ "304402204256146fcf8e73b0fd817ffa2a4e408ff0418ff987dd08a4f485b62546f6c43c02203f3c8c3e2febc051e1222867f5f9d0eaf039d6792911c10940aa3cc74123378e01",
+ "210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac",
+ 0.00000001
+ ],
+ "",
+ "0 0x20 0x1863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262",
+ "P2SH,WITNESS,WITNESS_PUBKEYTYPE",
+ "OK",
+ "Basic P2WSH with compressed key"
+],
+[
+ [
+ "304402204edf27486f11432466b744df533e1acac727e0c83e5f912eb289a3df5bf8035f022075809fdd876ede40ad21667eba8b7e96394938f9c9c50f11b6a1280cce2cea8601",
+ "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
+ 0.00000001
+ ],
+ "",
+ "0 0x14 0x751e76e8199196d454941c45d1b3a323f1433bd6",
+ "P2SH,WITNESS,WITNESS_PUBKEYTYPE",
+ "OK",
+ "Basic P2WPKH with compressed key"
+],
+[
+ [
+ "304402203a549090cc46bce1e5e95c4922ea2c12747988e0207b04c42f81cdbe87bb1539022050f57a245b875fd5119c419aaf050bcdf41384f0765f04b809e5bced1fe7093d01",
+ "210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac",
+ 0.00000001
+ ],
+ "0x22 0x00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262",
+ "HASH160 0x14 0xe4300531190587e3880d4c3004f5355d88ff928d EQUAL",
+ "P2SH,WITNESS,WITNESS_PUBKEYTYPE",
+ "OK",
+ "Basic P2SH(P2WSH) with compressed key"
+],
+[
+ [
+ "304402201bc0d53046827f4a35a3166e33e3b3366c4085540dc383b95d21ed2ab11e368a0220333e78c6231214f5f8e59621e15d7eeab0d4e4d0796437e00bfbd2680c5f9c1701",
+ "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
+ 0.00000001
+ ],
+ "0x16 0x0014751e76e8199196d454941c45d1b3a323f1433bd6",
+ "HASH160 0x14 0xbcfeb728b584253d5f3f70bcb780e9ef218a68f4 EQUAL",
+ "P2SH,WITNESS,WITNESS_PUBKEYTYPE",
+ "OK",
+ "Basic P2SH(P2WPKH) with compressed key"
+],
+
+["Testing with uncompressed keys in witness v0 with WITNESS_PUBKEYTYPE"],
+[
+ [
+ "304402200d461c140cfdfcf36b94961db57ae8c18d1cb80e9d95a9e47ac22470c1bf125502201c8dc1cbfef6a3ef90acbbb992ca22fe9466ee6f9d4898eda277a7ac3ab4b25101",
+ "410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac",
+ 0.00000001
+ ],
+ "",
+ "0 0x20 0xb95237b48faaa69eb078e1170be3b5cbb3fddf16d0a991e14ad274f7b33a4f64",
+ "P2SH,WITNESS,WITNESS_PUBKEYTYPE",
+ "WITNESS_PUBKEYTYPE",
+ "Basic P2WSH"
+],
+[
+ [
+ "304402201e7216e5ccb3b61d46946ec6cc7e8c4e0117d13ac2fd4b152197e4805191c74202203e9903e33e84d9ee1dd13fb057afb7ccfb47006c23f6a067185efbc9dd780fc501",
+ "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8",
+ 0.00000001
+ ],
+ "",
+ "0 0x14 0x91b24bf9f5288532960ac687abb035127b1d28a5",
+ "P2SH,WITNESS,WITNESS_PUBKEYTYPE",
+ "WITNESS_PUBKEYTYPE",
+ "Basic P2WPKH"
+],
+[
+ [
+ "3044022066e02c19a513049d49349cf5311a1b012b7c4fae023795a18ab1d91c23496c22022025e216342c8e07ce8ef51e8daee88f84306a9de66236cab230bb63067ded1ad301",
+ "410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac",
+ 0.00000001
+ ],
+ "0x22 0x0020b95237b48faaa69eb078e1170be3b5cbb3fddf16d0a991e14ad274f7b33a4f64",
+ "HASH160 0x14 0xf386c2ba255cc56d20cfa6ea8b062f8b59945518 EQUAL",
+ "P2SH,WITNESS,WITNESS_PUBKEYTYPE",
+ "WITNESS_PUBKEYTYPE",
+ "Basic P2SH(P2WSH)"
+],
+[
+ [
+ "304402200929d11561cd958460371200f82e9cae64c727a495715a31828e27a7ad57b36d0220361732ced04a6f97351ecca21a56d0b8cd4932c1da1f8f569a2b68e5e48aed7801",
+ "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8",
+ 0.00000001
+ ],
+ "0x16 0x001491b24bf9f5288532960ac687abb035127b1d28a5",
+ "HASH160 0x14 0x17743beb429c55c942d2ec703b98c4d57c2df5c6 EQUAL",
+ "P2SH,WITNESS,WITNESS_PUBKEYTYPE",
+ "WITNESS_PUBKEYTYPE",
+ "Basic P2SH(P2WPKH)"
+],
+
+["Testing P2WSH multisig with compressed keys"],
+[
+ [
+ "",
+ "304402207eb8a59b5c65fc3f6aeef77066556ed5c541948a53a3ba7f7c375b8eed76ee7502201e036a7a9a98ff919ff94dc905d67a1ec006f79ef7cff0708485c8bb79dce38e01",
+ "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae",
+ 0.00000001
+ ],
+ "",
+ "0 0x20 0x06c24420938f0fa3c1cb2707d867154220dca365cdbfa0dd2a83854730221460",
+ "P2SH,WITNESS,WITNESS_PUBKEYTYPE",
+ "OK",
+ "P2WSH CHECKMULTISIG with compressed keys"
+],
+[
+ [
+ "",
+ "3044022033706aed33b8155d5486df3b9bca8cdd3bd4bdb5436dce46d72cdaba51d22b4002203626e94fe53a178af46624f17315c6931f20a30b103f5e044e1eda0c3fe185c601",
+ "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae",
+ 0.00000001
+ ],
+ "0x22 0x002006c24420938f0fa3c1cb2707d867154220dca365cdbfa0dd2a83854730221460",
+ "HASH160 0x14 0x26282aad7c29369d15fed062a778b6100d31a340 EQUAL",
+ "P2SH,WITNESS,WITNESS_PUBKEYTYPE",
+ "OK",
+ "P2SH(P2WSH) CHECKMULTISIG with compressed keys"
+],
+[
+ [
+ "",
+ "304402204048b7371ab1c544362efb89af0c80154747d665aa4fcfb2edfd2d161e57b42e02207e043748e96637080ffc3acbd4dcc6fee1e58d30f6d1269535f32188e5ddae7301",
+ "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae",
+ 0.00000001
+ ],
+ "",
+ "0 0x20 0x06c24420938f0fa3c1cb2707d867154220dca365cdbfa0dd2a83854730221460",
+ "P2SH,WITNESS,WITNESS_PUBKEYTYPE",
+ "OK",
+ "P2WSH CHECKMULTISIG with compressed keys"
+],
+[
+ [
+ "",
+ "3044022073902ef0b8a554c36c44cc03c1b64df96ce2914ebcf946f5bb36078fd5245cdf02205b148f1ba127065fb8c83a5a9576f2dcd111739788ed4bb3ee08b2bd3860c91c01",
+ "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae",
+ 0.00000001
+ ],
+ "0x22 0x002006c24420938f0fa3c1cb2707d867154220dca365cdbfa0dd2a83854730221460",
+ "HASH160 0x14 0x26282aad7c29369d15fed062a778b6100d31a340 EQUAL",
+ "P2SH,WITNESS,WITNESS_PUBKEYTYPE",
+ "OK",
+ "P2SH(P2WSH) CHECKMULTISIG with compressed keys"
+],
+
+["Testing P2WSH multisig with compressed and uncompressed keys (first key being the key closer to the top of stack)"],
+[
+ [
+ "",
+ "304402202d092ededd1f060609dbf8cb76950634ff42b3e62cf4adb69ab92397b07d742302204ff886f8d0817491a96d1daccdcc820f6feb122ee6230143303100db37dfa79f01",
+ "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b852ae",
+ 0.00000001
+ ],
+ "",
+ "0 0x20 0x08a6665ebfd43b02323423e764e185d98d1587f903b81507dbb69bfc41005efa",
+ "P2SH,WITNESS",
+ "OK",
+ "P2WSH CHECKMULTISIG with first key uncompressed and signing with the first key"
+],
+[
+ [
+ "",
+ "304402202dd7e91243f2235481ffb626c3b7baf2c859ae3a5a77fb750ef97b99a8125dc002204960de3d3c3ab9496e218ec57e5240e0e10a6f9546316fe240c216d45116d29301",
+ "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b852ae",
+ 0.00000001
+ ],
+ "0x22 0x002008a6665ebfd43b02323423e764e185d98d1587f903b81507dbb69bfc41005efa",
+ "HASH160 0x14 0x6f5ecd4b83b77f3c438f5214eff96454934fc5d1 EQUAL",
+ "P2SH,WITNESS",
+ "OK",
+ "P2SH(P2WSH) CHECKMULTISIG first key uncompressed and signing with the first key"
+],
+[
+ [
+ "",
+ "304402202d092ededd1f060609dbf8cb76950634ff42b3e62cf4adb69ab92397b07d742302204ff886f8d0817491a96d1daccdcc820f6feb122ee6230143303100db37dfa79f01",
+ "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b852ae",
+ 0.00000001
+ ],
+ "",
+ "0 0x20 0x08a6665ebfd43b02323423e764e185d98d1587f903b81507dbb69bfc41005efa",
+ "P2SH,WITNESS,WITNESS_PUBKEYTYPE",
+ "WITNESS_PUBKEYTYPE",
+ "P2WSH CHECKMULTISIG with first key uncompressed and signing with the first key"
+],
+[
+ [
+ "",
+ "304402202dd7e91243f2235481ffb626c3b7baf2c859ae3a5a77fb750ef97b99a8125dc002204960de3d3c3ab9496e218ec57e5240e0e10a6f9546316fe240c216d45116d29301",
+ "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b852ae",
+ 0.00000001
+ ],
+ "0x22 0x002008a6665ebfd43b02323423e764e185d98d1587f903b81507dbb69bfc41005efa",
+ "HASH160 0x14 0x6f5ecd4b83b77f3c438f5214eff96454934fc5d1 EQUAL",
+ "P2SH,WITNESS,WITNESS_PUBKEYTYPE",
+ "WITNESS_PUBKEYTYPE",
+ "P2SH(P2WSH) CHECKMULTISIG with first key uncompressed and signing with the first key"
+],
+[
+ [
+ "",
+ "304402201e9e6f7deef5b2f21d8223c5189b7d5e82d237c10e97165dd08f547c4e5ce6ed02206796372eb1cc6acb52e13ee2d7f45807780bf96b132cb6697f69434be74b1af901",
+ "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b852ae",
+ 0.00000001
+ ],
+ "",
+ "0 0x20 0x08a6665ebfd43b02323423e764e185d98d1587f903b81507dbb69bfc41005efa",
+ "P2SH,WITNESS",
+ "OK",
+ "P2WSH CHECKMULTISIG with first key uncompressed and signing with the second key"
+],
+[
+ [
+ "",
+ "3044022045e667f3f0f3147b95597a24babe9afecea1f649fd23637dfa7ed7e9f3ac18440220295748e81005231135289fe3a88338dabba55afa1bdb4478691337009d82b68d01",
+ "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b852ae",
+ 0.00000001
+ ],
+ "0x22 0x002008a6665ebfd43b02323423e764e185d98d1587f903b81507dbb69bfc41005efa",
+ "HASH160 0x14 0x6f5ecd4b83b77f3c438f5214eff96454934fc5d1 EQUAL",
+ "P2SH,WITNESS",
+ "OK",
+ "P2SH(P2WSH) CHECKMULTISIG with first key uncompressed and signing with the second key"
+],
+[
+ [
+ "",
+ "304402201e9e6f7deef5b2f21d8223c5189b7d5e82d237c10e97165dd08f547c4e5ce6ed02206796372eb1cc6acb52e13ee2d7f45807780bf96b132cb6697f69434be74b1af901",
+ "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b852ae",
+ 0.00000001
+ ],
+ "",
+ "0 0x20 0x08a6665ebfd43b02323423e764e185d98d1587f903b81507dbb69bfc41005efa",
+ "P2SH,WITNESS,WITNESS_PUBKEYTYPE",
+ "WITNESS_PUBKEYTYPE",
+ "P2WSH CHECKMULTISIG with first key uncompressed and signing with the second key"
+],
+[
+ [
+ "",
+ "3044022045e667f3f0f3147b95597a24babe9afecea1f649fd23637dfa7ed7e9f3ac18440220295748e81005231135289fe3a88338dabba55afa1bdb4478691337009d82b68d01",
+ "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b852ae",
+ 0.00000001
+ ],
+ "0x22 0x002008a6665ebfd43b02323423e764e185d98d1587f903b81507dbb69bfc41005efa",
+ "HASH160 0x14 0x6f5ecd4b83b77f3c438f5214eff96454934fc5d1 EQUAL",
+ "P2SH,WITNESS,WITNESS_PUBKEYTYPE",
+ "WITNESS_PUBKEYTYPE",
+ "P2SH(P2WSH) CHECKMULTISIG with first key uncompressed and signing with the second key"
+],
+[
+ [
+ "",
+ "3044022046f5367a261fd8f8d7de6eb390491344f8ec2501638fb9a1095a0599a21d3f4c02205c1b3b51d20091c5f1020841bbca87b44ebe25405c64e4acf758f2eae8665f8401",
+ "5141048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae",
+ 0.00000001
+ ],
+ "",
+ "0 0x20 0x230828ed48871f0f362ce9432aa52f620f442cc8d9ce7a8b5e798365595a38bb",
+ "P2SH,WITNESS",
+ "OK",
+ "P2WSH CHECKMULTISIG with second key uncompressed and signing with the first key"
+],
+[
+ [
+ "",
+ "3044022053e210e4fb1881e6092fd75c3efc5163105599e246ded661c0ee2b5682cc2d6c02203a26b7ada8682a095b84c6d1b881637000b47d761fc837c4cee33555296d63f101",
+ "5141048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae",
+ 0.00000001
+ ],
+ "0x22 0x0020230828ed48871f0f362ce9432aa52f620f442cc8d9ce7a8b5e798365595a38bb",
+ "HASH160 0x14 0x3478e7019ce61a68148f87549579b704cbe4c393 EQUAL",
+ "P2SH,WITNESS",
+ "OK",
+ "P2SH(P2WSH) CHECKMULTISIG second key uncompressed and signing with the first key"
+],
+[
+ [
+ "",
+ "3044022046f5367a261fd8f8d7de6eb390491344f8ec2501638fb9a1095a0599a21d3f4c02205c1b3b51d20091c5f1020841bbca87b44ebe25405c64e4acf758f2eae8665f8401",
+ "5141048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae",
+ 0.00000001
+ ],
+ "",
+ "0 0x20 0x230828ed48871f0f362ce9432aa52f620f442cc8d9ce7a8b5e798365595a38bb",
+ "P2SH,WITNESS,WITNESS_PUBKEYTYPE",
+ "OK",
+ "P2WSH CHECKMULTISIG with second key uncompressed and signing with the first key should pass as the uncompressed key is not used"
+],
+[
+ [
+ "",
+ "3044022053e210e4fb1881e6092fd75c3efc5163105599e246ded661c0ee2b5682cc2d6c02203a26b7ada8682a095b84c6d1b881637000b47d761fc837c4cee33555296d63f101",
+ "5141048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae",
+ 0.00000001
+ ],
+ "0x22 0x0020230828ed48871f0f362ce9432aa52f620f442cc8d9ce7a8b5e798365595a38bb",
+ "HASH160 0x14 0x3478e7019ce61a68148f87549579b704cbe4c393 EQUAL",
+ "P2SH,WITNESS,WITNESS_PUBKEYTYPE",
+ "OK",
+ "P2SH(P2WSH) CHECKMULTISIG with second key uncompressed and signing with the first key should pass as the uncompressed key is not used"
+],
+[
+ [
+ "",
+ "304402206c6d9f5daf85b54af2a93ec38b15ab27f205dbf5c735365ff12451e43613d1f40220736a44be63423ed5ebf53491618b7cc3d8a5093861908da853739c73717938b701",
+ "5141048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae",
+ 0.00000001
+ ],
+ "",
+ "0 0x20 0x230828ed48871f0f362ce9432aa52f620f442cc8d9ce7a8b5e798365595a38bb",
+ "P2SH,WITNESS",
+ "OK",
+ "P2WSH CHECKMULTISIG with second key uncompressed and signing with the second key"
+],
+[
+ [
+ "",
+ "30440220687871bc6144012d75baf585bb26ce13997f7d8c626f4d8825b069c3b2d064470220108936fe1c57327764782253e99090b09c203ec400ed35ce9e026ce2ecf842a001",
+ "5141048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae",
+ 0.00000001
+ ],
+ "0x22 0x0020230828ed48871f0f362ce9432aa52f620f442cc8d9ce7a8b5e798365595a38bb",
+ "HASH160 0x14 0x3478e7019ce61a68148f87549579b704cbe4c393 EQUAL",
+ "P2SH,WITNESS",
+ "OK",
+ "P2SH(P2WSH) CHECKMULTISIG with second key uncompressed and signing with the second key"
+],
+[
+ [
+ "",
+ "304402206c6d9f5daf85b54af2a93ec38b15ab27f205dbf5c735365ff12451e43613d1f40220736a44be63423ed5ebf53491618b7cc3d8a5093861908da853739c73717938b701",
+ "5141048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae",
+ 0.00000001
+ ],
+ "",
+ "0 0x20 0x230828ed48871f0f362ce9432aa52f620f442cc8d9ce7a8b5e798365595a38bb",
+ "P2SH,WITNESS,WITNESS_PUBKEYTYPE",
+ "WITNESS_PUBKEYTYPE",
+ "P2WSH CHECKMULTISIG with second key uncompressed and signing with the second key"
+],
+[
+ [
+ "",
+ "30440220687871bc6144012d75baf585bb26ce13997f7d8c626f4d8825b069c3b2d064470220108936fe1c57327764782253e99090b09c203ec400ed35ce9e026ce2ecf842a001",
+ "5141048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae",
+ 0.00000001
+ ],
+ "0x22 0x0020230828ed48871f0f362ce9432aa52f620f442cc8d9ce7a8b5e798365595a38bb",
+ "HASH160 0x14 0x3478e7019ce61a68148f87549579b704cbe4c393 EQUAL",
+ "P2SH,WITNESS,WITNESS_PUBKEYTYPE",
+ "WITNESS_PUBKEYTYPE",
+ "P2SH(P2WSH) CHECKMULTISIG with second key uncompressed and signing with the second key"
+],
+
+["CHECKSEQUENCEVERIFY tests"],
["", "CHECKSEQUENCEVERIFY", "CHECKSEQUENCEVERIFY", "INVALID_STACK_OPERATION", "CSV automatically fails on a empty stack"],
["-1", "CHECKSEQUENCEVERIFY", "CHECKSEQUENCEVERIFY", "NEGATIVE_LOCKTIME", "CSV automatically fails if stack top is negative"],
["0x0100", "CHECKSEQUENCEVERIFY", "CHECKSEQUENCEVERIFY,MINIMALDATA", "UNKNOWN_ERROR", "CSV fails if stack top is not minimally encoded"],
["0", "CHECKSEQUENCEVERIFY", "CHECKSEQUENCEVERIFY", "UNSATISFIED_LOCKTIME", "CSV fails if stack top bit 1 << 31 is set and the tx version < 2"],
["4294967296", "CHECKSEQUENCEVERIFY", "CHECKSEQUENCEVERIFY", "UNSATISFIED_LOCKTIME",
"CSV fails if stack top bit 1 << 31 is not set, and tx version < 2"],
+
+["MINIMALIF tests"],
+["MINIMALIF is not applied to non-segwit scripts"],
+["1", "IF 1 ENDIF", "P2SH,WITNESS,MINIMALIF", "OK"],
+["2", "IF 1 ENDIF", "P2SH,WITNESS,MINIMALIF", "OK"],
+["0x02 0x0100", "IF 1 ENDIF", "P2SH,WITNESS,MINIMALIF", "OK"],
+["0", "IF 1 ENDIF", "P2SH,WITNESS,MINIMALIF", "EVAL_FALSE"],
+["0x01 0x00", "IF 1 ENDIF", "P2SH,WITNESS,MINIMALIF", "EVAL_FALSE"],
+["1", "NOTIF 1 ENDIF", "P2SH,WITNESS,MINIMALIF", "EVAL_FALSE"],
+["2", "NOTIF 1 ENDIF", "P2SH,WITNESS,MINIMALIF", "EVAL_FALSE"],
+["0x02 0x0100", "NOTIF 1 ENDIF", "P2SH,WITNESS,MINIMALIF", "EVAL_FALSE"],
+["0", "NOTIF 1 ENDIF", "P2SH,WITNESS,MINIMALIF", "OK"],
+["0x01 0x00", "NOTIF 1 ENDIF", "P2SH,WITNESS,MINIMALIF", "OK"],
+["Normal P2SH IF 1 ENDIF"],
+["1 0x03 0x635168", "HASH160 0x14 0xe7309652a8e3f600f06f5d8d52d6df03d2176cc3 EQUAL", "P2SH,WITNESS,MINIMALIF", "OK"],
+["2 0x03 0x635168", "HASH160 0x14 0xe7309652a8e3f600f06f5d8d52d6df03d2176cc3 EQUAL", "P2SH,WITNESS,MINIMALIF", "OK"],
+["0x02 0x0100 0x03 0x635168", "HASH160 0x14 0xe7309652a8e3f600f06f5d8d52d6df03d2176cc3 EQUAL", "P2SH,WITNESS,MINIMALIF", "OK"],
+["0 0x03 0x635168", "HASH160 0x14 0xe7309652a8e3f600f06f5d8d52d6df03d2176cc3 EQUAL", "P2SH,WITNESS,MINIMALIF", "EVAL_FALSE"],
+["0x01 0x00 0x03 0x635168", "HASH160 0x14 0xe7309652a8e3f600f06f5d8d52d6df03d2176cc3 EQUAL", "P2SH,WITNESS,MINIMALIF", "EVAL_FALSE"],
+["0x03 0x635168", "HASH160 0x14 0xe7309652a8e3f600f06f5d8d52d6df03d2176cc3 EQUAL", "P2SH,WITNESS,MINIMALIF", "UNBALANCED_CONDITIONAL"],
+["Normal P2SH NOTIF 1 ENDIF"],
+["1 0x03 0x645168", "HASH160 0x14 0x0c3f8fe3d6ca266e76311ecda544c67d15fdd5b0 EQUAL", "P2SH,WITNESS,MINIMALIF", "EVAL_FALSE"],
+["2 0x03 0x645168", "HASH160 0x14 0x0c3f8fe3d6ca266e76311ecda544c67d15fdd5b0 EQUAL", "P2SH,WITNESS,MINIMALIF", "EVAL_FALSE"],
+["0x02 0x0100 0x03 0x645168", "HASH160 0x14 0x0c3f8fe3d6ca266e76311ecda544c67d15fdd5b0 EQUAL", "P2SH,WITNESS,MINIMALIF", "EVAL_FALSE"],
+["0 0x03 0x645168", "HASH160 0x14 0x0c3f8fe3d6ca266e76311ecda544c67d15fdd5b0 EQUAL", "P2SH,WITNESS,MINIMALIF", "OK"],
+["0x01 0x00 0x03 0x645168", "HASH160 0x14 0x0c3f8fe3d6ca266e76311ecda544c67d15fdd5b0 EQUAL", "P2SH,WITNESS,MINIMALIF", "OK"],
+["0x03 0x645168", "HASH160 0x14 0x0c3f8fe3d6ca266e76311ecda544c67d15fdd5b0 EQUAL", "P2SH,WITNESS,MINIMALIF", "UNBALANCED_CONDITIONAL"],
+["P2WSH IF 1 ENDIF"],
+[["01", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS", "OK"],
+[["02", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS", "OK"],
+[["0100", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS", "OK"],
+[["", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS", "EVAL_FALSE"],
+[["00", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS", "EVAL_FALSE"],
+[["01", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS,MINIMALIF", "OK"],
+[["02", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"],
+[["0100", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"],
+[["", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS,MINIMALIF", "EVAL_FALSE"],
+[["00", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"],
+[["635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS", "UNBALANCED_CONDITIONAL"],
+[["635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS,MINIMALIF", "UNBALANCED_CONDITIONAL"],
+["P2WSH NOTIF 1 ENDIF"],
+[["01", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS", "EVAL_FALSE"],
+[["02", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS", "EVAL_FALSE"],
+[["0100", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS", "EVAL_FALSE"],
+[["", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS", "OK"],
+[["00", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS", "OK"],
+[["01", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS,MINIMALIF", "EVAL_FALSE"],
+[["02", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"],
+[["0100", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"],
+[["", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS,MINIMALIF", "OK"],
+[["00", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"],
+[["645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS", "UNBALANCED_CONDITIONAL"],
+[["645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS,MINIMALIF", "UNBALANCED_CONDITIONAL"],
+
+
+
+["P2SH-P2WSH IF 1 ENDIF"],
+[["01", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS", "OK"],
+[["02", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS", "OK"],
+[["0100", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS", "OK"],
+[["", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS", "EVAL_FALSE"],
+[["00", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS", "EVAL_FALSE"],
+[["01", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS,MINIMALIF", "OK"],
+[["02", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"],
+[["0100", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"],
+[["", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS,MINIMALIF", "EVAL_FALSE"],
+[["00", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"],
+[["635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS", "UNBALANCED_CONDITIONAL"],
+[["635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS,MINIMALIF", "UNBALANCED_CONDITIONAL"],
+["P2SH-P2WSH NOTIF 1 ENDIF"],
+[["01", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS", "EVAL_FALSE"],
+[["02", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS", "EVAL_FALSE"],
+[["0100", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS", "EVAL_FALSE"],
+[["", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS", "OK"],
+[["00", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS", "OK"],
+[["01", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS,MINIMALIF", "EVAL_FALSE"],
+[["02", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"],
+[["0100", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"],
+[["", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS,MINIMALIF", "OK"],
+[["00", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"],
+[["645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS", "UNBALANCED_CONDITIONAL"],
+[["645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS,MINIMALIF", "UNBALANCED_CONDITIONAL"],
+
+["NULLFAIL should cover all signatures and signatures only"],
+["0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG", "OK", "BIP66 and NULLFAIL-compliant"],
+["0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG,NULLFAIL", "OK", "BIP66 and NULLFAIL-compliant"],
+["1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG,NULLFAIL", "OK", "BIP66 and NULLFAIL-compliant, not NULLDUMMY-compliant"],
+["1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG,NULLFAIL,NULLDUMMY", "SIG_NULLDUMMY", "BIP66 and NULLFAIL-compliant, not NULLDUMMY-compliant"],
+["0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0x09 0x300602010102010101", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG", "OK", "BIP66-compliant but not NULLFAIL-compliant"],
+["0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0x09 0x300602010102010101", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG,NULLFAIL", "NULLFAIL", "BIP66-compliant but not NULLFAIL-compliant"],
+["0 0x09 0x300602010102010101 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG", "OK", "BIP66-compliant but not NULLFAIL-compliant"],
+["0 0x09 0x300602010102010101 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG,NULLFAIL", "NULLFAIL", "BIP66-compliant but not NULLFAIL-compliant"],
+
["The End"]
]
diff --git a/src/test/data/tt-delin1-out.json b/src/test/data/tt-delin1-out.json
new file mode 100644
index 0000000000..712a2c27f8
--- /dev/null
+++ b/src/test/data/tt-delin1-out.json
@@ -0,0 +1,217 @@
+{
+ "txid": "81b2035be1da1abe745c6141174a73d151009ec17b3d5ebffa2e177408c50dfd",
+ "hash": "81b2035be1da1abe745c6141174a73d151009ec17b3d5ebffa2e177408c50dfd",
+ "version": 1,
+ "locktime": 0,
+ "vin": [
+ {
+ "txid": "27871a1a27d833e99cd392a502a647beaaeda6da535417501c76312d52235cfd",
+ "vout": 332,
+ "scriptSig": {
+ "asm": "3046022100b4251ecd63778a3dde0155abe4cd162947620ae9ee45a874353551092325b116022100db307baf4ff3781ec520bd18f387948cedd15dc27bafe17c894b0fe6ffffcafa[ALL] 03091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc",
+ "hex": "493046022100b4251ecd63778a3dde0155abe4cd162947620ae9ee45a874353551092325b116022100db307baf4ff3781ec520bd18f387948cedd15dc27bafe17c894b0fe6ffffcafa012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "752f7f69b915637dc1c2f7aed1466ad676f6f3e24cf922809705f664e97ab3c1",
+ "vout": 1,
+ "scriptSig": {
+ "asm": "3044022079bd62ee09621a3be96b760c39e8ef78170101d46313923c6b07ae60a95c90670220238e51ea29fc70b04b65508450523caedbb11cb4dd5aa608c81487de798925ba[ALL] 027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34",
+ "hex": "473044022079bd62ee09621a3be96b760c39e8ef78170101d46313923c6b07ae60a95c90670220238e51ea29fc70b04b65508450523caedbb11cb4dd5aa608c81487de798925ba0121027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "b0ac9cca2e69cd02410e31b1f4402a25758e71abd1ab06c265ef9077dc05d0ed",
+ "vout": 209,
+ "scriptSig": {
+ "asm": "304502207722d6f9038673c86a1019b1c4de2d687ae246477cd4ca7002762be0299de385022100e594a11e3a313942595f7666dcf7078bcb14f1330f4206b95c917e7ec0e82fac[ALL] 03091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc",
+ "hex": "48304502207722d6f9038673c86a1019b1c4de2d687ae246477cd4ca7002762be0299de385022100e594a11e3a313942595f7666dcf7078bcb14f1330f4206b95c917e7ec0e82fac012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "a135eafb595eaf4c1ea59ccb111cdc0eae1b2c979b226a1e5aa8b76fe2d628df",
+ "vout": 0,
+ "scriptSig": {
+ "asm": "3045022100a63a4788027b79b65c6f9d9e054f68cf3b4eed19efd82a2d53f70dcbe64683390220526f243671425b2bd05745fcf2729361f985cfe84ea80c7cfc817b93d8134374[ALL] 03a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52",
+ "hex": "483045022100a63a4788027b79b65c6f9d9e054f68cf3b4eed19efd82a2d53f70dcbe64683390220526f243671425b2bd05745fcf2729361f985cfe84ea80c7cfc817b93d8134374012103a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "a5d6bf53ba21140b8a4d554feb00fe8bb9a62430ff9e4624aa2f58a120232aae",
+ "vout": 1,
+ "scriptSig": {
+ "asm": "3046022100b200ac6db16842f76dab9abe807ce423c992805879bc50abd46ed8275a59d9cf022100c0d518e85dd345b3c29dd4dc47b9a420d3ce817b18720e94966d2fe23413a408[ALL] 03091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc",
+ "hex": "493046022100b200ac6db16842f76dab9abe807ce423c992805879bc50abd46ed8275a59d9cf022100c0d518e85dd345b3c29dd4dc47b9a420d3ce817b18720e94966d2fe23413a408012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "1b299cf14f1a22e81ea56d71b7affbd7cf386807bf2b4d4b79a18a54125accb3",
+ "vout": 0,
+ "scriptSig": {
+ "asm": "3045022100ededc441c3103a6f2bd6cab7639421af0f6ec5e60503bce1e603cf34f00aee1c02205cb75f3f519a13fb348783b21db3085cb5ec7552c59e394fdbc3e1feea43f967[ALL] 03a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52",
+ "hex": "483045022100ededc441c3103a6f2bd6cab7639421af0f6ec5e60503bce1e603cf34f00aee1c02205cb75f3f519a13fb348783b21db3085cb5ec7552c59e394fdbc3e1feea43f967012103a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "071df1cdcb3f0070f9d6af7b0274f02d0be2324a274727cfd288383167531485",
+ "vout": 21,
+ "scriptSig": {
+ "asm": "3045022100d9eed5413d2a4b4b98625aa6e3169edc4fb4663e7862316d69224454e70cd8ca022061e506521d5ced51dd0ea36496e75904d756a4c4f9fb111568555075d5f68d9a[ALL] 03f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c",
+ "hex": "483045022100d9eed5413d2a4b4b98625aa6e3169edc4fb4663e7862316d69224454e70cd8ca022061e506521d5ced51dd0ea36496e75904d756a4c4f9fb111568555075d5f68d9a012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "b012e500eb7adf7a13ed332dd6ece849f94f7a62bb3eac5babab356d1fc19282",
+ "vout": 9,
+ "scriptSig": {
+ "asm": "304502207e84b27139c4c19c828cb1e30c349bba88e4d9b59be97286960793b5ddc0a2af0221008cdc7a951e7f31c20953ed5635fbabf228e80b7047f32faaa0313e7693005177[ALL] 03f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c",
+ "hex": "48304502207e84b27139c4c19c828cb1e30c349bba88e4d9b59be97286960793b5ddc0a2af0221008cdc7a951e7f31c20953ed5635fbabf228e80b7047f32faaa0313e7693005177012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "58840fee9c833f2f2d40575842f30f4b8d2553094d06ad88b03d06869acf3d88",
+ "vout": 30,
+ "scriptSig": {
+ "asm": "30440220426540dfed9c4ab5812e5f06df705b8bcf307dd7d20f7fa6512298b2a6314f420220064055096e3ca62f6c7352c66a5447767c53f946acdf35025ab3807ddb2fa404[ALL] 03f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c",
+ "hex": "4730440220426540dfed9c4ab5812e5f06df705b8bcf307dd7d20f7fa6512298b2a6314f420220064055096e3ca62f6c7352c66a5447767c53f946acdf35025ab3807ddb2fa404012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "e69f9cd16946e570a665245354428a3f507ea69f4568b581e4af98edb3db9766",
+ "vout": 114,
+ "scriptSig": {
+ "asm": "304402200a5e673996f2fc88e21cc8613611f08a650bc0370338803591d85d0ec5663764022040b6664a0d1ec83a7f01975b8fde5232992b8ca58bf48af6725d2f92a936ab2e[ALL] 03f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c",
+ "hex": "47304402200a5e673996f2fc88e21cc8613611f08a650bc0370338803591d85d0ec5663764022040b6664a0d1ec83a7f01975b8fde5232992b8ca58bf48af6725d2f92a936ab2e012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "595d1257f654ed2cbe5a65421e8aefd2b4d70b5b6c89a03f1d7e518221fc3f02",
+ "vout": 103,
+ "scriptSig": {
+ "asm": "3046022100d93b30219c5735f673be5c3b4688366d96f545561c74cb62c6958c00f6960806022100ec8200adcb028f2184fa2a4f6faac7f8bb57cb4503bb7584ac11051fece31b3d[ALL] 03091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc",
+ "hex": "493046022100d93b30219c5735f673be5c3b4688366d96f545561c74cb62c6958c00f6960806022100ec8200adcb028f2184fa2a4f6faac7f8bb57cb4503bb7584ac11051fece31b3d012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "06fc818f9555a261248ecd7aad0993eafb5a82ceb2b5c87c3ddfb06671c7f816",
+ "vout": 1,
+ "scriptSig": {
+ "asm": "3045022100a13934e68d3f5b22b130c4cb33f4da468cffc52323a47fbfbe06b64858162246022047081e0a70ff770e64a2e2d31e5d520d9102268b57a47009a72fe73ec7669018[ALL] 0234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cd",
+ "hex": "483045022100a13934e68d3f5b22b130c4cb33f4da468cffc52323a47fbfbe06b64858162246022047081e0a70ff770e64a2e2d31e5d520d9102268b57a47009a72fe73ec766901801210234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cd"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "fb416c8155d6bb1d43f9395466ca90a638a7c2dd3ff617aadf3a7ac8f3967b19",
+ "vout": 0,
+ "scriptSig": {
+ "asm": "304602210097f1f35d5bdc1a3a60390a1b015b8e7c4f916aa3847aafd969e04975e15bbe70022100a9052eb25517d481f1fda1b129eb1b534da50ea1a51f3ee012dca3601c11b86a[ALL] 027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34",
+ "hex": "49304602210097f1f35d5bdc1a3a60390a1b015b8e7c4f916aa3847aafd969e04975e15bbe70022100a9052eb25517d481f1fda1b129eb1b534da50ea1a51f3ee012dca3601c11b86a0121027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "3940b9683bd6104ad24c978e640ba4095993cafdb27d2ed91baa27ee61a2d920",
+ "vout": 221,
+ "scriptSig": {
+ "asm": "3045022012b3138c591bf7154b6fef457f2c4a3c7162225003788ac0024a99355865ff13022100b71b125ae1ffb2e1d1571f580cd3ebc8cd049a2d7a8a41f138ba94aeb982106f[ALL] 03091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc",
+ "hex": "483045022012b3138c591bf7154b6fef457f2c4a3c7162225003788ac0024a99355865ff13022100b71b125ae1ffb2e1d1571f580cd3ebc8cd049a2d7a8a41f138ba94aeb982106f012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "711b5714d3b5136147c02194cd95bde94a4648c4263ca6f972d86cd1d579f150",
+ "vout": 1,
+ "scriptSig": {
+ "asm": "3045022100f834ccc8b22ee72712a3e5e6ef4acb8b2fb791b5385b70e2cd4332674d6667f4022024fbda0a997e0c253503f217501f508a4d56edce2c813ecdd9ad796dbeba9074[ALL] 0234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cd",
+ "hex": "483045022100f834ccc8b22ee72712a3e5e6ef4acb8b2fb791b5385b70e2cd4332674d6667f4022024fbda0a997e0c253503f217501f508a4d56edce2c813ecdd9ad796dbeba907401210234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cd"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "6364b5c5efe018430789e7fb4e338209546cae5d9c5f5e300aac68155d861b55",
+ "vout": 27,
+ "scriptSig": {
+ "asm": "304502203b2fd1e39ae0e469d7a15768f262661b0de41470daf0fe8c4fd0c26542a0870002210081c57e331f9a2d214457d953e3542904727ee412c63028113635d7224da3dccc[ALL] 03f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c",
+ "hex": "48304502203b2fd1e39ae0e469d7a15768f262661b0de41470daf0fe8c4fd0c26542a0870002210081c57e331f9a2d214457d953e3542904727ee412c63028113635d7224da3dccc012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "0bb57f6e38012c86d4c5a28c904f2675082859147921a707d48961015a3e5057",
+ "vout": 1095,
+ "scriptSig": {
+ "asm": "304502206947a9c54f0664ece4430fd4ae999891dc50bb6126bc36b6a15a3189f29d25e9022100a86cfc4e2fdd9e39a20e305cfd1b76509c67b3e313e0f118229105caa0e823c9[ALL] 03f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c",
+ "hex": "48304502206947a9c54f0664ece4430fd4ae999891dc50bb6126bc36b6a15a3189f29d25e9022100a86cfc4e2fdd9e39a20e305cfd1b76509c67b3e313e0f118229105caa0e823c9012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "9b34274814a2540bb062107117f8f3e75ef85d953e9372d8261a3e9dfbc1163f",
+ "vout": 37,
+ "scriptSig": {
+ "asm": "3045022100c7128fe10b2d38744ae8177776054c29fc8ec13f07207723e70766ab7164847402201d2cf09009b9596de74c0183d1ab832e5edddb7a9965880bb400097e850850f8[ALL] 03f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c",
+ "hex": "483045022100c7128fe10b2d38744ae8177776054c29fc8ec13f07207723e70766ab7164847402201d2cf09009b9596de74c0183d1ab832e5edddb7a9965880bb400097e850850f8012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "b86b5cc0d8a7374d94e277850b0a249cb26a7b42ddf014f28a49b8859da64241",
+ "vout": 20,
+ "scriptSig": {
+ "asm": "304502203b89a71628a28cc3703d170ca3be77786cff6b867e38a18b719705f8a326578f022100b2a9879e1acf621faa6466c207746a7f3eb4c8514c1482969aba3f2a957f1321[ALL] 03f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c",
+ "hex": "48304502203b89a71628a28cc3703d170ca3be77786cff6b867e38a18b719705f8a326578f022100b2a9879e1acf621faa6466c207746a7f3eb4c8514c1482969aba3f2a957f1321012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "3d0a2353eeec44d3c10aed259038db321912122cd4150048f7bfa4c0ecfee236",
+ "vout": 242,
+ "scriptSig": {
+ "asm": "3046022100ef794a8ef7fd6752d2a183c18866ff6e8dc0f5bd889a63e2c21cf303a6302461022100c1b09662d9e92988c3f9fcf17d1bcc79b5403647095d7212b9f8a1278a532d68[ALL] 03091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc",
+ "hex": "493046022100ef794a8ef7fd6752d2a183c18866ff6e8dc0f5bd889a63e2c21cf303a6302461022100c1b09662d9e92988c3f9fcf17d1bcc79b5403647095d7212b9f8a1278a532d68012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
+ },
+ "sequence": 4294967295
+ }
+ ],
+ "vout": [
+ {
+ "value": 1.3782,
+ "n": 0,
+ "scriptPubKey": {
+ "asm": "OP_DUP OP_HASH160 8fd139bb39ced713f231c58a4d07bf6954d1c201 OP_EQUALVERIFY OP_CHECKSIG",
+ "hex": "76a9148fd139bb39ced713f231c58a4d07bf6954d1c20188ac",
+ "reqSigs": 1,
+ "type": "pubkeyhash",
+ "addresses": [
+ "1E7SGgAZFCHDnVZLuRViX3gUmxpMfdvd2o"
+ ]
+ }
+ },
+ {
+ "value": 0.01000001,
+ "n": 1,
+ "scriptPubKey": {
+ "asm": "OP_DUP OP_HASH160 6c772e9cf96371bba3da8cb733da70a2fcf20078 OP_EQUALVERIFY OP_CHECKSIG",
+ "hex": "76a9146c772e9cf96371bba3da8cb733da70a2fcf2007888ac",
+ "reqSigs": 1,
+ "type": "pubkeyhash",
+ "addresses": [
+ "1AtWkdmfmYkErU16d3KYykJUbEp9MAj9Sb"
+ ]
+ }
+ }
+ ],
+ "hex": "0100000014fd5c23522d31761c50175453daa6edaabe47a602a592d39ce933d8271a1a87274c0100006c493046022100b4251ecd63778a3dde0155abe4cd162947620ae9ee45a874353551092325b116022100db307baf4ff3781ec520bd18f387948cedd15dc27bafe17c894b0fe6ffffcafa012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adcffffffffc1b37ae964f605978022f94ce2f3f676d66a46d1aef7c2c17d6315b9697f2f75010000006a473044022079bd62ee09621a3be96b760c39e8ef78170101d46313923c6b07ae60a95c90670220238e51ea29fc70b04b65508450523caedbb11cb4dd5aa608c81487de798925ba0121027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34ffffffffedd005dc7790ef65c206abd1ab718e75252a40f4b1310e4102cd692eca9cacb0d10000006b48304502207722d6f9038673c86a1019b1c4de2d687ae246477cd4ca7002762be0299de385022100e594a11e3a313942595f7666dcf7078bcb14f1330f4206b95c917e7ec0e82fac012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adcffffffffdf28d6e26fb7a85a1e6a229b972c1bae0edc1c11cb9ca51e4caf5e59fbea35a1000000006b483045022100a63a4788027b79b65c6f9d9e054f68cf3b4eed19efd82a2d53f70dcbe64683390220526f243671425b2bd05745fcf2729361f985cfe84ea80c7cfc817b93d8134374012103a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52ffffffffae2a2320a1582faa24469eff3024a6b98bfe00eb4f554d8a0b1421ba53bfd6a5010000006c493046022100b200ac6db16842f76dab9abe807ce423c992805879bc50abd46ed8275a59d9cf022100c0d518e85dd345b3c29dd4dc47b9a420d3ce817b18720e94966d2fe23413a408012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adcffffffffb3cc5a12548aa1794b4d2bbf076838cfd7fbafb7716da51ee8221a4ff19c291b000000006b483045022100ededc441c3103a6f2bd6cab7639421af0f6ec5e60503bce1e603cf34f00aee1c02205cb75f3f519a13fb348783b21db3085cb5ec7552c59e394fdbc3e1feea43f967012103a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52ffffffff85145367313888d2cf2747274a32e20b2df074027bafd6f970003fcbcdf11d07150000006b483045022100d9eed5413d2a4b4b98625aa6e3169edc4fb4663e7862316d69224454e70cd8ca022061e506521d5ced51dd0ea36496e75904d756a4c4f9fb111568555075d5f68d9a012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64cffffffff8292c11f6d35abab5bac3ebb627a4ff949e8ecd62d33ed137adf7aeb00e512b0090000006b48304502207e84b27139c4c19c828cb1e30c349bba88e4d9b59be97286960793b5ddc0a2af0221008cdc7a951e7f31c20953ed5635fbabf228e80b7047f32faaa0313e7693005177012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64cffffffff883dcf9a86063db088ad064d0953258d4b0ff3425857402d2f3f839cee0f84581e0000006a4730440220426540dfed9c4ab5812e5f06df705b8bcf307dd7d20f7fa6512298b2a6314f420220064055096e3ca62f6c7352c66a5447767c53f946acdf35025ab3807ddb2fa404012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64cffffffff6697dbb3ed98afe481b568459fa67e503f8a4254532465a670e54669d19c9fe6720000006a47304402200a5e673996f2fc88e21cc8613611f08a650bc0370338803591d85d0ec5663764022040b6664a0d1ec83a7f01975b8fde5232992b8ca58bf48af6725d2f92a936ab2e012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64cffffffff023ffc2182517e1d3fa0896c5b0bd7b4d2ef8a1e42655abe2ced54f657125d59670000006c493046022100d93b30219c5735f673be5c3b4688366d96f545561c74cb62c6958c00f6960806022100ec8200adcb028f2184fa2a4f6faac7f8bb57cb4503bb7584ac11051fece31b3d012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adcffffffff16f8c77166b0df3d7cc8b5b2ce825afbea9309ad7acd8e2461a255958f81fc06010000006b483045022100a13934e68d3f5b22b130c4cb33f4da468cffc52323a47fbfbe06b64858162246022047081e0a70ff770e64a2e2d31e5d520d9102268b57a47009a72fe73ec766901801210234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cdffffffff197b96f3c87a3adfaa17f63fddc2a738a690ca665439f9431dbbd655816c41fb000000006c49304602210097f1f35d5bdc1a3a60390a1b015b8e7c4f916aa3847aafd969e04975e15bbe70022100a9052eb25517d481f1fda1b129eb1b534da50ea1a51f3ee012dca3601c11b86a0121027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34ffffffff20d9a261ee27aa1bd92e7db2fdca935909a40b648e974cd24a10d63b68b94039dd0000006b483045022012b3138c591bf7154b6fef457f2c4a3c7162225003788ac0024a99355865ff13022100b71b125ae1ffb2e1d1571f580cd3ebc8cd049a2d7a8a41f138ba94aeb982106f012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adcffffffff50f179d5d16cd872f9a63c26c448464ae9bd95cd9421c0476113b5d314571b71010000006b483045022100f834ccc8b22ee72712a3e5e6ef4acb8b2fb791b5385b70e2cd4332674d6667f4022024fbda0a997e0c253503f217501f508a4d56edce2c813ecdd9ad796dbeba907401210234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cdffffffff551b865d1568ac0a305e5f9c5dae6c540982334efbe789074318e0efc5b564631b0000006b48304502203b2fd1e39ae0e469d7a15768f262661b0de41470daf0fe8c4fd0c26542a0870002210081c57e331f9a2d214457d953e3542904727ee412c63028113635d7224da3dccc012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64cffffffff57503e5a016189d407a721791459280875264f908ca2c5d4862c01386e7fb50b470400006b48304502206947a9c54f0664ece4430fd4ae999891dc50bb6126bc36b6a15a3189f29d25e9022100a86cfc4e2fdd9e39a20e305cfd1b76509c67b3e313e0f118229105caa0e823c9012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64cffffffff3f16c1fb9d3e1a26d872933e955df85ee7f3f817711062b00b54a2144827349b250000006b483045022100c7128fe10b2d38744ae8177776054c29fc8ec13f07207723e70766ab7164847402201d2cf09009b9596de74c0183d1ab832e5edddb7a9965880bb400097e850850f8012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64cffffffff4142a69d85b8498af214f0dd427b6ab29c240a0b8577e2944d37a7d8c05c6bb8140000006b48304502203b89a71628a28cc3703d170ca3be77786cff6b867e38a18b719705f8a326578f022100b2a9879e1acf621faa6466c207746a7f3eb4c8514c1482969aba3f2a957f1321012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64cffffffff36e2feecc0a4bff7480015d42c12121932db389025ed0ac1d344ecee53230a3df20000006c493046022100ef794a8ef7fd6752d2a183c18866ff6e8dc0f5bd889a63e2c21cf303a6302461022100c1b09662d9e92988c3f9fcf17d1bcc79b5403647095d7212b9f8a1278a532d68012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adcffffffff0260f73608000000001976a9148fd139bb39ced713f231c58a4d07bf6954d1c20188ac41420f00000000001976a9146c772e9cf96371bba3da8cb733da70a2fcf2007888ac00000000"
+}
diff --git a/src/test/data/tt-delout1-out.json b/src/test/data/tt-delout1-out.json
new file mode 100644
index 0000000000..afc4e95762
--- /dev/null
+++ b/src/test/data/tt-delout1-out.json
@@ -0,0 +1,213 @@
+{
+ "txid": "c46ccd75b5050e942b2e86a3648f843f525fe6fc000bf0534ba5973063354493",
+ "hash": "c46ccd75b5050e942b2e86a3648f843f525fe6fc000bf0534ba5973063354493",
+ "version": 1,
+ "locktime": 0,
+ "vin": [
+ {
+ "txid": "27871a1a27d833e99cd392a502a647beaaeda6da535417501c76312d52235cfd",
+ "vout": 332,
+ "scriptSig": {
+ "asm": "3046022100b4251ecd63778a3dde0155abe4cd162947620ae9ee45a874353551092325b116022100db307baf4ff3781ec520bd18f387948cedd15dc27bafe17c894b0fe6ffffcafa[ALL] 03091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc",
+ "hex": "493046022100b4251ecd63778a3dde0155abe4cd162947620ae9ee45a874353551092325b116022100db307baf4ff3781ec520bd18f387948cedd15dc27bafe17c894b0fe6ffffcafa012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "a72ec96bd0d022d1b0c2f9078cdd46b3725b8eecdd001e17b21e3ababad14ecb",
+ "vout": 0,
+ "scriptSig": {
+ "asm": "3046022100a9b617843b68c284715d3e02fd120479cd0d96a6c43bf01e697fb0a460a21a3a022100ba0a12fbe8b993d4e7911fa3467615765dbe421ddf5c51b57a9c1ee19dcc00ba[ALL] 03e633b4fa4ceb705c2da712390767199be8ef2448b3095dc01652e11b2b751505",
+ "hex": "493046022100a9b617843b68c284715d3e02fd120479cd0d96a6c43bf01e697fb0a460a21a3a022100ba0a12fbe8b993d4e7911fa3467615765dbe421ddf5c51b57a9c1ee19dcc00ba012103e633b4fa4ceb705c2da712390767199be8ef2448b3095dc01652e11b2b751505"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "752f7f69b915637dc1c2f7aed1466ad676f6f3e24cf922809705f664e97ab3c1",
+ "vout": 1,
+ "scriptSig": {
+ "asm": "3044022079bd62ee09621a3be96b760c39e8ef78170101d46313923c6b07ae60a95c90670220238e51ea29fc70b04b65508450523caedbb11cb4dd5aa608c81487de798925ba[ALL] 027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34",
+ "hex": "473044022079bd62ee09621a3be96b760c39e8ef78170101d46313923c6b07ae60a95c90670220238e51ea29fc70b04b65508450523caedbb11cb4dd5aa608c81487de798925ba0121027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "b0ac9cca2e69cd02410e31b1f4402a25758e71abd1ab06c265ef9077dc05d0ed",
+ "vout": 209,
+ "scriptSig": {
+ "asm": "304502207722d6f9038673c86a1019b1c4de2d687ae246477cd4ca7002762be0299de385022100e594a11e3a313942595f7666dcf7078bcb14f1330f4206b95c917e7ec0e82fac[ALL] 03091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc",
+ "hex": "48304502207722d6f9038673c86a1019b1c4de2d687ae246477cd4ca7002762be0299de385022100e594a11e3a313942595f7666dcf7078bcb14f1330f4206b95c917e7ec0e82fac012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "a135eafb595eaf4c1ea59ccb111cdc0eae1b2c979b226a1e5aa8b76fe2d628df",
+ "vout": 0,
+ "scriptSig": {
+ "asm": "3045022100a63a4788027b79b65c6f9d9e054f68cf3b4eed19efd82a2d53f70dcbe64683390220526f243671425b2bd05745fcf2729361f985cfe84ea80c7cfc817b93d8134374[ALL] 03a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52",
+ "hex": "483045022100a63a4788027b79b65c6f9d9e054f68cf3b4eed19efd82a2d53f70dcbe64683390220526f243671425b2bd05745fcf2729361f985cfe84ea80c7cfc817b93d8134374012103a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "a5d6bf53ba21140b8a4d554feb00fe8bb9a62430ff9e4624aa2f58a120232aae",
+ "vout": 1,
+ "scriptSig": {
+ "asm": "3046022100b200ac6db16842f76dab9abe807ce423c992805879bc50abd46ed8275a59d9cf022100c0d518e85dd345b3c29dd4dc47b9a420d3ce817b18720e94966d2fe23413a408[ALL] 03091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc",
+ "hex": "493046022100b200ac6db16842f76dab9abe807ce423c992805879bc50abd46ed8275a59d9cf022100c0d518e85dd345b3c29dd4dc47b9a420d3ce817b18720e94966d2fe23413a408012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "1b299cf14f1a22e81ea56d71b7affbd7cf386807bf2b4d4b79a18a54125accb3",
+ "vout": 0,
+ "scriptSig": {
+ "asm": "3045022100ededc441c3103a6f2bd6cab7639421af0f6ec5e60503bce1e603cf34f00aee1c02205cb75f3f519a13fb348783b21db3085cb5ec7552c59e394fdbc3e1feea43f967[ALL] 03a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52",
+ "hex": "483045022100ededc441c3103a6f2bd6cab7639421af0f6ec5e60503bce1e603cf34f00aee1c02205cb75f3f519a13fb348783b21db3085cb5ec7552c59e394fdbc3e1feea43f967012103a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "071df1cdcb3f0070f9d6af7b0274f02d0be2324a274727cfd288383167531485",
+ "vout": 21,
+ "scriptSig": {
+ "asm": "3045022100d9eed5413d2a4b4b98625aa6e3169edc4fb4663e7862316d69224454e70cd8ca022061e506521d5ced51dd0ea36496e75904d756a4c4f9fb111568555075d5f68d9a[ALL] 03f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c",
+ "hex": "483045022100d9eed5413d2a4b4b98625aa6e3169edc4fb4663e7862316d69224454e70cd8ca022061e506521d5ced51dd0ea36496e75904d756a4c4f9fb111568555075d5f68d9a012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "b012e500eb7adf7a13ed332dd6ece849f94f7a62bb3eac5babab356d1fc19282",
+ "vout": 9,
+ "scriptSig": {
+ "asm": "304502207e84b27139c4c19c828cb1e30c349bba88e4d9b59be97286960793b5ddc0a2af0221008cdc7a951e7f31c20953ed5635fbabf228e80b7047f32faaa0313e7693005177[ALL] 03f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c",
+ "hex": "48304502207e84b27139c4c19c828cb1e30c349bba88e4d9b59be97286960793b5ddc0a2af0221008cdc7a951e7f31c20953ed5635fbabf228e80b7047f32faaa0313e7693005177012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "58840fee9c833f2f2d40575842f30f4b8d2553094d06ad88b03d06869acf3d88",
+ "vout": 30,
+ "scriptSig": {
+ "asm": "30440220426540dfed9c4ab5812e5f06df705b8bcf307dd7d20f7fa6512298b2a6314f420220064055096e3ca62f6c7352c66a5447767c53f946acdf35025ab3807ddb2fa404[ALL] 03f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c",
+ "hex": "4730440220426540dfed9c4ab5812e5f06df705b8bcf307dd7d20f7fa6512298b2a6314f420220064055096e3ca62f6c7352c66a5447767c53f946acdf35025ab3807ddb2fa404012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "e69f9cd16946e570a665245354428a3f507ea69f4568b581e4af98edb3db9766",
+ "vout": 114,
+ "scriptSig": {
+ "asm": "304402200a5e673996f2fc88e21cc8613611f08a650bc0370338803591d85d0ec5663764022040b6664a0d1ec83a7f01975b8fde5232992b8ca58bf48af6725d2f92a936ab2e[ALL] 03f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c",
+ "hex": "47304402200a5e673996f2fc88e21cc8613611f08a650bc0370338803591d85d0ec5663764022040b6664a0d1ec83a7f01975b8fde5232992b8ca58bf48af6725d2f92a936ab2e012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "595d1257f654ed2cbe5a65421e8aefd2b4d70b5b6c89a03f1d7e518221fc3f02",
+ "vout": 103,
+ "scriptSig": {
+ "asm": "3046022100d93b30219c5735f673be5c3b4688366d96f545561c74cb62c6958c00f6960806022100ec8200adcb028f2184fa2a4f6faac7f8bb57cb4503bb7584ac11051fece31b3d[ALL] 03091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc",
+ "hex": "493046022100d93b30219c5735f673be5c3b4688366d96f545561c74cb62c6958c00f6960806022100ec8200adcb028f2184fa2a4f6faac7f8bb57cb4503bb7584ac11051fece31b3d012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "06fc818f9555a261248ecd7aad0993eafb5a82ceb2b5c87c3ddfb06671c7f816",
+ "vout": 1,
+ "scriptSig": {
+ "asm": "3045022100a13934e68d3f5b22b130c4cb33f4da468cffc52323a47fbfbe06b64858162246022047081e0a70ff770e64a2e2d31e5d520d9102268b57a47009a72fe73ec7669018[ALL] 0234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cd",
+ "hex": "483045022100a13934e68d3f5b22b130c4cb33f4da468cffc52323a47fbfbe06b64858162246022047081e0a70ff770e64a2e2d31e5d520d9102268b57a47009a72fe73ec766901801210234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cd"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "fb416c8155d6bb1d43f9395466ca90a638a7c2dd3ff617aadf3a7ac8f3967b19",
+ "vout": 0,
+ "scriptSig": {
+ "asm": "304602210097f1f35d5bdc1a3a60390a1b015b8e7c4f916aa3847aafd969e04975e15bbe70022100a9052eb25517d481f1fda1b129eb1b534da50ea1a51f3ee012dca3601c11b86a[ALL] 027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34",
+ "hex": "49304602210097f1f35d5bdc1a3a60390a1b015b8e7c4f916aa3847aafd969e04975e15bbe70022100a9052eb25517d481f1fda1b129eb1b534da50ea1a51f3ee012dca3601c11b86a0121027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "3940b9683bd6104ad24c978e640ba4095993cafdb27d2ed91baa27ee61a2d920",
+ "vout": 221,
+ "scriptSig": {
+ "asm": "3045022012b3138c591bf7154b6fef457f2c4a3c7162225003788ac0024a99355865ff13022100b71b125ae1ffb2e1d1571f580cd3ebc8cd049a2d7a8a41f138ba94aeb982106f[ALL] 03091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc",
+ "hex": "483045022012b3138c591bf7154b6fef457f2c4a3c7162225003788ac0024a99355865ff13022100b71b125ae1ffb2e1d1571f580cd3ebc8cd049a2d7a8a41f138ba94aeb982106f012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "711b5714d3b5136147c02194cd95bde94a4648c4263ca6f972d86cd1d579f150",
+ "vout": 1,
+ "scriptSig": {
+ "asm": "3045022100f834ccc8b22ee72712a3e5e6ef4acb8b2fb791b5385b70e2cd4332674d6667f4022024fbda0a997e0c253503f217501f508a4d56edce2c813ecdd9ad796dbeba9074[ALL] 0234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cd",
+ "hex": "483045022100f834ccc8b22ee72712a3e5e6ef4acb8b2fb791b5385b70e2cd4332674d6667f4022024fbda0a997e0c253503f217501f508a4d56edce2c813ecdd9ad796dbeba907401210234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cd"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "6364b5c5efe018430789e7fb4e338209546cae5d9c5f5e300aac68155d861b55",
+ "vout": 27,
+ "scriptSig": {
+ "asm": "304502203b2fd1e39ae0e469d7a15768f262661b0de41470daf0fe8c4fd0c26542a0870002210081c57e331f9a2d214457d953e3542904727ee412c63028113635d7224da3dccc[ALL] 03f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c",
+ "hex": "48304502203b2fd1e39ae0e469d7a15768f262661b0de41470daf0fe8c4fd0c26542a0870002210081c57e331f9a2d214457d953e3542904727ee412c63028113635d7224da3dccc012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "0bb57f6e38012c86d4c5a28c904f2675082859147921a707d48961015a3e5057",
+ "vout": 1095,
+ "scriptSig": {
+ "asm": "304502206947a9c54f0664ece4430fd4ae999891dc50bb6126bc36b6a15a3189f29d25e9022100a86cfc4e2fdd9e39a20e305cfd1b76509c67b3e313e0f118229105caa0e823c9[ALL] 03f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c",
+ "hex": "48304502206947a9c54f0664ece4430fd4ae999891dc50bb6126bc36b6a15a3189f29d25e9022100a86cfc4e2fdd9e39a20e305cfd1b76509c67b3e313e0f118229105caa0e823c9012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "9b34274814a2540bb062107117f8f3e75ef85d953e9372d8261a3e9dfbc1163f",
+ "vout": 37,
+ "scriptSig": {
+ "asm": "3045022100c7128fe10b2d38744ae8177776054c29fc8ec13f07207723e70766ab7164847402201d2cf09009b9596de74c0183d1ab832e5edddb7a9965880bb400097e850850f8[ALL] 03f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c",
+ "hex": "483045022100c7128fe10b2d38744ae8177776054c29fc8ec13f07207723e70766ab7164847402201d2cf09009b9596de74c0183d1ab832e5edddb7a9965880bb400097e850850f8012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "b86b5cc0d8a7374d94e277850b0a249cb26a7b42ddf014f28a49b8859da64241",
+ "vout": 20,
+ "scriptSig": {
+ "asm": "304502203b89a71628a28cc3703d170ca3be77786cff6b867e38a18b719705f8a326578f022100b2a9879e1acf621faa6466c207746a7f3eb4c8514c1482969aba3f2a957f1321[ALL] 03f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c",
+ "hex": "48304502203b89a71628a28cc3703d170ca3be77786cff6b867e38a18b719705f8a326578f022100b2a9879e1acf621faa6466c207746a7f3eb4c8514c1482969aba3f2a957f1321012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "3d0a2353eeec44d3c10aed259038db321912122cd4150048f7bfa4c0ecfee236",
+ "vout": 242,
+ "scriptSig": {
+ "asm": "3046022100ef794a8ef7fd6752d2a183c18866ff6e8dc0f5bd889a63e2c21cf303a6302461022100c1b09662d9e92988c3f9fcf17d1bcc79b5403647095d7212b9f8a1278a532d68[ALL] 03091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc",
+ "hex": "493046022100ef794a8ef7fd6752d2a183c18866ff6e8dc0f5bd889a63e2c21cf303a6302461022100c1b09662d9e92988c3f9fcf17d1bcc79b5403647095d7212b9f8a1278a532d68012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
+ },
+ "sequence": 4294967295
+ }
+ ],
+ "vout": [
+ {
+ "value": 1.3782,
+ "n": 0,
+ "scriptPubKey": {
+ "asm": "OP_DUP OP_HASH160 8fd139bb39ced713f231c58a4d07bf6954d1c201 OP_EQUALVERIFY OP_CHECKSIG",
+ "hex": "76a9148fd139bb39ced713f231c58a4d07bf6954d1c20188ac",
+ "reqSigs": 1,
+ "type": "pubkeyhash",
+ "addresses": [
+ "1E7SGgAZFCHDnVZLuRViX3gUmxpMfdvd2o"
+ ]
+ }
+ }
+ ],
+ "hex": "0100000015fd5c23522d31761c50175453daa6edaabe47a602a592d39ce933d8271a1a87274c0100006c493046022100b4251ecd63778a3dde0155abe4cd162947620ae9ee45a874353551092325b116022100db307baf4ff3781ec520bd18f387948cedd15dc27bafe17c894b0fe6ffffcafa012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adcffffffffcb4ed1baba3a1eb2171e00ddec8e5b72b346dd8c07f9c2b0d122d0d06bc92ea7000000006c493046022100a9b617843b68c284715d3e02fd120479cd0d96a6c43bf01e697fb0a460a21a3a022100ba0a12fbe8b993d4e7911fa3467615765dbe421ddf5c51b57a9c1ee19dcc00ba012103e633b4fa4ceb705c2da712390767199be8ef2448b3095dc01652e11b2b751505ffffffffc1b37ae964f605978022f94ce2f3f676d66a46d1aef7c2c17d6315b9697f2f75010000006a473044022079bd62ee09621a3be96b760c39e8ef78170101d46313923c6b07ae60a95c90670220238e51ea29fc70b04b65508450523caedbb11cb4dd5aa608c81487de798925ba0121027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34ffffffffedd005dc7790ef65c206abd1ab718e75252a40f4b1310e4102cd692eca9cacb0d10000006b48304502207722d6f9038673c86a1019b1c4de2d687ae246477cd4ca7002762be0299de385022100e594a11e3a313942595f7666dcf7078bcb14f1330f4206b95c917e7ec0e82fac012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adcffffffffdf28d6e26fb7a85a1e6a229b972c1bae0edc1c11cb9ca51e4caf5e59fbea35a1000000006b483045022100a63a4788027b79b65c6f9d9e054f68cf3b4eed19efd82a2d53f70dcbe64683390220526f243671425b2bd05745fcf2729361f985cfe84ea80c7cfc817b93d8134374012103a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52ffffffffae2a2320a1582faa24469eff3024a6b98bfe00eb4f554d8a0b1421ba53bfd6a5010000006c493046022100b200ac6db16842f76dab9abe807ce423c992805879bc50abd46ed8275a59d9cf022100c0d518e85dd345b3c29dd4dc47b9a420d3ce817b18720e94966d2fe23413a408012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adcffffffffb3cc5a12548aa1794b4d2bbf076838cfd7fbafb7716da51ee8221a4ff19c291b000000006b483045022100ededc441c3103a6f2bd6cab7639421af0f6ec5e60503bce1e603cf34f00aee1c02205cb75f3f519a13fb348783b21db3085cb5ec7552c59e394fdbc3e1feea43f967012103a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52ffffffff85145367313888d2cf2747274a32e20b2df074027bafd6f970003fcbcdf11d07150000006b483045022100d9eed5413d2a4b4b98625aa6e3169edc4fb4663e7862316d69224454e70cd8ca022061e506521d5ced51dd0ea36496e75904d756a4c4f9fb111568555075d5f68d9a012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64cffffffff8292c11f6d35abab5bac3ebb627a4ff949e8ecd62d33ed137adf7aeb00e512b0090000006b48304502207e84b27139c4c19c828cb1e30c349bba88e4d9b59be97286960793b5ddc0a2af0221008cdc7a951e7f31c20953ed5635fbabf228e80b7047f32faaa0313e7693005177012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64cffffffff883dcf9a86063db088ad064d0953258d4b0ff3425857402d2f3f839cee0f84581e0000006a4730440220426540dfed9c4ab5812e5f06df705b8bcf307dd7d20f7fa6512298b2a6314f420220064055096e3ca62f6c7352c66a5447767c53f946acdf35025ab3807ddb2fa404012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64cffffffff6697dbb3ed98afe481b568459fa67e503f8a4254532465a670e54669d19c9fe6720000006a47304402200a5e673996f2fc88e21cc8613611f08a650bc0370338803591d85d0ec5663764022040b6664a0d1ec83a7f01975b8fde5232992b8ca58bf48af6725d2f92a936ab2e012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64cffffffff023ffc2182517e1d3fa0896c5b0bd7b4d2ef8a1e42655abe2ced54f657125d59670000006c493046022100d93b30219c5735f673be5c3b4688366d96f545561c74cb62c6958c00f6960806022100ec8200adcb028f2184fa2a4f6faac7f8bb57cb4503bb7584ac11051fece31b3d012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adcffffffff16f8c77166b0df3d7cc8b5b2ce825afbea9309ad7acd8e2461a255958f81fc06010000006b483045022100a13934e68d3f5b22b130c4cb33f4da468cffc52323a47fbfbe06b64858162246022047081e0a70ff770e64a2e2d31e5d520d9102268b57a47009a72fe73ec766901801210234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cdffffffff197b96f3c87a3adfaa17f63fddc2a738a690ca665439f9431dbbd655816c41fb000000006c49304602210097f1f35d5bdc1a3a60390a1b015b8e7c4f916aa3847aafd969e04975e15bbe70022100a9052eb25517d481f1fda1b129eb1b534da50ea1a51f3ee012dca3601c11b86a0121027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34ffffffff20d9a261ee27aa1bd92e7db2fdca935909a40b648e974cd24a10d63b68b94039dd0000006b483045022012b3138c591bf7154b6fef457f2c4a3c7162225003788ac0024a99355865ff13022100b71b125ae1ffb2e1d1571f580cd3ebc8cd049a2d7a8a41f138ba94aeb982106f012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adcffffffff50f179d5d16cd872f9a63c26c448464ae9bd95cd9421c0476113b5d314571b71010000006b483045022100f834ccc8b22ee72712a3e5e6ef4acb8b2fb791b5385b70e2cd4332674d6667f4022024fbda0a997e0c253503f217501f508a4d56edce2c813ecdd9ad796dbeba907401210234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cdffffffff551b865d1568ac0a305e5f9c5dae6c540982334efbe789074318e0efc5b564631b0000006b48304502203b2fd1e39ae0e469d7a15768f262661b0de41470daf0fe8c4fd0c26542a0870002210081c57e331f9a2d214457d953e3542904727ee412c63028113635d7224da3dccc012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64cffffffff57503e5a016189d407a721791459280875264f908ca2c5d4862c01386e7fb50b470400006b48304502206947a9c54f0664ece4430fd4ae999891dc50bb6126bc36b6a15a3189f29d25e9022100a86cfc4e2fdd9e39a20e305cfd1b76509c67b3e313e0f118229105caa0e823c9012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64cffffffff3f16c1fb9d3e1a26d872933e955df85ee7f3f817711062b00b54a2144827349b250000006b483045022100c7128fe10b2d38744ae8177776054c29fc8ec13f07207723e70766ab7164847402201d2cf09009b9596de74c0183d1ab832e5edddb7a9965880bb400097e850850f8012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64cffffffff4142a69d85b8498af214f0dd427b6ab29c240a0b8577e2944d37a7d8c05c6bb8140000006b48304502203b89a71628a28cc3703d170ca3be77786cff6b867e38a18b719705f8a326578f022100b2a9879e1acf621faa6466c207746a7f3eb4c8514c1482969aba3f2a957f1321012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64cffffffff36e2feecc0a4bff7480015d42c12121932db389025ed0ac1d344ecee53230a3df20000006c493046022100ef794a8ef7fd6752d2a183c18866ff6e8dc0f5bd889a63e2c21cf303a6302461022100c1b09662d9e92988c3f9fcf17d1bcc79b5403647095d7212b9f8a1278a532d68012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adcffffffff0160f73608000000001976a9148fd139bb39ced713f231c58a4d07bf6954d1c20188ac00000000"
+}
diff --git a/src/test/data/tt-locktime317000-out.json b/src/test/data/tt-locktime317000-out.json
new file mode 100644
index 0000000000..2b9075f8ac
--- /dev/null
+++ b/src/test/data/tt-locktime317000-out.json
@@ -0,0 +1,226 @@
+{
+ "txid": "aded538f642c17e15f4d3306b8be7e1a4d1ae0c4616d641ab51ea09ba65e5cb5",
+ "hash": "aded538f642c17e15f4d3306b8be7e1a4d1ae0c4616d641ab51ea09ba65e5cb5",
+ "version": 1,
+ "locktime": 317000,
+ "vin": [
+ {
+ "txid": "27871a1a27d833e99cd392a502a647beaaeda6da535417501c76312d52235cfd",
+ "vout": 332,
+ "scriptSig": {
+ "asm": "3046022100b4251ecd63778a3dde0155abe4cd162947620ae9ee45a874353551092325b116022100db307baf4ff3781ec520bd18f387948cedd15dc27bafe17c894b0fe6ffffcafa[ALL] 03091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc",
+ "hex": "493046022100b4251ecd63778a3dde0155abe4cd162947620ae9ee45a874353551092325b116022100db307baf4ff3781ec520bd18f387948cedd15dc27bafe17c894b0fe6ffffcafa012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "a72ec96bd0d022d1b0c2f9078cdd46b3725b8eecdd001e17b21e3ababad14ecb",
+ "vout": 0,
+ "scriptSig": {
+ "asm": "3046022100a9b617843b68c284715d3e02fd120479cd0d96a6c43bf01e697fb0a460a21a3a022100ba0a12fbe8b993d4e7911fa3467615765dbe421ddf5c51b57a9c1ee19dcc00ba[ALL] 03e633b4fa4ceb705c2da712390767199be8ef2448b3095dc01652e11b2b751505",
+ "hex": "493046022100a9b617843b68c284715d3e02fd120479cd0d96a6c43bf01e697fb0a460a21a3a022100ba0a12fbe8b993d4e7911fa3467615765dbe421ddf5c51b57a9c1ee19dcc00ba012103e633b4fa4ceb705c2da712390767199be8ef2448b3095dc01652e11b2b751505"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "752f7f69b915637dc1c2f7aed1466ad676f6f3e24cf922809705f664e97ab3c1",
+ "vout": 1,
+ "scriptSig": {
+ "asm": "3044022079bd62ee09621a3be96b760c39e8ef78170101d46313923c6b07ae60a95c90670220238e51ea29fc70b04b65508450523caedbb11cb4dd5aa608c81487de798925ba[ALL] 027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34",
+ "hex": "473044022079bd62ee09621a3be96b760c39e8ef78170101d46313923c6b07ae60a95c90670220238e51ea29fc70b04b65508450523caedbb11cb4dd5aa608c81487de798925ba0121027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "b0ac9cca2e69cd02410e31b1f4402a25758e71abd1ab06c265ef9077dc05d0ed",
+ "vout": 209,
+ "scriptSig": {
+ "asm": "304502207722d6f9038673c86a1019b1c4de2d687ae246477cd4ca7002762be0299de385022100e594a11e3a313942595f7666dcf7078bcb14f1330f4206b95c917e7ec0e82fac[ALL] 03091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc",
+ "hex": "48304502207722d6f9038673c86a1019b1c4de2d687ae246477cd4ca7002762be0299de385022100e594a11e3a313942595f7666dcf7078bcb14f1330f4206b95c917e7ec0e82fac012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "a135eafb595eaf4c1ea59ccb111cdc0eae1b2c979b226a1e5aa8b76fe2d628df",
+ "vout": 0,
+ "scriptSig": {
+ "asm": "3045022100a63a4788027b79b65c6f9d9e054f68cf3b4eed19efd82a2d53f70dcbe64683390220526f243671425b2bd05745fcf2729361f985cfe84ea80c7cfc817b93d8134374[ALL] 03a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52",
+ "hex": "483045022100a63a4788027b79b65c6f9d9e054f68cf3b4eed19efd82a2d53f70dcbe64683390220526f243671425b2bd05745fcf2729361f985cfe84ea80c7cfc817b93d8134374012103a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "a5d6bf53ba21140b8a4d554feb00fe8bb9a62430ff9e4624aa2f58a120232aae",
+ "vout": 1,
+ "scriptSig": {
+ "asm": "3046022100b200ac6db16842f76dab9abe807ce423c992805879bc50abd46ed8275a59d9cf022100c0d518e85dd345b3c29dd4dc47b9a420d3ce817b18720e94966d2fe23413a408[ALL] 03091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc",
+ "hex": "493046022100b200ac6db16842f76dab9abe807ce423c992805879bc50abd46ed8275a59d9cf022100c0d518e85dd345b3c29dd4dc47b9a420d3ce817b18720e94966d2fe23413a408012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "1b299cf14f1a22e81ea56d71b7affbd7cf386807bf2b4d4b79a18a54125accb3",
+ "vout": 0,
+ "scriptSig": {
+ "asm": "3045022100ededc441c3103a6f2bd6cab7639421af0f6ec5e60503bce1e603cf34f00aee1c02205cb75f3f519a13fb348783b21db3085cb5ec7552c59e394fdbc3e1feea43f967[ALL] 03a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52",
+ "hex": "483045022100ededc441c3103a6f2bd6cab7639421af0f6ec5e60503bce1e603cf34f00aee1c02205cb75f3f519a13fb348783b21db3085cb5ec7552c59e394fdbc3e1feea43f967012103a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "071df1cdcb3f0070f9d6af7b0274f02d0be2324a274727cfd288383167531485",
+ "vout": 21,
+ "scriptSig": {
+ "asm": "3045022100d9eed5413d2a4b4b98625aa6e3169edc4fb4663e7862316d69224454e70cd8ca022061e506521d5ced51dd0ea36496e75904d756a4c4f9fb111568555075d5f68d9a[ALL] 03f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c",
+ "hex": "483045022100d9eed5413d2a4b4b98625aa6e3169edc4fb4663e7862316d69224454e70cd8ca022061e506521d5ced51dd0ea36496e75904d756a4c4f9fb111568555075d5f68d9a012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "b012e500eb7adf7a13ed332dd6ece849f94f7a62bb3eac5babab356d1fc19282",
+ "vout": 9,
+ "scriptSig": {
+ "asm": "304502207e84b27139c4c19c828cb1e30c349bba88e4d9b59be97286960793b5ddc0a2af0221008cdc7a951e7f31c20953ed5635fbabf228e80b7047f32faaa0313e7693005177[ALL] 03f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c",
+ "hex": "48304502207e84b27139c4c19c828cb1e30c349bba88e4d9b59be97286960793b5ddc0a2af0221008cdc7a951e7f31c20953ed5635fbabf228e80b7047f32faaa0313e7693005177012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "58840fee9c833f2f2d40575842f30f4b8d2553094d06ad88b03d06869acf3d88",
+ "vout": 30,
+ "scriptSig": {
+ "asm": "30440220426540dfed9c4ab5812e5f06df705b8bcf307dd7d20f7fa6512298b2a6314f420220064055096e3ca62f6c7352c66a5447767c53f946acdf35025ab3807ddb2fa404[ALL] 03f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c",
+ "hex": "4730440220426540dfed9c4ab5812e5f06df705b8bcf307dd7d20f7fa6512298b2a6314f420220064055096e3ca62f6c7352c66a5447767c53f946acdf35025ab3807ddb2fa404012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "e69f9cd16946e570a665245354428a3f507ea69f4568b581e4af98edb3db9766",
+ "vout": 114,
+ "scriptSig": {
+ "asm": "304402200a5e673996f2fc88e21cc8613611f08a650bc0370338803591d85d0ec5663764022040b6664a0d1ec83a7f01975b8fde5232992b8ca58bf48af6725d2f92a936ab2e[ALL] 03f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c",
+ "hex": "47304402200a5e673996f2fc88e21cc8613611f08a650bc0370338803591d85d0ec5663764022040b6664a0d1ec83a7f01975b8fde5232992b8ca58bf48af6725d2f92a936ab2e012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "595d1257f654ed2cbe5a65421e8aefd2b4d70b5b6c89a03f1d7e518221fc3f02",
+ "vout": 103,
+ "scriptSig": {
+ "asm": "3046022100d93b30219c5735f673be5c3b4688366d96f545561c74cb62c6958c00f6960806022100ec8200adcb028f2184fa2a4f6faac7f8bb57cb4503bb7584ac11051fece31b3d[ALL] 03091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc",
+ "hex": "493046022100d93b30219c5735f673be5c3b4688366d96f545561c74cb62c6958c00f6960806022100ec8200adcb028f2184fa2a4f6faac7f8bb57cb4503bb7584ac11051fece31b3d012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "06fc818f9555a261248ecd7aad0993eafb5a82ceb2b5c87c3ddfb06671c7f816",
+ "vout": 1,
+ "scriptSig": {
+ "asm": "3045022100a13934e68d3f5b22b130c4cb33f4da468cffc52323a47fbfbe06b64858162246022047081e0a70ff770e64a2e2d31e5d520d9102268b57a47009a72fe73ec7669018[ALL] 0234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cd",
+ "hex": "483045022100a13934e68d3f5b22b130c4cb33f4da468cffc52323a47fbfbe06b64858162246022047081e0a70ff770e64a2e2d31e5d520d9102268b57a47009a72fe73ec766901801210234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cd"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "fb416c8155d6bb1d43f9395466ca90a638a7c2dd3ff617aadf3a7ac8f3967b19",
+ "vout": 0,
+ "scriptSig": {
+ "asm": "304602210097f1f35d5bdc1a3a60390a1b015b8e7c4f916aa3847aafd969e04975e15bbe70022100a9052eb25517d481f1fda1b129eb1b534da50ea1a51f3ee012dca3601c11b86a[ALL] 027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34",
+ "hex": "49304602210097f1f35d5bdc1a3a60390a1b015b8e7c4f916aa3847aafd969e04975e15bbe70022100a9052eb25517d481f1fda1b129eb1b534da50ea1a51f3ee012dca3601c11b86a0121027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "3940b9683bd6104ad24c978e640ba4095993cafdb27d2ed91baa27ee61a2d920",
+ "vout": 221,
+ "scriptSig": {
+ "asm": "3045022012b3138c591bf7154b6fef457f2c4a3c7162225003788ac0024a99355865ff13022100b71b125ae1ffb2e1d1571f580cd3ebc8cd049a2d7a8a41f138ba94aeb982106f[ALL] 03091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc",
+ "hex": "483045022012b3138c591bf7154b6fef457f2c4a3c7162225003788ac0024a99355865ff13022100b71b125ae1ffb2e1d1571f580cd3ebc8cd049a2d7a8a41f138ba94aeb982106f012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "711b5714d3b5136147c02194cd95bde94a4648c4263ca6f972d86cd1d579f150",
+ "vout": 1,
+ "scriptSig": {
+ "asm": "3045022100f834ccc8b22ee72712a3e5e6ef4acb8b2fb791b5385b70e2cd4332674d6667f4022024fbda0a997e0c253503f217501f508a4d56edce2c813ecdd9ad796dbeba9074[ALL] 0234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cd",
+ "hex": "483045022100f834ccc8b22ee72712a3e5e6ef4acb8b2fb791b5385b70e2cd4332674d6667f4022024fbda0a997e0c253503f217501f508a4d56edce2c813ecdd9ad796dbeba907401210234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cd"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "6364b5c5efe018430789e7fb4e338209546cae5d9c5f5e300aac68155d861b55",
+ "vout": 27,
+ "scriptSig": {
+ "asm": "304502203b2fd1e39ae0e469d7a15768f262661b0de41470daf0fe8c4fd0c26542a0870002210081c57e331f9a2d214457d953e3542904727ee412c63028113635d7224da3dccc[ALL] 03f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c",
+ "hex": "48304502203b2fd1e39ae0e469d7a15768f262661b0de41470daf0fe8c4fd0c26542a0870002210081c57e331f9a2d214457d953e3542904727ee412c63028113635d7224da3dccc012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "0bb57f6e38012c86d4c5a28c904f2675082859147921a707d48961015a3e5057",
+ "vout": 1095,
+ "scriptSig": {
+ "asm": "304502206947a9c54f0664ece4430fd4ae999891dc50bb6126bc36b6a15a3189f29d25e9022100a86cfc4e2fdd9e39a20e305cfd1b76509c67b3e313e0f118229105caa0e823c9[ALL] 03f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c",
+ "hex": "48304502206947a9c54f0664ece4430fd4ae999891dc50bb6126bc36b6a15a3189f29d25e9022100a86cfc4e2fdd9e39a20e305cfd1b76509c67b3e313e0f118229105caa0e823c9012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "9b34274814a2540bb062107117f8f3e75ef85d953e9372d8261a3e9dfbc1163f",
+ "vout": 37,
+ "scriptSig": {
+ "asm": "3045022100c7128fe10b2d38744ae8177776054c29fc8ec13f07207723e70766ab7164847402201d2cf09009b9596de74c0183d1ab832e5edddb7a9965880bb400097e850850f8[ALL] 03f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c",
+ "hex": "483045022100c7128fe10b2d38744ae8177776054c29fc8ec13f07207723e70766ab7164847402201d2cf09009b9596de74c0183d1ab832e5edddb7a9965880bb400097e850850f8012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "b86b5cc0d8a7374d94e277850b0a249cb26a7b42ddf014f28a49b8859da64241",
+ "vout": 20,
+ "scriptSig": {
+ "asm": "304502203b89a71628a28cc3703d170ca3be77786cff6b867e38a18b719705f8a326578f022100b2a9879e1acf621faa6466c207746a7f3eb4c8514c1482969aba3f2a957f1321[ALL] 03f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c",
+ "hex": "48304502203b89a71628a28cc3703d170ca3be77786cff6b867e38a18b719705f8a326578f022100b2a9879e1acf621faa6466c207746a7f3eb4c8514c1482969aba3f2a957f1321012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c"
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "3d0a2353eeec44d3c10aed259038db321912122cd4150048f7bfa4c0ecfee236",
+ "vout": 242,
+ "scriptSig": {
+ "asm": "3046022100ef794a8ef7fd6752d2a183c18866ff6e8dc0f5bd889a63e2c21cf303a6302461022100c1b09662d9e92988c3f9fcf17d1bcc79b5403647095d7212b9f8a1278a532d68[ALL] 03091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc",
+ "hex": "493046022100ef794a8ef7fd6752d2a183c18866ff6e8dc0f5bd889a63e2c21cf303a6302461022100c1b09662d9e92988c3f9fcf17d1bcc79b5403647095d7212b9f8a1278a532d68012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc"
+ },
+ "sequence": 4294967295
+ }
+ ],
+ "vout": [
+ {
+ "value": 1.3782,
+ "n": 0,
+ "scriptPubKey": {
+ "asm": "OP_DUP OP_HASH160 8fd139bb39ced713f231c58a4d07bf6954d1c201 OP_EQUALVERIFY OP_CHECKSIG",
+ "hex": "76a9148fd139bb39ced713f231c58a4d07bf6954d1c20188ac",
+ "reqSigs": 1,
+ "type": "pubkeyhash",
+ "addresses": [
+ "1E7SGgAZFCHDnVZLuRViX3gUmxpMfdvd2o"
+ ]
+ }
+ },
+ {
+ "value": 0.01000001,
+ "n": 1,
+ "scriptPubKey": {
+ "asm": "OP_DUP OP_HASH160 6c772e9cf96371bba3da8cb733da70a2fcf20078 OP_EQUALVERIFY OP_CHECKSIG",
+ "hex": "76a9146c772e9cf96371bba3da8cb733da70a2fcf2007888ac",
+ "reqSigs": 1,
+ "type": "pubkeyhash",
+ "addresses": [
+ "1AtWkdmfmYkErU16d3KYykJUbEp9MAj9Sb"
+ ]
+ }
+ }
+ ],
+ "hex": "0100000015fd5c23522d31761c50175453daa6edaabe47a602a592d39ce933d8271a1a87274c0100006c493046022100b4251ecd63778a3dde0155abe4cd162947620ae9ee45a874353551092325b116022100db307baf4ff3781ec520bd18f387948cedd15dc27bafe17c894b0fe6ffffcafa012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adcffffffffcb4ed1baba3a1eb2171e00ddec8e5b72b346dd8c07f9c2b0d122d0d06bc92ea7000000006c493046022100a9b617843b68c284715d3e02fd120479cd0d96a6c43bf01e697fb0a460a21a3a022100ba0a12fbe8b993d4e7911fa3467615765dbe421ddf5c51b57a9c1ee19dcc00ba012103e633b4fa4ceb705c2da712390767199be8ef2448b3095dc01652e11b2b751505ffffffffc1b37ae964f605978022f94ce2f3f676d66a46d1aef7c2c17d6315b9697f2f75010000006a473044022079bd62ee09621a3be96b760c39e8ef78170101d46313923c6b07ae60a95c90670220238e51ea29fc70b04b65508450523caedbb11cb4dd5aa608c81487de798925ba0121027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34ffffffffedd005dc7790ef65c206abd1ab718e75252a40f4b1310e4102cd692eca9cacb0d10000006b48304502207722d6f9038673c86a1019b1c4de2d687ae246477cd4ca7002762be0299de385022100e594a11e3a313942595f7666dcf7078bcb14f1330f4206b95c917e7ec0e82fac012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adcffffffffdf28d6e26fb7a85a1e6a229b972c1bae0edc1c11cb9ca51e4caf5e59fbea35a1000000006b483045022100a63a4788027b79b65c6f9d9e054f68cf3b4eed19efd82a2d53f70dcbe64683390220526f243671425b2bd05745fcf2729361f985cfe84ea80c7cfc817b93d8134374012103a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52ffffffffae2a2320a1582faa24469eff3024a6b98bfe00eb4f554d8a0b1421ba53bfd6a5010000006c493046022100b200ac6db16842f76dab9abe807ce423c992805879bc50abd46ed8275a59d9cf022100c0d518e85dd345b3c29dd4dc47b9a420d3ce817b18720e94966d2fe23413a408012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adcffffffffb3cc5a12548aa1794b4d2bbf076838cfd7fbafb7716da51ee8221a4ff19c291b000000006b483045022100ededc441c3103a6f2bd6cab7639421af0f6ec5e60503bce1e603cf34f00aee1c02205cb75f3f519a13fb348783b21db3085cb5ec7552c59e394fdbc3e1feea43f967012103a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52ffffffff85145367313888d2cf2747274a32e20b2df074027bafd6f970003fcbcdf11d07150000006b483045022100d9eed5413d2a4b4b98625aa6e3169edc4fb4663e7862316d69224454e70cd8ca022061e506521d5ced51dd0ea36496e75904d756a4c4f9fb111568555075d5f68d9a012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64cffffffff8292c11f6d35abab5bac3ebb627a4ff949e8ecd62d33ed137adf7aeb00e512b0090000006b48304502207e84b27139c4c19c828cb1e30c349bba88e4d9b59be97286960793b5ddc0a2af0221008cdc7a951e7f31c20953ed5635fbabf228e80b7047f32faaa0313e7693005177012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64cffffffff883dcf9a86063db088ad064d0953258d4b0ff3425857402d2f3f839cee0f84581e0000006a4730440220426540dfed9c4ab5812e5f06df705b8bcf307dd7d20f7fa6512298b2a6314f420220064055096e3ca62f6c7352c66a5447767c53f946acdf35025ab3807ddb2fa404012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64cffffffff6697dbb3ed98afe481b568459fa67e503f8a4254532465a670e54669d19c9fe6720000006a47304402200a5e673996f2fc88e21cc8613611f08a650bc0370338803591d85d0ec5663764022040b6664a0d1ec83a7f01975b8fde5232992b8ca58bf48af6725d2f92a936ab2e012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64cffffffff023ffc2182517e1d3fa0896c5b0bd7b4d2ef8a1e42655abe2ced54f657125d59670000006c493046022100d93b30219c5735f673be5c3b4688366d96f545561c74cb62c6958c00f6960806022100ec8200adcb028f2184fa2a4f6faac7f8bb57cb4503bb7584ac11051fece31b3d012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adcffffffff16f8c77166b0df3d7cc8b5b2ce825afbea9309ad7acd8e2461a255958f81fc06010000006b483045022100a13934e68d3f5b22b130c4cb33f4da468cffc52323a47fbfbe06b64858162246022047081e0a70ff770e64a2e2d31e5d520d9102268b57a47009a72fe73ec766901801210234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cdffffffff197b96f3c87a3adfaa17f63fddc2a738a690ca665439f9431dbbd655816c41fb000000006c49304602210097f1f35d5bdc1a3a60390a1b015b8e7c4f916aa3847aafd969e04975e15bbe70022100a9052eb25517d481f1fda1b129eb1b534da50ea1a51f3ee012dca3601c11b86a0121027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34ffffffff20d9a261ee27aa1bd92e7db2fdca935909a40b648e974cd24a10d63b68b94039dd0000006b483045022012b3138c591bf7154b6fef457f2c4a3c7162225003788ac0024a99355865ff13022100b71b125ae1ffb2e1d1571f580cd3ebc8cd049a2d7a8a41f138ba94aeb982106f012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adcffffffff50f179d5d16cd872f9a63c26c448464ae9bd95cd9421c0476113b5d314571b71010000006b483045022100f834ccc8b22ee72712a3e5e6ef4acb8b2fb791b5385b70e2cd4332674d6667f4022024fbda0a997e0c253503f217501f508a4d56edce2c813ecdd9ad796dbeba907401210234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cdffffffff551b865d1568ac0a305e5f9c5dae6c540982334efbe789074318e0efc5b564631b0000006b48304502203b2fd1e39ae0e469d7a15768f262661b0de41470daf0fe8c4fd0c26542a0870002210081c57e331f9a2d214457d953e3542904727ee412c63028113635d7224da3dccc012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64cffffffff57503e5a016189d407a721791459280875264f908ca2c5d4862c01386e7fb50b470400006b48304502206947a9c54f0664ece4430fd4ae999891dc50bb6126bc36b6a15a3189f29d25e9022100a86cfc4e2fdd9e39a20e305cfd1b76509c67b3e313e0f118229105caa0e823c9012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64cffffffff3f16c1fb9d3e1a26d872933e955df85ee7f3f817711062b00b54a2144827349b250000006b483045022100c7128fe10b2d38744ae8177776054c29fc8ec13f07207723e70766ab7164847402201d2cf09009b9596de74c0183d1ab832e5edddb7a9965880bb400097e850850f8012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64cffffffff4142a69d85b8498af214f0dd427b6ab29c240a0b8577e2944d37a7d8c05c6bb8140000006b48304502203b89a71628a28cc3703d170ca3be77786cff6b867e38a18b719705f8a326578f022100b2a9879e1acf621faa6466c207746a7f3eb4c8514c1482969aba3f2a957f1321012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64cffffffff36e2feecc0a4bff7480015d42c12121932db389025ed0ac1d344ecee53230a3df20000006c493046022100ef794a8ef7fd6752d2a183c18866ff6e8dc0f5bd889a63e2c21cf303a6302461022100c1b09662d9e92988c3f9fcf17d1bcc79b5403647095d7212b9f8a1278a532d68012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adcffffffff0260f73608000000001976a9148fd139bb39ced713f231c58a4d07bf6954d1c20188ac41420f00000000001976a9146c772e9cf96371bba3da8cb733da70a2fcf2007888ac48d60400"
+}
diff --git a/src/test/data/tx_invalid.json b/src/test/data/tx_invalid.json
index f8baee0577..f7d9e1847f 100644
--- a/src/test/data/tx_invalid.json
+++ b/src/test/data/tx_invalid.json
@@ -314,5 +314,31 @@
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x60 0x21 0xff25429251b5a84f452230a3c75fd886b7fc5a7865ce4a7bb7a9d7c5be6da3dbff", 1000]],
"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e803000000000000015100000000", "P2SH,WITNESS,DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"],
+["FindAndDelete tests"],
+["This is a test of FindAndDelete. The first tx is a spend of normal scriptPubKey and the second tx is a spend of bare P2WSH."],
+["The redeemScript/witnessScript is CHECKSIGVERIFY <0x30450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01>."],
+["The signature is <0x30450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01> <pubkey>,"],
+["where the pubkey is obtained through key recovery with sig and the wrong sighash."],
+["This is to show that FindAndDelete is applied only to non-segwit scripts"],
+["To show that the tests are 'correctly wrong', they should pass by modifying OP_CHECKSIG under interpreter.cpp"],
+["by replacing (sigversion == SIGVERSION_BASE) with (sigversion != SIGVERSION_BASE)"],
+["Non-segwit: wrong sighash (without FindAndDelete) = 1ba1fe3bc90c5d1265460e684ce6774e324f0fabdf67619eda729e64e8b6bc08"],
+[[["f18783ace138abac5d3a7a5cf08e88fe6912f267ef936452e0c27d090621c169", 7000, "HASH160 0x14 0x0c746489e2d83cdbb5b90b432773342ba809c134 EQUAL", 200000]],
+"010000000169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f1581b0000b64830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e012103b12a1ec8428fc74166926318c15e17408fea82dbb157575e16a8c365f546248f4aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01ffffffff0101000000000000000000000000", "P2SH,WITNESS"],
+["BIP143: wrong sighash (with FindAndDelete) = 71c9cd9b2869b9c70b01b1f0360c148f42dee72297db312638df136f43311f23"],
+[[["f18783ace138abac5d3a7a5cf08e88fe6912f267ef936452e0c27d090621c169", 7500, "0x00 0x20 0x9e1be07558ea5cc8e02ed1d80c0911048afad949affa36d5c3951e3159dbea19", 200000]],
+"0100000000010169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f14c1d000000ffffffff01010000000000000000034830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e012102a9d7ed6e161f0e255c10bbfcca0128a9e2035c2c8da58899c54d22d3a31afdef4aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0100000000", "P2SH,WITNESS"],
+["This is multisig version of the FindAndDelete tests"],
+["Script is 2 CHECKMULTISIGVERIFY <sig1> <sig2> DROP"],
+["52af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960175"],
+["Signature is 0 <sig1> <sig2> 2 <key1> <key2>"],
+["Should pass by replacing (sigversion == SIGVERSION_BASE) with (sigversion != SIGVERSION_BASE) under OP_CHECKMULTISIG"],
+["Non-segwit: wrong sighash (without FindAndDelete) = 4bc6a53e8e16ef508c19e38bba08831daba85228b0211f323d4cb0999cf2a5e8"],
+[[["9628667ad48219a169b41b020800162287d2c0f713c04157e95c484a8dcb7592", 7000, "HASH160 0x14 0x5748407f5ca5cdca53ba30b79040260770c9ee1b EQUAL", 200000]],
+"01000000019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a662896581b0000fd6f01004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c039596015221023fd5dd42b44769c5653cbc5947ff30ab8871f240ad0c0e7432aefe84b5b4ff3421039d52178dbde360b83f19cf348deb04fa8360e1bf5634577be8e50fafc2b0e4ef4c9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960175ffffffff0101000000000000000000000000", "P2SH,WITNESS"],
+["BIP143: wrong sighash (with FindAndDelete) = 17c50ec2181ecdfdc85ca081174b248199ba81fff730794d4f69b8ec031f2dce"],
+[[["9628667ad48219a169b41b020800162287d2c0f713c04157e95c484a8dcb7592", 7500, "0x00 0x20 0x9b66c15b4e0b4eb49fa877982cafded24859fe5b0e2dbfbe4f0df1de7743fd52", 200000]],
+"010000000001019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a6628964c1d000000ffffffff0101000000000000000007004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c03959601010221023cb6055f4b57a1580c5a753e19610cafaedf7e0ff377731c77837fd666eae1712102c1b1db303ac232ffa8e5e7cc2cf5f96c6e40d3e6914061204c0541cb2043a0969552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c039596017500000000", "P2SH,WITNESS"],
+
["Make diffs cleaner by leaving a comment here without comma at the end"]
]
diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json
index 1ea70135b4..a3f47fcee2 100644
--- a/src/test/data/tx_valid.json
+++ b/src/test/data/tx_valid.json
@@ -53,7 +53,7 @@
["The following tests for the presence of a bug in the handling of SIGHASH_SINGLE"],
["It results in signing the constant 1, instead of something generated based on the transaction,"],
["when the input doing the signing has an index greater than the maximum output index"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0xe52b482f2faa8ecbf0db344f93c84ac908557f33 EQUALVERIFY CHECKSIG"], ["0000000000000000000000000000000000000000000000000000000000000200", 0, "1"]],
+[[["0000000000000000000000000000000000000000000000000000000000000200", 0, "1"], ["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0xe52b482f2faa8ecbf0db344f93c84ac908557f33 EQUALVERIFY CHECKSIG"]],
"01000000020002000000000000000000000000000000000000000000000000000000000000000000000151ffffffff0001000000000000000000000000000000000000000000000000000000000000000000006b483045022100c9cdd08798a28af9d1baf44a6c77bcc7e279f47dc487c8c899911bc48feaffcc0220503c5c50ae3998a733263c5c0f7061b483e2b56c4c41b456e7d2f5a78a74c077032102d5c25adb51b61339d2b05315791e21bbe80ea470a49db0135720983c905aace0ffffffff010000000000000000015100000000", "P2SH"],
["An invalid P2SH Transaction"],
@@ -334,9 +334,9 @@
"0100000000010100010000000000000000000000000000000000000000000000000000000000000000000023220020ff25429251b5a84f452230a3c75fd886b7fc5a7865ce4a7bb7a9d7c5be6da3dbffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100aa5d8aa40a90f23ce2c3d11bc845ca4a12acd99cbea37de6b9f6d86edebba8cb022022dedc2aa0a255f74d04c0b76ece2d7c691f9dd11a64a8ac49f62a99c3a05f9d01232103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ac00000000", "P2SH,WITNESS"],
["Witness with SigHash Single|AnyoneCanPay"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1100],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3100],
["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000],
-["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3100],
+["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1100],
["0000000000000000000000000000000000000000000000000000000000000100", 3, "0x51", 4100]],
"0100000000010400010000000000000000000000000000000000000000000000000000000000000200000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000300000000ffffffff05540b0000000000000151d0070000000000000151840300000000000001513c0f00000000000001512c010000000000000151000248304502210092f4777a0f17bf5aeb8ae768dec5f2c14feabf9d1fe2c89c78dfed0f13fdb86902206da90a86042e252bcd1e80a168c719e4a1ddcc3cebea24b9812c5453c79107e9832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71000000000000", "P2SH,WITNESS"],
@@ -359,9 +359,9 @@
"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b000000000000015100024730440220699e6b0cfe015b64ca3283e6551440a34f901ba62dd4c72fe1cb815afb2e6761022021cc5e84db498b1479de14efda49093219441adc6c543e5534979605e273d80b032103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "P2SH,WITNESS"],
["Witness with SigHash None|AnyoneCanPay"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1100],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3100],
+["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1100],
["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000],
-["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3100],
["0000000000000000000000000000000000000000000000000000000000000100", 3, "0x51", 4100]],
"0100000000010400010000000000000000000000000000000000000000000000000000000000000200000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000300000000ffffffff04b60300000000000001519e070000000000000151860b00000000000001009600000000000000015100000248304502210091b32274295c2a3fa02f5bce92fb2789e3fc6ea947fbe1a76e52ea3f4ef2381a022079ad72aefa3837a2e0c033a8652a59731da05fa4a813f4fc48e87c075037256b822103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "P2SH,WITNESS"],
@@ -390,9 +390,9 @@
"01000000000103000100000000000000000000000000000000000000000000000000000000000000000000000200000000010000000000000000000000000000000000000000000000000000000000000100000000ffffffff000100000000000000000000000000000000000000000000000000000000000002000000000200000003e8030000000000000151d0070000000000000151b80b00000000000001510002473044022022fceb54f62f8feea77faac7083c3b56c4676a78f93745adc8a35800bc36adfa022026927df9abcf0a8777829bcfcce3ff0a385fa54c3f9df577405e3ef24ee56479022103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "P2SH,WITNESS"],
["Witness with SigHash All|AnyoneCanPay"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1100],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3100],
+["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1100],
["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000],
-["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3100],
["0000000000000000000000000000000000000000000000000000000000000100", 3, "0x51", 4100]],
"0100000000010400010000000000000000000000000000000000000000000000000000000000000200000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000300000000ffffffff03e8030000000000000151d0070000000000000151b80b0000000000000151000002483045022100a3cec69b52cba2d2de623eeef89e0ba1606184ea55476c0f8189fda231bc9cbb022003181ad597f7c380a7d1c740286b1d022b8b04ded028b833282e055e03b8efef812103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "P2SH,WITNESS"],
@@ -458,8 +458,8 @@
"0100000000010200010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff02e8030000000000000151e90300000000000001510247304402206d59682663faab5e4cb733c562e22cdae59294895929ec38d7c016621ff90da0022063ef0af5f970afe8a45ea836e3509b8847ed39463253106ac17d19c437d3d56b832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710248304502210085001a820bfcbc9f9de0298af714493f8a37b3b354bfd21a7097c3e009f2018c022050a8b4dbc8155d4d04da2f5cdd575dcf8dd0108de8bec759bd897ea01ecb3af7832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7100000000", "P2SH,WITNESS"],
["Witness Single|AnyoneCanPay does not hash input's position (permutation)"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1000],
-["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1001]],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1001],
+["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1000]],
"0100000000010200010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff02e9030000000000000151e80300000000000001510248304502210085001a820bfcbc9f9de0298af714493f8a37b3b354bfd21a7097c3e009f2018c022050a8b4dbc8155d4d04da2f5cdd575dcf8dd0108de8bec759bd897ea01ecb3af7832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710247304402206d59682663faab5e4cb733c562e22cdae59294895929ec38d7c016621ff90da0022063ef0af5f970afe8a45ea836e3509b8847ed39463253106ac17d19c437d3d56b832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7100000000", "P2SH,WITNESS"],
["Non witness Single|AnyoneCanPay hash input's position"],
@@ -478,7 +478,7 @@
["1b2a9a426ba603ba357ce7773cb5805cb9c7c2b386d100d1fc9263513188e680", 0, "0x00 0x20 0xd9bbfbe56af7c4b7f960a70d7ea107156913d9e5a26b0a71429df5e097ca6537", 16777215]],
"01000000000102e9b542c5176808107ff1df906f46bb1f2583b16112b95ee5380665ba7fcfc0010000000000ffffffff80e68831516392fcd100d186b3c2c7b95c80b53c77e77c35ba03a66b429a2a1b0000000000ffffffff0280969800000000001976a914de4b231626ef508c9a74a8517e6783c0546d6b2888ac80969800000000001976a9146648a8cd4531e1ec47f35916de8e259237294d1e88ac02483045022100f6a10b8604e6dc910194b79ccfc93e1bc0ec7c03453caaa8987f7d6c3413566002206216229ede9b4d6ec2d325be245c5b508ff0339bf1794078e20bfe0babc7ffe683270063ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac024730440220032521802a76ad7bf74d0e2c218b72cf0cbc867066e2e53db905ba37f130397e02207709e2188ed7f08f4c952d9d13986da504502b8c3be59617e043552f506c46ff83275163ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac00000000", "P2SH,WITNESS"],
-["BIP143 example: Same as the previous example with input-output paris swapped"],
+["BIP143 example: Same as the previous example with input-output pairs swapped"],
[[["1b2a9a426ba603ba357ce7773cb5805cb9c7c2b386d100d1fc9263513188e680", 0, "0x00 0x20 0xd9bbfbe56af7c4b7f960a70d7ea107156913d9e5a26b0a71429df5e097ca6537", 16777215],
["01c0cf7fba650638e55eb91261b183251fbb466f90dff17f10086817c542b5e9", 0, "0x00 0x20 0xba468eea561b26301e4cf69fa34bde4ad60c81e70f059f045ca9a79931004a4d", 16777215]],
"0100000000010280e68831516392fcd100d186b3c2c7b95c80b53c77e77c35ba03a66b429a2a1b0000000000ffffffffe9b542c5176808107ff1df906f46bb1f2583b16112b95ee5380665ba7fcfc0010000000000ffffffff0280969800000000001976a9146648a8cd4531e1ec47f35916de8e259237294d1e88ac80969800000000001976a914de4b231626ef508c9a74a8517e6783c0546d6b2888ac024730440220032521802a76ad7bf74d0e2c218b72cf0cbc867066e2e53db905ba37f130397e02207709e2188ed7f08f4c952d9d13986da504502b8c3be59617e043552f506c46ff83275163ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac02483045022100f6a10b8604e6dc910194b79ccfc93e1bc0ec7c03453caaa8987f7d6c3413566002206216229ede9b4d6ec2d325be245c5b508ff0339bf1794078e20bfe0babc7ffe683270063ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac00000000", "P2SH,WITNESS"],
@@ -487,5 +487,28 @@
[[["6eb98797a21c6c10aa74edf29d618be109f48a8e94c694f3701e08ca69186436", 1, "HASH160 0x14 0x9993a429037b5d912407a71c252019287b8d27a5 EQUAL", 987654321]],
"0100000000010136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000023220020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac080047304402206ac44d672dac41f9b00e28f4df20c52eeb087207e8d758d76d92c6fab3b73e2b0220367750dbbe19290069cba53d096f44530e4f98acaa594810388cf7409a1870ce01473044022068c7946a43232757cbdf9176f009a928e1cd9a1a8c212f15c1e11ac9f2925d9002205b75f937ff2f9f3c1246e547e54f62e027f64eefa2695578cc6432cdabce271502473044022059ebf56d98010a932cf8ecfec54c48e6139ed6adb0728c09cbe1e4fa0915302e022007cd986c8fa870ff5d2b3a89139c9fe7e499259875357e20fcbb15571c76795403483045022100fbefd94bd0a488d50b79102b5dad4ab6ced30c4069f1eaa69a4b5a763414067e02203156c6a5c9cf88f91265f5a942e96213afae16d83321c8b31bb342142a14d16381483045022100a5263ea0553ba89221984bd7f0b13613db16e7a70c549a86de0cc0444141a407022005c360ef0ae5a5d4f9f2f87a56c1546cc8268cab08c73501d6b3be2e1e1a8a08824730440220525406a1482936d5a21888260dc165497a90a15669636d8edca6b9fe490d309c022032af0c646a34a44d1f4576bf6a4a74b67940f8faa84c7df9abe12a01a11e2b4783cf56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae00000000", "P2SH,WITNESS"],
+["FindAndDelete tests"],
+["This is a test of FindAndDelete. The first tx is a spend of normal P2SH and the second tx is a spend of bare P2WSH."],
+["The redeemScript/witnessScript is CHECKSIGVERIFY <0x30450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01>."],
+["The signature is <0x30450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01> <pubkey>,"],
+["where the pubkey is obtained through key recovery with sig and correct sighash."],
+["This is to show that FindAndDelete is applied only to non-segwit scripts"],
+["Non-segwit: correct sighash (with FindAndDelete) = 1ba1fe3bc90c5d1265460e684ce6774e324f0fabdf67619eda729e64e8b6bc08"],
+[[["f18783ace138abac5d3a7a5cf08e88fe6912f267ef936452e0c27d090621c169", 7000, "HASH160 0x14 0x0c746489e2d83cdbb5b90b432773342ba809c134 EQUAL", 200000]],
+"010000000169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f1581b0000b64830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0121037a3fb04bcdb09eba90f69961ba1692a3528e45e67c85b200df820212d7594d334aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01ffffffff0101000000000000000000000000", "P2SH,WITNESS"],
+["BIP143: correct sighash (without FindAndDelete) = 71c9cd9b2869b9c70b01b1f0360c148f42dee72297db312638df136f43311f23"],
+[[["f18783ace138abac5d3a7a5cf08e88fe6912f267ef936452e0c27d090621c169", 7500, "0x00 0x20 0x9e1be07558ea5cc8e02ed1d80c0911048afad949affa36d5c3951e3159dbea19", 200000]],
+"0100000000010169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f14c1d000000ffffffff01010000000000000000034830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e012102a9781d66b61fb5a7ef00ac5ad5bc6ffc78be7b44a566e3c87870e1079368df4c4aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0100000000", "P2SH,WITNESS"],
+["This is multisig version of the FindAndDelete tests"],
+["Script is 2 CHECKMULTISIGVERIFY <sig1> <sig2> DROP"],
+["52af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960175"],
+["Signature is 0 <sig1> <sig2> 2 <key1> <key2>"],
+["Non-segwit: correct sighash (with FindAndDelete) = 1d50f00ba4db2917b903b0ec5002e017343bb38876398c9510570f5dce099295"],
+[[["9628667ad48219a169b41b020800162287d2c0f713c04157e95c484a8dcb7592", 7000, "HASH160 0x14 0x5748407f5ca5cdca53ba30b79040260770c9ee1b EQUAL", 200000]],
+"01000000019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a662896581b0000fd6f01004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c03959601522102cd74a2809ffeeed0092bc124fd79836706e41f048db3f6ae9df8708cefb83a1c2102e615999372426e46fd107b76eaf007156a507584aa2cc21de9eee3bdbd26d36c4c9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960175ffffffff0101000000000000000000000000", "P2SH,WITNESS"],
+["BIP143: correct sighash (without FindAndDelete) = c1628a1e7c67f14ca0c27c06e4fdeec2e6d1a73c7a91d7c046ff83e835aebb72"],
+[[["9628667ad48219a169b41b020800162287d2c0f713c04157e95c484a8dcb7592", 7500, "0x00 0x20 0x9b66c15b4e0b4eb49fa877982cafded24859fe5b0e2dbfbe4f0df1de7743fd52", 200000]],
+"010000000001019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a6628964c1d000000ffffffff0101000000000000000007004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960101022102966f109c54e85d3aee8321301136cedeb9fc710fdef58a9de8a73942f8e567c021034ffc99dd9a79dd3cb31e2ab3e0b09e0e67db41ac068c625cd1f491576016c84e9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c039596017500000000", "P2SH,WITNESS"],
+
["Make diffs cleaner by leaving a comment here without comma at the end"]
]
diff --git a/src/test/data/txcreate1.hex b/src/test/data/txcreate1.hex
index e2981a51c9..9ec6ee3531 100644
--- a/src/test/data/txcreate1.hex
+++ b/src/test/data/txcreate1.hex
@@ -1 +1 @@
-01000000031f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000ffffffff7cca453133921c50d5025878f7f738d1df891fd359763331935784cf6b9c82bf1200000000fffffffffccd319e04a996c96cfc0bf4c07539aa90bd0b1a700ef72fae535d6504f9a6220100000000ffffffff0280a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac0084d717000000001976a914f2d4db28cad6502226ee484ae24505c2885cb12d88ac00000000
+02000000031f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000ffffffff7cca453133921c50d5025878f7f738d1df891fd359763331935784cf6b9c82bf1200000000fffffffffccd319e04a996c96cfc0bf4c07539aa90bd0b1a700ef72fae535d6504f9a6220100000000ffffffff0280a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac0084d717000000001976a914f2d4db28cad6502226ee484ae24505c2885cb12d88ac00000000
diff --git a/src/test/data/txcreate1.json b/src/test/data/txcreate1.json
new file mode 100644
index 0000000000..f83e036f33
--- /dev/null
+++ b/src/test/data/txcreate1.json
@@ -0,0 +1,64 @@
+{
+ "txid": "fe7d174f42dce0cffa7a527e9bc8368956057619ec817648f6138b98f2533e8f",
+ "hash": "fe7d174f42dce0cffa7a527e9bc8368956057619ec817648f6138b98f2533e8f",
+ "version": 2,
+ "locktime": 0,
+ "vin": [
+ {
+ "txid": "5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f",
+ "vout": 0,
+ "scriptSig": {
+ "asm": "",
+ "hex": ""
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "bf829c6bcf84579331337659d31f89dfd138f7f7785802d5501c92333145ca7c",
+ "vout": 18,
+ "scriptSig": {
+ "asm": "",
+ "hex": ""
+ },
+ "sequence": 4294967295
+ },
+ {
+ "txid": "22a6f904655d53ae2ff70e701a0bbd90aa3975c0f40bfc6cc996a9049e31cdfc",
+ "vout": 1,
+ "scriptSig": {
+ "asm": "",
+ "hex": ""
+ },
+ "sequence": 4294967295
+ }
+ ],
+ "vout": [
+ {
+ "value": 0.18,
+ "n": 0,
+ "scriptPubKey": {
+ "asm": "OP_DUP OP_HASH160 1fc11f39be1729bf973a7ab6a615ca4729d64574 OP_EQUALVERIFY OP_CHECKSIG",
+ "hex": "76a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac",
+ "reqSigs": 1,
+ "type": "pubkeyhash",
+ "addresses": [
+ "13tuJJDR2RgArmgfv6JScSdreahzgc4T6o"
+ ]
+ }
+ },
+ {
+ "value": 4.00,
+ "n": 1,
+ "scriptPubKey": {
+ "asm": "OP_DUP OP_HASH160 f2d4db28cad6502226ee484ae24505c2885cb12d OP_EQUALVERIFY OP_CHECKSIG",
+ "hex": "76a914f2d4db28cad6502226ee484ae24505c2885cb12d88ac",
+ "reqSigs": 1,
+ "type": "pubkeyhash",
+ "addresses": [
+ "1P8yWvZW8jVihP1bzHeqfE4aoXNX8AVa46"
+ ]
+ }
+ }
+ ],
+ "hex": "02000000031f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000ffffffff7cca453133921c50d5025878f7f738d1df891fd359763331935784cf6b9c82bf1200000000fffffffffccd319e04a996c96cfc0bf4c07539aa90bd0b1a700ef72fae535d6504f9a6220100000000ffffffff0280a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac0084d717000000001976a914f2d4db28cad6502226ee484ae24505c2885cb12d88ac00000000"
+}
diff --git a/src/test/data/txcreate2.hex b/src/test/data/txcreate2.hex
index 5243c2d02e..38bb7b1046 100644
--- a/src/test/data/txcreate2.hex
+++ b/src/test/data/txcreate2.hex
@@ -1 +1 @@
-01000000000100000000000000000000000000
+02000000000100000000000000000000000000
diff --git a/src/test/data/txcreate2.json b/src/test/data/txcreate2.json
new file mode 100644
index 0000000000..fb5e177db7
--- /dev/null
+++ b/src/test/data/txcreate2.json
@@ -0,0 +1,20 @@
+{
+ "txid": "0481afb29931341d0d7861d8a2f6f26456fa042abf54a23e96440ed7946e0715",
+ "hash": "0481afb29931341d0d7861d8a2f6f26456fa042abf54a23e96440ed7946e0715",
+ "version": 2,
+ "locktime": 0,
+ "vin": [
+ ],
+ "vout": [
+ {
+ "value": 0.00,
+ "n": 0,
+ "scriptPubKey": {
+ "asm": "",
+ "hex": "",
+ "type": "nonstandard"
+ }
+ }
+ ],
+ "hex": "02000000000100000000000000000000000000"
+}
diff --git a/src/test/data/txcreatedata1.hex b/src/test/data/txcreatedata1.hex
index eccc7604e6..cefd1a05a6 100644
--- a/src/test/data/txcreatedata1.hex
+++ b/src/test/data/txcreatedata1.hex
@@ -1 +1 @@
-01000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000ffffffff0280a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac0084d71700000000526a4c4f54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e00000000
+02000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000ffffffff0280a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac0084d71700000000526a4c4f54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e00000000
diff --git a/src/test/data/txcreatedata1.json b/src/test/data/txcreatedata1.json
new file mode 100644
index 0000000000..760518d30a
--- /dev/null
+++ b/src/test/data/txcreatedata1.json
@@ -0,0 +1,42 @@
+{
+ "txid": "07894b4d12fe7853dd911402db1620920d261b9627c447f931417d330c25f06e",
+ "hash": "07894b4d12fe7853dd911402db1620920d261b9627c447f931417d330c25f06e",
+ "version": 1,
+ "locktime": 0,
+ "vin": [
+ {
+ "txid": "5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f",
+ "vout": 0,
+ "scriptSig": {
+ "asm": "",
+ "hex": ""
+ },
+ "sequence": 4294967295
+ }
+ ],
+ "vout": [
+ {
+ "value": 0.18,
+ "n": 0,
+ "scriptPubKey": {
+ "asm": "OP_DUP OP_HASH160 1fc11f39be1729bf973a7ab6a615ca4729d64574 OP_EQUALVERIFY OP_CHECKSIG",
+ "hex": "76a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac",
+ "reqSigs": 1,
+ "type": "pubkeyhash",
+ "addresses": [
+ "13tuJJDR2RgArmgfv6JScSdreahzgc4T6o"
+ ]
+ }
+ },
+ {
+ "value": 4.00,
+ "n": 1,
+ "scriptPubKey": {
+ "asm": "OP_RETURN 54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e",
+ "hex": "6a4c4f54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e",
+ "type": "nulldata"
+ }
+ }
+ ],
+ "hex": "01000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000ffffffff0280a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac0084d71700000000526a4c4f54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e00000000"
+}
diff --git a/src/test/data/txcreatedata2.hex b/src/test/data/txcreatedata2.hex
index 3c7644c297..d69cf58ba1 100644
--- a/src/test/data/txcreatedata2.hex
+++ b/src/test/data/txcreatedata2.hex
@@ -1 +1 @@
-01000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000ffffffff0280a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac0000000000000000526a4c4f54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e00000000
+02000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000ffffffff0280a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac0000000000000000526a4c4f54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e00000000
diff --git a/src/test/data/txcreatedata2.json b/src/test/data/txcreatedata2.json
new file mode 100644
index 0000000000..3c6da40f90
--- /dev/null
+++ b/src/test/data/txcreatedata2.json
@@ -0,0 +1,42 @@
+{
+ "txid": "c14b007fa3a6c1e7765919c1d14c1cfc2b8642c3a5d3be4b1fa8c4ccfec98bb0",
+ "hash": "c14b007fa3a6c1e7765919c1d14c1cfc2b8642c3a5d3be4b1fa8c4ccfec98bb0",
+ "version": 2,
+ "locktime": 0,
+ "vin": [
+ {
+ "txid": "5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f",
+ "vout": 0,
+ "scriptSig": {
+ "asm": "",
+ "hex": ""
+ },
+ "sequence": 4294967295
+ }
+ ],
+ "vout": [
+ {
+ "value": 0.18,
+ "n": 0,
+ "scriptPubKey": {
+ "asm": "OP_DUP OP_HASH160 1fc11f39be1729bf973a7ab6a615ca4729d64574 OP_EQUALVERIFY OP_CHECKSIG",
+ "hex": "76a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac",
+ "reqSigs": 1,
+ "type": "pubkeyhash",
+ "addresses": [
+ "13tuJJDR2RgArmgfv6JScSdreahzgc4T6o"
+ ]
+ }
+ },
+ {
+ "value": 0.00,
+ "n": 1,
+ "scriptPubKey": {
+ "asm": "OP_RETURN 54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e",
+ "hex": "6a4c4f54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e",
+ "type": "nulldata"
+ }
+ }
+ ],
+ "hex": "02000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000ffffffff0280a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac0000000000000000526a4c4f54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e00000000"
+}
diff --git a/src/test/data/txcreatedata_seq0.hex b/src/test/data/txcreatedata_seq0.hex
index db02b5e4a4..54b89d2381 100644
--- a/src/test/data/txcreatedata_seq0.hex
+++ b/src/test/data/txcreatedata_seq0.hex
@@ -1 +1 @@
-01000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000fdffffff0180a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac00000000
+02000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000fdffffff0180a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac00000000
diff --git a/src/test/data/txcreatedata_seq0.json b/src/test/data/txcreatedata_seq0.json
new file mode 100644
index 0000000000..d272a4c447
--- /dev/null
+++ b/src/test/data/txcreatedata_seq0.json
@@ -0,0 +1,33 @@
+{
+ "txid": "8df6ed527472542dd5e137c242a7c5a9f337ac34f7b257ae4af886aeaebb51b0",
+ "hash": "8df6ed527472542dd5e137c242a7c5a9f337ac34f7b257ae4af886aeaebb51b0",
+ "version": 2,
+ "locktime": 0,
+ "vin": [
+ {
+ "txid": "5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f",
+ "vout": 0,
+ "scriptSig": {
+ "asm": "",
+ "hex": ""
+ },
+ "sequence": 4294967293
+ }
+ ],
+ "vout": [
+ {
+ "value": 0.18,
+ "n": 0,
+ "scriptPubKey": {
+ "asm": "OP_DUP OP_HASH160 1fc11f39be1729bf973a7ab6a615ca4729d64574 OP_EQUALVERIFY OP_CHECKSIG",
+ "hex": "76a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac",
+ "reqSigs": 1,
+ "type": "pubkeyhash",
+ "addresses": [
+ "13tuJJDR2RgArmgfv6JScSdreahzgc4T6o"
+ ]
+ }
+ }
+ ],
+ "hex": "02000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000fdffffff0180a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac00000000"
+}
diff --git a/src/test/data/txcreatedata_seq1.json b/src/test/data/txcreatedata_seq1.json
new file mode 100644
index 0000000000..d323255418
--- /dev/null
+++ b/src/test/data/txcreatedata_seq1.json
@@ -0,0 +1,42 @@
+{
+ "txid": "c4dea671b0d7b48f8ab10bc46650e8329d3c5766931f548f513847a19f5ba75b",
+ "hash": "c4dea671b0d7b48f8ab10bc46650e8329d3c5766931f548f513847a19f5ba75b",
+ "version": 1,
+ "locktime": 0,
+ "vin": [
+ {
+ "txid": "5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f",
+ "vout": 0,
+ "scriptSig": {
+ "asm": "",
+ "hex": ""
+ },
+ "sequence": 4294967293
+ },
+ {
+ "txid": "5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f",
+ "vout": 0,
+ "scriptSig": {
+ "asm": "",
+ "hex": ""
+ },
+ "sequence": 1
+ }
+ ],
+ "vout": [
+ {
+ "value": 0.18,
+ "n": 0,
+ "scriptPubKey": {
+ "asm": "OP_DUP OP_HASH160 1fc11f39be1729bf973a7ab6a615ca4729d64574 OP_EQUALVERIFY OP_CHECKSIG",
+ "hex": "76a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac",
+ "reqSigs": 1,
+ "type": "pubkeyhash",
+ "addresses": [
+ "13tuJJDR2RgArmgfv6JScSdreahzgc4T6o"
+ ]
+ }
+ }
+ ],
+ "hex": "01000000021f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000fdffffff1f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000010000000180a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac00000000"
+}
diff --git a/src/test/data/txcreatesign.hex b/src/test/data/txcreatesignv1.hex
index a46fcc88cb..a46fcc88cb 100644
--- a/src/test/data/txcreatesign.hex
+++ b/src/test/data/txcreatesignv1.hex
diff --git a/src/test/data/txcreatesignv1.json b/src/test/data/txcreatesignv1.json
new file mode 100644
index 0000000000..ff39e71b40
--- /dev/null
+++ b/src/test/data/txcreatesignv1.json
@@ -0,0 +1,33 @@
+{
+ "txid": "977e7cd286cb72cd470d539ba6cb48400f8f387d97451d45cdb8819437a303af",
+ "hash": "977e7cd286cb72cd470d539ba6cb48400f8f387d97451d45cdb8819437a303af",
+ "version": 1,
+ "locktime": 0,
+ "vin": [
+ {
+ "txid": "4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485",
+ "vout": 0,
+ "scriptSig": {
+ "asm": "304502210096a75056c9e2cc62b7214777b3d2a592cfda7092520126d4ebfcd6d590c99bd8022051bb746359cf98c0603f3004477eac68701132380db8facba19c89dc5ab5c5e2[ALL] 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8",
+ "hex": "48304502210096a75056c9e2cc62b7214777b3d2a592cfda7092520126d4ebfcd6d590c99bd8022051bb746359cf98c0603f3004477eac68701132380db8facba19c89dc5ab5c5e201410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8"
+ },
+ "sequence": 4294967295
+ }
+ ],
+ "vout": [
+ {
+ "value": 0.001,
+ "n": 0,
+ "scriptPubKey": {
+ "asm": "OP_DUP OP_HASH160 5834479edbbe0539b31ffd3a8f8ebadc2165ed01 OP_EQUALVERIFY OP_CHECKSIG",
+ "hex": "76a9145834479edbbe0539b31ffd3a8f8ebadc2165ed0188ac",
+ "reqSigs": 1,
+ "type": "pubkeyhash",
+ "addresses": [
+ "193P6LtvS4nCnkDvM9uXn1gsSRqh4aDAz7"
+ ]
+ }
+ }
+ ],
+ "hex": "01000000018594c5bdcaec8f06b78b596f31cd292a294fd031e24eec716f43dac91ea7494d000000008b48304502210096a75056c9e2cc62b7214777b3d2a592cfda7092520126d4ebfcd6d590c99bd8022051bb746359cf98c0603f3004477eac68701132380db8facba19c89dc5ab5c5e201410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ffffffff01a0860100000000001976a9145834479edbbe0539b31ffd3a8f8ebadc2165ed0188ac00000000"
+}
diff --git a/src/test/data/txcreatesignv2.hex b/src/test/data/txcreatesignv2.hex
new file mode 100644
index 0000000000..ee425cd98c
--- /dev/null
+++ b/src/test/data/txcreatesignv2.hex
@@ -0,0 +1 @@
+02000000018594c5bdcaec8f06b78b596f31cd292a294fd031e24eec716f43dac91ea7494d000000008a473044022079c7aa014177a2e973caf6df7c7b8f15399083b91eba370ea1e19c4caed9181e02205f8f8763505ce8e6cbdd2cd28fab3fd407a75003e7d0dc04e6bebb0a3c89e7cb01410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ffffffff01a0860100000000001976a9145834479edbbe0539b31ffd3a8f8ebadc2165ed0188ac00000000
diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp
index d4d825d199..2d791ee18d 100644
--- a/src/test/dbwrapper_tests.cpp
+++ b/src/test/dbwrapper_tests.cpp
@@ -254,7 +254,7 @@ struct StringContentsSerializer {
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
if (ser_action.ForRead()) {
str.clear();
char c = 0;
diff --git a/src/test/hash_tests.cpp b/src/test/hash_tests.cpp
index fa9624f13d..8be9c580c4 100644
--- a/src/test/hash_tests.cpp
+++ b/src/test/hash_tests.cpp
@@ -124,7 +124,11 @@ BOOST_AUTO_TEST_CASE(siphash)
}
CHashWriter ss(SER_DISK, CLIENT_VERSION);
- ss << CTransaction();
+ CMutableTransaction tx;
+ // Note these tests were originally written with tx.nVersion=1
+ // and the test would be affected by default tx version bumps if not fixed.
+ tx.nVersion = 1;
+ ss << tx;
BOOST_CHECK_EQUAL(SipHashUint256(1, 2, ss.GetHash()), 0x79751e980c2a0a35ULL);
}
diff --git a/src/test/limitedmap_tests.cpp b/src/test/limitedmap_tests.cpp
index faaddffad8..e3531486aa 100644
--- a/src/test/limitedmap_tests.cpp
+++ b/src/test/limitedmap_tests.cpp
@@ -47,7 +47,7 @@ BOOST_AUTO_TEST_CASE(limitedmap_test)
// make sure the item is present
BOOST_CHECK(map.count(i) == 1);
- // use the iterator to check for the expected key adn value
+ // use the iterator to check for the expected key and value
BOOST_CHECK(it->first == i);
BOOST_CHECK(it->second == i + 1);
diff --git a/src/test/main_tests.cpp b/src/test/main_tests.cpp
index dbfbdd934f..697fbf792c 100644
--- a/src/test/main_tests.cpp
+++ b/src/test/main_tests.cpp
@@ -3,7 +3,8 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "chainparams.h"
-#include "main.h"
+#include "validation.h"
+#include "net.h"
#include "test/test_bitcoin.h"
diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp
index 033a50f94f..84871600b2 100644
--- a/src/test/mempool_tests.cpp
+++ b/src/test/mempool_tests.cpp
@@ -55,17 +55,17 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest)
CTxMemPool testPool(CFeeRate(0));
- std::list<CTransaction> removed;
// Nothing in pool, remove should do nothing:
- testPool.removeRecursive(txParent, removed);
- BOOST_CHECK_EQUAL(removed.size(), 0);
+ unsigned int poolSize = testPool.size();
+ testPool.removeRecursive(txParent);
+ BOOST_CHECK_EQUAL(testPool.size(), poolSize);
// Just the parent:
testPool.addUnchecked(txParent.GetHash(), entry.FromTx(txParent));
- testPool.removeRecursive(txParent, removed);
- BOOST_CHECK_EQUAL(removed.size(), 1);
- removed.clear();
+ poolSize = testPool.size();
+ testPool.removeRecursive(txParent);
+ BOOST_CHECK_EQUAL(testPool.size(), poolSize - 1);
// Parent, children, grandchildren:
testPool.addUnchecked(txParent.GetHash(), entry.FromTx(txParent));
@@ -75,19 +75,21 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest)
testPool.addUnchecked(txGrandChild[i].GetHash(), entry.FromTx(txGrandChild[i]));
}
// Remove Child[0], GrandChild[0] should be removed:
- testPool.removeRecursive(txChild[0], removed);
- BOOST_CHECK_EQUAL(removed.size(), 2);
- removed.clear();
+ poolSize = testPool.size();
+ testPool.removeRecursive(txChild[0]);
+ BOOST_CHECK_EQUAL(testPool.size(), poolSize - 2);
// ... make sure grandchild and child are gone:
- testPool.removeRecursive(txGrandChild[0], removed);
- BOOST_CHECK_EQUAL(removed.size(), 0);
- testPool.removeRecursive(txChild[0], removed);
- BOOST_CHECK_EQUAL(removed.size(), 0);
+ poolSize = testPool.size();
+ testPool.removeRecursive(txGrandChild[0]);
+ BOOST_CHECK_EQUAL(testPool.size(), poolSize);
+ poolSize = testPool.size();
+ testPool.removeRecursive(txChild[0]);
+ BOOST_CHECK_EQUAL(testPool.size(), poolSize);
// Remove parent, all children/grandchildren should go:
- testPool.removeRecursive(txParent, removed);
- BOOST_CHECK_EQUAL(removed.size(), 5);
+ poolSize = testPool.size();
+ testPool.removeRecursive(txParent);
+ BOOST_CHECK_EQUAL(testPool.size(), poolSize - 5);
BOOST_CHECK_EQUAL(testPool.size(), 0);
- removed.clear();
// Add children and grandchildren, but NOT the parent (simulate the parent being in a block)
for (int i = 0; i < 3; i++)
@@ -97,10 +99,10 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest)
}
// Now remove the parent, as might happen if a block-re-org occurs but the parent cannot be
// put into the mempool (maybe because it is non-standard):
- testPool.removeRecursive(txParent, removed);
- BOOST_CHECK_EQUAL(removed.size(), 6);
+ poolSize = testPool.size();
+ testPool.removeRecursive(txParent);
+ BOOST_CHECK_EQUAL(testPool.size(), poolSize - 6);
BOOST_CHECK_EQUAL(testPool.size(), 0);
- removed.clear();
}
template<typename name>
@@ -281,12 +283,11 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
BOOST_CHECK_EQUAL(pool.size(), 10);
// Now try removing tx10 and verify the sort order returns to normal
- std::list<CTransaction> removed;
- pool.removeRecursive(pool.mapTx.find(tx10.GetHash())->GetTx(), removed);
+ pool.removeRecursive(pool.mapTx.find(tx10.GetHash())->GetTx());
CheckSort<descendant_score>(pool, snapshotOrder);
- pool.removeRecursive(pool.mapTx.find(tx9.GetHash())->GetTx(), removed);
- pool.removeRecursive(pool.mapTx.find(tx8.GetHash())->GetTx(), removed);
+ pool.removeRecursive(pool.mapTx.find(tx9.GetHash())->GetTx());
+ pool.removeRecursive(pool.mapTx.find(tx8.GetHash())->GetTx());
/* Now check the sort on the mining score index.
* Final order should be:
*
@@ -389,7 +390,12 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
pool.addUnchecked(tx6.GetHash(), entry.Fee(0LL).FromTx(tx6));
BOOST_CHECK_EQUAL(pool.size(), 6);
- sortedOrder.push_back(tx6.GetHash().ToString());
+ // Ties are broken by hash
+ if (tx3.GetHash() < tx6.GetHash())
+ sortedOrder.push_back(tx6.GetHash().ToString());
+ else
+ sortedOrder.insert(sortedOrder.end()-1,tx6.GetHash().ToString());
+
CheckSort<ancestor_score>(pool, sortedOrder);
CMutableTransaction tx7 = CMutableTransaction();
@@ -411,13 +417,16 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
CheckSort<ancestor_score>(pool, sortedOrder);
/* after tx6 is mined, tx7 should move up in the sort */
- std::vector<CTransaction> vtx;
- vtx.push_back(tx6);
- std::list<CTransaction> dummy;
- pool.removeForBlock(vtx, 1, dummy, false);
+ std::vector<CTransactionRef> vtx;
+ vtx.push_back(MakeTransactionRef(tx6));
+ pool.removeForBlock(vtx, 1);
sortedOrder.erase(sortedOrder.begin()+1);
- sortedOrder.pop_back();
+ // Ties are broken by hash
+ if (tx3.GetHash() < tx6.GetHash())
+ sortedOrder.pop_back();
+ else
+ sortedOrder.erase(sortedOrder.end()-2);
sortedOrder.insert(sortedOrder.begin(), tx7.GetHash().ToString());
CheckSort<ancestor_score>(pool, sortedOrder);
}
@@ -548,13 +557,12 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
pool.addUnchecked(tx5.GetHash(), entry.Fee(1000LL).FromTx(tx5, &pool));
pool.addUnchecked(tx7.GetHash(), entry.Fee(9000LL).FromTx(tx7, &pool));
- std::vector<CTransaction> vtx;
- std::list<CTransaction> conflicts;
+ std::vector<CTransactionRef> vtx;
SetMockTime(42);
SetMockTime(42 + CTxMemPool::ROLLING_FEE_HALFLIFE);
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000);
// ... we should keep the same min fee until we get a block
- pool.removeForBlock(vtx, 1, conflicts);
+ pool.removeForBlock(vtx, 1);
SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE);
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/2);
// ... then feerate should drop 1/2 each halflife
diff --git a/src/test/merkle_tests.cpp b/src/test/merkle_tests.cpp
index b40ab848dc..55e6852a15 100644
--- a/src/test/merkle_tests.cpp
+++ b/src/test/merkle_tests.cpp
@@ -4,7 +4,7 @@
#include "consensus/merkle.h"
#include "test/test_bitcoin.h"
-#include "random.h"
+#include "test/test_random.h"
#include <boost/test/unit_test.hpp>
@@ -15,8 +15,8 @@ static uint256 BlockBuildMerkleTree(const CBlock& block, bool* fMutated, std::ve
{
vMerkleTree.clear();
vMerkleTree.reserve(block.vtx.size() * 2 + 16); // Safe upper bound for the number of total nodes.
- for (std::vector<CTransaction>::const_iterator it(block.vtx.begin()); it != block.vtx.end(); ++it)
- vMerkleTree.push_back(it->GetHash());
+ for (std::vector<CTransactionRef>::const_iterator it(block.vtx.begin()); it != block.vtx.end(); ++it)
+ vMerkleTree.push_back((*it)->GetHash());
int j = 0;
bool mutated = false;
for (int nSize = block.vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
@@ -86,7 +86,7 @@ BOOST_AUTO_TEST_CASE(merkle_test)
for (int j = 0; j < ntx; j++) {
CMutableTransaction mtx;
mtx.nLockTime = j;
- block.vtx[j] = mtx;
+ block.vtx[j] = MakeTransactionRef(std::move(mtx));
}
// Compute the root of the block before mutating it.
bool unmutatedMutated = false;
@@ -126,7 +126,7 @@ BOOST_AUTO_TEST_CASE(merkle_test)
std::vector<uint256> newBranch = BlockMerkleBranch(block, mtx);
std::vector<uint256> oldBranch = BlockGetMerkleBranch(block, merkleTree, mtx);
BOOST_CHECK(oldBranch == newBranch);
- BOOST_CHECK(ComputeMerkleRootFromBranch(block.vtx[mtx].GetHash(), newBranch, mtx) == oldRoot);
+ BOOST_CHECK(ComputeMerkleRootFromBranch(block.vtx[mtx]->GetHash(), newBranch, mtx) == oldRoot);
}
}
}
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index d3aa2364d1..892e731a7a 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -7,7 +7,7 @@
#include "consensus/consensus.h"
#include "consensus/merkle.h"
#include "consensus/validation.h"
-#include "main.h"
+#include "validation.h"
#include "miner.h"
#include "pubkey.h"
#include "script/standard.h"
@@ -18,6 +18,8 @@
#include "test/test_bitcoin.h"
+#include <memory>
+
#include <boost/test/unit_test.hpp>
BOOST_FIXTURE_TEST_SUITE(miner_tests, TestingSetup)
@@ -75,7 +77,7 @@ bool TestSequenceLocks(const CTransaction &tx, int flags)
// Implemented as an additional function, rather than a separate test case,
// to allow reusing the blockchain created in CreateNewBlock_validity.
// Note that this test assumes blockprioritysize is 0.
-void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey, std::vector<CTransaction *>& txFirst)
+void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey, std::vector<CTransactionRef>& txFirst)
{
// Test the ancestor feerate transaction selection.
TestMemPoolEntryHelper entry;
@@ -105,10 +107,10 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey,
uint256 hashHighFeeTx = tx.GetHash();
mempool.addUnchecked(hashHighFeeTx, entry.Fee(50000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
- CBlockTemplate *pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey);
- BOOST_CHECK(pblocktemplate->block.vtx[1].GetHash() == hashParentTx);
- BOOST_CHECK(pblocktemplate->block.vtx[2].GetHash() == hashHighFeeTx);
- BOOST_CHECK(pblocktemplate->block.vtx[3].GetHash() == hashMediumFeeTx);
+ std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey);
+ BOOST_CHECK(pblocktemplate->block.vtx[1]->GetHash() == hashParentTx);
+ BOOST_CHECK(pblocktemplate->block.vtx[2]->GetHash() == hashHighFeeTx);
+ BOOST_CHECK(pblocktemplate->block.vtx[3]->GetHash() == hashMediumFeeTx);
// Test that a package below the min relay fee doesn't get included
tx.vin[0].prevout.hash = hashHighFeeTx;
@@ -128,21 +130,20 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey,
pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey);
// Verify that the free tx and the low fee tx didn't get selected
for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) {
- BOOST_CHECK(pblocktemplate->block.vtx[i].GetHash() != hashFreeTx);
- BOOST_CHECK(pblocktemplate->block.vtx[i].GetHash() != hashLowFeeTx);
+ BOOST_CHECK(pblocktemplate->block.vtx[i]->GetHash() != hashFreeTx);
+ BOOST_CHECK(pblocktemplate->block.vtx[i]->GetHash() != hashLowFeeTx);
}
// Test that packages above the min relay fee do get included, even if one
// of the transactions is below the min relay fee
// Remove the low fee transaction and replace with a higher fee transaction
- std::list<CTransaction> dummy;
- mempool.removeRecursive(tx, dummy);
+ mempool.removeRecursive(tx);
tx.vout[0].nValue -= 2; // Now we should be just over the min relay fee
hashLowFeeTx = tx.GetHash();
mempool.addUnchecked(hashLowFeeTx, entry.Fee(feeToUse+2).FromTx(tx));
pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey);
- BOOST_CHECK(pblocktemplate->block.vtx[4].GetHash() == hashFreeTx);
- BOOST_CHECK(pblocktemplate->block.vtx[5].GetHash() == hashLowFeeTx);
+ BOOST_CHECK(pblocktemplate->block.vtx[4]->GetHash() == hashFreeTx);
+ BOOST_CHECK(pblocktemplate->block.vtx[5]->GetHash() == hashLowFeeTx);
// Test that transaction selection properly updates ancestor fee
// calculations as ancestor transactions get included in a block.
@@ -165,8 +166,8 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey,
// Verify that this tx isn't selected.
for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) {
- BOOST_CHECK(pblocktemplate->block.vtx[i].GetHash() != hashFreeTx2);
- BOOST_CHECK(pblocktemplate->block.vtx[i].GetHash() != hashLowFeeTx2);
+ BOOST_CHECK(pblocktemplate->block.vtx[i]->GetHash() != hashFreeTx2);
+ BOOST_CHECK(pblocktemplate->block.vtx[i]->GetHash() != hashLowFeeTx2);
}
// This tx will be mineable, and should cause hashLowFeeTx2 to be selected
@@ -175,7 +176,7 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey,
tx.vout[0].nValue = 100000000 - 10000; // 10k satoshi fee
mempool.addUnchecked(tx.GetHash(), entry.Fee(10000).FromTx(tx));
pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey);
- BOOST_CHECK(pblocktemplate->block.vtx[8].GetHash() == hashLowFeeTx2);
+ BOOST_CHECK(pblocktemplate->block.vtx[8]->GetHash() == hashLowFeeTx2);
}
// NOTE: These tests rely on CreateNewBlock doing its own self-validation!
@@ -184,7 +185,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
// Note that by default, these tests run with size accounting enabled.
const CChainParams& chainparams = Params(CBaseChainParams::MAIN);
CScript scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG;
- CBlockTemplate *pblocktemplate;
+ std::unique_ptr<CBlockTemplate> pblocktemplate;
CMutableTransaction tx,tx2;
CScript script;
uint256 hash;
@@ -202,35 +203,33 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
// We can't make transactions until we have inputs
// Therefore, load 100 blocks :)
int baseheight = 0;
- std::vector<CTransaction*>txFirst;
+ std::vector<CTransactionRef> txFirst;
for (unsigned int i = 0; i < sizeof(blockinfo)/sizeof(*blockinfo); ++i)
{
CBlock *pblock = &pblocktemplate->block; // pointer for convenience
pblock->nVersion = 1;
pblock->nTime = chainActive.Tip()->GetMedianTimePast()+1;
- CMutableTransaction txCoinbase(pblock->vtx[0]);
+ CMutableTransaction txCoinbase(*pblock->vtx[0]);
txCoinbase.nVersion = 1;
txCoinbase.vin[0].scriptSig = CScript();
txCoinbase.vin[0].scriptSig.push_back(blockinfo[i].extranonce);
txCoinbase.vin[0].scriptSig.push_back(chainActive.Height());
+ txCoinbase.vout.resize(1); // Ignore the (optional) segwit commitment added by CreateNewBlock (as the hardcoded nonces don't account for this)
txCoinbase.vout[0].scriptPubKey = CScript();
- pblock->vtx[0] = CTransaction(txCoinbase);
+ pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
if (txFirst.size() == 0)
baseheight = chainActive.Height();
if (txFirst.size() < 4)
- txFirst.push_back(new CTransaction(pblock->vtx[0]));
+ txFirst.push_back(pblock->vtx[0]);
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
pblock->nNonce = blockinfo[i].nonce;
- CValidationState state;
- BOOST_CHECK(ProcessNewBlock(state, chainparams, NULL, pblock, true, NULL, connman));
- BOOST_CHECK(state.IsValid());
+ std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock);
+ BOOST_CHECK(ProcessNewBlock(chainparams, shared_pblock, true, NULL));
pblock->hashPrevBlock = pblock->GetHash();
}
- delete pblocktemplate;
// Just to make sure we can still make simple blocks
BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey));
- delete pblocktemplate;
const CAmount BLOCKSUBSIDY = 50*COIN;
const CAmount LOWFEE = CENT;
@@ -269,7 +268,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vin[0].prevout.hash = hash;
}
BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey));
- delete pblocktemplate;
mempool.clear();
// block size > limit
@@ -290,7 +288,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vin[0].prevout.hash = hash;
}
BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey));
- delete pblocktemplate;
mempool.clear();
// orphan in mempool, template creation fails
@@ -314,7 +311,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
hash = tx.GetHash();
mempool.addUnchecked(hash, entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey));
- delete pblocktemplate;
mempool.clear();
// coinbase in mempool, template creation fails
@@ -372,7 +368,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
chainActive.SetTip(next);
}
BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey));
- delete pblocktemplate;
// Extend to a 210000-long block chain.
while (chainActive.Tip()->nHeight < 210000) {
CBlockIndex* prev = chainActive.Tip();
@@ -385,7 +380,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
chainActive.SetTip(next);
}
BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey));
- delete pblocktemplate;
// Delete the dummy blocks again.
while (chainActive.Tip()->nHeight > nHeight) {
CBlockIndex* del = chainActive.Tip();
@@ -478,7 +472,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
// but relative locked txs will if inconsistently added to mempool.
// For now these will still generate a valid template until BIP68 soft fork
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 3);
- delete pblocktemplate;
// However if we advance height by 1 and time by 512, all of them should be mined
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
chainActive.Tip()->GetAncestor(chainActive.Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
@@ -487,7 +480,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey));
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 5);
- delete pblocktemplate;
chainActive.Tip()->nHeight--;
SetMockTime(0);
@@ -495,9 +487,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
TestPackageSelection(chainparams, scriptPubKey, txFirst);
- BOOST_FOREACH(CTransaction *_tx, txFirst)
- delete _tx;
-
fCheckpointsEnabled = true;
}
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index bc9a98ab04..87cb38daac 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -17,29 +17,29 @@ using namespace std;
class CAddrManSerializationMock : public CAddrMan
{
public:
- virtual void Serialize(CDataStream& s, int nType, int nVersionDummy) const = 0;
+ virtual void Serialize(CDataStream& s) const = 0;
//! Ensure that bucket placement is always the same for testing purposes.
void MakeDeterministic()
{
nKey.SetNull();
- seed_insecure_rand(true);
+ insecure_rand = FastRandomContext(true);
}
};
class CAddrManUncorrupted : public CAddrManSerializationMock
{
public:
- void Serialize(CDataStream& s, int nType, int nVersionDummy) const
+ void Serialize(CDataStream& s) const
{
- CAddrMan::Serialize(s, nType, nVersionDummy);
+ CAddrMan::Serialize(s);
}
};
class CAddrManCorrupted : public CAddrManSerializationMock
{
public:
- void Serialize(CDataStream& s, int nType, int nVersionDummy) const
+ void Serialize(CDataStream& s) const
{
// Produces corrupt output that claims addrman has 20 addrs when it only has one addr.
unsigned char nVersion = 1;
@@ -62,11 +62,11 @@ public:
}
};
-CDataStream AddrmanToStream(CAddrManSerializationMock& addrman)
+CDataStream AddrmanToStream(CAddrManSerializationMock& _addrman)
{
CDataStream ssPeersIn(SER_DISK, CLIENT_VERSION);
ssPeersIn << FLATDATA(Params().MessageStart());
- ssPeersIn << addrman;
+ ssPeersIn << _addrman;
std::string str = ssPeersIn.str();
vector<unsigned char> vchData(str.begin(), str.end());
return CDataStream(vchData, SER_DISK, CLIENT_VERSION);
@@ -164,12 +164,12 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
bool fInboundIn = false;
// Test that fFeeler is false by default.
- CNode* pnode1 = new CNode(id++, NODE_NETWORK, height, hSocket, addr, pszDest, fInboundIn);
+ CNode* pnode1 = new CNode(id++, NODE_NETWORK, height, hSocket, addr, 0, 0, pszDest, fInboundIn);
BOOST_CHECK(pnode1->fInbound == false);
BOOST_CHECK(pnode1->fFeeler == false);
fInboundIn = true;
- CNode* pnode2 = new CNode(id++, NODE_NETWORK, height, hSocket, addr, pszDest, fInboundIn);
+ CNode* pnode2 = new CNode(id++, NODE_NETWORK, height, hSocket, addr, 1, 1, pszDest, fInboundIn);
BOOST_CHECK(pnode2->fInbound == true);
BOOST_CHECK(pnode2->fFeeler == false);
}
diff --git a/src/test/pmt_tests.cpp b/src/test/pmt_tests.cpp
index e9c8691745..e6b689bc6c 100644
--- a/src/test/pmt_tests.cpp
+++ b/src/test/pmt_tests.cpp
@@ -9,8 +9,8 @@
#include "uint256.h"
#include "arith_uint256.h"
#include "version.h"
-#include "random.h"
#include "test/test_bitcoin.h"
+#include "test/test_random.h"
#include <vector>
@@ -45,14 +45,14 @@ BOOST_AUTO_TEST_CASE(pmt_test1)
for (unsigned int j=0; j<nTx; j++) {
CMutableTransaction tx;
tx.nLockTime = j; // actual transaction data doesn't matter; just make the nLockTime's unique
- block.vtx.push_back(CTransaction(tx));
+ block.vtx.push_back(MakeTransactionRef(std::move(tx)));
}
// calculate actual merkle root and height
uint256 merkleRoot1 = BlockMerkleRoot(block);
std::vector<uint256> vTxid(nTx, uint256());
for (unsigned int j=0; j<nTx; j++)
- vTxid[j] = block.vtx[j].GetHash();
+ vTxid[j] = block.vtx[j]->GetHash();
int nHeight = 1, nTx_ = nTx;
while (nTx_ > 1) {
nTx_ = (nTx_+1)/2;
diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp
index 5c902387f1..c57feaec90 100644
--- a/src/test/policyestimator_tests.cpp
+++ b/src/test/policyestimator_tests.cpp
@@ -19,26 +19,18 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
CTxMemPool mpool(CFeeRate(1000));
TestMemPoolEntryHelper entry;
CAmount basefee(2000);
- double basepri = 1e6;
CAmount deltaFee(100);
- double deltaPri=5e5;
- std::vector<CAmount> feeV[2];
- std::vector<double> priV[2];
+ std::vector<CAmount> feeV;
- // Populate vectors of increasing fees or priorities
+ // Populate vectors of increasing fees
for (int j = 0; j < 10; j++) {
- //V[0] is for fee transactions
- feeV[0].push_back(basefee * (j+1));
- priV[0].push_back(0);
- //V[1] is for priority transactions
- feeV[1].push_back(CAmount(0));
- priV[1].push_back(basepri * pow(10, j+1));
+ feeV.push_back(basefee * (j+1));
}
// Store the hashes of transactions that have been
- // added to the mempool by their associate fee/pri
+ // added to the mempool by their associate fee
// txHashes[j] is populated with transactions either of
- // fee = basefee * (j+1) OR pri = 10^6 * 10^(j+1)
+ // fee = basefee * (j+1)
std::vector<uint256> txHashes[10];
// Create a transaction template
@@ -46,7 +38,6 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
for (unsigned int i = 0; i < 128; i++)
garbage.push_back('X');
CMutableTransaction tx;
- std::list<CTransaction> dummyConflicted;
tx.vin.resize(1);
tx.vin[0].scriptSig = garbage;
tx.vout.resize(1);
@@ -54,34 +45,34 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
CFeeRate baseRate(basefee, GetVirtualTransactionSize(tx));
// Create a fake block
- std::vector<CTransaction> block;
+ std::vector<CTransactionRef> block;
int blocknum = 0;
// Loop through 200 blocks
// At a decay .998 and 4 fee transactions per block
// This makes the tx count about 1.33 per bucket, above the 1 threshold
while (blocknum < 200) {
- for (int j = 0; j < 10; j++) { // For each fee/pri multiple
- for (int k = 0; k < 5; k++) { // add 4 fee txs for every priority tx
+ for (int j = 0; j < 10; j++) { // For each fee
+ for (int k = 0; k < 4; k++) { // add 4 fee txs
tx.vin[0].prevout.n = 10000*blocknum+100*j+k; // make transaction unique
uint256 hash = tx.GetHash();
- mpool.addUnchecked(hash, entry.Fee(feeV[k/4][j]).Time(GetTime()).Priority(priV[k/4][j]).Height(blocknum).FromTx(tx, &mpool));
+ mpool.addUnchecked(hash, entry.Fee(feeV[j]).Time(GetTime()).Priority(0).Height(blocknum).FromTx(tx, &mpool));
txHashes[j].push_back(hash);
}
}
- //Create blocks where higher fee/pri txs are included more often
+ //Create blocks where higher fee txs are included more often
for (int h = 0; h <= blocknum%10; h++) {
- // 10/10 blocks add highest fee/pri transactions
+ // 10/10 blocks add highest fee transactions
// 9/10 blocks add 2nd highest and so on until ...
- // 1/10 blocks add lowest fee/pri transactions
+ // 1/10 blocks add lowest fee transactions
while (txHashes[9-h].size()) {
- std::shared_ptr<const CTransaction> ptx = mpool.get(txHashes[9-h].back());
+ CTransactionRef ptx = mpool.get(txHashes[9-h].back());
if (ptx)
- block.push_back(*ptx);
+ block.push_back(ptx);
txHashes[9-h].pop_back();
}
}
- mpool.removeForBlock(block, ++blocknum, dummyConflicted);
+ mpool.removeForBlock(block, ++blocknum);
block.clear();
if (blocknum == 30) {
// At this point we should need to combine 5 buckets to get enough data points
@@ -101,106 +92,104 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
}
std::vector<CAmount> origFeeEst;
- std::vector<double> origPriEst;
// Highest feerate is 10*baseRate and gets in all blocks,
// second highest feerate is 9*baseRate and gets in 9/10 blocks = 90%,
// third highest feerate is 8*base rate, and gets in 8/10 blocks = 80%,
- // so estimateFee(1) should return 10*baseRate.
+ // so estimateFee(1) would return 10*baseRate but is hardcoded to return failure
// Second highest feerate has 100% chance of being included by 2 blocks,
// so estimateFee(2) should return 9*baseRate etc...
for (int i = 1; i < 10;i++) {
origFeeEst.push_back(mpool.estimateFee(i).GetFeePerK());
- origPriEst.push_back(mpool.estimatePriority(i));
- if (i > 1) { // Fee estimates should be monotonically decreasing
+ if (i > 2) { // Fee estimates should be monotonically decreasing
BOOST_CHECK(origFeeEst[i-1] <= origFeeEst[i-2]);
- BOOST_CHECK(origPriEst[i-1] <= origPriEst[i-2]);
}
int mult = 11-i;
- BOOST_CHECK(origFeeEst[i-1] < mult*baseRate.GetFeePerK() + deltaFee);
- BOOST_CHECK(origFeeEst[i-1] > mult*baseRate.GetFeePerK() - deltaFee);
- BOOST_CHECK(origPriEst[i-1] < pow(10,mult) * basepri + deltaPri);
- BOOST_CHECK(origPriEst[i-1] > pow(10,mult) * basepri - deltaPri);
+ if (i > 1) {
+ BOOST_CHECK(origFeeEst[i-1] < mult*baseRate.GetFeePerK() + deltaFee);
+ BOOST_CHECK(origFeeEst[i-1] > mult*baseRate.GetFeePerK() - deltaFee);
+ }
+ else {
+ BOOST_CHECK(origFeeEst[i-1] == CFeeRate(0).GetFeePerK());
+ }
}
// Mine 50 more blocks with no transactions happening, estimates shouldn't change
// We haven't decayed the moving average enough so we still have enough data points in every bucket
while (blocknum < 250)
- mpool.removeForBlock(block, ++blocknum, dummyConflicted);
+ mpool.removeForBlock(block, ++blocknum);
- for (int i = 1; i < 10;i++) {
+ BOOST_CHECK(mpool.estimateFee(1) == CFeeRate(0));
+ for (int i = 2; i < 10;i++) {
BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() < origFeeEst[i-1] + deltaFee);
BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
- BOOST_CHECK(mpool.estimatePriority(i) < origPriEst[i-1] + deltaPri);
- BOOST_CHECK(mpool.estimatePriority(i) > origPriEst[i-1] - deltaPri);
}
// Mine 15 more blocks with lots of transactions happening and not getting mined
// Estimates should go up
while (blocknum < 265) {
- for (int j = 0; j < 10; j++) { // For each fee/pri multiple
- for (int k = 0; k < 5; k++) { // add 4 fee txs for every priority tx
+ for (int j = 0; j < 10; j++) { // For each fee multiple
+ for (int k = 0; k < 4; k++) { // add 4 fee txs
tx.vin[0].prevout.n = 10000*blocknum+100*j+k;
uint256 hash = tx.GetHash();
- mpool.addUnchecked(hash, entry.Fee(feeV[k/4][j]).Time(GetTime()).Priority(priV[k/4][j]).Height(blocknum).FromTx(tx, &mpool));
+ mpool.addUnchecked(hash, entry.Fee(feeV[j]).Time(GetTime()).Priority(0).Height(blocknum).FromTx(tx, &mpool));
txHashes[j].push_back(hash);
}
}
- mpool.removeForBlock(block, ++blocknum, dummyConflicted);
+ mpool.removeForBlock(block, ++blocknum);
}
int answerFound;
for (int i = 1; i < 10;i++) {
BOOST_CHECK(mpool.estimateFee(i) == CFeeRate(0) || mpool.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
BOOST_CHECK(mpool.estimateSmartFee(i, &answerFound).GetFeePerK() > origFeeEst[answerFound-1] - deltaFee);
- BOOST_CHECK(mpool.estimatePriority(i) == -1 || mpool.estimatePriority(i) > origPriEst[i-1] - deltaPri);
- BOOST_CHECK(mpool.estimateSmartPriority(i, &answerFound) > origPriEst[answerFound-1] - deltaPri);
}
// Mine all those transactions
// Estimates should still not be below original
for (int j = 0; j < 10; j++) {
while(txHashes[j].size()) {
- std::shared_ptr<const CTransaction> ptx = mpool.get(txHashes[j].back());
+ CTransactionRef ptx = mpool.get(txHashes[j].back());
if (ptx)
- block.push_back(*ptx);
+ block.push_back(ptx);
txHashes[j].pop_back();
}
}
- mpool.removeForBlock(block, 265, dummyConflicted);
+ mpool.removeForBlock(block, 265);
block.clear();
- for (int i = 1; i < 10;i++) {
+ BOOST_CHECK(mpool.estimateFee(1) == CFeeRate(0));
+ for (int i = 2; i < 10;i++) {
BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
- BOOST_CHECK(mpool.estimatePriority(i) > origPriEst[i-1] - deltaPri);
}
// Mine 200 more blocks where everything is mined every block
// Estimates should be below original estimates
while (blocknum < 465) {
- for (int j = 0; j < 10; j++) { // For each fee/pri multiple
- for (int k = 0; k < 5; k++) { // add 4 fee txs for every priority tx
+ for (int j = 0; j < 10; j++) { // For each fee multiple
+ for (int k = 0; k < 4; k++) { // add 4 fee txs
tx.vin[0].prevout.n = 10000*blocknum+100*j+k;
uint256 hash = tx.GetHash();
- mpool.addUnchecked(hash, entry.Fee(feeV[k/4][j]).Time(GetTime()).Priority(priV[k/4][j]).Height(blocknum).FromTx(tx, &mpool));
- std::shared_ptr<const CTransaction> ptx = mpool.get(hash);
+ mpool.addUnchecked(hash, entry.Fee(feeV[j]).Time(GetTime()).Priority(0).Height(blocknum).FromTx(tx, &mpool));
+ CTransactionRef ptx = mpool.get(hash);
if (ptx)
- block.push_back(*ptx);
+ block.push_back(ptx);
+
}
}
- mpool.removeForBlock(block, ++blocknum, dummyConflicted);
+ mpool.removeForBlock(block, ++blocknum);
block.clear();
}
- for (int i = 1; i < 10; i++) {
+ BOOST_CHECK(mpool.estimateFee(1) == CFeeRate(0));
+ for (int i = 2; i < 10; i++) {
BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() < origFeeEst[i-1] - deltaFee);
- BOOST_CHECK(mpool.estimatePriority(i) < origPriEst[i-1] - deltaPri);
}
// Test that if the mempool is limited, estimateSmartFee won't return a value below the mempool min fee
// and that estimateSmartPriority returns essentially an infinite value
- mpool.addUnchecked(tx.GetHash(), entry.Fee(feeV[0][5]).Time(GetTime()).Priority(priV[1][5]).Height(blocknum).FromTx(tx, &mpool));
- // evict that transaction which should set a mempool min fee of minRelayTxFee + feeV[0][5]
+ mpool.addUnchecked(tx.GetHash(), entry.Fee(feeV[5]).Time(GetTime()).Priority(0).Height(blocknum).FromTx(tx, &mpool));
+ // evict that transaction which should set a mempool min fee of minRelayTxFee + feeV[5]
mpool.TrimToSize(1);
- BOOST_CHECK(mpool.GetMinFee(1).GetFeePerK() > feeV[0][5]);
+ BOOST_CHECK(mpool.GetMinFee(1).GetFeePerK() > feeV[5]);
for (int i = 1; i < 10; i++) {
BOOST_CHECK(mpool.estimateSmartFee(i).GetFeePerK() >= mpool.estimateFee(i).GetFeePerK());
BOOST_CHECK(mpool.estimateSmartFee(i).GetFeePerK() >= mpool.GetMinFee(1).GetFeePerK());
diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp
index b8c45ca564..1e5de2021c 100644
--- a/src/test/prevector_tests.cpp
+++ b/src/test/prevector_tests.cpp
@@ -4,12 +4,12 @@
#include <vector>
#include "prevector.h"
-#include "random.h"
#include "serialize.h"
#include "streams.h"
#include "test/test_bitcoin.h"
+#include "test/test_random.h"
#include <boost/test/unit_test.hpp>
@@ -27,8 +27,7 @@ class prevector_tester {
typedef typename pretype::size_type Size;
bool passed = true;
- uint32_t insecure_rand_Rz_cache;
- uint32_t insecure_rand_Rw_cache;
+ FastRandomContext rand_cache;
template <typename A, typename B>
@@ -171,15 +170,14 @@ public:
test();
}
~prevector_tester() {
- BOOST_CHECK_MESSAGE(passed, "insecure_rand_Rz: "
- << insecure_rand_Rz_cache
+ BOOST_CHECK_MESSAGE(passed, "insecure_rand_Rz: "
+ << rand_cache.Rz
<< ", insecure_rand_Rw: "
- << insecure_rand_Rw_cache);
+ << rand_cache.Rw);
}
prevector_tester() {
seed_insecure_rand();
- insecure_rand_Rz_cache = insecure_rand_Rz;
- insecure_rand_Rw_cache = insecure_rand_Rw;
+ rand_cache = insecure_rand_ctx;
}
};
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
index a15915aad2..a359598ddc 100644
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -24,11 +24,14 @@ UniValue CallRPC(string args)
boost::split(vArgs, args, boost::is_any_of(" \t"));
string strMethod = vArgs[0];
vArgs.erase(vArgs.begin());
- UniValue params = RPCConvertValues(strMethod, vArgs);
+ JSONRPCRequest request;
+ request.strMethod = strMethod;
+ request.params = RPCConvertValues(strMethod, vArgs);
+ request.fHelp = false;
BOOST_CHECK(tableRPC[strMethod]);
rpcfn_type method = tableRPC[strMethod]->actor;
try {
- UniValue result = (*method)(params, false);
+ UniValue result = (*method)(request);
return result;
}
catch (const UniValue& objError) {
@@ -81,6 +84,28 @@ BOOST_AUTO_TEST_CASE(rpc_rawparams)
BOOST_CHECK_THROW(CallRPC(string("sendrawtransaction ")+rawtx+" extra"), runtime_error);
}
+BOOST_AUTO_TEST_CASE(rpc_togglenetwork)
+{
+ UniValue r;
+
+ r = CallRPC("getnetworkinfo");
+ bool netState = find_value(r.get_obj(), "networkactive").get_bool();
+ BOOST_CHECK_EQUAL(netState, true);
+
+ BOOST_CHECK_NO_THROW(CallRPC("setnetworkactive false"));
+ r = CallRPC("getnetworkinfo");
+ int numConnection = find_value(r.get_obj(), "connections").get_int();
+ BOOST_CHECK_EQUAL(numConnection, 0);
+
+ netState = find_value(r.get_obj(), "networkactive").get_bool();
+ BOOST_CHECK_EQUAL(netState, false);
+
+ BOOST_CHECK_NO_THROW(CallRPC("setnetworkactive true"));
+ r = CallRPC("getnetworkinfo");
+ netState = find_value(r.get_obj(), "networkactive").get_bool();
+ BOOST_CHECK_EQUAL(netState, true);
+}
+
BOOST_AUTO_TEST_CASE(rpc_rawsign)
{
UniValue r;
diff --git a/src/test/scheduler_tests.cpp b/src/test/scheduler_tests.cpp
index aa12dfbd54..891ecf5015 100644
--- a/src/test/scheduler_tests.cpp
+++ b/src/test/scheduler_tests.cpp
@@ -42,8 +42,6 @@ static void MicroSleep(uint64_t n)
BOOST_AUTO_TEST_CASE(manythreads)
{
- seed_insecure_rand(false);
-
// Stress test: hundreds of microsecond-scheduled tasks,
// serviced by 10 threads.
//
@@ -58,7 +56,7 @@ BOOST_AUTO_TEST_CASE(manythreads)
boost::mutex counterMutex[10];
int counter[10] = { 0 };
- boost::random::mt19937 rng(insecure_rand());
+ boost::random::mt19937 rng(42);
boost::random::uniform_int_distribution<> zeroToNine(0, 9);
boost::random::uniform_int_distribution<> randomMsec(-11, 1000);
boost::random::uniform_int_distribution<> randomDelta(-1000, 1000);
diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp
index 1a01593a8e..789cc70d7f 100644
--- a/src/test/script_P2SH_tests.cpp
+++ b/src/test/script_P2SH_tests.cpp
@@ -5,7 +5,7 @@
#include "core_io.h"
#include "key.h"
#include "keystore.h"
-#include "main.h"
+#include "validation.h"
#include "policy/policy.h"
#include "script/script.h"
#include "script/script_error.h"
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index 5a9aaf9bc0..d7f3a3a657 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -89,6 +89,8 @@ static ScriptErrorDesc script_errors[]={
{SCRIPT_ERR_SIG_NULLDUMMY, "SIG_NULLDUMMY"},
{SCRIPT_ERR_PUBKEYTYPE, "PUBKEYTYPE"},
{SCRIPT_ERR_CLEANSTACK, "CLEANSTACK"},
+ {SCRIPT_ERR_MINIMALIF, "MINIMALIF"},
+ {SCRIPT_ERR_SIG_NULLFAIL, "NULLFAIL"},
{SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS, "DISCOURAGE_UPGRADABLE_NOPS"},
{SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM, "DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"},
{SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH, "WITNESS_PROGRAM_WRONG_LENGTH"},
@@ -97,6 +99,7 @@ static ScriptErrorDesc script_errors[]={
{SCRIPT_ERR_WITNESS_MALLEATED, "WITNESS_MALLEATED"},
{SCRIPT_ERR_WITNESS_MALLEATED_P2SH, "WITNESS_MALLEATED_P2SH"},
{SCRIPT_ERR_WITNESS_UNEXPECTED, "WITNESS_UNEXPECTED"},
+ {SCRIPT_ERR_WITNESS_PUBKEYTYPE, "WITNESS_PUBKEYTYPE"},
};
const char *FormatScriptError(ScriptError_t err)
@@ -170,11 +173,14 @@ void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, const CScript
#if defined(HAVE_CONSENSUS_LIB)
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << tx2;
- if (flags & bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS) {
- BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(begin_ptr(scriptPubKey), scriptPubKey.size(), txCredit.vout[0].nValue, (const unsigned char*)&stream[0], stream.size(), 0, flags, NULL) == expect, message);
- } else {
- BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(begin_ptr(scriptPubKey), scriptPubKey.size(), 0, (const unsigned char*)&stream[0], stream.size(), 0, flags, NULL) == expect, message);
- BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script(begin_ptr(scriptPubKey), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), 0, flags, NULL) == expect,message);
+ int libconsensus_flags = flags & bitcoinconsensus_SCRIPT_FLAGS_VERIFY_ALL;
+ if (libconsensus_flags == flags) {
+ if (flags & bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS) {
+ BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(scriptPubKey.data(), scriptPubKey.size(), txCredit.vout[0].nValue, (const unsigned char*)&stream[0], stream.size(), 0, libconsensus_flags, NULL) == expect, message);
+ } else {
+ BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(scriptPubKey.data(), scriptPubKey.size(), 0, (const unsigned char*)&stream[0], stream.size(), 0, libconsensus_flags, NULL) == expect, message);
+ BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), 0, libconsensus_flags, NULL) == expect,message);
+ }
}
#endif
}
@@ -270,7 +276,7 @@ private:
//! The Witness embedded script
CScript witscript;
CScriptWitness scriptWitness;
- CTransaction creditTx;
+ CTransactionRef creditTx;
CMutableTransaction spendTx;
bool havePush;
std::vector<unsigned char> push;
@@ -313,8 +319,8 @@ public:
redeemscript = scriptPubKey;
scriptPubKey = CScript() << OP_HASH160 << ToByteVector(CScriptID(redeemscript)) << OP_EQUAL;
}
- creditTx = BuildCreditingTransaction(scriptPubKey, nValue);
- spendTx = BuildSpendingTransaction(CScript(), CScriptWitness(), creditTx);
+ creditTx = MakeTransactionRef(BuildCreditingTransaction(scriptPubKey, nValue));
+ spendTx = BuildSpendingTransaction(CScript(), CScriptWitness(), *creditTx);
}
TestBuilder& ScriptError(ScriptError_t err)
@@ -323,10 +329,10 @@ public:
return *this;
}
- TestBuilder& Add(const CScript& script)
+ TestBuilder& Add(const CScript& _script)
{
DoPush();
- spendTx.vin[0].scriptSig += script;
+ spendTx.vin[0].scriptSig += _script;
return *this;
}
@@ -343,8 +349,8 @@ public:
return *this;
}
- TestBuilder& Push(const CScript& script) {
- DoPush(std::vector<unsigned char>(script.begin(), script.end()));
+ TestBuilder& Push(const CScript& _script) {
+ DoPush(std::vector<unsigned char>(_script.begin(), _script.end()));
return *this;
}
@@ -415,7 +421,7 @@ public:
{
TestBuilder copy = *this; // Make a copy so we can rollback the push.
DoPush();
- DoTest(creditTx.vout[0].scriptPubKey, spendTx.vin[0].scriptSig, scriptWitness, flags, comment, scriptError, nValue);
+ DoTest(creditTx->vout[0].scriptPubKey, spendTx.vin[0].scriptSig, scriptWitness, flags, comment, scriptError, nValue);
*this = copy;
return *this;
}
@@ -441,7 +447,7 @@ public:
array.push_back(wit);
}
array.push_back(FormatScript(spendTx.vin[0].scriptSig));
- array.push_back(FormatScript(creditTx.vout[0].scriptPubKey));
+ array.push_back(FormatScript(creditTx->vout[0].scriptPubKey));
array.push_back(FormatScriptFlags(flags));
array.push_back(FormatScriptError((ScriptError_t)scriptError));
array.push_back(comment);
@@ -455,7 +461,7 @@ public:
const CScript& GetScriptPubKey()
{
- return creditTx.vout[0].scriptPubKey;
+ return creditTx->vout[0].scriptPubKey;
}
};
@@ -823,6 +829,99 @@ BOOST_AUTO_TEST_CASE(script_build)
"P2PK with witness", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH
).PushSig(keys.key0).Push("0").AsWit().ScriptError(SCRIPT_ERR_WITNESS_UNEXPECTED));
+ // Compressed keys should pass SCRIPT_VERIFY_WITNESS_PUBKEYTYPE
+ tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0C) << OP_CHECKSIG,
+ "Basic P2WSH with compressed key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_SH,
+ 0, 1).PushWitSig(keys.key0C).PushWitRedeem());
+ tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0C),
+ "Basic P2WPKH with compressed key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_PKH,
+ 0, 1).PushWitSig(keys.key0C).Push(keys.pubkey0C).AsWit());
+ tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0C) << OP_CHECKSIG,
+ "Basic P2SH(P2WSH) with compressed key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_SH,
+ 0, 1).PushWitSig(keys.key0C).PushWitRedeem().PushRedeem());
+ tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0C),
+ "Basic P2SH(P2WPKH) with compressed key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_PKH,
+ 0, 1).PushWitSig(keys.key0C).Push(keys.pubkey0C).AsWit().PushRedeem());
+
+ // Testing uncompressed key in witness with SCRIPT_VERIFY_WITNESS_PUBKEYTYPE
+ tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0) << OP_CHECKSIG,
+ "Basic P2WSH", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_SH,
+ 0, 1).PushWitSig(keys.key0).PushWitRedeem().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE));
+ tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0),
+ "Basic P2WPKH", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_PKH,
+ 0, 1).PushWitSig(keys.key0).Push(keys.pubkey0).AsWit().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE));
+ tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0) << OP_CHECKSIG,
+ "Basic P2SH(P2WSH)", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_SH,
+ 0, 1).PushWitSig(keys.key0).PushWitRedeem().PushRedeem().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE));
+ tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0),
+ "Basic P2SH(P2WPKH)", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_PKH,
+ 0, 1).PushWitSig(keys.key0).Push(keys.pubkey0).AsWit().PushRedeem().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE));
+
+ // P2WSH 1-of-2 multisig with compressed keys
+ tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG,
+ "P2WSH CHECKMULTISIG with compressed keys", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_SH,
+ 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0C).PushWitRedeem());
+ tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG,
+ "P2SH(P2WSH) CHECKMULTISIG with compressed keys", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_SH,
+ 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0C).PushWitRedeem().PushRedeem());
+ tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG,
+ "P2WSH CHECKMULTISIG with compressed keys", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_SH,
+ 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1C).PushWitRedeem());
+ tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG,
+ "P2SH(P2WSH) CHECKMULTISIG with compressed keys", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_SH,
+ 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1C).PushWitRedeem().PushRedeem());
+
+ // P2WSH 1-of-2 multisig with first key uncompressed
+ tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0) << OP_2 << OP_CHECKMULTISIG,
+ "P2WSH CHECKMULTISIG with first key uncompressed and signing with the first key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WITNESS_SH,
+ 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0).PushWitRedeem());
+ tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0) << OP_2 << OP_CHECKMULTISIG,
+ "P2SH(P2WSH) CHECKMULTISIG first key uncompressed and signing with the first key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WITNESS_SH,
+ 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0).PushWitRedeem().PushRedeem());
+ tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0) << OP_2 << OP_CHECKMULTISIG,
+ "P2WSH CHECKMULTISIG with first key uncompressed and signing with the first key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_SH,
+ 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0).PushWitRedeem().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE));
+ tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0) << OP_2 << OP_CHECKMULTISIG,
+ "P2SH(P2WSH) CHECKMULTISIG with first key uncompressed and signing with the first key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_SH,
+ 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0).PushWitRedeem().PushRedeem().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE));
+ tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0) << OP_2 << OP_CHECKMULTISIG,
+ "P2WSH CHECKMULTISIG with first key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WITNESS_SH,
+ 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1C).PushWitRedeem());
+ tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0) << OP_2 << OP_CHECKMULTISIG,
+ "P2SH(P2WSH) CHECKMULTISIG with first key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WITNESS_SH,
+ 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1C).PushWitRedeem().PushRedeem());
+ tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0) << OP_2 << OP_CHECKMULTISIG,
+ "P2WSH CHECKMULTISIG with first key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_SH,
+ 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1C).PushWitRedeem().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE));
+ tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0) << OP_2 << OP_CHECKMULTISIG,
+ "P2SH(P2WSH) CHECKMULTISIG with first key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_SH,
+ 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1C).PushWitRedeem().PushRedeem().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE));
+ // P2WSH 1-of-2 multisig with second key uncompressed
+ tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG,
+ "P2WSH CHECKMULTISIG with second key uncompressed and signing with the first key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WITNESS_SH,
+ 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0C).PushWitRedeem());
+ tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG,
+ "P2SH(P2WSH) CHECKMULTISIG second key uncompressed and signing with the first key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WITNESS_SH,
+ 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0C).PushWitRedeem().PushRedeem());
+ tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG,
+ "P2WSH CHECKMULTISIG with second key uncompressed and signing with the first key should pass as the uncompressed key is not used", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_SH,
+ 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0C).PushWitRedeem());
+ tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG,
+ "P2SH(P2WSH) CHECKMULTISIG with second key uncompressed and signing with the first key should pass as the uncompressed key is not used", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_SH,
+ 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0C).PushWitRedeem().PushRedeem());
+ tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG,
+ "P2WSH CHECKMULTISIG with second key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WITNESS_SH,
+ 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1).PushWitRedeem());
+ tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG,
+ "P2SH(P2WSH) CHECKMULTISIG with second key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WITNESS_SH,
+ 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1).PushWitRedeem().PushRedeem());
+ tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG,
+ "P2WSH CHECKMULTISIG with second key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_SH,
+ 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1).PushWitRedeem().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE));
+ tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG,
+ "P2SH(P2WSH) CHECKMULTISIG with second key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_SH,
+ 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1).PushWitRedeem().PushRedeem().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE));
+
std::set<std::string> tests_set;
{
diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp
index bec2c7459d..1dc86eb116 100644
--- a/src/test/serialize_tests.cpp
+++ b/src/test/serialize_tests.cpp
@@ -10,11 +10,54 @@
#include <stdint.h>
#include <boost/test/unit_test.hpp>
-
using namespace std;
BOOST_FIXTURE_TEST_SUITE(serialize_tests, BasicTestingSetup)
+class CSerializeMethodsTestSingle
+{
+protected:
+ int intval;
+ bool boolval;
+ std::string stringval;
+ const char* charstrval;
+ CTransactionRef txval;
+public:
+ CSerializeMethodsTestSingle() = default;
+ CSerializeMethodsTestSingle(int intvalin, bool boolvalin, std::string stringvalin, const char* charstrvalin, CTransaction txvalin) : intval(intvalin), boolval(boolvalin), stringval(std::move(stringvalin)), charstrval(charstrvalin), txval(MakeTransactionRef(txvalin)){}
+ ADD_SERIALIZE_METHODS;
+
+ template <typename Stream, typename Operation>
+ inline void SerializationOp(Stream& s, Operation ser_action) {
+ READWRITE(intval);
+ READWRITE(boolval);
+ READWRITE(stringval);
+ READWRITE(FLATDATA(charstrval));
+ READWRITE(txval);
+ }
+
+ bool operator==(const CSerializeMethodsTestSingle& rhs)
+ {
+ return intval == rhs.intval && \
+ boolval == rhs.boolval && \
+ stringval == rhs.stringval && \
+ strcmp(charstrval, rhs.charstrval) == 0 && \
+ *txval == *rhs.txval;
+ }
+};
+
+class CSerializeMethodsTestMany : public CSerializeMethodsTestSingle
+{
+public:
+ using CSerializeMethodsTestSingle::CSerializeMethodsTestSingle;
+ ADD_SERIALIZE_METHODS;
+
+ template <typename Stream, typename Operation>
+ inline void SerializationOp(Stream& s, Operation ser_action) {
+ READWRITEMANY(intval, boolval, stringval, FLATDATA(charstrval), txval);
+ }
+};
+
BOOST_AUTO_TEST_CASE(sizes)
{
BOOST_CHECK_EQUAL(sizeof(char), GetSerializeSize(char(0), 0));
@@ -297,4 +340,30 @@ BOOST_AUTO_TEST_CASE(insert_delete)
BOOST_CHECK_EQUAL(ss.size(), 0);
}
+BOOST_AUTO_TEST_CASE(class_methods)
+{
+ int intval(100);
+ bool boolval(true);
+ std::string stringval("testing");
+ const char* charstrval("testing charstr");
+ CMutableTransaction txval;
+ CSerializeMethodsTestSingle methodtest1(intval, boolval, stringval, charstrval, txval);
+ CSerializeMethodsTestMany methodtest2(intval, boolval, stringval, charstrval, txval);
+ CSerializeMethodsTestSingle methodtest3;
+ CSerializeMethodsTestMany methodtest4;
+ CDataStream ss(SER_DISK, PROTOCOL_VERSION);
+ BOOST_CHECK(methodtest1 == methodtest2);
+ ss << methodtest1;
+ ss >> methodtest4;
+ ss << methodtest2;
+ ss >> methodtest3;
+ BOOST_CHECK(methodtest1 == methodtest2);
+ BOOST_CHECK(methodtest2 == methodtest3);
+ BOOST_CHECK(methodtest3 == methodtest4);
+
+ CDataStream ss2(SER_DISK, PROTOCOL_VERSION, intval, boolval, stringval, FLATDATA(charstrval), txval);
+ ss2 >> methodtest3;
+ BOOST_CHECK(methodtest3 == methodtest4);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp
index 4a48347b70..a524f5b946 100644
--- a/src/test/sighash_tests.cpp
+++ b/src/test/sighash_tests.cpp
@@ -5,13 +5,13 @@
#include "consensus/validation.h"
#include "data/sighash.json.h"
#include "hash.h"
-#include "main.h" // For CheckTransaction
-#include "random.h"
+#include "validation.h" // For CheckTransaction
#include "script/interpreter.h"
#include "script/script.h"
#include "serialize.h"
#include "streams.h"
#include "test/test_bitcoin.h"
+#include "test/test_random.h"
#include "util.h"
#include "utilstrencodings.h"
#include "version.h"
@@ -184,7 +184,7 @@ BOOST_AUTO_TEST_CASE(sighash_from_data)
std::string raw_tx, raw_script, sigHashHex;
int nIn, nHashType;
uint256 sh;
- CTransaction tx;
+ CTransactionRef tx;
CScript scriptCode = CScript();
try {
@@ -199,7 +199,7 @@ BOOST_AUTO_TEST_CASE(sighash_from_data)
stream >> tx;
CValidationState state;
- BOOST_CHECK_MESSAGE(CheckTransaction(tx, state), strTest);
+ BOOST_CHECK_MESSAGE(CheckTransaction(*tx, state), strTest);
BOOST_CHECK(state.IsValid());
std::vector<unsigned char> raw = ParseHex(raw_script);
@@ -209,7 +209,7 @@ BOOST_AUTO_TEST_CASE(sighash_from_data)
continue;
}
- sh = SignatureHash(scriptCode, tx, nIn, nHashType, 0, SIGVERSION_BASE);
+ sh = SignatureHash(scriptCode, *tx, nIn, nHashType, 0, SIGVERSION_BASE);
BOOST_CHECK_MESSAGE(sh.GetHex() == sigHashHex, strTest);
}
}
diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp
index e8a63ae60c..2e974cc612 100644
--- a/src/test/sigopcount_tests.cpp
+++ b/src/test/sigopcount_tests.cpp
@@ -2,7 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "main.h"
+#include "validation.h"
#include "pubkey.h"
#include "key.h"
#include "script/script.h"
diff --git a/src/test/skiplist_tests.cpp b/src/test/skiplist_tests.cpp
index f14b902fe1..d6835df71f 100644
--- a/src/test/skiplist_tests.cpp
+++ b/src/test/skiplist_tests.cpp
@@ -3,9 +3,9 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "chain.h"
-#include "random.h"
#include "util.h"
#include "test/test_bitcoin.h"
+#include "test/test_random.h"
#include <vector>
diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp
index 34f501e867..8b715ce93e 100644
--- a/src/test/streams_tests.cpp
+++ b/src/test/streams_tests.cpp
@@ -15,6 +15,64 @@ using namespace boost::assign; // bring 'operator+=()' into scope
BOOST_FIXTURE_TEST_SUITE(streams_tests, BasicTestingSetup)
+BOOST_AUTO_TEST_CASE(streams_vector_writer)
+{
+ unsigned char a(1);
+ unsigned char b(2);
+ unsigned char bytes[] = { 3, 4, 5, 6 };
+ std::vector<unsigned char> vch;
+
+ // Each test runs twice. Serializing a second time at the same starting
+ // point should yield the same results, even if the first test grew the
+ // vector.
+
+ CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 0, a, b);
+ BOOST_CHECK((vch == std::vector<unsigned char>{{1, 2}}));
+ CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 0, a, b);
+ BOOST_CHECK((vch == std::vector<unsigned char>{{1, 2}}));
+ vch.clear();
+
+ CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, b);
+ BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 1, 2}}));
+ CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, b);
+ BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 1, 2}}));
+ vch.clear();
+
+ vch.resize(5, 0);
+ CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, b);
+ BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 1, 2, 0}}));
+ CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, b);
+ BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 1, 2, 0}}));
+ vch.clear();
+
+ vch.resize(4, 0);
+ CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 3, a, b);
+ BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 0, 1, 2}}));
+ CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 3, a, b);
+ BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 0, 1, 2}}));
+ vch.clear();
+
+ vch.resize(4, 0);
+ CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 4, a, b);
+ BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 0, 0, 1, 2}}));
+ CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 4, a, b);
+ BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 0, 0, 1, 2}}));
+ vch.clear();
+
+ CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 0, FLATDATA(bytes));
+ BOOST_CHECK((vch == std::vector<unsigned char>{{3, 4, 5, 6}}));
+ CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 0, FLATDATA(bytes));
+ BOOST_CHECK((vch == std::vector<unsigned char>{{3, 4, 5, 6}}));
+ vch.clear();
+
+ vch.resize(4, 8);
+ CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, FLATDATA(bytes), b);
+ BOOST_CHECK((vch == std::vector<unsigned char>{{8, 8, 1, 3, 4, 5, 6, 2}}));
+ CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, FLATDATA(bytes), b);
+ BOOST_CHECK((vch == std::vector<unsigned char>{{8, 8, 1, 3, 4, 5, 6, 2}}));
+ vch.clear();
+}
+
BOOST_AUTO_TEST_CASE(streams_serializedata_xor)
{
std::vector<char> in;
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index b1ceef4f64..2a5a78de02 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -10,8 +10,9 @@
#include "consensus/consensus.h"
#include "consensus/validation.h"
#include "key.h"
-#include "main.h"
+#include "validation.h"
#include "miner.h"
+#include "net_processing.h"
#include "pubkey.h"
#include "random.h"
#include "txdb.h"
@@ -19,14 +20,18 @@
#include "ui_interface.h"
#include "rpc/server.h"
#include "rpc/register.h"
+#include "script/sigcache.h"
#include "test/testutil.h"
+#include <memory>
+
#include <boost/filesystem.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/thread.hpp>
std::unique_ptr<CConnman> g_connman;
+FastRandomContext insecure_rand_ctx(true);
extern bool fPrintToConsole;
extern void noui_connect();
@@ -36,6 +41,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
ECC_Start();
SetupEnvironment();
SetupNetworking();
+ InitSignatureCache();
fPrintToDebugLog = false; // don't want to write to debug.log file
fCheckBlockIndex = true;
SelectParams(chainName);
@@ -72,7 +78,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
nScriptCheckThreads = 3;
for (int i=0; i < nScriptCheckThreads-1; i++)
threadGroup.create_thread(&ThreadScriptCheck);
- g_connman = std::unique_ptr<CConnman>(new CConnman());
+ g_connman = std::unique_ptr<CConnman>(new CConnman(0x1337, 0x1337)); // Deterministic randomness for tests.
connman = g_connman.get();
RegisterNodeSignals(GetNodeSignals());
}
@@ -98,7 +104,7 @@ TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST)
{
std::vector<CMutableTransaction> noTxns;
CBlock b = CreateAndProcessBlock(noTxns, scriptPubKey);
- coinbaseTxns.push_back(b.vtx[0]);
+ coinbaseTxns.push_back(*b.vtx[0]);
}
}
@@ -110,24 +116,23 @@ CBlock
TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns, const CScript& scriptPubKey)
{
const CChainParams& chainparams = Params();
- CBlockTemplate *pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey);
+ std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey);
CBlock& block = pblocktemplate->block;
// Replace mempool-selected txns with just coinbase plus passed-in txns:
block.vtx.resize(1);
BOOST_FOREACH(const CMutableTransaction& tx, txns)
- block.vtx.push_back(tx);
+ block.vtx.push_back(MakeTransactionRef(tx));
// IncrementExtraNonce creates a valid coinbase and merkleRoot
unsigned int extraNonce = 0;
IncrementExtraNonce(&block, chainActive.Tip(), extraNonce);
while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;
- CValidationState state;
- ProcessNewBlock(state, chainparams, NULL, &block, true, NULL, connman);
+ std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(block);
+ ProcessNewBlock(chainparams, shared_pblock, true, NULL);
CBlock result = block;
- delete pblocktemplate;
return result;
}
@@ -136,12 +141,12 @@ TestChain100Setup::~TestChain100Setup()
}
-CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(CMutableTransaction &tx, CTxMemPool *pool) {
+CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction &tx, CTxMemPool *pool) {
CTransaction txn(tx);
return FromTx(txn, pool);
}
-CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(CTransaction &txn, CTxMemPool *pool) {
+CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransaction &txn, CTxMemPool *pool) {
bool hasNoDependencies = pool ? pool->HasNoInputsOf(txn) : hadNoDependencies;
// Hack to assume either its completely dependent on other mempool txs or not at all
CAmount inChainValue = hasNoDependencies ? txn.GetValueOut() : 0;
diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h
index 9819a7097d..3dea20445d 100644
--- a/src/test/test_bitcoin.h
+++ b/src/test/test_bitcoin.h
@@ -79,8 +79,8 @@ struct TestMemPoolEntryHelper
nFee(0), nTime(0), dPriority(0.0), nHeight(1),
hadNoDependencies(false), spendsCoinbase(false), sigOpCost(4) { }
- CTxMemPoolEntry FromTx(CMutableTransaction &tx, CTxMemPool *pool = NULL);
- CTxMemPoolEntry FromTx(CTransaction &tx, CTxMemPool *pool = NULL);
+ CTxMemPoolEntry FromTx(const CMutableTransaction &tx, CTxMemPool *pool = NULL);
+ CTxMemPoolEntry FromTx(const CTransaction &tx, CTxMemPool *pool = NULL);
// Change the default value
TestMemPoolEntryHelper &Fee(CAmount _fee) { nFee = _fee; return *this; }
diff --git a/src/test/test_bitcoin_fuzzy.cpp b/src/test/test_bitcoin_fuzzy.cpp
new file mode 100644
index 0000000000..584e6ed008
--- /dev/null
+++ b/src/test/test_bitcoin_fuzzy.cpp
@@ -0,0 +1,256 @@
+// Copyright (c) 2009-2015 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#if defined(HAVE_CONFIG_H)
+#include "config/bitcoin-config.h"
+#endif
+
+#include "consensus/merkle.h"
+#include "primitives/block.h"
+#include "script/script.h"
+#include "addrman.h"
+#include "chain.h"
+#include "coins.h"
+#include "compressor.h"
+#include "net.h"
+#include "protocol.h"
+#include "streams.h"
+#include "undo.h"
+#include "version.h"
+
+#include <stdint.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <vector>
+
+enum TEST_ID {
+ CBLOCK_DESERIALIZE=0,
+ CTRANSACTION_DESERIALIZE,
+ CBLOCKLOCATOR_DESERIALIZE,
+ CBLOCKMERKLEROOT,
+ CADDRMAN_DESERIALIZE,
+ CBLOCKHEADER_DESERIALIZE,
+ CBANENTRY_DESERIALIZE,
+ CTXUNDO_DESERIALIZE,
+ CBLOCKUNDO_DESERIALIZE,
+ CCOINS_DESERIALIZE,
+ CNETADDR_DESERIALIZE,
+ CSERVICE_DESERIALIZE,
+ CMESSAGEHEADER_DESERIALIZE,
+ CADDRESS_DESERIALIZE,
+ CINV_DESERIALIZE,
+ CBLOOMFILTER_DESERIALIZE,
+ CDISKBLOCKINDEX_DESERIALIZE,
+ CTXOUTCOMPRESSOR_DESERIALIZE,
+ TEST_ID_END
+};
+
+bool read_stdin(std::vector<char> &data) {
+ char buffer[1024];
+ ssize_t length=0;
+ while((length = read(STDIN_FILENO, buffer, 1024)) > 0) {
+ data.insert(data.end(), buffer, buffer+length);
+
+ if (data.size() > (1<<20)) return false;
+ }
+ return length==0;
+}
+
+int main(int argc, char **argv)
+{
+ std::vector<char> buffer;
+ if (!read_stdin(buffer)) return 0;
+
+ if (buffer.size() < sizeof(uint32_t)) return 0;
+
+ uint32_t test_id = 0xffffffff;
+ memcpy(&test_id, &buffer[0], sizeof(uint32_t));
+ buffer.erase(buffer.begin(), buffer.begin() + sizeof(uint32_t));
+
+ if (test_id >= TEST_ID_END) return 0;
+
+ CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION);
+ try {
+ int nVersion;
+ ds >> nVersion;
+ ds.SetVersion(nVersion);
+ } catch (const std::ios_base::failure& e) {
+ return 0;
+ }
+
+ switch(test_id) {
+ case CBLOCK_DESERIALIZE:
+ {
+ try
+ {
+ CBlock block;
+ ds >> block;
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CTRANSACTION_DESERIALIZE:
+ {
+ try
+ {
+ CTransaction tx(deserialize, ds);
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CBLOCKLOCATOR_DESERIALIZE:
+ {
+ try
+ {
+ CBlockLocator bl;
+ ds >> bl;
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CBLOCKMERKLEROOT:
+ {
+ try
+ {
+ CBlock block;
+ ds >> block;
+ bool mutated;
+ BlockMerkleRoot(block, &mutated);
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CADDRMAN_DESERIALIZE:
+ {
+ try
+ {
+ CAddrMan am;
+ ds >> am;
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CBLOCKHEADER_DESERIALIZE:
+ {
+ try
+ {
+ CBlockHeader bh;
+ ds >> bh;
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CBANENTRY_DESERIALIZE:
+ {
+ try
+ {
+ CBanEntry be;
+ ds >> be;
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CTXUNDO_DESERIALIZE:
+ {
+ try
+ {
+ CTxUndo tu;
+ ds >> tu;
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CBLOCKUNDO_DESERIALIZE:
+ {
+ try
+ {
+ CBlockUndo bu;
+ ds >> bu;
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CCOINS_DESERIALIZE:
+ {
+ try
+ {
+ CCoins block;
+ ds >> block;
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CNETADDR_DESERIALIZE:
+ {
+ try
+ {
+ CNetAddr na;
+ ds >> na;
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CSERVICE_DESERIALIZE:
+ {
+ try
+ {
+ CService s;
+ ds >> s;
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CMESSAGEHEADER_DESERIALIZE:
+ {
+ CMessageHeader::MessageStartChars pchMessageStart = {0x00, 0x00, 0x00, 0x00};
+ try
+ {
+ CMessageHeader mh(pchMessageStart);
+ ds >> mh;
+ if (!mh.IsValid(pchMessageStart)) {return 0;}
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CADDRESS_DESERIALIZE:
+ {
+ try
+ {
+ CAddress a;
+ ds >> a;
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CINV_DESERIALIZE:
+ {
+ try
+ {
+ CInv i;
+ ds >> i;
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CBLOOMFILTER_DESERIALIZE:
+ {
+ try
+ {
+ CBloomFilter bf;
+ ds >> bf;
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CDISKBLOCKINDEX_DESERIALIZE:
+ {
+ try
+ {
+ CDiskBlockIndex dbi;
+ ds >> dbi;
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CTXOUTCOMPRESSOR_DESERIALIZE:
+ {
+ CTxOut to;
+ CTxOutCompressor toc(to);
+ try
+ {
+ ds >> toc;
+ } catch (const std::ios_base::failure& e) {return 0;}
+
+ break;
+ }
+ default:
+ return 0;
+ }
+ return 0;
+}
+
diff --git a/src/test/test_random.h b/src/test/test_random.h
new file mode 100644
index 0000000000..e61b92b7bc
--- /dev/null
+++ b/src/test/test_random.h
@@ -0,0 +1,23 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2014 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_TEST_RANDOM_H
+#define BITCOIN_TEST_RANDOM_H
+
+#include "random.h"
+
+extern FastRandomContext insecure_rand_ctx;
+
+static inline void seed_insecure_rand(bool fDeterministic = false)
+{
+ insecure_rand_ctx = FastRandomContext(fDeterministic);
+}
+
+static inline uint32_t insecure_rand(void)
+{
+ return insecure_rand_ctx.rand32();
+}
+
+#endif
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index b5af400bc5..ae5406ef7e 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2015 The Bitcoin Core developers
+// Copyright (c) 2011-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -12,7 +12,7 @@
#include "core_io.h"
#include "key.h"
#include "keystore.h"
-#include "main.h" // For CheckTransaction
+#include "validation.h" // For CheckTransaction
#include "policy/policy.h"
#include "script/script.h"
#include "script/sign.h"
@@ -50,10 +50,13 @@ static std::map<string, unsigned int> mapFlagNames = boost::assign::map_list_of
(string("NULLDUMMY"), (unsigned int)SCRIPT_VERIFY_NULLDUMMY)
(string("DISCOURAGE_UPGRADABLE_NOPS"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
(string("CLEANSTACK"), (unsigned int)SCRIPT_VERIFY_CLEANSTACK)
+ (string("MINIMALIF"), (unsigned int)SCRIPT_VERIFY_MINIMALIF)
+ (string("NULLFAIL"), (unsigned int)SCRIPT_VERIFY_NULLFAIL)
(string("CHECKLOCKTIMEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)
(string("CHECKSEQUENCEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKSEQUENCEVERIFY)
(string("WITNESS"), (unsigned int)SCRIPT_VERIFY_WITNESS)
- (string("DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM);
+ (string("DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM)
+ (string("WITNESS_PUBKEYTYPE"), (unsigned int)SCRIPT_VERIFY_WITNESS_PUBKEYTYPE);
unsigned int ParseScriptFlags(string strFlags)
{
@@ -147,8 +150,7 @@ BOOST_AUTO_TEST_CASE(tx_valid)
string transaction = test[1].get_str();
CDataStream stream(ParseHex(transaction), SER_NETWORK, PROTOCOL_VERSION);
- CTransaction tx;
- stream >> tx;
+ CTransaction tx(deserialize, stream);
CValidationState state;
BOOST_CHECK_MESSAGE(CheckTransaction(tx, state), strTest);
@@ -233,8 +235,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
string transaction = test[1].get_str();
CDataStream stream(ParseHex(transaction), SER_NETWORK, PROTOCOL_VERSION );
- CTransaction tx;
- stream >> tx;
+ CTransaction tx(deserialize, stream);
CValidationState state;
fValid = CheckTransaction(tx, state) && state.IsValid();
@@ -343,7 +344,7 @@ BOOST_AUTO_TEST_CASE(test_Get)
BOOST_CHECK_EQUAL(coins.GetValueIn(t1), (50+21+22)*CENT);
}
-void CreateCreditAndSpend(const CKeyStore& keystore, const CScript& outscript, CTransaction& output, CMutableTransaction& input, bool success = true)
+void CreateCreditAndSpend(const CKeyStore& keystore, const CScript& outscript, CTransactionRef& output, CMutableTransaction& input, bool success = true)
{
CMutableTransaction outputm;
outputm.nVersion = 1;
@@ -357,22 +358,22 @@ void CreateCreditAndSpend(const CKeyStore& keystore, const CScript& outscript, C
CDataStream ssout(SER_NETWORK, PROTOCOL_VERSION);
ssout << outputm;
ssout >> output;
- assert(output.vin.size() == 1);
- assert(output.vin[0] == outputm.vin[0]);
- assert(output.vout.size() == 1);
- assert(output.vout[0] == outputm.vout[0]);
- assert(output.wit.vtxinwit.size() == 0);
+ assert(output->vin.size() == 1);
+ assert(output->vin[0] == outputm.vin[0]);
+ assert(output->vout.size() == 1);
+ assert(output->vout[0] == outputm.vout[0]);
+ assert(output->wit.vtxinwit.size() == 0);
CMutableTransaction inputm;
inputm.nVersion = 1;
inputm.vin.resize(1);
- inputm.vin[0].prevout.hash = output.GetHash();
+ inputm.vin[0].prevout.hash = output->GetHash();
inputm.vin[0].prevout.n = 0;
inputm.wit.vtxinwit.resize(1);
inputm.vout.resize(1);
inputm.vout[0].nValue = 1;
inputm.vout[0].scriptPubKey = CScript();
- bool ret = SignSignature(keystore, output, inputm, 0, SIGHASH_ALL);
+ bool ret = SignSignature(keystore, *output, inputm, 0, SIGHASH_ALL);
assert(ret == success);
CDataStream ssin(SER_NETWORK, PROTOCOL_VERSION);
ssin << inputm;
@@ -390,11 +391,11 @@ void CreateCreditAndSpend(const CKeyStore& keystore, const CScript& outscript, C
}
}
-void CheckWithFlag(const CTransaction& output, const CMutableTransaction& input, int flags, bool success)
+void CheckWithFlag(const CTransactionRef& output, const CMutableTransaction& input, int flags, bool success)
{
ScriptError error;
CTransaction inputi(input);
- bool ret = VerifyScript(inputi.vin[0].scriptSig, output.vout[0].scriptPubKey, inputi.wit.vtxinwit.size() > 0 ? &inputi.wit.vtxinwit[0].scriptWitness : NULL, flags, TransactionSignatureChecker(&inputi, 0, output.vout[0].nValue), &error);
+ bool ret = VerifyScript(inputi.vin[0].scriptSig, output->vout[0].scriptPubKey, inputi.wit.vtxinwit.size() > 0 ? &inputi.wit.vtxinwit[0].scriptWitness : NULL, flags, TransactionSignatureChecker(&inputi, 0, output->vout[0].nValue), &error);
assert(ret == success);
}
@@ -427,7 +428,7 @@ BOOST_AUTO_TEST_CASE(test_big_witness_transaction) {
mtx.nVersion = 1;
CKey key;
- key.MakeNewKey(false);
+ key.MakeNewKey(true); // Need to use compressed keys in segwit or the signing will fail
CBasicKeyStore keystore;
keystore.AddKeyPubKey(key, key.GetPubKey());
CKeyID hash = key.GetPubKey().GetID();
@@ -463,10 +464,10 @@ BOOST_AUTO_TEST_CASE(test_big_witness_transaction) {
assert(hashSigned);
}
- CTransaction tx;
CDataStream ssout(SER_NETWORK, PROTOCOL_VERSION);
- WithOrVersion(&ssout, 0) << mtx;
- WithOrVersion(&ssout, 0) >> tx;
+ auto vstream = WithOrVersion(&ssout, 0);
+ vstream << mtx;
+ CTransaction tx(deserialize, vstream);
// check all inputs concurrently, with the cache
PrecomputedTransactionData txdata(tx);
@@ -544,7 +545,7 @@ BOOST_AUTO_TEST_CASE(test_witness)
keystore2.AddCScript(GetScriptForWitness(scriptMulti));
keystore2.AddKeyPubKey(key3, pubkey3);
- CTransaction output1, output2;
+ CTransactionRef output1, output2;
CMutableTransaction input1, input2;
SignatureData sigdata;
@@ -623,38 +624,21 @@ BOOST_AUTO_TEST_CASE(test_witness)
CheckWithFlag(output1, input2, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false);
CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false);
- // Witness pay-to-uncompressed-pubkey (v1).
- CreateCreditAndSpend(keystore, GetScriptForWitness(scriptPubkey1L), output1, input1);
- CreateCreditAndSpend(keystore, GetScriptForWitness(scriptPubkey2L), output2, input2);
- CheckWithFlag(output1, input1, 0, true);
- CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
- CheckWithFlag(output1, input1, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true);
- CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
- CheckWithFlag(output1, input2, 0, true);
- CheckWithFlag(output1, input2, SCRIPT_VERIFY_P2SH, true);
- CheckWithFlag(output1, input2, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false);
- CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false);
+ // Signing disabled for witness pay-to-uncompressed-pubkey (v1).
+ CreateCreditAndSpend(keystore, GetScriptForWitness(scriptPubkey1L), output1, input1, false);
+ CreateCreditAndSpend(keystore, GetScriptForWitness(scriptPubkey2L), output2, input2, false);
- // P2SH witness pay-to-uncompressed-pubkey (v1).
- CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptPubkey1L))), output1, input1);
- CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptPubkey2L))), output2, input2);
- ReplaceRedeemScript(input2.vin[0].scriptSig, GetScriptForWitness(scriptPubkey1L));
- CheckWithFlag(output1, input1, 0, true);
- CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
- CheckWithFlag(output1, input1, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true);
- CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
- CheckWithFlag(output1, input2, 0, true);
- CheckWithFlag(output1, input2, SCRIPT_VERIFY_P2SH, true);
- CheckWithFlag(output1, input2, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false);
- CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false);
+ // Signing disabled for P2SH witness pay-to-uncompressed-pubkey (v1).
+ CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptPubkey1L))), output1, input1, false);
+ CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptPubkey2L))), output2, input2, false);
// Normal 2-of-2 multisig
CreateCreditAndSpend(keystore, scriptMulti, output1, input1, false);
CheckWithFlag(output1, input1, 0, false);
CreateCreditAndSpend(keystore2, scriptMulti, output2, input2, false);
CheckWithFlag(output2, input2, 0, false);
- BOOST_CHECK(output1 == output2);
- UpdateTransaction(input1, 0, CombineSignatures(output1.vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1.vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0)));
+ BOOST_CHECK(*output1 == *output2);
+ UpdateTransaction(input1, 0, CombineSignatures(output1->vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1->vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0)));
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
// P2SH 2-of-2 multisig
@@ -664,8 +648,8 @@ BOOST_AUTO_TEST_CASE(test_witness)
CreateCreditAndSpend(keystore2, GetScriptForDestination(CScriptID(scriptMulti)), output2, input2, false);
CheckWithFlag(output2, input2, 0, true);
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH, false);
- BOOST_CHECK(output1 == output2);
- UpdateTransaction(input1, 0, CombineSignatures(output1.vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1.vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0)));
+ BOOST_CHECK(*output1 == *output2);
+ UpdateTransaction(input1, 0, CombineSignatures(output1->vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1->vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0)));
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
@@ -676,8 +660,8 @@ BOOST_AUTO_TEST_CASE(test_witness)
CreateCreditAndSpend(keystore2, GetScriptForWitness(scriptMulti), output2, input2, false);
CheckWithFlag(output2, input2, 0, true);
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false);
- BOOST_CHECK(output1 == output2);
- UpdateTransaction(input1, 0, CombineSignatures(output1.vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1.vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0)));
+ BOOST_CHECK(*output1 == *output2);
+ UpdateTransaction(input1, 0, CombineSignatures(output1->vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1->vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0)));
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true);
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
@@ -688,8 +672,8 @@ BOOST_AUTO_TEST_CASE(test_witness)
CreateCreditAndSpend(keystore2, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptMulti))), output2, input2, false);
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH, true);
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false);
- BOOST_CHECK(output1 == output2);
- UpdateTransaction(input1, 0, CombineSignatures(output1.vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1.vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0)));
+ BOOST_CHECK(*output1 == *output2);
+ UpdateTransaction(input1, 0, CombineSignatures(output1->vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1->vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0)));
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true);
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
}
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index 76e4e7a4be..9f58f004d5 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -4,7 +4,7 @@
#include "consensus/validation.h"
#include "key.h"
-#include "main.h"
+#include "validation.h"
#include "miner.h"
#include "pubkey.h"
#include "txmempool.h"
@@ -39,6 +39,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
spends.resize(2);
for (int i = 0; i < 2; i++)
{
+ spends[i].nVersion = 1;
spends[i].vin.resize(1);
spends[i].vin[0].prevout.hash = coinbaseTxns[0].GetHash();
spends[i].vin[0].prevout.n = 0;
diff --git a/src/test/uint256_tests.cpp b/src/test/uint256_tests.cpp
index da0a3d73e0..2732948060 100644
--- a/src/test/uint256_tests.cpp
+++ b/src/test/uint256_tests.cpp
@@ -184,25 +184,25 @@ BOOST_AUTO_TEST_CASE( methods ) // GetHex SetHex begin() end() size() GetLow64 G
BOOST_CHECK(OneL.begin() + 32 == OneL.end());
BOOST_CHECK(MaxL.begin() + 32 == MaxL.end());
BOOST_CHECK(TmpL.begin() + 32 == TmpL.end());
- BOOST_CHECK(R1L.GetSerializeSize(0,PROTOCOL_VERSION) == 32);
- BOOST_CHECK(ZeroL.GetSerializeSize(0,PROTOCOL_VERSION) == 32);
+ BOOST_CHECK(GetSerializeSize(R1L, 0, PROTOCOL_VERSION) == 32);
+ BOOST_CHECK(GetSerializeSize(ZeroL, 0, PROTOCOL_VERSION) == 32);
- std::stringstream ss;
- R1L.Serialize(ss,0,PROTOCOL_VERSION);
+ CDataStream ss(0, PROTOCOL_VERSION);
+ ss << R1L;
BOOST_CHECK(ss.str() == std::string(R1Array,R1Array+32));
- TmpL.Unserialize(ss,0,PROTOCOL_VERSION);
+ ss >> TmpL;
BOOST_CHECK(R1L == TmpL);
- ss.str("");
- ZeroL.Serialize(ss,0,PROTOCOL_VERSION);
+ ss.clear();
+ ss << ZeroL;
BOOST_CHECK(ss.str() == std::string(ZeroArray,ZeroArray+32));
- TmpL.Unserialize(ss,0,PROTOCOL_VERSION);
+ ss >> TmpL;
BOOST_CHECK(ZeroL == TmpL);
- ss.str("");
- MaxL.Serialize(ss,0,PROTOCOL_VERSION);
+ ss.clear();
+ ss << MaxL;
BOOST_CHECK(ss.str() == std::string(MaxArray,MaxArray+32));
- TmpL.Unserialize(ss,0,PROTOCOL_VERSION);
+ ss >> TmpL;
BOOST_CHECK(MaxL == TmpL);
- ss.str("");
+ ss.clear();
BOOST_CHECK(R1S.GetHex() == R1S.ToString());
BOOST_CHECK(R2S.GetHex() == R2S.ToString());
@@ -230,24 +230,24 @@ BOOST_AUTO_TEST_CASE( methods ) // GetHex SetHex begin() end() size() GetLow64 G
BOOST_CHECK(OneS.begin() + 20 == OneS.end());
BOOST_CHECK(MaxS.begin() + 20 == MaxS.end());
BOOST_CHECK(TmpS.begin() + 20 == TmpS.end());
- BOOST_CHECK(R1S.GetSerializeSize(0,PROTOCOL_VERSION) == 20);
- BOOST_CHECK(ZeroS.GetSerializeSize(0,PROTOCOL_VERSION) == 20);
+ BOOST_CHECK(GetSerializeSize(R1S, 0, PROTOCOL_VERSION) == 20);
+ BOOST_CHECK(GetSerializeSize(ZeroS, 0, PROTOCOL_VERSION) == 20);
- R1S.Serialize(ss,0,PROTOCOL_VERSION);
+ ss << R1S;
BOOST_CHECK(ss.str() == std::string(R1Array,R1Array+20));
- TmpS.Unserialize(ss,0,PROTOCOL_VERSION);
+ ss >> TmpS;
BOOST_CHECK(R1S == TmpS);
- ss.str("");
- ZeroS.Serialize(ss,0,PROTOCOL_VERSION);
+ ss.clear();
+ ss << ZeroS;
BOOST_CHECK(ss.str() == std::string(ZeroArray,ZeroArray+20));
- TmpS.Unserialize(ss,0,PROTOCOL_VERSION);
+ ss >> TmpS;
BOOST_CHECK(ZeroS == TmpS);
- ss.str("");
- MaxS.Serialize(ss,0,PROTOCOL_VERSION);
+ ss.clear();
+ ss << MaxS;
BOOST_CHECK(ss.str() == std::string(MaxArray,MaxArray+20));
- TmpS.Unserialize(ss,0,PROTOCOL_VERSION);
+ ss >> TmpS;
BOOST_CHECK(MaxS == TmpS);
- ss.str("");
+ ss.clear();
}
BOOST_AUTO_TEST_CASE( conversion )
diff --git a/src/test/univalue_tests.cpp b/src/test/univalue_tests.cpp
index 45d480c816..7f794fcbe9 100644
--- a/src/test/univalue_tests.cpp
+++ b/src/test/univalue_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 BitPay, Inc.
+// Copyright (c) 2014 BitPay Inc.
// Copyright (c) 2014-2015 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index efd4498747..bad72ffc0f 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -6,11 +6,11 @@
#include "clientversion.h"
#include "primitives/transaction.h"
-#include "random.h"
#include "sync.h"
#include "utilstrencodings.h"
#include "utilmoneystr.h"
#include "test/test_bitcoin.h"
+#include "test/test_random.h"
#include <stdint.h>
#include <vector>
diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp
index 1f86a06a3f..bae0eff7e5 100644
--- a/src/test/versionbits_tests.cpp
+++ b/src/test/versionbits_tests.cpp
@@ -1,13 +1,13 @@
-// Copyright (c) 2014-2015 The Bitcoin Core developers
+// Copyright (c) 2014-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "chain.h"
-#include "random.h"
#include "versionbits.h"
#include "test/test_bitcoin.h"
+#include "test/test_random.h"
#include "chainparams.h"
-#include "main.h"
+#include "validation.h"
#include "consensus/params.h"
#include <boost/test/unit_test.hpp>
@@ -30,6 +30,7 @@ public:
bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const { return (pindex->nVersion & 0x100); }
ThresholdState GetStateFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateFor(pindexPrev, paramsDummy, cache); }
+ int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, paramsDummy, cache); }
};
#define CHECKERS 6
@@ -78,6 +79,16 @@ public:
return *this;
}
+ VersionBitsTester& TestStateSinceHeight(int height) {
+ for (int i = 0; i < CHECKERS; i++) {
+ if ((insecure_rand() & ((1 << i) - 1)) == 0) {
+ BOOST_CHECK_MESSAGE(checker[i].GetStateSinceHeightFor(vpblock.empty() ? NULL : vpblock.back()) == height, strprintf("Test %i for StateSinceHeight", num));
+ }
+ }
+ num++;
+ return *this;
+ }
+
VersionBitsTester& TestDefined() {
for (int i = 0; i < CHECKERS; i++) {
if ((insecure_rand() & ((1 << i) - 1)) == 0) {
@@ -137,53 +148,64 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
{
for (int i = 0; i < 64; i++) {
// DEFINED -> FAILED
- VersionBitsTester().TestDefined()
- .Mine(1, TestTime(1), 0x100).TestDefined()
- .Mine(11, TestTime(11), 0x100).TestDefined()
- .Mine(989, TestTime(989), 0x100).TestDefined()
- .Mine(999, TestTime(20000), 0x100).TestDefined()
- .Mine(1000, TestTime(20000), 0x100).TestFailed()
- .Mine(1999, TestTime(30001), 0x100).TestFailed()
- .Mine(2000, TestTime(30002), 0x100).TestFailed()
- .Mine(2001, TestTime(30003), 0x100).TestFailed()
- .Mine(2999, TestTime(30004), 0x100).TestFailed()
- .Mine(3000, TestTime(30005), 0x100).TestFailed()
+ VersionBitsTester().TestDefined().TestStateSinceHeight(0)
+ .Mine(1, TestTime(1), 0x100).TestDefined().TestStateSinceHeight(0)
+ .Mine(11, TestTime(11), 0x100).TestDefined().TestStateSinceHeight(0)
+ .Mine(989, TestTime(989), 0x100).TestDefined().TestStateSinceHeight(0)
+ .Mine(999, TestTime(20000), 0x100).TestDefined().TestStateSinceHeight(0)
+ .Mine(1000, TestTime(20000), 0x100).TestFailed().TestStateSinceHeight(1000)
+ .Mine(1999, TestTime(30001), 0x100).TestFailed().TestStateSinceHeight(1000)
+ .Mine(2000, TestTime(30002), 0x100).TestFailed().TestStateSinceHeight(1000)
+ .Mine(2001, TestTime(30003), 0x100).TestFailed().TestStateSinceHeight(1000)
+ .Mine(2999, TestTime(30004), 0x100).TestFailed().TestStateSinceHeight(1000)
+ .Mine(3000, TestTime(30005), 0x100).TestFailed().TestStateSinceHeight(1000)
// DEFINED -> STARTED -> FAILED
- .Reset().TestDefined()
- .Mine(1, TestTime(1), 0).TestDefined()
- .Mine(1000, TestTime(10000) - 1, 0x100).TestDefined() // One second more and it would be defined
- .Mine(2000, TestTime(10000), 0x100).TestStarted() // So that's what happens the next period
- .Mine(2051, TestTime(10010), 0).TestStarted() // 51 old blocks
- .Mine(2950, TestTime(10020), 0x100).TestStarted() // 899 new blocks
- .Mine(3000, TestTime(20000), 0).TestFailed() // 50 old blocks (so 899 out of the past 1000)
- .Mine(4000, TestTime(20010), 0x100).TestFailed()
+ .Reset().TestDefined().TestStateSinceHeight(0)
+ .Mine(1, TestTime(1), 0).TestDefined().TestStateSinceHeight(0)
+ .Mine(1000, TestTime(10000) - 1, 0x100).TestDefined().TestStateSinceHeight(0) // One second more and it would be defined
+ .Mine(2000, TestTime(10000), 0x100).TestStarted().TestStateSinceHeight(2000) // So that's what happens the next period
+ .Mine(2051, TestTime(10010), 0).TestStarted().TestStateSinceHeight(2000) // 51 old blocks
+ .Mine(2950, TestTime(10020), 0x100).TestStarted().TestStateSinceHeight(2000) // 899 new blocks
+ .Mine(3000, TestTime(20000), 0).TestFailed().TestStateSinceHeight(3000) // 50 old blocks (so 899 out of the past 1000)
+ .Mine(4000, TestTime(20010), 0x100).TestFailed().TestStateSinceHeight(3000)
// DEFINED -> STARTED -> FAILED while threshold reached
- .Reset().TestDefined()
- .Mine(1, TestTime(1), 0).TestDefined()
- .Mine(1000, TestTime(10000) - 1, 0x101).TestDefined() // One second more and it would be defined
- .Mine(2000, TestTime(10000), 0x101).TestStarted() // So that's what happens the next period
- .Mine(2999, TestTime(30000), 0x100).TestStarted() // 999 new blocks
- .Mine(3000, TestTime(30000), 0x100).TestFailed() // 1 new block (so 1000 out of the past 1000 are new)
- .Mine(3999, TestTime(30001), 0).TestFailed()
- .Mine(4000, TestTime(30002), 0).TestFailed()
- .Mine(14333, TestTime(30003), 0).TestFailed()
- .Mine(24000, TestTime(40000), 0).TestFailed()
+ .Reset().TestDefined().TestStateSinceHeight(0)
+ .Mine(1, TestTime(1), 0).TestDefined().TestStateSinceHeight(0)
+ .Mine(1000, TestTime(10000) - 1, 0x101).TestDefined().TestStateSinceHeight(0) // One second more and it would be defined
+ .Mine(2000, TestTime(10000), 0x101).TestStarted().TestStateSinceHeight(2000) // So that's what happens the next period
+ .Mine(2999, TestTime(30000), 0x100).TestStarted().TestStateSinceHeight(2000) // 999 new blocks
+ .Mine(3000, TestTime(30000), 0x100).TestFailed().TestStateSinceHeight(3000) // 1 new block (so 1000 out of the past 1000 are new)
+ .Mine(3999, TestTime(30001), 0).TestFailed().TestStateSinceHeight(3000)
+ .Mine(4000, TestTime(30002), 0).TestFailed().TestStateSinceHeight(3000)
+ .Mine(14333, TestTime(30003), 0).TestFailed().TestStateSinceHeight(3000)
+ .Mine(24000, TestTime(40000), 0).TestFailed().TestStateSinceHeight(3000)
// DEFINED -> STARTED -> LOCKEDIN at the last minute -> ACTIVE
.Reset().TestDefined()
- .Mine(1, TestTime(1), 0).TestDefined()
- .Mine(1000, TestTime(10000) - 1, 0x101).TestDefined() // One second more and it would be defined
- .Mine(2000, TestTime(10000), 0x101).TestStarted() // So that's what happens the next period
- .Mine(2050, TestTime(10010), 0x200).TestStarted() // 50 old blocks
- .Mine(2950, TestTime(10020), 0x100).TestStarted() // 900 new blocks
- .Mine(2999, TestTime(19999), 0x200).TestStarted() // 49 old blocks
- .Mine(3000, TestTime(29999), 0x200).TestLockedIn() // 1 old block (so 900 out of the past 1000)
- .Mine(3999, TestTime(30001), 0).TestLockedIn()
- .Mine(4000, TestTime(30002), 0).TestActive()
- .Mine(14333, TestTime(30003), 0).TestActive()
- .Mine(24000, TestTime(40000), 0).TestActive();
+ .Mine(1, TestTime(1), 0).TestDefined().TestStateSinceHeight(0)
+ .Mine(1000, TestTime(10000) - 1, 0x101).TestDefined().TestStateSinceHeight(0) // One second more and it would be defined
+ .Mine(2000, TestTime(10000), 0x101).TestStarted().TestStateSinceHeight(2000) // So that's what happens the next period
+ .Mine(2050, TestTime(10010), 0x200).TestStarted().TestStateSinceHeight(2000) // 50 old blocks
+ .Mine(2950, TestTime(10020), 0x100).TestStarted().TestStateSinceHeight(2000) // 900 new blocks
+ .Mine(2999, TestTime(19999), 0x200).TestStarted().TestStateSinceHeight(2000) // 49 old blocks
+ .Mine(3000, TestTime(29999), 0x200).TestLockedIn().TestStateSinceHeight(3000) // 1 old block (so 900 out of the past 1000)
+ .Mine(3999, TestTime(30001), 0).TestLockedIn().TestStateSinceHeight(3000)
+ .Mine(4000, TestTime(30002), 0).TestActive().TestStateSinceHeight(4000)
+ .Mine(14333, TestTime(30003), 0).TestActive().TestStateSinceHeight(4000)
+ .Mine(24000, TestTime(40000), 0).TestActive().TestStateSinceHeight(4000)
+
+ // DEFINED multiple periods -> STARTED multiple periods -> FAILED
+ .Reset().TestDefined().TestStateSinceHeight(0)
+ .Mine(999, TestTime(999), 0).TestDefined().TestStateSinceHeight(0)
+ .Mine(1000, TestTime(1000), 0).TestDefined().TestStateSinceHeight(0)
+ .Mine(2000, TestTime(2000), 0).TestDefined().TestStateSinceHeight(0)
+ .Mine(3000, TestTime(10000), 0).TestStarted().TestStateSinceHeight(3000)
+ .Mine(4000, TestTime(10000), 0).TestStarted().TestStateSinceHeight(3000)
+ .Mine(5000, TestTime(10000), 0).TestStarted().TestStateSinceHeight(3000)
+ .Mine(6000, TestTime(20000), 0).TestFailed().TestStateSinceHeight(6000)
+ .Mine(7000, TestTime(20000), 0x100).TestFailed().TestStateSinceHeight(6000);
}
// Sanity checks of version bit deployments
diff --git a/src/timedata.cpp b/src/timedata.cpp
index 25fc494121..b3cbdcf883 100644
--- a/src/timedata.cpp
+++ b/src/timedata.cpp
@@ -13,6 +13,7 @@
#include "ui_interface.h"
#include "util.h"
#include "utilstrencodings.h"
+#include "warnings.h"
#include <boost/foreach.hpp>
@@ -103,8 +104,8 @@ void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample)
if (!fMatch)
{
fDone = true;
- string strMessage = strprintf(_("Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly."), _(PACKAGE_NAME));
- strMiscWarning = strMessage;
+ std::string strMessage = strprintf(_("Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly."), _(PACKAGE_NAME));
+ SetMiscWarning(strMessage);
uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_WARNING);
}
}
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index 99c45d489c..67a69363f0 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -122,8 +122,8 @@ private:
static void eventcb(struct bufferevent *bev, short what, void *ctx);
};
-TorControlConnection::TorControlConnection(struct event_base *base):
- base(base), b_conn(0)
+TorControlConnection::TorControlConnection(struct event_base *_base):
+ base(_base), b_conn(0)
{
}
@@ -194,7 +194,7 @@ void TorControlConnection::eventcb(struct bufferevent *bev, short what, void *ct
}
}
-bool TorControlConnection::Connect(const std::string &target, const ConnectionCB& connected, const ConnectionCB& disconnected)
+bool TorControlConnection::Connect(const std::string &target, const ConnectionCB& _connected, const ConnectionCB& _disconnected)
{
if (b_conn)
Disconnect();
@@ -213,8 +213,8 @@ bool TorControlConnection::Connect(const std::string &target, const ConnectionCB
return false;
bufferevent_setcb(b_conn, TorControlConnection::readcb, NULL, TorControlConnection::eventcb, this);
bufferevent_enable(b_conn, EV_READ|EV_WRITE);
- this->connected = connected;
- this->disconnected = disconnected;
+ this->connected = _connected;
+ this->disconnected = _disconnected;
// Finally, connect to target
if (bufferevent_socket_connect(b_conn, (struct sockaddr*)&connect_to_addr, connect_to_addrlen) < 0) {
@@ -394,18 +394,18 @@ private:
static void reconnect_cb(evutil_socket_t fd, short what, void *arg);
};
-TorController::TorController(struct event_base* baseIn, const std::string& target):
- base(baseIn),
- target(target), conn(base), reconnect(true), reconnect_ev(0),
+TorController::TorController(struct event_base* _base, const std::string& _target):
+ base(_base),
+ target(_target), conn(base), reconnect(true), reconnect_ev(0),
reconnect_timeout(RECONNECT_TIMEOUT_START)
{
reconnect_ev = event_new(base, -1, 0, reconnect_cb, this);
if (!reconnect_ev)
LogPrintf("tor: Failed to create event for reconnection: out of memory?\n");
// Start connection attempts immediately
- if (!conn.Connect(target, boost::bind(&TorController::connected_cb, this, _1),
+ if (!conn.Connect(_target, boost::bind(&TorController::connected_cb, this, _1),
boost::bind(&TorController::disconnected_cb, this, _1) )) {
- LogPrintf("tor: Initiating connection to Tor control port %s failed\n", target);
+ LogPrintf("tor: Initiating connection to Tor control port %s failed\n", _target);
}
// Read service private key if cached
std::pair<bool,std::string> pkf = ReadBinaryFile(GetPrivateKeyFile());
@@ -426,7 +426,7 @@ TorController::~TorController()
}
}
-void TorController::add_onion_cb(TorControlConnection& conn, const TorControlReply& reply)
+void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlReply& reply)
{
if (reply.code == 250) {
LogPrint("tor", "tor: ADD_ONION successful\n");
@@ -454,7 +454,7 @@ void TorController::add_onion_cb(TorControlConnection& conn, const TorControlRep
}
}
-void TorController::auth_cb(TorControlConnection& conn, const TorControlReply& reply)
+void TorController::auth_cb(TorControlConnection& _conn, const TorControlReply& reply)
{
if (reply.code == 250) {
LogPrint("tor", "tor: Authentication successful\n");
@@ -470,11 +470,11 @@ void TorController::auth_cb(TorControlConnection& conn, const TorControlReply& r
// Finally - now create the service
if (private_key.empty()) // No private key, generate one
- private_key = "NEW:BEST";
+ private_key = "NEW:RSA1024"; // Explicitly request RSA1024 - see issue #9214
// Request hidden service, redirect port.
// Note that the 'virtual' port doesn't have to be the same as our internal port, but this is just a convenient
// choice. TODO; refactor the shutdown sequence some day.
- conn.Command(strprintf("ADD_ONION %s Port=%i,127.0.0.1:%i", private_key, GetListenPort(), GetListenPort()),
+ _conn.Command(strprintf("ADD_ONION %s Port=%i,127.0.0.1:%i", private_key, GetListenPort(), GetListenPort()),
boost::bind(&TorController::add_onion_cb, this, _1, _2));
} else {
LogPrintf("tor: Authentication failed\n");
@@ -501,14 +501,14 @@ static std::vector<uint8_t> ComputeResponse(const std::string &key, const std::v
{
CHMAC_SHA256 computeHash((const uint8_t*)key.data(), key.size());
std::vector<uint8_t> computedHash(CHMAC_SHA256::OUTPUT_SIZE, 0);
- computeHash.Write(begin_ptr(cookie), cookie.size());
- computeHash.Write(begin_ptr(clientNonce), clientNonce.size());
- computeHash.Write(begin_ptr(serverNonce), serverNonce.size());
- computeHash.Finalize(begin_ptr(computedHash));
+ computeHash.Write(cookie.data(), cookie.size());
+ computeHash.Write(clientNonce.data(), clientNonce.size());
+ computeHash.Write(serverNonce.data(), serverNonce.size());
+ computeHash.Finalize(computedHash.data());
return computedHash;
}
-void TorController::authchallenge_cb(TorControlConnection& conn, const TorControlReply& reply)
+void TorController::authchallenge_cb(TorControlConnection& _conn, const TorControlReply& reply)
{
if (reply.code == 250) {
LogPrint("tor", "tor: SAFECOOKIE authentication challenge successful\n");
@@ -530,7 +530,7 @@ void TorController::authchallenge_cb(TorControlConnection& conn, const TorContro
}
std::vector<uint8_t> computedClientHash = ComputeResponse(TOR_SAFE_CLIENTKEY, cookie, clientNonce, serverNonce);
- conn.Command("AUTHENTICATE " + HexStr(computedClientHash), boost::bind(&TorController::auth_cb, this, _1, _2));
+ _conn.Command("AUTHENTICATE " + HexStr(computedClientHash), boost::bind(&TorController::auth_cb, this, _1, _2));
} else {
LogPrintf("tor: Invalid reply to AUTHCHALLENGE\n");
}
@@ -539,7 +539,7 @@ void TorController::authchallenge_cb(TorControlConnection& conn, const TorContro
}
}
-void TorController::protocolinfo_cb(TorControlConnection& conn, const TorControlReply& reply)
+void TorController::protocolinfo_cb(TorControlConnection& _conn, const TorControlReply& reply)
{
if (reply.code == 250) {
std::set<std::string> methods;
@@ -579,23 +579,23 @@ void TorController::protocolinfo_cb(TorControlConnection& conn, const TorControl
if (methods.count("HASHEDPASSWORD")) {
LogPrint("tor", "tor: Using HASHEDPASSWORD authentication\n");
boost::replace_all(torpassword, "\"", "\\\"");
- conn.Command("AUTHENTICATE \"" + torpassword + "\"", boost::bind(&TorController::auth_cb, this, _1, _2));
+ _conn.Command("AUTHENTICATE \"" + torpassword + "\"", boost::bind(&TorController::auth_cb, this, _1, _2));
} else {
LogPrintf("tor: Password provided with -torpassword, but HASHEDPASSWORD authentication is not available\n");
}
} else if (methods.count("NULL")) {
LogPrint("tor", "tor: Using NULL authentication\n");
- conn.Command("AUTHENTICATE", boost::bind(&TorController::auth_cb, this, _1, _2));
+ _conn.Command("AUTHENTICATE", boost::bind(&TorController::auth_cb, this, _1, _2));
} else if (methods.count("SAFECOOKIE")) {
// Cookie: hexdump -e '32/1 "%02x""\n"' ~/.tor/control_auth_cookie
LogPrint("tor", "tor: Using SAFECOOKIE authentication, reading cookie authentication from %s\n", cookiefile);
std::pair<bool,std::string> status_cookie = ReadBinaryFile(cookiefile, TOR_COOKIE_SIZE);
if (status_cookie.first && status_cookie.second.size() == TOR_COOKIE_SIZE) {
- // conn.Command("AUTHENTICATE " + HexStr(status_cookie.second), boost::bind(&TorController::auth_cb, this, _1, _2));
+ // _conn.Command("AUTHENTICATE " + HexStr(status_cookie.second), boost::bind(&TorController::auth_cb, this, _1, _2));
cookie = std::vector<uint8_t>(status_cookie.second.begin(), status_cookie.second.end());
clientNonce = std::vector<uint8_t>(TOR_NONCE_SIZE, 0);
GetRandBytes(&clientNonce[0], TOR_NONCE_SIZE);
- conn.Command("AUTHCHALLENGE SAFECOOKIE " + HexStr(clientNonce), boost::bind(&TorController::authchallenge_cb, this, _1, _2));
+ _conn.Command("AUTHCHALLENGE SAFECOOKIE " + HexStr(clientNonce), boost::bind(&TorController::authchallenge_cb, this, _1, _2));
} else {
if (status_cookie.first) {
LogPrintf("tor: Authentication cookie %s is not exactly %i bytes, as is required by the spec\n", cookiefile, TOR_COOKIE_SIZE);
@@ -613,15 +613,15 @@ void TorController::protocolinfo_cb(TorControlConnection& conn, const TorControl
}
}
-void TorController::connected_cb(TorControlConnection& conn)
+void TorController::connected_cb(TorControlConnection& _conn)
{
reconnect_timeout = RECONNECT_TIMEOUT_START;
// First send a PROTOCOLINFO command to figure out what authentication is expected
- if (!conn.Command("PROTOCOLINFO 1", boost::bind(&TorController::protocolinfo_cb, this, _1, _2)))
+ if (!_conn.Command("PROTOCOLINFO 1", boost::bind(&TorController::protocolinfo_cb, this, _1, _2)))
LogPrintf("tor: Error sending initial protocolinfo command\n");
}
-void TorController::disconnected_cb(TorControlConnection& conn)
+void TorController::disconnected_cb(TorControlConnection& _conn)
{
// Stop advertising service when disconnected
if (service.IsValid())
diff --git a/src/txdb.h b/src/txdb.h
index adb3f66327..687c686775 100644
--- a/src/txdb.h
+++ b/src/txdb.h
@@ -43,7 +43,7 @@ struct CDiskTxPos : public CDiskBlockPos
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(*(CDiskBlockPos*)this);
READWRITE(VARINT(nTxOffset));
}
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 0a00d757a2..c3da69c3f0 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -8,7 +8,7 @@
#include "clientversion.h"
#include "consensus/consensus.h"
#include "consensus/validation.h"
-#include "main.h"
+#include "validation.h"
#include "policy/policy.h"
#include "policy/fees.h"
#include "streams.h"
@@ -24,7 +24,7 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
int64_t _nTime, double _entryPriority, unsigned int _entryHeight,
bool poolHasNoInputsOf, CAmount _inChainInputValue,
bool _spendsCoinbase, int64_t _sigOpsCost, LockPoints lp):
- tx(std::make_shared<CTransaction>(_tx)), nFee(_nFee), nTime(_nTime), entryPriority(_entryPriority), entryHeight(_entryHeight),
+ tx(MakeTransactionRef(_tx)), nFee(_nFee), nTime(_nTime), entryPriority(_entryPriority), entryHeight(_entryHeight),
hadNoDependencies(poolHasNoInputsOf), inChainInputValue(_inChainInputValue),
spendsCoinbase(_spendsCoinbase), sigOpCost(_sigOpsCost), lockPoints(lp)
{
@@ -444,7 +444,7 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
totalTxSize += entry.GetTxSize();
minerPolicyEstimator->processTransaction(entry, fCurrentEstimate);
- vTxHashes.emplace_back(hash, newit);
+ vTxHashes.emplace_back(tx.GetWitnessHash(), newit);
newit->vTxHashesIdx = vTxHashes.size() - 1;
return true;
@@ -503,7 +503,7 @@ void CTxMemPool::CalculateDescendants(txiter entryit, setEntries &setDescendants
}
}
-void CTxMemPool::removeRecursive(const CTransaction &origTx, std::list<CTransaction>& removed)
+void CTxMemPool::removeRecursive(const CTransaction &origTx)
{
// Remove transaction from memory pool
{
@@ -530,9 +530,6 @@ void CTxMemPool::removeRecursive(const CTransaction &origTx, std::list<CTransact
BOOST_FOREACH(txiter it, txToRemove) {
CalculateDescendants(it, setAllRemoves);
}
- BOOST_FOREACH(txiter it, setAllRemoves) {
- removed.push_back(it->GetTx());
- }
RemoveStaged(setAllRemoves, false);
}
}
@@ -541,7 +538,7 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem
{
// Remove transactions spending a coinbase which are now immature and no-longer-final transactions
LOCK(cs);
- list<CTransaction> transactionsToRemove;
+ setEntries txToRemove;
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
const CTransaction& tx = it->GetTx();
LockPoints lp = it->GetLockPoints();
@@ -549,16 +546,16 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem
if (!CheckFinalTx(tx, flags) || !CheckSequenceLocks(tx, flags, &lp, validLP)) {
// Note if CheckSequenceLocks fails the LockPoints may still be invalid
// So it's critical that we remove the tx and not depend on the LockPoints.
- transactionsToRemove.push_back(tx);
+ txToRemove.insert(it);
} else if (it->GetSpendsCoinbase()) {
BOOST_FOREACH(const CTxIn& txin, tx.vin) {
indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash);
if (it2 != mapTx.end())
continue;
const CCoins *coins = pcoins->AccessCoins(txin.prevout.hash);
- if (nCheckFrequency != 0) assert(coins);
+ if (nCheckFrequency != 0) assert(coins);
if (!coins || (coins->IsCoinBase() && ((signed long)nMemPoolHeight) - coins->nHeight < COINBASE_MATURITY)) {
- transactionsToRemove.push_back(tx);
+ txToRemove.insert(it);
break;
}
}
@@ -567,13 +564,14 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem
mapTx.modify(it, update_lock_points(lp));
}
}
- BOOST_FOREACH(const CTransaction& tx, transactionsToRemove) {
- list<CTransaction> removed;
- removeRecursive(tx, removed);
+ setEntries setAllRemoves;
+ for (txiter it : txToRemove) {
+ CalculateDescendants(it, setAllRemoves);
}
+ RemoveStaged(setAllRemoves, false);
}
-void CTxMemPool::removeConflicts(const CTransaction &tx, std::list<CTransaction>& removed)
+void CTxMemPool::removeConflicts(const CTransaction &tx)
{
// Remove transactions which depend on inputs of tx, recursively
LOCK(cs);
@@ -583,7 +581,7 @@ void CTxMemPool::removeConflicts(const CTransaction &tx, std::list<CTransaction>
const CTransaction &txConflict = *it->second;
if (txConflict != tx)
{
- removeRecursive(txConflict, removed);
+ removeRecursive(txConflict);
ClearPrioritisation(txConflict.GetHash());
}
}
@@ -593,29 +591,29 @@ void CTxMemPool::removeConflicts(const CTransaction &tx, std::list<CTransaction>
/**
* Called when a block is connected. Removes from mempool and updates the miner fee estimator.
*/
-void CTxMemPool::removeForBlock(const std::vector<CTransaction>& vtx, unsigned int nBlockHeight,
- std::list<CTransaction>& conflicts, bool fCurrentEstimate)
+void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight,
+ bool fCurrentEstimate)
{
LOCK(cs);
std::vector<CTxMemPoolEntry> entries;
- BOOST_FOREACH(const CTransaction& tx, vtx)
+ for (const auto& tx : vtx)
{
- uint256 hash = tx.GetHash();
+ uint256 hash = tx->GetHash();
indexed_transaction_set::iterator i = mapTx.find(hash);
if (i != mapTx.end())
entries.push_back(*i);
}
- BOOST_FOREACH(const CTransaction& tx, vtx)
+ for (const auto& tx : vtx)
{
- txiter it = mapTx.find(tx.GetHash());
+ txiter it = mapTx.find(tx->GetHash());
if (it != mapTx.end()) {
setEntries stage;
stage.insert(it);
RemoveStaged(stage, true);
}
- removeConflicts(tx, conflicts);
- ClearPrioritisation(tx.GetHash());
+ removeConflicts(*tx);
+ ClearPrioritisation(tx->GetHash());
}
// After the txs in the new block have been removed from the mempool, update policy estimates
minerPolicyEstimator->processBlock(nBlockHeight, entries, fCurrentEstimate);
@@ -647,7 +645,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
if (nCheckFrequency == 0)
return;
- if (insecure_rand() >= nCheckFrequency)
+ if (GetRand(std::numeric_limits<uint32_t>::max()) >= nCheckFrequency)
return;
LogPrint("mempool", "Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size());
@@ -830,6 +828,10 @@ void CTxMemPool::queryHashes(vector<uint256>& vtxid)
}
}
+static TxMempoolInfo GetInfo(CTxMemPool::indexed_transaction_set::const_iterator it) {
+ return TxMempoolInfo{it->GetSharedTx(), it->GetTime(), CFeeRate(it->GetFee(), it->GetTxSize()), it->GetModifiedFee() - it->GetFee()};
+}
+
std::vector<TxMempoolInfo> CTxMemPool::infoAll() const
{
LOCK(cs);
@@ -838,13 +840,13 @@ std::vector<TxMempoolInfo> CTxMemPool::infoAll() const
std::vector<TxMempoolInfo> ret;
ret.reserve(mapTx.size());
for (auto it : iters) {
- ret.push_back(TxMempoolInfo{it->GetSharedTx(), it->GetTime(), CFeeRate(it->GetFee(), it->GetTxSize())});
+ ret.push_back(GetInfo(it));
}
return ret;
}
-std::shared_ptr<const CTransaction> CTxMemPool::get(const uint256& hash) const
+CTransactionRef CTxMemPool::get(const uint256& hash) const
{
LOCK(cs);
indexed_transaction_set::const_iterator i = mapTx.find(hash);
@@ -859,7 +861,7 @@ TxMempoolInfo CTxMemPool::info(const uint256& hash) const
indexed_transaction_set::const_iterator i = mapTx.find(hash);
if (i == mapTx.end())
return TxMempoolInfo();
- return TxMempoolInfo{i->GetSharedTx(), i->GetTime(), CFeeRate(i->GetFee(), i->GetTxSize())};
+ return GetInfo(i);
}
CFeeRate CTxMemPool::estimateFee(int nBlocks) const
@@ -888,7 +890,7 @@ CTxMemPool::WriteFeeEstimates(CAutoFile& fileout) const
{
try {
LOCK(cs);
- fileout << 109900; // version required to read: 0.10.99 or later
+ fileout << 139900; // version required to read: 0.13.99 or later
fileout << CLIENT_VERSION; // version that wrote the file
minerPolicyEstimator->Write(fileout);
}
@@ -907,9 +909,8 @@ CTxMemPool::ReadFeeEstimates(CAutoFile& filein)
filein >> nVersionRequired >> nVersionThatWrote;
if (nVersionRequired > CLIENT_VERSION)
return error("CTxMemPool::ReadFeeEstimates(): up-version (%d) fee estimate file", nVersionRequired);
-
LOCK(cs);
- minerPolicyEstimator->Read(filein);
+ minerPolicyEstimator->Read(filein, nVersionThatWrote);
}
catch (const std::exception&) {
LogPrintf("CTxMemPool::ReadFeeEstimates(): unable to read policy estimator data (non-fatal)\n");
@@ -972,7 +973,7 @@ bool CCoinsViewMemPool::GetCoins(const uint256 &txid, CCoins &coins) const {
// If an entry in the mempool exists, always return that one, as it's guaranteed to never
// conflict with the underlying cache, and it cannot have pruned entries (as it contains full)
// transactions. First checking the underlying cache risks returning a pruned entry instead.
- shared_ptr<const CTransaction> ptx = mempool.get(txid);
+ CTransactionRef ptx = mempool.get(txid);
if (ptx) {
coins = CCoins(*ptx, MEMPOOL_HEIGHT);
return true;
@@ -1116,8 +1117,8 @@ void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<uint256>* pvNoSpendsRe
std::vector<CTransaction> txn;
if (pvNoSpendsRemaining) {
txn.reserve(stage.size());
- BOOST_FOREACH(txiter it, stage)
- txn.push_back(it->GetTx());
+ BOOST_FOREACH(txiter iter, stage)
+ txn.push_back(iter->GetTx());
}
RemoveStaged(stage, false);
if (pvNoSpendsRemaining) {
@@ -1125,8 +1126,8 @@ void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<uint256>* pvNoSpendsRe
BOOST_FOREACH(const CTxIn& txin, tx.vin) {
if (exists(txin.prevout.hash))
continue;
- auto it = mapNextTx.lower_bound(COutPoint(txin.prevout.hash, 0));
- if (it == mapNextTx.end() || it->first->hash != txin.prevout.hash)
+ auto iter = mapNextTx.lower_bound(COutPoint(txin.prevout.hash, 0));
+ if (iter == mapNextTx.end() || iter->first->hash != txin.prevout.hash)
pvNoSpendsRemaining->push_back(txin.prevout.hash);
}
}
@@ -1136,3 +1137,10 @@ void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<uint256>* pvNoSpendsRe
if (maxFeeRateRemoved > CFeeRate(0))
LogPrint("mempool", "Removed %u txn, rolling minimum fee bumped to %s\n", nTxnRemoved, maxFeeRateRemoved.ToString());
}
+
+bool CTxMemPool::TransactionWithinChainLimit(const uint256& txid, size_t chainLimit) const {
+ LOCK(cs);
+ auto it = mapTx.find(txid);
+ return it == mapTx.end() || (it->GetCountWithAncestors() < chainLimit &&
+ it->GetCountWithDescendants() < chainLimit);
+}
diff --git a/src/txmempool.h b/src/txmempool.h
index 2c2127f326..8a5787a886 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -6,15 +6,19 @@
#ifndef BITCOIN_TXMEMPOOL_H
#define BITCOIN_TXMEMPOOL_H
-#include <list>
#include <memory>
#include <set>
+#include <map>
+#include <vector>
+#include <utility>
+#include <string>
#include "amount.h"
#include "coins.h"
#include "indirectmap.h"
#include "primitives/transaction.h"
#include "sync.h"
+#include "random.h"
#undef foreach
#include "boost/multi_index_container.hpp"
@@ -58,7 +62,7 @@ class CTxMemPool;
/** \class CTxMemPoolEntry
*
- * CTxMemPoolEntry stores data about the correponding transaction, as well
+ * CTxMemPoolEntry stores data about the corresponding transaction, as well
* as data about all in-mempool transactions that depend on the transaction
* ("descendant" transactions).
*
@@ -76,7 +80,7 @@ class CTxMemPool;
class CTxMemPoolEntry
{
private:
- std::shared_ptr<const CTransaction> tx;
+ CTransactionRef tx;
CAmount nFee; //!< Cached to avoid expensive parent-transaction lookups
size_t nTxWeight; //!< ... and avoid recomputing tx weight (also used for GetTxSize())
size_t nModSize; //!< ... and modified size for priority
@@ -114,7 +118,7 @@ public:
CTxMemPoolEntry(const CTxMemPoolEntry& other);
const CTransaction& GetTx() const { return *this->tx; }
- std::shared_ptr<const CTransaction> GetSharedTx() const { return this->tx; }
+ CTransactionRef GetSharedTx() const { return this->tx; }
/**
* Fast calculation of lower bound of current priority as update
* from entry priority. Only inputs that were originally in-chain will age.
@@ -318,24 +322,30 @@ class CBlockPolicyEstimator;
struct TxMempoolInfo
{
/** The transaction itself */
- std::shared_ptr<const CTransaction> tx;
+ CTransactionRef tx;
/** Time the transaction entered the mempool. */
int64_t nTime;
/** Feerate of the transaction. */
CFeeRate feeRate;
+
+ /** The fee delta. */
+ int64_t nFeeDelta;
};
/**
- * CTxMemPool stores valid-according-to-the-current-best-chain
- * transactions that may be included in the next block.
+ * CTxMemPool stores valid-according-to-the-current-best-chain transactions
+ * that may be included in the next block.
*
- * Transactions are added when they are seen on the network
- * (or created by the local node), but not all transactions seen
- * are added to the pool: if a new transaction double-spends
- * an input of a transaction in the pool, it is dropped,
- * as are non-standard transactions.
+ * Transactions are added when they are seen on the network (or created by the
+ * local node), but not all transactions seen are added to the pool. For
+ * example, the following new transactions will not be added to the mempool:
+ * - a transaction which doesn't make the mimimum fee requirements.
+ * - a new transaction that double-spends an input of a transaction already in
+ * the pool where the new transaction does not meet the Replace-By-Fee
+ * requirements as defined in BIP 125.
+ * - a non-standard transaction.
*
* CTxMemPool::mapTx, and CTxMemPoolEntry bookkeeping:
*
@@ -462,7 +472,7 @@ public:
indexed_transaction_set mapTx;
typedef indexed_transaction_set::nth_index<0>::type::iterator txiter;
- std::vector<std::pair<uint256, txiter> > vTxHashes; //!< All tx hashes/entries in mapTx, in random order
+ std::vector<std::pair<uint256, txiter> > vTxHashes; //!< All tx witness hashes/entries in mapTx, in random order
struct CompareIteratorByHash {
bool operator()(const txiter &a, const txiter &b) const {
@@ -517,11 +527,11 @@ public:
bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, bool fCurrentEstimate = true);
bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, setEntries &setAncestors, bool fCurrentEstimate = true);
- void removeRecursive(const CTransaction &tx, std::list<CTransaction>& removed);
+ void removeRecursive(const CTransaction &tx);
void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags);
- void removeConflicts(const CTransaction &tx, std::list<CTransaction>& removed);
- void removeForBlock(const std::vector<CTransaction>& vtx, unsigned int nBlockHeight,
- std::list<CTransaction>& conflicts, bool fCurrentEstimate = true);
+ void removeConflicts(const CTransaction &tx);
+ void removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight,
+ bool fCurrentEstimate = true);
void clear();
void _clear(); //lock free
bool CompareDepthAndScore(const uint256& hasha, const uint256& hashb);
@@ -595,6 +605,9 @@ public:
/** Expire all transaction (and their dependencies) in the mempool older than time. Return the number of removed transactions. */
int Expire(int64_t time);
+ /** Returns false if the transaction is in the mempool and not within the chain limit specified. */
+ bool TransactionWithinChainLimit(const uint256& txid, size_t chainLimit) const;
+
unsigned long size()
{
LOCK(cs);
@@ -613,7 +626,7 @@ public:
return (mapTx.count(hash) != 0);
}
- std::shared_ptr<const CTransaction> get(const uint256& hash) const;
+ CTransactionRef get(const uint256& hash) const;
TxMempoolInfo info(const uint256& hash) const;
std::vector<TxMempoolInfo> infoAll() const;
diff --git a/src/ui_interface.cpp b/src/ui_interface.cpp
index c778e40a90..74a13e0e05 100644
--- a/src/ui_interface.cpp
+++ b/src/ui_interface.cpp
@@ -18,6 +18,11 @@ void InitWarning(const std::string& str)
uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_WARNING);
}
+std::string AmountHighWarn(const std::string& optname)
+{
+ return strprintf(_("%s is set very high!"), optname);
+}
+
std::string AmountErrMsg(const char* const optname, const std::string& strValue)
{
return strprintf(_("Invalid amount for -%s=<amount>: '%s'"), optname, strValue);
diff --git a/src/ui_interface.h b/src/ui_interface.h
index 7e6557f8e2..15b9614f63 100644
--- a/src/ui_interface.h
+++ b/src/ui_interface.h
@@ -85,6 +85,9 @@ public:
/** Number of network connections changed. */
boost::signals2::signal<void (int newNumConnections)> NotifyNumConnectionsChanged;
+ /** Network activity state changed. */
+ boost::signals2::signal<void (bool networkActive)> NotifyNetworkActiveChanged;
+
/**
* Status bar alerts changed.
*/
@@ -112,6 +115,8 @@ void InitWarning(const std::string& str);
/** Show error message **/
bool InitError(const std::string& str);
+std::string AmountHighWarn(const std::string& optname);
+
std::string AmountErrMsg(const char* const optname, const std::string& strValue);
extern CClientUIInterface uiInterface;
diff --git a/src/uint256.h b/src/uint256.h
index dd8432d74c..86e7c0b6c6 100644
--- a/src/uint256.h
+++ b/src/uint256.h
@@ -78,11 +78,6 @@ public:
return sizeof(data);
}
- unsigned int GetSerializeSize(int nType, int nVersion) const
- {
- return sizeof(data);
- }
-
uint64_t GetUint64(int pos) const
{
const uint8_t* ptr = data + pos * 8;
@@ -97,13 +92,13 @@ public:
}
template<typename Stream>
- void Serialize(Stream& s, int nType, int nVersion) const
+ void Serialize(Stream& s) const
{
s.write((char*)data, sizeof(data));
}
template<typename Stream>
- void Unserialize(Stream& s, int nType, int nVersion)
+ void Unserialize(Stream& s)
{
s.read((char*)data, sizeof(data));
}
diff --git a/src/undo.h b/src/undo.h
index d4fc84c90c..a5d276e7f3 100644
--- a/src/undo.h
+++ b/src/undo.h
@@ -27,29 +27,23 @@ public:
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<typename Stream>
- void Serialize(Stream &s, int nType, int nVersion) const {
- ::Serialize(s, VARINT(nHeight*2+(fCoinBase ? 1 : 0)), nType, nVersion);
+ void Serialize(Stream &s) const {
+ ::Serialize(s, VARINT(nHeight*2+(fCoinBase ? 1 : 0)));
if (nHeight > 0)
- ::Serialize(s, VARINT(this->nVersion), nType, nVersion);
- ::Serialize(s, CTxOutCompressor(REF(txout)), nType, nVersion);
+ ::Serialize(s, VARINT(this->nVersion));
+ ::Serialize(s, CTxOutCompressor(REF(txout)));
}
template<typename Stream>
- void Unserialize(Stream &s, int nType, int nVersion) {
+ void Unserialize(Stream &s) {
unsigned int nCode = 0;
- ::Unserialize(s, VARINT(nCode), nType, nVersion);
+ ::Unserialize(s, VARINT(nCode));
nHeight = nCode / 2;
fCoinBase = nCode & 1;
if (nHeight > 0)
- ::Unserialize(s, VARINT(this->nVersion), nType, nVersion);
- ::Unserialize(s, REF(CTxOutCompressor(REF(txout))), nType, nVersion);
+ ::Unserialize(s, VARINT(this->nVersion));
+ ::Unserialize(s, REF(CTxOutCompressor(REF(txout))));
}
};
@@ -63,7 +57,7 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(vprevout);
}
};
@@ -77,7 +71,7 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(vtxundo);
}
};
diff --git a/src/univalue/.travis.yml b/src/univalue/.travis.yml
index d318d9cc8f..132743d349 100644
--- a/src/univalue/.travis.yml
+++ b/src/univalue/.travis.yml
@@ -1,4 +1,3 @@
-
language: cpp
compiler:
@@ -26,6 +25,7 @@ addons:
- pkg-config
before_script:
+ - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew uninstall libtool; brew install libtool; fi
- if [ -n "$USE_SHELL" ]; then export CONFIG_SHELL="$USE_SHELL"; fi
- test -n "$USE_SHELL" && eval '"$USE_SHELL" -c "./autogen.sh"' || ./autogen.sh
diff --git a/src/univalue/include/univalue.h b/src/univalue/include/univalue.h
index 8428b1c683..e8ce283519 100644
--- a/src/univalue/include/univalue.h
+++ b/src/univalue/include/univalue.h
@@ -56,7 +56,7 @@ public:
bool setNumStr(const std::string& val);
bool setInt(uint64_t val);
bool setInt(int64_t val);
- bool setInt(int val) { return setInt((int64_t)val); }
+ bool setInt(int val_) { return setInt((int64_t)val_); }
bool setFloat(double val);
bool setStr(const std::string& val);
bool setArray();
@@ -95,28 +95,28 @@ public:
bool push_backV(const std::vector<UniValue>& vec);
bool pushKV(const std::string& key, const UniValue& val);
- bool pushKV(const std::string& key, const std::string& val) {
- UniValue tmpVal(VSTR, val);
+ bool pushKV(const std::string& key, const std::string& val_) {
+ UniValue tmpVal(VSTR, val_);
return pushKV(key, tmpVal);
}
bool pushKV(const std::string& key, const char *val_) {
- std::string val(val_);
- return pushKV(key, val);
+ std::string _val(val_);
+ return pushKV(key, _val);
}
- bool pushKV(const std::string& key, int64_t val) {
- UniValue tmpVal(val);
+ bool pushKV(const std::string& key, int64_t val_) {
+ UniValue tmpVal(val_);
return pushKV(key, tmpVal);
}
- bool pushKV(const std::string& key, uint64_t val) {
- UniValue tmpVal(val);
+ bool pushKV(const std::string& key, uint64_t val_) {
+ UniValue tmpVal(val_);
return pushKV(key, tmpVal);
}
- bool pushKV(const std::string& key, int val) {
- UniValue tmpVal((int64_t)val);
+ bool pushKV(const std::string& key, int val_) {
+ UniValue tmpVal((int64_t)val_);
return pushKV(key, tmpVal);
}
- bool pushKV(const std::string& key, double val) {
- UniValue tmpVal(val);
+ bool pushKV(const std::string& key, double val_) {
+ UniValue tmpVal(val_);
return pushKV(key, tmpVal);
}
bool pushKVs(const UniValue& obj);
@@ -142,10 +142,10 @@ private:
public:
// Strict type-specific getters, these throw std::runtime_error if the
// value is of unexpected type
- std::vector<std::string> getKeys() const;
- std::vector<UniValue> getValues() const;
+ const std::vector<std::string>& getKeys() const;
+ const std::vector<UniValue>& getValues() const;
bool get_bool() const;
- std::string get_str() const;
+ const std::string& get_str() const;
int get_int() const;
int64_t get_int64() const;
double get_real() const;
diff --git a/src/univalue/lib/univalue.cpp b/src/univalue/lib/univalue.cpp
index 0076d6678e..5a2860c13f 100644
--- a/src/univalue/lib/univalue.cpp
+++ b/src/univalue/lib/univalue.cpp
@@ -119,32 +119,29 @@ bool UniValue::setNumStr(const string& val_)
return true;
}
-bool UniValue::setInt(uint64_t val)
+bool UniValue::setInt(uint64_t val_)
{
- string s;
ostringstream oss;
- oss << val;
+ oss << val_;
return setNumStr(oss.str());
}
-bool UniValue::setInt(int64_t val)
+bool UniValue::setInt(int64_t val_)
{
- string s;
ostringstream oss;
- oss << val;
+ oss << val_;
return setNumStr(oss.str());
}
-bool UniValue::setFloat(double val)
+bool UniValue::setFloat(double val_)
{
- string s;
ostringstream oss;
- oss << std::setprecision(16) << val;
+ oss << std::setprecision(16) << val_;
bool ret = setNumStr(oss.str());
typ = VNUM;
@@ -173,12 +170,12 @@ bool UniValue::setObject()
return true;
}
-bool UniValue::push_back(const UniValue& val)
+bool UniValue::push_back(const UniValue& val_)
{
if (typ != VARR)
return false;
- values.push_back(val);
+ values.push_back(val_);
return true;
}
@@ -192,13 +189,13 @@ bool UniValue::push_backV(const std::vector<UniValue>& vec)
return true;
}
-bool UniValue::pushKV(const std::string& key, const UniValue& val)
+bool UniValue::pushKV(const std::string& key, const UniValue& val_)
{
if (typ != VOBJ)
return false;
keys.push_back(key);
- values.push_back(val);
+ values.push_back(val_);
return true;
}
@@ -228,7 +225,7 @@ int UniValue::findKey(const std::string& key) const
bool UniValue::checkObject(const std::map<std::string,UniValue::VType>& t)
{
for (std::map<std::string,UniValue::VType>::const_iterator it = t.begin();
- it != t.end(); it++) {
+ it != t.end(); ++it) {
int idx = findKey(it->first);
if (idx < 0)
return false;
@@ -286,14 +283,14 @@ const UniValue& find_value(const UniValue& obj, const std::string& name)
return NullUniValue;
}
-std::vector<std::string> UniValue::getKeys() const
+const std::vector<std::string>& UniValue::getKeys() const
{
if (typ != VOBJ)
throw std::runtime_error("JSON value is not an object as expected");
return keys;
}
-std::vector<UniValue> UniValue::getValues() const
+const std::vector<UniValue>& UniValue::getValues() const
{
if (typ != VOBJ && typ != VARR)
throw std::runtime_error("JSON value is not an object or array as expected");
@@ -307,7 +304,7 @@ bool UniValue::get_bool() const
return getBool();
}
-std::string UniValue::get_str() const
+const std::string& UniValue::get_str() const
{
if (typ != VSTR)
throw std::runtime_error("JSON value is not a string as expected");
diff --git a/src/util.cpp b/src/util.cpp
index c7d147a11e..977f8993ed 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -107,8 +107,7 @@ map<string, vector<string> > mapMultiArgs;
bool fDebug = false;
bool fPrintToConsole = false;
bool fPrintToDebugLog = true;
-bool fServer = false;
-string strMiscWarning;
+
bool fLogTimestamps = DEFAULT_LOGTIMESTAMPS;
bool fLogTimeMicros = DEFAULT_LOGTIMEMICROS;
bool fLogIPs = DEFAULT_LOGIPS;
@@ -259,7 +258,7 @@ bool LogAcceptCategory(const char* category)
* suppress printing of the timestamp when multiple calls are made that don't
* end in a newline. Initialize it to true, and hold it, in the calling context.
*/
-static std::string LogTimestampStr(const std::string &str, bool *fStartedNewLine)
+static std::string LogTimestampStr(const std::string &str, std::atomic_bool *fStartedNewLine)
{
string strStamped;
@@ -286,7 +285,7 @@ static std::string LogTimestampStr(const std::string &str, bool *fStartedNewLine
int LogPrintStr(const std::string &str)
{
int ret = 0; // Returns total number of characters written
- static bool fStartedNewLine = true;
+ static std::atomic_bool fStartedNewLine(true);
string strTimestamped = LogTimestampStr(str, &fStartedNewLine);
@@ -518,19 +517,20 @@ void ClearDatadirCache()
pathCachedNetSpecific = boost::filesystem::path();
}
-boost::filesystem::path GetConfigFile()
+boost::filesystem::path GetConfigFile(const std::string& confPath)
{
- boost::filesystem::path pathConfigFile(GetArg("-conf", BITCOIN_CONF_FILENAME));
+ boost::filesystem::path pathConfigFile(confPath);
if (!pathConfigFile.is_complete())
pathConfigFile = GetDataDir(false) / pathConfigFile;
return pathConfigFile;
}
-void ReadConfigFile(map<string, string>& mapSettingsRet,
+void ReadConfigFile(const std::string& confPath,
+ map<string, string>& mapSettingsRet,
map<string, vector<string> >& mapMultiSettingsRet)
{
- boost::filesystem::ifstream streamConfig(GetConfigFile());
+ boost::filesystem::ifstream streamConfig(GetConfigFile(confPath));
if (!streamConfig.good())
return; // No bitcoin.conf file is OK
@@ -600,19 +600,19 @@ bool TryCreateDirectory(const boost::filesystem::path& p)
return false;
}
-void FileCommit(FILE *fileout)
+void FileCommit(FILE *file)
{
- fflush(fileout); // harmless if redundantly called
+ fflush(file); // harmless if redundantly called
#ifdef WIN32
- HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(fileout));
+ HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
FlushFileBuffers(hFile);
#else
#if defined(__linux__) || defined(__NetBSD__)
- fdatasync(fileno(fileout));
+ fdatasync(fileno(file));
#elif defined(__APPLE__) && defined(F_FULLFSYNC)
- fcntl(fileno(fileout), F_FULLFSYNC, 0);
+ fcntl(fileno(file), F_FULLFSYNC, 0);
#else
- fsync(fileno(fileout));
+ fsync(fileno(file));
#endif
#endif
}
@@ -704,13 +704,13 @@ void ShrinkDebugFile()
// Restart the file with some of the end
std::vector <char> vch(200000,0);
fseek(file, -((long)vch.size()), SEEK_END);
- int nBytes = fread(begin_ptr(vch), 1, vch.size(), file);
+ int nBytes = fread(vch.data(), 1, vch.size(), file);
fclose(file);
file = fopen(pathLog.string().c_str(), "w");
if (file)
{
- fwrite(begin_ptr(vch), 1, nBytes, file);
+ fwrite(vch.data(), 1, nBytes, file);
fclose(file);
}
}
diff --git a/src/util.h b/src/util.h
index 39328b51ef..3ec38a7c7d 100644
--- a/src/util.h
+++ b/src/util.h
@@ -46,8 +46,7 @@ extern std::map<std::string, std::vector<std::string> > mapMultiArgs;
extern bool fDebug;
extern bool fPrintToConsole;
extern bool fPrintToDebugLog;
-extern bool fServer;
-extern std::string strMiscWarning;
+
extern bool fLogTimestamps;
extern bool fLogTimeMicros;
extern bool fLogIPs;
@@ -93,7 +92,7 @@ bool error(const char* fmt, const Args&... args)
void PrintExceptionContinue(const std::exception *pex, const char* pszThread);
void ParseParameters(int argc, const char*const argv[]);
-void FileCommit(FILE *fileout);
+void FileCommit(FILE *file);
bool TruncateFile(FILE *file, unsigned int length);
int RaiseFileDescriptorLimit(int nMinFD);
void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length);
@@ -102,12 +101,12 @@ bool TryCreateDirectory(const boost::filesystem::path& p);
boost::filesystem::path GetDefaultDataDir();
const boost::filesystem::path &GetDataDir(bool fNetSpecific = true);
void ClearDatadirCache();
-boost::filesystem::path GetConfigFile();
+boost::filesystem::path GetConfigFile(const std::string& confPath);
#ifndef WIN32
boost::filesystem::path GetPidFile();
void CreatePidFile(const boost::filesystem::path &path, pid_t pid);
#endif
-void ReadConfigFile(std::map<std::string, std::string>& mapSettingsRet, std::map<std::string, std::vector<std::string> >& mapMultiSettingsRet);
+void ReadConfigFile(const std::string& confPath, std::map<std::string, std::string>& mapSettingsRet, std::map<std::string, std::vector<std::string> >& mapMultiSettingsRet);
#ifdef WIN32
boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate = true);
#endif
diff --git a/src/utiltime.cpp b/src/utiltime.cpp
index da590f8889..51d545ef8a 100644
--- a/src/utiltime.cpp
+++ b/src/utiltime.cpp
@@ -74,8 +74,9 @@ void MilliSleep(int64_t n)
std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime)
{
+ static std::locale classic(std::locale::classic());
// std::locale takes ownership of the pointer
- std::locale loc(std::locale::classic(), new boost::posix_time::time_facet(pszFormat));
+ std::locale loc(classic, new boost::posix_time::time_facet(pszFormat));
std::stringstream ss;
ss.imbue(loc);
ss << boost::posix_time::from_time_t(nTime);
diff --git a/src/main.cpp b/src/validation.cpp
index 593897f516..0f5975922b 100644
--- a/src/main.cpp
+++ b/src/validation.cpp
@@ -1,13 +1,11 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2015 The Bitcoin Core developers
+// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "main.h"
+#include "validation.h"
-#include "addrman.h"
#include "arith_uint256.h"
-#include "blockencodings.h"
#include "chainparams.h"
#include "checkpoints.h"
#include "checkqueue.h"
@@ -16,8 +14,6 @@
#include "consensus/validation.h"
#include "hash.h"
#include "init.h"
-#include "merkleblock.h"
-#include "net.h"
#include "policy/fees.h"
#include "policy/policy.h"
#include "pow.h"
@@ -27,6 +23,7 @@
#include "script/script.h"
#include "script/sigcache.h"
#include "script/standard.h"
+#include "timedata.h"
#include "tinyformat.h"
#include "txdb.h"
#include "txmempool.h"
@@ -37,6 +34,7 @@
#include "utilstrencodings.h"
#include "validationinterface.h"
#include "versionbits.h"
+#include "warnings.h"
#include <atomic>
#include <sstream>
@@ -63,11 +61,10 @@ CCriticalSection cs_main;
BlockMap mapBlockIndex;
CChain chainActive;
CBlockIndex *pindexBestHeader = NULL;
-int64_t nTimeBestReceived = 0;
CWaitableCriticalSection csBestBlock;
CConditionVariable cvBlockChange;
int nScriptCheckThreads = 0;
-bool fImporting = false;
+std::atomic_bool fImporting(false);
bool fReindex = false;
bool fTxIndex = false;
bool fHavePruned = false;
@@ -86,25 +83,6 @@ CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE;
CTxMemPool mempool(::minRelayTxFee);
-FeeFilterRounder filterRounder(::minRelayTxFee);
-
-struct IteratorComparator
-{
- template<typename I>
- bool operator()(const I& a, const I& b)
- {
- return &(*a) < &(*b);
- }
-};
-
-struct COrphanTx {
- CTransaction tx;
- NodeId fromPeer;
- int64_t nTimeExpire;
-};
-map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(cs_main);
-map<COutPoint, set<map<uint256, COrphanTx>::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(cs_main);
-void EraseOrphansFor(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
static void CheckBlockIndex(const Consensus::Params& consensusParams);
@@ -145,8 +123,6 @@ namespace {
* missing the data for the block.
*/
set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates;
- /** Number of nodes with fSyncStarted. */
- int nSyncStarted = 0;
/** All pairs A->B, where A (or one of its ancestors) misses transactions, but B has transactions.
* Pruned nodes may have entries where B is missing data.
*/
@@ -167,486 +143,19 @@ namespace {
*/
CCriticalSection cs_nBlockSequenceId;
/** Blocks loaded from disk are assigned id 0, so start the counter at 1. */
- uint32_t nBlockSequenceId = 1;
-
- /**
- * Sources of received blocks, saved to be able to send them reject
- * messages or ban them when processing happens afterwards. Protected by
- * cs_main.
- */
- map<uint256, NodeId> mapBlockSource;
-
- /**
- * Filter for transactions that were recently rejected by
- * AcceptToMemoryPool. These are not rerequested until the chain tip
- * changes, at which point the entire filter is reset. Protected by
- * cs_main.
- *
- * Without this filter we'd be re-requesting txs from each of our peers,
- * increasing bandwidth consumption considerably. For instance, with 100
- * peers, half of which relay a tx we don't accept, that might be a 50x
- * bandwidth increase. A flooding attacker attempting to roll-over the
- * filter using minimum-sized, 60byte, transactions might manage to send
- * 1000/sec if we have fast peers, so we pick 120,000 to give our peers a
- * two minute window to send invs to us.
- *
- * Decreasing the false positive rate is fairly cheap, so we pick one in a
- * million to make it highly unlikely for users to have issues with this
- * filter.
- *
- * Memory used: 1.3 MB
- */
- std::unique_ptr<CRollingBloomFilter> recentRejects;
- uint256 hashRecentRejectsChainTip;
-
- /** Blocks that are in flight, and that are in the queue to be downloaded. Protected by cs_main. */
- struct QueuedBlock {
- uint256 hash;
- CBlockIndex* pindex; //!< Optional.
- bool fValidatedHeaders; //!< Whether this block has validated headers at the time of request.
- std::unique_ptr<PartiallyDownloadedBlock> partialBlock; //!< Optional, used for CMPCTBLOCK downloads
- };
- map<uint256, pair<NodeId, list<QueuedBlock>::iterator> > mapBlocksInFlight;
-
- /** Stack of nodes which we have set to announce using compact blocks */
- list<NodeId> lNodesAnnouncingHeaderAndIDs;
-
- /** Number of preferable block download peers. */
- int nPreferredDownload = 0;
+ int32_t nBlockSequenceId = 1;
+ /** Decreasing counter (used by subsequent preciousblock calls). */
+ int32_t nBlockReverseSequenceId = -1;
+ /** chainwork for the last block that preciousblock has been applied to. */
+ arith_uint256 nLastPreciousChainwork = 0;
/** Dirty block index entries. */
set<CBlockIndex*> setDirtyBlockIndex;
/** Dirty block file entries. */
set<int> setDirtyFileInfo;
-
- /** Number of peers from which we're downloading blocks. */
- int nPeersWithValidatedDownloads = 0;
-
- /** Relay map, protected by cs_main. */
- typedef std::map<uint256, std::shared_ptr<const CTransaction>> MapRelay;
- MapRelay mapRelay;
- /** Expiration-time ordered list of (expire time, relay map entry) pairs, protected by cs_main). */
- std::deque<std::pair<int64_t, MapRelay::iterator>> vRelayExpiration;
-} // anon namespace
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// Registration of network node signals.
-//
-
-namespace {
-
-struct CBlockReject {
- unsigned char chRejectCode;
- string strRejectReason;
- uint256 hashBlock;
-};
-
-/**
- * Maintain validation-specific state about nodes, protected by cs_main, instead
- * by CNode's own locks. This simplifies asynchronous operation, where
- * processing of incoming data is done after the ProcessMessage call returns,
- * and we're no longer holding the node's locks.
- */
-struct CNodeState {
- //! The peer's address
- CService address;
- //! Whether we have a fully established connection.
- bool fCurrentlyConnected;
- //! Accumulated misbehaviour score for this peer.
- int nMisbehavior;
- //! Whether this peer should be disconnected and banned (unless whitelisted).
- bool fShouldBan;
- //! String name of this peer (debugging/logging purposes).
- std::string name;
- //! List of asynchronously-determined block rejections to notify this peer about.
- std::vector<CBlockReject> rejects;
- //! The best known block we know this peer has announced.
- CBlockIndex *pindexBestKnownBlock;
- //! The hash of the last unknown block this peer has announced.
- uint256 hashLastUnknownBlock;
- //! The last full block we both have.
- CBlockIndex *pindexLastCommonBlock;
- //! The best header we have sent our peer.
- CBlockIndex *pindexBestHeaderSent;
- //! Length of current-streak of unconnecting headers announcements
- int nUnconnectingHeaders;
- //! Whether we've started headers synchronization with this peer.
- bool fSyncStarted;
- //! Since when we're stalling block download progress (in microseconds), or 0.
- int64_t nStallingSince;
- list<QueuedBlock> vBlocksInFlight;
- //! When the first entry in vBlocksInFlight started downloading. Don't care when vBlocksInFlight is empty.
- int64_t nDownloadingSince;
- int nBlocksInFlight;
- int nBlocksInFlightValidHeaders;
- //! Whether we consider this a preferred download peer.
- bool fPreferredDownload;
- //! Whether this peer wants invs or headers (when possible) for block announcements.
- bool fPreferHeaders;
- //! Whether this peer wants invs or cmpctblocks (when possible) for block announcements.
- bool fPreferHeaderAndIDs;
- //! Whether this peer will send us cmpctblocks if we request them
- bool fProvidesHeaderAndIDs;
- //! Whether this peer can give us witnesses
- bool fHaveWitness;
-
- CNodeState() {
- fCurrentlyConnected = false;
- nMisbehavior = 0;
- fShouldBan = false;
- pindexBestKnownBlock = NULL;
- hashLastUnknownBlock.SetNull();
- pindexLastCommonBlock = NULL;
- pindexBestHeaderSent = NULL;
- nUnconnectingHeaders = 0;
- fSyncStarted = false;
- nStallingSince = 0;
- nDownloadingSince = 0;
- nBlocksInFlight = 0;
- nBlocksInFlightValidHeaders = 0;
- fPreferredDownload = false;
- fPreferHeaders = false;
- fPreferHeaderAndIDs = false;
- fProvidesHeaderAndIDs = false;
- fHaveWitness = false;
- }
-};
-
-/** Map maintaining per-node state. Requires cs_main. */
-map<NodeId, CNodeState> mapNodeState;
-
-// Requires cs_main.
-CNodeState *State(NodeId pnode) {
- map<NodeId, CNodeState>::iterator it = mapNodeState.find(pnode);
- if (it == mapNodeState.end())
- return NULL;
- return &it->second;
-}
-
-void UpdatePreferredDownload(CNode* node, CNodeState* state)
-{
- nPreferredDownload -= state->fPreferredDownload;
-
- // Whether this node should be marked as a preferred download node.
- state->fPreferredDownload = (!node->fInbound || node->fWhitelisted) && !node->fOneShot && !node->fClient;
-
- nPreferredDownload += state->fPreferredDownload;
-}
-
-void InitializeNode(NodeId nodeid, const CNode *pnode) {
- LOCK(cs_main);
- CNodeState &state = mapNodeState.insert(std::make_pair(nodeid, CNodeState())).first->second;
- state.name = pnode->addrName;
- state.address = pnode->addr;
-}
-
-void FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) {
- fUpdateConnectionTime = false;
- LOCK(cs_main);
- CNodeState *state = State(nodeid);
-
- if (state->fSyncStarted)
- nSyncStarted--;
-
- if (state->nMisbehavior == 0 && state->fCurrentlyConnected) {
- fUpdateConnectionTime = true;
- }
-
- BOOST_FOREACH(const QueuedBlock& entry, state->vBlocksInFlight) {
- mapBlocksInFlight.erase(entry.hash);
- }
- EraseOrphansFor(nodeid);
- nPreferredDownload -= state->fPreferredDownload;
- nPeersWithValidatedDownloads -= (state->nBlocksInFlightValidHeaders != 0);
- assert(nPeersWithValidatedDownloads >= 0);
-
- mapNodeState.erase(nodeid);
-
- if (mapNodeState.empty()) {
- // Do a consistency check after the last peer is removed.
- assert(mapBlocksInFlight.empty());
- assert(nPreferredDownload == 0);
- assert(nPeersWithValidatedDownloads == 0);
- }
-}
-
-// Requires cs_main.
-// Returns a bool indicating whether we requested this block.
-// Also used if a block was /not/ received and timed out or started with another peer
-bool MarkBlockAsReceived(const uint256& hash) {
- map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash);
- if (itInFlight != mapBlocksInFlight.end()) {
- CNodeState *state = State(itInFlight->second.first);
- state->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders;
- if (state->nBlocksInFlightValidHeaders == 0 && itInFlight->second.second->fValidatedHeaders) {
- // Last validated block on the queue was received.
- nPeersWithValidatedDownloads--;
- }
- if (state->vBlocksInFlight.begin() == itInFlight->second.second) {
- // First block on the queue was received, update the start download time for the next one
- state->nDownloadingSince = std::max(state->nDownloadingSince, GetTimeMicros());
- }
- state->vBlocksInFlight.erase(itInFlight->second.second);
- state->nBlocksInFlight--;
- state->nStallingSince = 0;
- mapBlocksInFlight.erase(itInFlight);
- return true;
- }
- return false;
-}
-
-// Requires cs_main.
-// returns false, still setting pit, if the block was already in flight from the same peer
-// pit will only be valid as long as the same cs_main lock is being held
-bool MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Params& consensusParams, CBlockIndex *pindex = NULL, list<QueuedBlock>::iterator **pit = NULL) {
- CNodeState *state = State(nodeid);
- assert(state != NULL);
-
- // Short-circuit most stuff in case its from the same node
- map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash);
- if (itInFlight != mapBlocksInFlight.end() && itInFlight->second.first == nodeid) {
- *pit = &itInFlight->second.second;
- return false;
- }
-
- // Make sure it's not listed somewhere already.
- MarkBlockAsReceived(hash);
-
- list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(),
- {hash, pindex, pindex != NULL, std::unique_ptr<PartiallyDownloadedBlock>(pit ? new PartiallyDownloadedBlock(&mempool) : NULL)});
- state->nBlocksInFlight++;
- state->nBlocksInFlightValidHeaders += it->fValidatedHeaders;
- if (state->nBlocksInFlight == 1) {
- // We're starting a block download (batch) from this peer.
- state->nDownloadingSince = GetTimeMicros();
- }
- if (state->nBlocksInFlightValidHeaders == 1 && pindex != NULL) {
- nPeersWithValidatedDownloads++;
- }
- itInFlight = mapBlocksInFlight.insert(std::make_pair(hash, std::make_pair(nodeid, it))).first;
- if (pit)
- *pit = &itInFlight->second.second;
- return true;
-}
-
-/** Check whether the last unknown block a peer advertised is not yet known. */
-void ProcessBlockAvailability(NodeId nodeid) {
- CNodeState *state = State(nodeid);
- assert(state != NULL);
-
- if (!state->hashLastUnknownBlock.IsNull()) {
- BlockMap::iterator itOld = mapBlockIndex.find(state->hashLastUnknownBlock);
- if (itOld != mapBlockIndex.end() && itOld->second->nChainWork > 0) {
- if (state->pindexBestKnownBlock == NULL || itOld->second->nChainWork >= state->pindexBestKnownBlock->nChainWork)
- state->pindexBestKnownBlock = itOld->second;
- state->hashLastUnknownBlock.SetNull();
- }
- }
-}
-
-/** Update tracking information about which blocks a peer is assumed to have. */
-void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) {
- CNodeState *state = State(nodeid);
- assert(state != NULL);
-
- ProcessBlockAvailability(nodeid);
-
- BlockMap::iterator it = mapBlockIndex.find(hash);
- if (it != mapBlockIndex.end() && it->second->nChainWork > 0) {
- // An actually better block was announced.
- if (state->pindexBestKnownBlock == NULL || it->second->nChainWork >= state->pindexBestKnownBlock->nChainWork)
- state->pindexBestKnownBlock = it->second;
- } else {
- // An unknown block was announced; just assume that the latest one is the best one.
- state->hashLastUnknownBlock = hash;
- }
-}
-
-void MaybeSetPeerAsAnnouncingHeaderAndIDs(const CNodeState* nodestate, CNode* pfrom, CConnman& connman) {
- if (pfrom->GetLocalServices() & NODE_WITNESS) {
- // Don't ever request compact blocks when segwit is enabled.
- return;
- }
- if (nodestate->fProvidesHeaderAndIDs) {
- BOOST_FOREACH(const NodeId nodeid, lNodesAnnouncingHeaderAndIDs)
- if (nodeid == pfrom->GetId())
- return;
- bool fAnnounceUsingCMPCTBLOCK = false;
- uint64_t nCMPCTBLOCKVersion = 1;
- if (lNodesAnnouncingHeaderAndIDs.size() >= 3) {
- // As per BIP152, we only get 3 of our peers to announce
- // blocks using compact encodings.
- bool found = connman.ForNode(lNodesAnnouncingHeaderAndIDs.front(), [fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion](CNode* pnodeStop){
- pnodeStop->PushMessage(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion);
- return true;
- });
- if(found)
- lNodesAnnouncingHeaderAndIDs.pop_front();
- }
- fAnnounceUsingCMPCTBLOCK = true;
- pfrom->PushMessage(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion);
- lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId());
- }
-}
-
-// Requires cs_main
-bool CanDirectFetch(const Consensus::Params &consensusParams)
-{
- return chainActive.Tip()->GetBlockTime() > GetAdjustedTime() - consensusParams.nPowTargetSpacing * 20;
-}
-
-// Requires cs_main
-bool PeerHasHeader(CNodeState *state, CBlockIndex *pindex)
-{
- if (state->pindexBestKnownBlock && pindex == state->pindexBestKnownBlock->GetAncestor(pindex->nHeight))
- return true;
- if (state->pindexBestHeaderSent && pindex == state->pindexBestHeaderSent->GetAncestor(pindex->nHeight))
- return true;
- return false;
-}
-
-/** Find the last common ancestor two blocks have.
- * Both pa and pb must be non-NULL. */
-CBlockIndex* LastCommonAncestor(CBlockIndex* pa, CBlockIndex* pb) {
- if (pa->nHeight > pb->nHeight) {
- pa = pa->GetAncestor(pb->nHeight);
- } else if (pb->nHeight > pa->nHeight) {
- pb = pb->GetAncestor(pa->nHeight);
- }
-
- while (pa != pb && pa && pb) {
- pa = pa->pprev;
- pb = pb->pprev;
- }
-
- // Eventually all chain branches meet at the genesis block.
- assert(pa == pb);
- return pa;
-}
-
-/** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has
- * at most count entries. */
-void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<CBlockIndex*>& vBlocks, NodeId& nodeStaller, const Consensus::Params& consensusParams) {
- if (count == 0)
- return;
-
- vBlocks.reserve(vBlocks.size() + count);
- CNodeState *state = State(nodeid);
- assert(state != NULL);
-
- // Make sure pindexBestKnownBlock is up to date, we'll need it.
- ProcessBlockAvailability(nodeid);
-
- if (state->pindexBestKnownBlock == NULL || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork) {
- // This peer has nothing interesting.
- return;
- }
-
- if (state->pindexLastCommonBlock == NULL) {
- // Bootstrap quickly by guessing a parent of our best tip is the forking point.
- // Guessing wrong in either direction is not a problem.
- state->pindexLastCommonBlock = chainActive[std::min(state->pindexBestKnownBlock->nHeight, chainActive.Height())];
- }
-
- // If the peer reorganized, our previous pindexLastCommonBlock may not be an ancestor
- // of its current tip anymore. Go back enough to fix that.
- state->pindexLastCommonBlock = LastCommonAncestor(state->pindexLastCommonBlock, state->pindexBestKnownBlock);
- if (state->pindexLastCommonBlock == state->pindexBestKnownBlock)
- return;
-
- std::vector<CBlockIndex*> vToFetch;
- CBlockIndex *pindexWalk = state->pindexLastCommonBlock;
- // Never fetch further than the best block we know the peer has, or more than BLOCK_DOWNLOAD_WINDOW + 1 beyond the last
- // linked block we have in common with this peer. The +1 is so we can detect stalling, namely if we would be able to
- // download that next block if the window were 1 larger.
- int nWindowEnd = state->pindexLastCommonBlock->nHeight + BLOCK_DOWNLOAD_WINDOW;
- int nMaxHeight = std::min<int>(state->pindexBestKnownBlock->nHeight, nWindowEnd + 1);
- NodeId waitingfor = -1;
- while (pindexWalk->nHeight < nMaxHeight) {
- // Read up to 128 (or more, if more blocks than that are needed) successors of pindexWalk (towards
- // pindexBestKnownBlock) into vToFetch. We fetch 128, because CBlockIndex::GetAncestor may be as expensive
- // as iterating over ~100 CBlockIndex* entries anyway.
- int nToFetch = std::min(nMaxHeight - pindexWalk->nHeight, std::max<int>(count - vBlocks.size(), 128));
- vToFetch.resize(nToFetch);
- pindexWalk = state->pindexBestKnownBlock->GetAncestor(pindexWalk->nHeight + nToFetch);
- vToFetch[nToFetch - 1] = pindexWalk;
- for (unsigned int i = nToFetch - 1; i > 0; i--) {
- vToFetch[i - 1] = vToFetch[i]->pprev;
- }
-
- // Iterate over those blocks in vToFetch (in forward direction), adding the ones that
- // are not yet downloaded and not in flight to vBlocks. In the mean time, update
- // pindexLastCommonBlock as long as all ancestors are already downloaded, or if it's
- // already part of our chain (and therefore don't need it even if pruned).
- BOOST_FOREACH(CBlockIndex* pindex, vToFetch) {
- if (!pindex->IsValid(BLOCK_VALID_TREE)) {
- // We consider the chain that this peer is on invalid.
- return;
- }
- if (!State(nodeid)->fHaveWitness && IsWitnessEnabled(pindex->pprev, consensusParams)) {
- // We wouldn't download this block or its descendants from this peer.
- return;
- }
- if (pindex->nStatus & BLOCK_HAVE_DATA || chainActive.Contains(pindex)) {
- if (pindex->nChainTx)
- state->pindexLastCommonBlock = pindex;
- } else if (mapBlocksInFlight.count(pindex->GetBlockHash()) == 0) {
- // The block is not already downloaded, and not yet in flight.
- if (pindex->nHeight > nWindowEnd) {
- // We reached the end of the window.
- if (vBlocks.size() == 0 && waitingfor != nodeid) {
- // We aren't able to fetch anything, but we would be if the download window was one larger.
- nodeStaller = waitingfor;
- }
- return;
- }
- vBlocks.push_back(pindex);
- if (vBlocks.size() == count) {
- return;
- }
- } else if (waitingfor == -1) {
- // This is the first already-in-flight block.
- waitingfor = mapBlocksInFlight[pindex->GetBlockHash()].first;
- }
- }
- }
-}
-
} // anon namespace
-bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) {
- LOCK(cs_main);
- CNodeState *state = State(nodeid);
- if (state == NULL)
- return false;
- stats.nMisbehavior = state->nMisbehavior;
- stats.nSyncHeight = state->pindexBestKnownBlock ? state->pindexBestKnownBlock->nHeight : -1;
- stats.nCommonHeight = state->pindexLastCommonBlock ? state->pindexLastCommonBlock->nHeight : -1;
- BOOST_FOREACH(const QueuedBlock& queue, state->vBlocksInFlight) {
- if (queue.pindex)
- stats.vHeightInFlight.push_back(queue.pindex->nHeight);
- }
- return true;
-}
-
-void RegisterNodeSignals(CNodeSignals& nodeSignals)
-{
- nodeSignals.ProcessMessages.connect(&ProcessMessages);
- nodeSignals.SendMessages.connect(&SendMessages);
- nodeSignals.InitializeNode.connect(&InitializeNode);
- nodeSignals.FinalizeNode.connect(&FinalizeNode);
-}
-
-void UnregisterNodeSignals(CNodeSignals& nodeSignals)
-{
- nodeSignals.ProcessMessages.disconnect(&ProcessMessages);
- nodeSignals.SendMessages.disconnect(&SendMessages);
- nodeSignals.InitializeNode.disconnect(&InitializeNode);
- nodeSignals.FinalizeNode.disconnect(&FinalizeNode);
-}
-
CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator)
{
// Find the first block the caller has in the main chain
@@ -668,111 +177,15 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc
CCoinsViewCache *pcoinsTip = NULL;
CBlockTreeDB *pblocktree = NULL;
-//////////////////////////////////////////////////////////////////////////////
-//
-// mapOrphanTransactions
-//
-
-bool AddOrphanTx(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
-{
- uint256 hash = tx.GetHash();
- if (mapOrphanTransactions.count(hash))
- return false;
-
- // Ignore big transactions, to avoid a
- // send-big-orphans memory exhaustion attack. If a peer has a legitimate
- // large transaction with a missing parent then we assume
- // it will rebroadcast it later, after the parent transaction(s)
- // have been mined or received.
- // 100 orphans, each of which is at most 99,999 bytes big is
- // at most 10 megabytes of orphans and somewhat more byprev index (in the worst case):
- unsigned int sz = GetTransactionWeight(tx);
- if (sz >= MAX_STANDARD_TX_WEIGHT)
- {
- LogPrint("mempool", "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString());
- return false;
- }
-
- auto ret = mapOrphanTransactions.emplace(hash, COrphanTx{tx, peer, GetTime() + ORPHAN_TX_EXPIRE_TIME});
- assert(ret.second);
- BOOST_FOREACH(const CTxIn& txin, tx.vin) {
- mapOrphanTransactionsByPrev[txin.prevout].insert(ret.first);
- }
-
- LogPrint("mempool", "stored orphan tx %s (mapsz %u outsz %u)\n", hash.ToString(),
- mapOrphanTransactions.size(), mapOrphanTransactionsByPrev.size());
- return true;
-}
-
-int static EraseOrphanTx(uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
-{
- map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.find(hash);
- if (it == mapOrphanTransactions.end())
- return 0;
- BOOST_FOREACH(const CTxIn& txin, it->second.tx.vin)
- {
- auto itPrev = mapOrphanTransactionsByPrev.find(txin.prevout);
- if (itPrev == mapOrphanTransactionsByPrev.end())
- continue;
- itPrev->second.erase(it);
- if (itPrev->second.empty())
- mapOrphanTransactionsByPrev.erase(itPrev);
- }
- mapOrphanTransactions.erase(it);
- return 1;
-}
-
-void EraseOrphansFor(NodeId peer)
-{
- int nErased = 0;
- map<uint256, COrphanTx>::iterator iter = mapOrphanTransactions.begin();
- while (iter != mapOrphanTransactions.end())
- {
- map<uint256, COrphanTx>::iterator maybeErase = iter++; // increment to avoid iterator becoming invalid
- if (maybeErase->second.fromPeer == peer)
- {
- nErased += EraseOrphanTx(maybeErase->second.tx.GetHash());
- }
- }
- if (nErased > 0) LogPrint("mempool", "Erased %d orphan tx from peer %d\n", nErased, peer);
-}
-
+enum FlushStateMode {
+ FLUSH_STATE_NONE,
+ FLUSH_STATE_IF_NEEDED,
+ FLUSH_STATE_PERIODIC,
+ FLUSH_STATE_ALWAYS
+};
-unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
-{
- unsigned int nEvicted = 0;
- static int64_t nNextSweep;
- int64_t nNow = GetTime();
- if (nNextSweep <= nNow) {
- // Sweep out expired orphan pool entries:
- int nErased = 0;
- int64_t nMinExpTime = nNow + ORPHAN_TX_EXPIRE_TIME - ORPHAN_TX_EXPIRE_INTERVAL;
- map<uint256, COrphanTx>::iterator iter = mapOrphanTransactions.begin();
- while (iter != mapOrphanTransactions.end())
- {
- map<uint256, COrphanTx>::iterator maybeErase = iter++;
- if (maybeErase->second.nTimeExpire <= nNow) {
- nErased += EraseOrphanTx(maybeErase->second.tx.GetHash());
- } else {
- nMinExpTime = std::min(maybeErase->second.nTimeExpire, nMinExpTime);
- }
- }
- // Sweep again 5 minutes after the next entry that expires in order to batch the linear scan.
- nNextSweep = nMinExpTime + ORPHAN_TX_EXPIRE_INTERVAL;
- if (nErased > 0) LogPrint("mempool", "Erased %d orphan tx due to expiration\n", nErased);
- }
- while (mapOrphanTransactions.size() > nMaxOrphans)
- {
- // Evict a random orphan:
- uint256 randomhash = GetRandHash();
- map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
- if (it == mapOrphanTransactions.end())
- it = mapOrphanTransactions.begin();
- EraseOrphanTx(it->first);
- ++nEvicted;
- }
- return nEvicted;
-}
+// See definition for documentation
+bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode);
bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
{
@@ -1043,7 +456,7 @@ int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& i
-bool CheckTransaction(const CTransaction& tx, CValidationState &state)
+bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fCheckDuplicateInputs)
{
// Basic checks that don't depend on any context
if (tx.vin.empty())
@@ -1067,13 +480,14 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
return state.DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge");
}
- // Check for duplicate inputs
- set<COutPoint> vInOutPoints;
- for (const auto& txin : tx.vin)
- {
- if (vInOutPoints.count(txin.prevout))
- return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate");
- vInOutPoints.insert(txin.prevout);
+ // Check for duplicate inputs - note that this check is slow so we skip it in CheckBlock
+ if (fCheckDuplicateInputs) {
+ set<COutPoint> vInOutPoints;
+ for (const auto& txin : tx.vin)
+ {
+ if (!vInOutPoints.insert(txin.prevout).second)
+ return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate");
+ }
}
if (tx.IsCoinBase())
@@ -1112,7 +526,7 @@ std::string FormatStateMessage(const CValidationState &state)
}
bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const CTransaction& tx, bool fLimitFree,
- bool* pfMissingInputs, bool fOverrideMempoolLimit, const CAmount& nAbsurdFee,
+ bool* pfMissingInputs, int64_t nAcceptTime, bool fOverrideMempoolLimit, const CAmount& nAbsurdFee,
std::vector<uint256>& vHashTxnToUncache)
{
const uint256 hash = tx.GetHash();
@@ -1127,14 +541,6 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
if (tx.IsCoinBase())
return state.DoS(100, false, REJECT_INVALID, "coinbase");
- // Don't relay version 2 transactions until CSV is active, and we can be
- // sure that such transactions will be mined (unless we're on
- // -testnet/-regtest).
- const CChainParams& chainparams = Params();
- if (fRequireStandard && tx.nVersion >= 2 && VersionBitsTipState(chainparams.GetConsensus(), Consensus::DEPLOYMENT_CSV) != THRESHOLD_ACTIVE) {
- return state.DoS(0, false, REJECT_NONSTANDARD, "premature-version2-tx");
- }
-
// Reject transactions with witness before segregated witness activates (override with -prematurewitness)
bool witnessEnabled = IsWitnessEnabled(chainActive.Tip(), Params().GetConsensus());
if (!GetBoolArg("-prematurewitness",false) && !tx.wit.IsNull() && !witnessEnabled) {
@@ -1183,9 +589,9 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
bool fReplacementOptOut = true;
if (fEnableReplacement)
{
- BOOST_FOREACH(const CTxIn &txin, ptxConflicting->vin)
+ BOOST_FOREACH(const CTxIn &_txin, ptxConflicting->vin)
{
- if (txin.nSequence < std::numeric_limits<unsigned int>::max()-1)
+ if (_txin.nSequence < std::numeric_limits<unsigned int>::max()-1)
{
fReplacementOptOut = false;
break;
@@ -1258,6 +664,10 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
if (fRequireStandard && !AreInputsStandard(tx, view))
return state.Invalid(false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs");
+ // Check for non-standard witness in P2WSH
+ if (!tx.wit.IsNull() && fRequireStandard && !IsWitnessStandard(tx, view))
+ return state.DoS(0, false, REJECT_NONSTANDARD, "bad-witness-nonstandard", true);
+
int64_t nSigOpsCost = GetTransactionSigOpCost(tx, view, STANDARD_SCRIPT_VERIFY_FLAGS);
CAmount nValueOut = tx.GetValueOut();
@@ -1281,7 +691,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
}
}
- CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOpsCost, lp);
+ CTxMemPoolEntry entry(tx, nFees, nAcceptTime, dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOpsCost, lp);
unsigned int nSize = entry.GetTxSize();
// Check that the transaction doesn't have an excessive number of
@@ -1540,34 +950,43 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
}
}
- SyncWithWallets(tx, NULL);
+ GetMainSignals().SyncTransaction(tx, NULL, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK);
return true;
}
-bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
- bool* pfMissingInputs, bool fOverrideMempoolLimit, const CAmount nAbsurdFee)
+bool AcceptToMemoryPoolWithTime(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
+ bool* pfMissingInputs, int64_t nAcceptTime, bool fOverrideMempoolLimit, const CAmount nAbsurdFee)
{
std::vector<uint256> vHashTxToUncache;
- bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, fOverrideMempoolLimit, nAbsurdFee, vHashTxToUncache);
+ bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, nAcceptTime, fOverrideMempoolLimit, nAbsurdFee, vHashTxToUncache);
if (!res) {
BOOST_FOREACH(const uint256& hashTx, vHashTxToUncache)
pcoinsTip->Uncache(hashTx);
}
+ // After we've (potentially) uncached entries, ensure our coins cache is still within its size limits
+ CValidationState stateDummy;
+ FlushStateToDisk(stateDummy, FLUSH_STATE_PERIODIC);
return res;
}
-/** 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, const Consensus::Params& consensusParams, uint256 &hashBlock, bool fAllowSlow)
+bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
+ bool* pfMissingInputs, bool fOverrideMempoolLimit, const CAmount nAbsurdFee)
+{
+ return AcceptToMemoryPoolWithTime(pool, state, tx, fLimitFree, pfMissingInputs, GetTime(), fOverrideMempoolLimit, nAbsurdFee);
+}
+
+/** Return transaction in txOut, and if it was found inside a block, its hash is placed in hashBlock */
+bool GetTransaction(const uint256 &hash, CTransactionRef &txOut, const Consensus::Params& consensusParams, uint256 &hashBlock, bool fAllowSlow)
{
CBlockIndex *pindexSlow = NULL;
LOCK(cs_main);
- std::shared_ptr<const CTransaction> ptx = mempool.get(hash);
+ CTransactionRef ptx = mempool.get(hash);
if (ptx)
{
- txOut = *ptx;
+ txOut = ptx;
return true;
}
@@ -1586,7 +1005,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::P
return error("%s: Deserialize or I/O error - %s", __func__, e.what());
}
hashBlock = header.GetHash();
- if (txOut.GetHash() != hash)
+ if (txOut->GetHash() != hash)
return error("%s: txid mismatch", __func__);
return true;
}
@@ -1607,8 +1026,8 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::P
if (pindexSlow) {
CBlock block;
if (ReadBlockFromDisk(block, pindexSlow, consensusParams)) {
- BOOST_FOREACH(const CTransaction &tx, block.vtx) {
- if (tx.GetHash() == hash) {
+ for (const auto& tx : block.vtx) {
+ if (tx->GetHash() == hash) {
txOut = tx;
hashBlock = pindexSlow->GetBlockHash();
return true;
@@ -1638,7 +1057,7 @@ bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHea
return error("WriteBlockToDisk: OpenBlockFile failed");
// Write index header
- unsigned int nSize = fileout.GetSerializeSize(block);
+ unsigned int nSize = GetSerializeSize(fileout, block);
fileout << FLATDATA(messageStart) << nSize;
// Write block
@@ -1713,17 +1132,16 @@ bool IsInitialBlockDownload()
return false;
if (fImporting || fReindex)
return true;
- if (fCheckpointsEnabled && chainActive.Height() < Checkpoints::GetTotalBlocksEstimate(chainParams.Checkpoints()))
+ if (chainActive.Tip() == NULL)
return true;
- bool state = (chainActive.Height() < pindexBestHeader->nHeight - 24 * 6 ||
- std::max(chainActive.Tip()->GetBlockTime(), pindexBestHeader->GetBlockTime()) < GetTime() - nMaxTipAge);
- if (!state)
- latchToFalse.store(true, std::memory_order_relaxed);
- return state;
+ if (chainActive.Tip()->nChainWork < UintToArith256(chainParams.GetConsensus().nMinimumChainWork))
+ return true;
+ if (chainActive.Tip()->GetBlockTime() < (GetTime() - nMaxTipAge))
+ return true;
+ latchToFalse.store(true, std::memory_order_relaxed);
+ return false;
}
-bool fLargeWorkForkFound = false;
-bool fLargeWorkInvalidChainFound = false;
CBlockIndex *pindexBestForkTip = NULL, *pindexBestForkBase = NULL;
static void AlertNotify(const std::string& strMessage)
@@ -1747,7 +1165,7 @@ void CheckForkWarningConditions()
{
AssertLockHeld(cs_main);
// Before we get past initial download, we cannot reliably alert about forks
- // (we assume we don't get stuck on a fork before the last checkpoint)
+ // (we assume we don't get stuck on a fork before finishing our initial sync)
if (IsInitialBlockDownload())
return;
@@ -1758,7 +1176,7 @@ void CheckForkWarningConditions()
if (pindexBestForkTip || (pindexBestInvalid && pindexBestInvalid->nChainWork > chainActive.Tip()->nChainWork + (GetBlockProof(*chainActive.Tip()) * 6)))
{
- if (!fLargeWorkForkFound && pindexBestForkBase)
+ if (!GetfLargeWorkForkFound() && pindexBestForkBase)
{
std::string warning = std::string("'Warning: Large-work fork detected, forking after block ") +
pindexBestForkBase->phashBlock->ToString() + std::string("'");
@@ -1769,18 +1187,18 @@ void CheckForkWarningConditions()
LogPrintf("%s: Warning: Large valid fork found\n forking the chain at height %d (%s)\n lasting to height %d (%s).\nChain state database corruption likely.\n", __func__,
pindexBestForkBase->nHeight, pindexBestForkBase->phashBlock->ToString(),
pindexBestForkTip->nHeight, pindexBestForkTip->phashBlock->ToString());
- fLargeWorkForkFound = true;
+ SetfLargeWorkForkFound(true);
}
else
{
LogPrintf("%s: Warning: Found invalid chain at least ~6 blocks longer than our best chain.\nChain state database corruption likely.\n", __func__);
- fLargeWorkInvalidChainFound = true;
+ SetfLargeWorkInvalidChainFound(true);
}
}
else
{
- fLargeWorkForkFound = false;
- fLargeWorkInvalidChainFound = false;
+ SetfLargeWorkForkFound(false);
+ SetfLargeWorkInvalidChainFound(false);
}
}
@@ -1817,26 +1235,6 @@ void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip)
CheckForkWarningConditions();
}
-// Requires cs_main.
-void Misbehaving(NodeId pnode, int howmuch)
-{
- if (howmuch == 0)
- return;
-
- CNodeState *state = State(pnode);
- if (state == NULL)
- return;
-
- state->nMisbehavior += howmuch;
- int banscore = GetArg("-banscore", DEFAULT_BANSCORE_THRESHOLD);
- if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore)
- {
- LogPrintf("%s: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", __func__, state->name, state->nMisbehavior-howmuch, state->nMisbehavior);
- state->fShouldBan = true;
- } else
- LogPrintf("%s: %s (%d -> %d)\n", __func__, state->name, state->nMisbehavior-howmuch, state->nMisbehavior);
-}
-
void static InvalidChainFound(CBlockIndex* pindexNew)
{
if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork)
@@ -1855,17 +1253,6 @@ void static InvalidChainFound(CBlockIndex* pindexNew)
}
void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) {
- int nDoS = 0;
- if (state.IsInvalid(nDoS)) {
- std::map<uint256, NodeId>::iterator it = mapBlockSource.find(pindex->GetBlockHash());
- if (it != mapBlockSource.end() && State(it->second)) {
- assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes
- CBlockReject reject = {(unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), pindex->GetBlockHash()};
- State(it->second)->rejects.push_back(reject);
- if (nDoS > 0)
- Misbehaving(it->second, nDoS);
- }
- }
if (!state.CorruptionPossible()) {
pindex->nStatus |= BLOCK_FAILED_VALID;
setDirtyBlockIndex.insert(pindex);
@@ -2038,7 +1425,7 @@ bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint
return error("%s: OpenUndoFile failed", __func__);
// Write index header
- unsigned int nSize = fileout.GetSerializeSize(blockundo);
+ unsigned int nSize = GetSerializeSize(fileout, blockundo);
fileout << FLATDATA(messageStart) << nSize;
// Write undo data
@@ -2062,7 +1449,7 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uin
// Open history file to read
CAutoFile filein(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION);
if (filein.IsNull())
- return error("%s: OpenBlockFile failed", __func__);
+ return error("%s: OpenUndoFile failed", __func__);
// Read block
uint256 hashChecksum;
@@ -2087,7 +1474,7 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uin
/** Abort with a message */
bool AbortNode(const std::string& strMessage, const std::string& userMessage="")
{
- strMiscWarning = strMessage;
+ SetMiscWarning(strMessage);
LogPrintf("*** %s\n", strMessage);
uiInterface.ThreadSafeMessageBox(
userMessage.empty() ? _("Error: A fatal internal error occurred, see debug.log for details") : userMessage,
@@ -2158,7 +1545,7 @@ bool DisconnectBlock(const CBlock& block, CValidationState& state, const CBlockI
// undo transactions in reverse order
for (int i = block.vtx.size() - 1; i >= 0; i--) {
- const CTransaction &tx = block.vtx[i];
+ const CTransaction &tx = *(block.vtx[i]);
uint256 hash = tx.GetHash();
// Check that all outputs are available and match the outputs in the block itself
@@ -2352,8 +1739,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
fEnforceBIP30 = fEnforceBIP30 && (!pindexBIP34height || !(pindexBIP34height->GetBlockHash() == chainparams.GetConsensus().BIP34Hash));
if (fEnforceBIP30) {
- BOOST_FOREACH(const CTransaction& tx, block.vtx) {
- const CCoins* coins = view.AccessCoins(tx.GetHash());
+ for (const auto& tx : block.vtx) {
+ const CCoins* coins = view.AccessCoins(tx->GetHash());
if (coins && !coins->IsPruned())
return state.DoS(100, error("ConnectBlock(): tried to overwrite transaction"),
REJECT_INVALID, "bad-txns-BIP30");
@@ -2386,6 +1773,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
// Start enforcing WITNESS rules using versionbits logic.
if (IsWitnessEnabled(pindex->pprev, chainparams.GetConsensus())) {
flags |= SCRIPT_VERIFY_WITNESS;
+ flags |= SCRIPT_VERIFY_NULLDUMMY;
}
int64_t nTime2 = GetTimeMicros(); nTimeForks += nTime2 - nTime1;
@@ -2395,7 +1783,6 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
CCheckQueueControl<CScriptCheck> control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL);
- std::vector<uint256> vOrphanErase;
std::vector<int> prevheights;
CAmount nFees = 0;
int nInputs = 0;
@@ -2408,7 +1795,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
txdata.reserve(block.vtx.size()); // Required so that pointers to individual PrecomputedTransactionData don't get invalidated
for (unsigned int i = 0; i < block.vtx.size(); i++)
{
- const CTransaction &tx = block.vtx[i];
+ const CTransaction &tx = *(block.vtx[i]);
nInputs += tx.vin.size();
@@ -2426,17 +1813,6 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
prevheights[j] = view.AccessCoins(tx.vin[j].prevout.hash)->nHeight;
}
- // Which orphan pool entries must we evict?
- for (size_t j = 0; j < tx.vin.size(); j++) {
- auto itByPrev = mapOrphanTransactionsByPrev.find(tx.vin[j].prevout);
- if (itByPrev == mapOrphanTransactionsByPrev.end()) continue;
- for (auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); ++mi) {
- const CTransaction& orphanTx = (*mi)->second.tx;
- const uint256& orphanHash = orphanTx.GetHash();
- vOrphanErase.push_back(orphanHash);
- }
- }
-
if (!SequenceLocks(tx, nLockTimeFlags, &prevheights, *pindex)) {
return state.DoS(100, error("%s: contains a non-BIP68-final transaction", __func__),
REJECT_INVALID, "bad-txns-nonfinal");
@@ -2478,10 +1854,10 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime3 - nTime2), 0.001 * (nTime3 - nTime2) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime3 - nTime2) / (nInputs-1), nTimeConnect * 0.000001);
CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus());
- if (block.vtx[0].GetValueOut() > blockReward)
+ if (block.vtx[0]->GetValueOut() > blockReward)
return state.DoS(100,
error("ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)",
- block.vtx[0].GetValueOut(), blockReward),
+ block.vtx[0]->GetValueOut(), blockReward),
REJECT_INVALID, "bad-cb-amount");
if (!control.Wait())
@@ -2496,14 +1872,14 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
if (pindex->GetUndoPos().IsNull() || !pindex->IsValid(BLOCK_VALID_SCRIPTS))
{
if (pindex->GetUndoPos().IsNull()) {
- CDiskBlockPos pos;
- if (!FindUndoPos(state, pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40))
+ CDiskBlockPos _pos;
+ if (!FindUndoPos(state, pindex->nFile, _pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40))
return error("ConnectBlock(): FindUndoPos failed");
- if (!UndoWriteToDisk(blockundo, pos, pindex->pprev->GetBlockHash(), chainparams.MessageStart()))
+ if (!UndoWriteToDisk(blockundo, _pos, pindex->pprev->GetBlockHash(), chainparams.MessageStart()))
return AbortNode(state, "Failed to write undo data");
// update nUndoPos in block index
- pindex->nUndoPos = pos.nPos;
+ pindex->nUndoPos = _pos.nPos;
pindex->nStatus |= BLOCK_HAVE_UNDO;
}
@@ -2524,16 +1900,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
// Watch for changes to the previous coinbase transaction.
static uint256 hashPrevBestCoinBase;
GetMainSignals().UpdatedTransaction(hashPrevBestCoinBase);
- hashPrevBestCoinBase = block.vtx[0].GetHash();
+ hashPrevBestCoinBase = block.vtx[0]->GetHash();
- // Erase orphan transactions include or precluded by this block
- if (vOrphanErase.size()) {
- int nErased = 0;
- BOOST_FOREACH(uint256 &orphanHash, vOrphanErase) {
- nErased += EraseOrphanTx(orphanHash);
- }
- LogPrint("mempool", "Erased %d orphan tx included or conflicted by block\n", nErased);
- }
int64_t nTime6 = GetTimeMicros(); nTimeCallbacks += nTime6 - nTime5;
LogPrint("bench", " - Callbacks: %.2fms [%.2fs]\n", 0.001 * (nTime6 - nTime5), nTimeCallbacks * 0.000001);
@@ -2541,13 +1909,6 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
return true;
}
-enum FlushStateMode {
- FLUSH_STATE_NONE,
- FLUSH_STATE_IF_NEEDED,
- FLUSH_STATE_PERIODIC,
- FLUSH_STATE_ALWAYS
-};
-
/**
* Update the on-disk chain state.
* The caches and indexes are flushed depending on the mode we're called with
@@ -2667,7 +2028,6 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) {
chainActive.SetTip(pindexNew);
// New best block
- nTimeBestReceived = GetTime();
mempool.AddTransactionsUpdated(1);
cvBlockChange.notify_all();
@@ -2683,9 +2043,10 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) {
ThresholdState state = checker.GetStateFor(pindex, chainParams.GetConsensus(), warningcache[bit]);
if (state == THRESHOLD_ACTIVE || state == THRESHOLD_LOCKED_IN) {
if (state == THRESHOLD_ACTIVE) {
- strMiscWarning = strprintf(_("Warning: unknown new rules activated (versionbit %i)"), bit);
+ std::string strWarning = strprintf(_("Warning: unknown new rules activated (versionbit %i)"), bit);
+ SetMiscWarning(strWarning);
if (!fWarned) {
- AlertNotify(strMiscWarning);
+ AlertNotify(strWarning);
fWarned = true;
}
} else {
@@ -2705,10 +2066,11 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) {
warningMessages.push_back(strprintf("%d of last 100 blocks have unexpected version", nUpgraded));
if (nUpgraded > 100/2)
{
- // strMiscWarning is read by GetWarnings(), called by Qt and the JSON-RPC code to warn the user:
- strMiscWarning = _("Warning: Unknown block versions being mined! It's possible unknown rules are in effect");
+ std::string strWarning = _("Warning: Unknown block versions being mined! It's possible unknown rules are in effect");
+ // notify GetWarnings(), called by Qt and the JSON-RPC code to warn the user:
+ SetMiscWarning(strWarning);
if (!fWarned) {
- AlertNotify(strMiscWarning);
+ AlertNotify(strWarning);
fWarned = true;
}
}
@@ -2739,7 +2101,8 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
CCoinsViewCache view(pcoinsTip);
if (!DisconnectBlock(block, state, pindexDelete, view))
return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString());
- assert(view.Flush());
+ bool flushed = view.Flush();
+ assert(flushed);
}
LogPrint("bench", "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * 0.001);
// Write the chain state to disk, if necessary.
@@ -2749,12 +2112,12 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
if (!fBare) {
// Resurrect mempool transactions from the disconnected block.
std::vector<uint256> vHashUpdate;
- BOOST_FOREACH(const CTransaction &tx, block.vtx) {
+ for (const auto& it : block.vtx) {
+ const CTransaction& tx = *it;
// ignore validation errors in resurrected transactions
- list<CTransaction> removed;
CValidationState stateDummy;
if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL, true)) {
- mempool.removeRecursive(tx, removed);
+ mempool.removeRecursive(tx);
} else if (mempool.exists(tx.GetHash())) {
vHashUpdate.push_back(tx.GetHash());
}
@@ -2771,8 +2134,8 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
UpdateTip(pindexDelete->pprev, chainparams);
// Let wallets know transactions went from 1-confirmed to
// 0-confirmed or conflicted:
- BOOST_FOREACH(const CTransaction &tx, block.vtx) {
- SyncWithWallets(tx, pindexDelete->pprev);
+ for (const auto& tx : block.vtx) {
+ GetMainSignals().SyncTransaction(*tx, pindexDelete->pprev, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK);
}
return true;
}
@@ -2784,37 +2147,52 @@ static int64_t nTimeChainState = 0;
static int64_t nTimePostConnect = 0;
/**
+ * Used to track blocks whose transactions were applied to the UTXO state as a
+ * part of a single ActivateBestChainStep call.
+ */
+struct ConnectTrace {
+ std::vector<std::pair<CBlockIndex*, std::shared_ptr<const CBlock> > > blocksConnected;
+};
+
+/**
* Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock
* corresponding to pindexNew, to bypass loading it again from disk.
+ *
+ * The block is always added to connectTrace (either after loading from disk or by copying
+ * pblock) - if that is not intended, care must be taken to remove the last entry in
+ * blocksConnected in case of failure.
*/
-bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const CBlock* pblock, std::list<CTransaction> &txConflicted, std::vector<std::tuple<CTransaction,CBlockIndex*,int>> &txChanged)
+bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace)
{
assert(pindexNew->pprev == chainActive.Tip());
// Read block from disk.
int64_t nTime1 = GetTimeMicros();
- CBlock block;
if (!pblock) {
- if (!ReadBlockFromDisk(block, pindexNew, chainparams.GetConsensus()))
+ std::shared_ptr<CBlock> pblockNew = std::make_shared<CBlock>();
+ connectTrace.blocksConnected.emplace_back(pindexNew, pblockNew);
+ if (!ReadBlockFromDisk(*pblockNew, pindexNew, chainparams.GetConsensus()))
return AbortNode(state, "Failed to read block");
- pblock = &block;
+ } else {
+ connectTrace.blocksConnected.emplace_back(pindexNew, pblock);
}
+ const CBlock& blockConnecting = *connectTrace.blocksConnected.back().second;
// Apply the block atomically to the chain state.
int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1;
int64_t nTime3;
LogPrint("bench", " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001);
{
CCoinsViewCache view(pcoinsTip);
- bool rv = ConnectBlock(*pblock, state, pindexNew, view, chainparams);
- GetMainSignals().BlockChecked(*pblock, state);
+ bool rv = ConnectBlock(blockConnecting, state, pindexNew, view, chainparams);
+ GetMainSignals().BlockChecked(blockConnecting, state);
if (!rv) {
if (state.IsInvalid())
InvalidBlockFound(pindexNew, state);
return error("ConnectTip(): ConnectBlock %s failed", pindexNew->GetBlockHash().ToString());
}
- mapBlockSource.erase(pindexNew->GetBlockHash());
nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2;
LogPrint("bench", " - Connect total: %.2fms [%.2fs]\n", (nTime3 - nTime2) * 0.001, nTimeConnectTotal * 0.000001);
- assert(view.Flush());
+ bool flushed = view.Flush();
+ assert(flushed);
}
int64_t nTime4 = GetTimeMicros(); nTimeFlush += nTime4 - nTime3;
LogPrint("bench", " - Flush: %.2fms [%.2fs]\n", (nTime4 - nTime3) * 0.001, nTimeFlush * 0.000001);
@@ -2824,13 +2202,10 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4;
LogPrint("bench", " - Writing chainstate: %.2fms [%.2fs]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001);
// Remove conflicting transactions from the mempool.;
- mempool.removeForBlock(pblock->vtx, pindexNew->nHeight, txConflicted, !IsInitialBlockDownload());
+ mempool.removeForBlock(blockConnecting.vtx, pindexNew->nHeight, !IsInitialBlockDownload());
// Update chainActive & related variables.
UpdateTip(pindexNew, chainparams);
- for(unsigned int i=0; i < pblock->vtx.size(); i++)
- txChanged.emplace_back(pblock->vtx[i], pindexNew, i);
-
int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
LogPrint("bench", " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001);
LogPrint("bench", "- Connect block: %.2fms [%.2fs]\n", (nTime6 - nTime1) * 0.001, nTimeTotal * 0.000001);
@@ -2911,7 +2286,7 @@ static void PruneBlockIndexCandidates() {
* Try to make some progress towards making pindexMostWork the active block.
* pblock is either NULL or a pointer to a CBlock corresponding to pindexMostWork.
*/
-static bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const CBlock* pblock, bool& fInvalidFound, std::list<CTransaction>& txConflicted, std::vector<std::tuple<CTransaction,CBlockIndex*,int>>& txChanged)
+static bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace)
{
AssertLockHeld(cs_main);
const CBlockIndex *pindexOldTip = chainActive.Tip();
@@ -2944,7 +2319,7 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
// Connect new blocks.
BOOST_REVERSE_FOREACH(CBlockIndex *pindexConnect, vpindexToConnect) {
- if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL, txConflicted, txChanged)) {
+ if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace)) {
if (state.IsInvalid()) {
// The block violates a consensus rule.
if (!state.CorruptionPossible())
@@ -2952,6 +2327,8 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
state = CValidationState();
fInvalidFound = true;
fContinue = false;
+ // If we didn't actually connect the block, don't notify listeners about it
+ connectTrace.blocksConnected.pop_back();
break;
} else {
// A system error occurred (disk space, database error, ...).
@@ -2990,9 +2367,8 @@ static void NotifyHeaderTip() {
CBlockIndex* pindexHeader = NULL;
{
LOCK(cs_main);
- if (!setBlockIndexCandidates.empty()) {
- pindexHeader = *setBlockIndexCandidates.rbegin();
- }
+ pindexHeader = pindexBestHeader;
+
if (pindexHeader != pindexHeaderOld) {
fNotify = true;
fInitialBlockDownload = IsInitialBlockDownload();
@@ -3010,22 +2386,17 @@ static void NotifyHeaderTip() {
* or an activated best chain. pblock is either NULL or a pointer to a block
* that is already loaded (to avoid loading it again from disk).
*/
-bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, const CBlock *pblock, CConnman* connman) {
+bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) {
CBlockIndex *pindexMostWork = NULL;
CBlockIndex *pindexNewTip = NULL;
- std::vector<std::tuple<CTransaction,CBlockIndex*,int>> txChanged;
- if (pblock)
- txChanged.reserve(pblock->vtx.size());
do {
- txChanged.clear();
boost::this_thread::interruption_point();
if (ShutdownRequested())
break;
const CBlockIndex *pindexFork;
- std::list<CTransaction> txConflicted;
+ ConnectTrace connectTrace;
bool fInitialDownload;
- int nNewHeight;
{
LOCK(cs_main);
CBlockIndex *pindexOldTip = chainActive.Tip();
@@ -3038,7 +2409,8 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
return true;
bool fInvalidFound = false;
- if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL, fInvalidFound, txConflicted, txChanged))
+ std::shared_ptr<const CBlock> nullBlockPtr;
+ if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connectTrace))
return false;
if (fInvalidFound) {
@@ -3048,59 +2420,26 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
pindexNewTip = chainActive.Tip();
pindexFork = chainActive.FindFork(pindexOldTip);
fInitialDownload = IsInitialBlockDownload();
- nNewHeight = chainActive.Height();
}
// When we reach this point, we switched to a new tip (stored in pindexNewTip).
// Notifications/callbacks that can run without cs_main
- if(connman)
- connman->SetBestHeight(nNewHeight);
// throw all transactions though the signal-interface
// while _not_ holding the cs_main lock
- BOOST_FOREACH(const CTransaction &tx, txConflicted)
- {
- SyncWithWallets(tx, pindexNewTip);
+ for (const auto& pair : connectTrace.blocksConnected) {
+ assert(pair.second);
+ const CBlock& block = *(pair.second);
+ for (unsigned int i = 0; i < block.vtx.size(); i++)
+ GetMainSignals().SyncTransaction(*block.vtx[i], pair.first, i);
}
- // ... and about transactions that got confirmed:
- for(unsigned int i = 0; i < txChanged.size(); i++)
- SyncWithWallets(std::get<0>(txChanged[i]), std::get<1>(txChanged[i]), std::get<2>(txChanged[i]));
+
+ // Notify external listeners about the new tip.
+ GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload);
// Always notify the UI if a new block tip was connected
if (pindexFork != pindexNewTip) {
uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip);
-
- if (!fInitialDownload) {
- // Find the hashes of all blocks that weren't previously in the best chain.
- std::vector<uint256> vHashes;
- CBlockIndex *pindexToAnnounce = pindexNewTip;
- while (pindexToAnnounce != pindexFork) {
- vHashes.push_back(pindexToAnnounce->GetBlockHash());
- pindexToAnnounce = pindexToAnnounce->pprev;
- if (vHashes.size() == MAX_BLOCKS_TO_ANNOUNCE) {
- // Limit announcements in case of a huge reorganization.
- // Rely on the peer's synchronization mechanism in that case.
- break;
- }
- }
- // Relay inventory, but don't relay old inventory during initial block download.
- int nBlockEstimate = 0;
- if (fCheckpointsEnabled)
- nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints());
- if(connman) {
- connman->ForEachNode([nNewHeight, nBlockEstimate, &vHashes](CNode* pnode) {
- if (nNewHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) {
- BOOST_REVERSE_FOREACH(const uint256& hash, vHashes) {
- pnode->PushBlockHash(hash);
- }
- }
- });
- }
- // Notify external listeners about the new tip.
- if (!vHashes.empty()) {
- GetMainSignals().UpdatedBlockTip(pindexNewTip);
- }
- }
}
} while (pindexNewTip != pindexMostWork);
CheckBlockIndex(chainparams.GetConsensus());
@@ -3113,6 +2452,36 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
return true;
}
+
+bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex)
+{
+ {
+ LOCK(cs_main);
+ if (pindex->nChainWork < chainActive.Tip()->nChainWork) {
+ // Nothing to do, this block is not at the tip.
+ return true;
+ }
+ if (chainActive.Tip()->nChainWork > nLastPreciousChainwork) {
+ // The chain has been extended since the last call, reset the counter.
+ nBlockReverseSequenceId = -1;
+ }
+ nLastPreciousChainwork = chainActive.Tip()->nChainWork;
+ setBlockIndexCandidates.erase(pindex);
+ pindex->nSequenceId = nBlockReverseSequenceId;
+ if (nBlockReverseSequenceId > std::numeric_limits<int32_t>::min()) {
+ // We can't keep reducing the counter if somebody really wants to
+ // call preciousblock 2**31-1 times on the same set of tips...
+ nBlockReverseSequenceId--;
+ }
+ if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && pindex->nChainTx) {
+ setBlockIndexCandidates.insert(pindex);
+ PruneBlockIndexCandidates();
+ }
+ }
+
+ return ActivateBestChain(state, params);
+}
+
bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex)
{
AssertLockHeld(cs_main);
@@ -3149,6 +2518,7 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C
InvalidChainFound(pindex);
mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS);
+ uiInterface.NotifyBlockTip(IsInitialBlockDownload(), pindex->pprev);
return true;
}
@@ -3402,22 +2772,22 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, "size limits failed");
// First transaction must be coinbase, the rest must not be
- if (block.vtx.empty() || !block.vtx[0].IsCoinBase())
+ if (block.vtx.empty() || !block.vtx[0]->IsCoinBase())
return state.DoS(100, false, REJECT_INVALID, "bad-cb-missing", false, "first tx is not coinbase");
for (unsigned int i = 1; i < block.vtx.size(); i++)
- if (block.vtx[i].IsCoinBase())
+ if (block.vtx[i]->IsCoinBase())
return state.DoS(100, false, REJECT_INVALID, "bad-cb-multiple", false, "more than one coinbase");
// Check transactions
for (const auto& tx : block.vtx)
- if (!CheckTransaction(tx, state))
+ if (!CheckTransaction(*tx, state, false))
return state.Invalid(false, state.GetRejectCode(), state.GetRejectReason(),
- strprintf("Transaction check failed (tx hash %s) %s", tx.GetHash().ToString(), state.GetDebugMessage()));
+ strprintf("Transaction check failed (tx hash %s) %s", tx->GetHash().ToString(), state.GetDebugMessage()));
unsigned int nSigOps = 0;
for (const auto& tx : block.vtx)
{
- nSigOps += GetLegacySigOpCount(tx);
+ nSigOps += GetLegacySigOpCount(*tx);
}
if (nSigOps * WITNESS_SCALE_FACTOR > MAX_BLOCK_SIGOPS_COST)
return state.DoS(100, false, REJECT_INVALID, "bad-blk-sigops", false, "out-of-bounds SigOpCount");
@@ -3453,8 +2823,8 @@ bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& pa
static int GetWitnessCommitmentIndex(const CBlock& block)
{
int commitpos = -1;
- for (size_t o = 0; o < block.vtx[0].vout.size(); o++) {
- if (block.vtx[0].vout[o].scriptPubKey.size() >= 38 && block.vtx[0].vout[o].scriptPubKey[0] == OP_RETURN && block.vtx[0].vout[o].scriptPubKey[1] == 0x24 && block.vtx[0].vout[o].scriptPubKey[2] == 0xaa && block.vtx[0].vout[o].scriptPubKey[3] == 0x21 && block.vtx[0].vout[o].scriptPubKey[4] == 0xa9 && block.vtx[0].vout[o].scriptPubKey[5] == 0xed) {
+ for (size_t o = 0; o < block.vtx[0]->vout.size(); o++) {
+ if (block.vtx[0]->vout[o].scriptPubKey.size() >= 38 && block.vtx[0]->vout[o].scriptPubKey[0] == OP_RETURN && block.vtx[0]->vout[o].scriptPubKey[1] == 0x24 && block.vtx[0]->vout[o].scriptPubKey[2] == 0xaa && block.vtx[0]->vout[o].scriptPubKey[3] == 0x21 && block.vtx[0]->vout[o].scriptPubKey[4] == 0xa9 && block.vtx[0]->vout[o].scriptPubKey[5] == 0xed) {
commitpos = o;
}
}
@@ -3465,10 +2835,12 @@ void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPr
{
int commitpos = GetWitnessCommitmentIndex(block);
static const std::vector<unsigned char> nonce(32, 0x00);
- if (commitpos != -1 && IsWitnessEnabled(pindexPrev, consensusParams) && block.vtx[0].wit.IsEmpty()) {
- block.vtx[0].wit.vtxinwit.resize(1);
- block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.resize(1);
- block.vtx[0].wit.vtxinwit[0].scriptWitness.stack[0] = nonce;
+ if (commitpos != -1 && IsWitnessEnabled(pindexPrev, consensusParams) && block.vtx[0]->wit.IsEmpty()) {
+ CMutableTransaction tx(*block.vtx[0]);
+ tx.wit.vtxinwit.resize(1);
+ tx.wit.vtxinwit[0].scriptWitness.stack.resize(1);
+ tx.wit.vtxinwit[0].scriptWitness.stack[0] = nonce;
+ block.vtx[0] = MakeTransactionRef(std::move(tx));
}
}
@@ -3476,15 +2848,8 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
{
std::vector<unsigned char> commitment;
int commitpos = GetWitnessCommitmentIndex(block);
- bool fHaveWitness = false;
- for (size_t t = 1; t < block.vtx.size(); t++) {
- if (!block.vtx[t].wit.IsNull()) {
- fHaveWitness = true;
- break;
- }
- }
std::vector<unsigned char> ret(32, 0x00);
- if (fHaveWitness && IsWitnessEnabled(pindexPrev, consensusParams)) {
+ if (consensusParams.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout != 0) {
if (commitpos == -1) {
uint256 witnessroot = BlockWitnessMerkleRoot(block, NULL);
CHash256().Write(witnessroot.begin(), 32).Write(&ret[0], 32).Finalize(witnessroot.begin());
@@ -3499,8 +2864,9 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
out.scriptPubKey[5] = 0xed;
memcpy(&out.scriptPubKey[6], witnessroot.begin(), 32);
commitment = std::vector<unsigned char>(out.scriptPubKey.begin(), out.scriptPubKey.end());
- const_cast<std::vector<CTxOut>*>(&block.vtx[0].vout)->push_back(out);
- block.vtx[0].UpdateHash();
+ CMutableTransaction tx(*block.vtx[0]);
+ tx.vout.push_back(out);
+ block.vtx[0] = MakeTransactionRef(std::move(tx));
}
}
UpdateUncommittedBlockStructures(block, pindexPrev, consensusParams);
@@ -3549,7 +2915,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Co
// Check that all transactions are finalized
for (const auto& tx : block.vtx) {
- if (!IsFinalTx(tx, nHeight, nLockTimeCutoff)) {
+ if (!IsFinalTx(*tx, nHeight, nLockTimeCutoff)) {
return state.DoS(10, false, REJECT_INVALID, "bad-txns-nonfinal", false, "non-final transaction");
}
}
@@ -3558,8 +2924,8 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Co
if (nHeight >= consensusParams.BIP34Height)
{
CScript expect = CScript() << nHeight;
- if (block.vtx[0].vin[0].scriptSig.size() < expect.size() ||
- !std::equal(expect.begin(), expect.end(), block.vtx[0].vin[0].scriptSig.begin())) {
+ if (block.vtx[0]->vin[0].scriptSig.size() < expect.size() ||
+ !std::equal(expect.begin(), expect.end(), block.vtx[0]->vin[0].scriptSig.begin())) {
return state.DoS(100, false, REJECT_INVALID, "bad-cb-height", false, "block height mismatch in coinbase");
}
}
@@ -3581,11 +2947,11 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Co
// The malleation check is ignored; as the transaction tree itself
// already does not permit it, it is impossible to trigger in the
// witness tree.
- if (block.vtx[0].wit.vtxinwit.size() != 1 || block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.size() != 1 || block.vtx[0].wit.vtxinwit[0].scriptWitness.stack[0].size() != 32) {
+ if (block.vtx[0]->wit.vtxinwit.size() != 1 || block.vtx[0]->wit.vtxinwit[0].scriptWitness.stack.size() != 1 || block.vtx[0]->wit.vtxinwit[0].scriptWitness.stack[0].size() != 32) {
return state.DoS(100, false, REJECT_INVALID, "bad-witness-nonce-size", true, strprintf("%s : invalid witness nonce size", __func__));
}
- CHash256().Write(hashWitness.begin(), 32).Write(&block.vtx[0].wit.vtxinwit[0].scriptWitness.stack[0][0], 32).Finalize(hashWitness.begin());
- if (memcmp(hashWitness.begin(), &block.vtx[0].vout[commitpos].scriptPubKey[6], 32)) {
+ CHash256().Write(hashWitness.begin(), 32).Write(&block.vtx[0]->wit.vtxinwit[0].scriptWitness.stack[0][0], 32).Finalize(hashWitness.begin());
+ if (memcmp(hashWitness.begin(), &block.vtx[0]->vout[commitpos].scriptPubKey[6], 32)) {
return state.DoS(100, false, REJECT_INVALID, "bad-witness-merkle-match", true, strprintf("%s : witness merkle commitment mismatch", __func__));
}
fHaveWitness = true;
@@ -3595,7 +2961,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Co
// No witness data is allowed in blocks that don't commit to witness data, as this would otherwise leave room for spam
if (!fHaveWitness) {
for (size_t i = 0; i < block.vtx.size(); i++) {
- if (!block.vtx[i].wit.IsNull()) {
+ if (!block.vtx[i]->wit.IsNull()) {
return state.DoS(100, false, REJECT_INVALID, "unexpected-witness", true, strprintf("%s : unexpected witness data found", __func__));
}
}
@@ -3614,7 +2980,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Co
return true;
}
-static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex=NULL)
+static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex)
{
AssertLockHeld(cs_main);
// Check for duplicate
@@ -3658,6 +3024,23 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state
if (ppindex)
*ppindex = pindex;
+ CheckBlockIndex(chainparams.GetConsensus());
+
+ return true;
+}
+
+// Exposed wrapper for AcceptBlockHeader
+bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex)
+{
+ {
+ LOCK(cs_main);
+ for (const CBlockHeader& header : headers) {
+ if (!AcceptBlockHeader(header, state, chainparams, ppindex)) {
+ return false;
+ }
+ }
+ }
+ NotifyHeaderTip();
return true;
}
@@ -3685,6 +3068,11 @@ static bool AcceptBlock(const CBlock& block, CValidationState& state, const CCha
// not process unrequested blocks.
bool fTooFarAhead = (pindex->nHeight > int(chainActive.Height() + MIN_BLOCKS_TO_KEEP));
+ // TODO: Decouple this function from the block download logic by removing fRequested
+ // This requires some new chain datastructure to efficiently look up if a
+ // block is in a chain leading to a candidate for best tip, despite not
+ // being such a candidate itself.
+
// TODO: deal better with return value and error conditions for duplicate
// and unrequested blocks.
if (fAlreadyHave) return true;
@@ -3729,29 +3117,27 @@ static bool AcceptBlock(const CBlock& block, CValidationState& state, const CCha
return true;
}
-bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp, CConnman* connman)
+bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock> pblock, bool fForceProcessing, bool *fNewBlock)
{
{
LOCK(cs_main);
- bool fRequested = MarkBlockAsReceived(pblock->GetHash());
- fRequested |= fForceProcessing;
// Store to disk
CBlockIndex *pindex = NULL;
- bool fNewBlock = false;
- bool ret = AcceptBlock(*pblock, state, chainparams, &pindex, fRequested, dbp, &fNewBlock);
- if (pindex && pfrom) {
- mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId();
- if (fNewBlock) pfrom->nLastBlockTime = GetTime();
- }
+ if (fNewBlock) *fNewBlock = false;
+ CValidationState state;
+ bool ret = AcceptBlock(*pblock, state, chainparams, &pindex, fForceProcessing, NULL, fNewBlock);
CheckBlockIndex(chainparams.GetConsensus());
- if (!ret)
+ if (!ret) {
+ GetMainSignals().BlockChecked(*pblock, state);
return error("%s: AcceptBlock FAILED", __func__);
+ }
}
NotifyHeaderTip();
- if (!ActivateBestChain(state, chainparams, pblock, connman))
+ CValidationState state; // Only used to report errors, not invalidity - ignore it
+ if (!ActivateBestChain(state, chainparams, pblock))
return error("%s: ActivateBestChain failed", __func__);
return true;
@@ -3816,10 +3202,10 @@ void PruneOneBlockFile(const int fileNumber)
// mapBlocksUnlinked or setBlockIndexCandidates.
std::pair<std::multimap<CBlockIndex*, CBlockIndex*>::iterator, std::multimap<CBlockIndex*, CBlockIndex*>::iterator> range = mapBlocksUnlinked.equal_range(pindex->pprev);
while (range.first != range.second) {
- std::multimap<CBlockIndex *, CBlockIndex *>::iterator it = range.first;
+ std::multimap<CBlockIndex *, CBlockIndex *>::iterator _it = range.first;
range.first++;
- if (it->second == pindex) {
- mapBlocksUnlinked.erase(it);
+ if (_it->second == pindex) {
+ mapBlocksUnlinked.erase(_it);
}
}
}
@@ -3955,9 +3341,8 @@ CBlockIndex * InsertBlockIndex(uint256 hash)
return pindexNew;
}
-bool static LoadBlockIndexDB()
+bool static LoadBlockIndexDB(const CChainParams& chainparams)
{
- const CChainParams& chainparams = Params();
if (!pblocktree->LoadBlockIndexGuts(InsertBlockIndex))
return false;
@@ -4252,6 +3637,9 @@ bool RewindBlockIndex(const CChainParams& params)
return true;
}
+// May NOT be used after any connections are up as much
+// of the peer-processing logic assumes a consistent
+// block index state
void UnloadBlockIndex()
{
LOCK(cs_main);
@@ -4260,20 +3648,12 @@ void UnloadBlockIndex()
pindexBestInvalid = NULL;
pindexBestHeader = NULL;
mempool.clear();
- mapOrphanTransactions.clear();
- mapOrphanTransactionsByPrev.clear();
- nSyncStarted = 0;
mapBlocksUnlinked.clear();
vinfoBlockFile.clear();
nLastBlockFile = 0;
nBlockSequenceId = 1;
- mapBlockSource.clear();
- mapBlocksInFlight.clear();
- nPreferredDownload = 0;
setDirtyBlockIndex.clear();
setDirtyFileInfo.clear();
- mapNodeState.clear();
- recentRejects.reset(NULL);
versionbitscache.Clear();
for (int b = 0; b < VERSIONBITS_NUM_BITS; b++) {
warningcache[b].clear();
@@ -4286,10 +3666,10 @@ void UnloadBlockIndex()
fHavePruned = false;
}
-bool LoadBlockIndex()
+bool LoadBlockIndex(const CChainParams& chainparams)
{
// Load block index from databases
- if (!fReindex && !LoadBlockIndexDB())
+ if (!fReindex && !LoadBlockIndexDB(chainparams))
return false;
return true;
}
@@ -4298,9 +3678,6 @@ bool InitBlockIndex(const CChainParams& chainparams)
{
LOCK(cs_main);
- // Initialize global variables that cannot be constructed at startup.
- recentRejects.reset(new CRollingBloomFilter(120000, 0.000001));
-
// Check whether we're already initialized
if (chainActive.Genesis() != NULL)
return true;
@@ -4355,11 +3732,11 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB
unsigned int nSize = 0;
try {
// locate a header
- unsigned char buf[MESSAGE_START_SIZE];
+ unsigned char buf[CMessageHeader::MESSAGE_START_SIZE];
blkdat.FindByte(chainparams.MessageStart()[0]);
nRewind = blkdat.GetPos()+1;
blkdat >> FLATDATA(buf);
- if (memcmp(buf, chainparams.MessageStart(), MESSAGE_START_SIZE))
+ if (memcmp(buf, chainparams.MessageStart(), CMessageHeader::MESSAGE_START_SIZE))
continue;
// read size
blkdat >> nSize;
@@ -4507,7 +3884,7 @@ void static CheckBlockIndex(const Consensus::Params& consensusParams)
assert(pindex->GetBlockHash() == consensusParams.hashGenesisBlock); // Genesis block's hash must match.
assert(pindex == chainActive.Genesis()); // The current active chain's genesis block must be this block.
}
- if (pindex->nChainTx == 0) assert(pindex->nSequenceId == 0); // nSequenceId can't be set for blocks that aren't linked
+ if (pindex->nChainTx == 0) assert(pindex->nSequenceId <= 0); // nSequenceId can't be set positive for blocks that aren't linked (negative is used for preciousblock)
// VALID_TRANSACTIONS is equivalent to nTx > 0 for all nodes (whether or not pruning has occurred).
// HAVE_DATA is only equivalent to nTx > 0 (or VALID_TRANSACTIONS) if no pruning has occurred.
if (!fHavePruned) {
@@ -4634,2193 +4011,133 @@ void static CheckBlockIndex(const Consensus::Params& consensusParams)
assert(nNodes == forward.size());
}
-std::string GetWarnings(const std::string& strFor)
-{
- string strStatusBar;
- string strRPC;
- string strGUI;
- const string uiAlertSeperator = "<hr />";
-
- 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";
- strGUI = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications");
- }
-
- if (GetBoolArg("-testsafemode", DEFAULT_TESTSAFEMODE))
- strStatusBar = strRPC = strGUI = "testsafemode enabled";
-
- // Misc warnings like out of disk space and clock is wrong
- if (strMiscWarning != "")
- {
- strStatusBar = strMiscWarning;
- strGUI += (strGUI.empty() ? "" : uiAlertSeperator) + strMiscWarning;
- }
-
- if (fLargeWorkForkFound)
- {
- strStatusBar = strRPC = "Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.";
- strGUI += (strGUI.empty() ? "" : uiAlertSeperator) + _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.");
- }
- else if (fLargeWorkInvalidChainFound)
- {
- strStatusBar = strRPC = "Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.";
- strGUI += (strGUI.empty() ? "" : uiAlertSeperator) + _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.");
- }
-
- if (strFor == "gui")
- return strGUI;
- else if (strFor == "statusbar")
- return strStatusBar;
- else if (strFor == "rpc")
- return strRPC;
- assert(!"GetWarnings(): invalid parameter");
- return "error";
-}
-
-
-
-
-
-
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// Messages
-//
-
-
-bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
-{
- switch (inv.type)
- {
- case MSG_TX:
- case MSG_WITNESS_TX:
- {
- assert(recentRejects);
- if (chainActive.Tip()->GetBlockHash() != hashRecentRejectsChainTip)
- {
- // If the chain tip has changed previously rejected transactions
- // might be now valid, e.g. due to a nLockTime'd tx becoming valid,
- // or a double-spend. Reset the rejects filter and give those
- // txs a second chance.
- hashRecentRejectsChainTip = chainActive.Tip()->GetBlockHash();
- recentRejects->reset();
- }
-
- // Use pcoinsTip->HaveCoinsInCache as a quick approximation to exclude
- // requesting or processing some txs which have already been included in a block
- return recentRejects->contains(inv.hash) ||
- mempool.exists(inv.hash) ||
- mapOrphanTransactions.count(inv.hash) ||
- pcoinsTip->HaveCoinsInCache(inv.hash);
- }
- case MSG_BLOCK:
- case MSG_WITNESS_BLOCK:
- return mapBlockIndex.count(inv.hash);
- }
- // Don't know what it is, just say we already got one
- return true;
-}
-
-static void RelayTransaction(const CTransaction& tx, CConnman& connman)
+std::string CBlockFileInfo::ToString() const
{
- CInv inv(MSG_TX, tx.GetHash());
- connman.ForEachNode([&inv](CNode* pnode)
- {
- pnode->PushInventory(inv);
- });
+ return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst), DateTimeStrFormat("%Y-%m-%d", nTimeLast));
}
-static void RelayAddress(const CAddress& addr, bool fReachable, CConnman& connman)
+ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos)
{
- int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s)
-
- // Relay to a limited number of other nodes
- // Use deterministic randomness to send to the same nodes for 24 hours
- // at a time so the addrKnowns of the chosen nodes prevent repeats
- static const uint64_t salt0 = GetRand(std::numeric_limits<uint64_t>::max());
- static const uint64_t salt1 = GetRand(std::numeric_limits<uint64_t>::max());
- uint64_t hashAddr = addr.GetHash();
- std::multimap<uint64_t, CNode*> mapMix;
- const CSipHasher hasher = CSipHasher(salt0, salt1).Write(hashAddr << 32).Write((GetTime() + hashAddr) / (24*60*60));
-
- auto sortfunc = [&mapMix, &hasher](CNode* pnode) {
- if (pnode->nVersion >= CADDR_TIME_VERSION) {
- uint64_t hashKey = CSipHasher(hasher).Write(pnode->id).Finalize();
- mapMix.emplace(hashKey, pnode);
- }
- };
-
- auto pushfunc = [&addr, &mapMix, &nRelayNodes] {
- for (auto mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi)
- mi->second->PushAddress(addr);
- };
-
- connman.ForEachNodeThen(std::move(sortfunc), std::move(pushfunc));
+ LOCK(cs_main);
+ return VersionBitsState(chainActive.Tip(), params, pos, versionbitscache);
}
-void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParams, CConnman& connman)
+int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::DeploymentPos pos)
{
- std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin();
- unsigned int nMaxSendBufferSize = connman.GetSendBufferSize();
-
- vector<CInv> vNotFound;
-
LOCK(cs_main);
-
- while (it != pfrom->vRecvGetData.end()) {
- // Don't bother if send buffer is too full to respond anyway
- if (pfrom->nSendSize >= nMaxSendBufferSize)
- break;
-
- const CInv &inv = *it;
- {
- boost::this_thread::interruption_point();
- it++;
-
- if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK)
- {
- bool send = false;
- BlockMap::iterator mi = mapBlockIndex.find(inv.hash);
- if (mi != mapBlockIndex.end())
- {
- if (chainActive.Contains(mi->second)) {
- send = true;
- } else {
- static const int nOneMonth = 30 * 24 * 60 * 60;
- // To prevent fingerprinting attacks, only send blocks outside of the active
- // chain if they are valid, and no more than a month older (both in time, and in
- // best equivalent proof of work) than the best header chain we know about.
- send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != NULL) &&
- (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() < nOneMonth) &&
- (GetBlockProofEquivalentTime(*pindexBestHeader, *mi->second, *pindexBestHeader, consensusParams) < nOneMonth);
- if (!send) {
- LogPrintf("%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId());
- }
- }
- }
- // disconnect node in case we have reached the outbound limit for serving historical blocks
- // never disconnect whitelisted nodes
- static const int nOneWeek = 7 * 24 * 60 * 60; // assume > 1 week = historical
- if (send && connman.OutboundTargetReached(true) && ( ((pindexBestHeader != NULL) && (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() > nOneWeek)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted)
- {
- LogPrint("net", "historical block serving limit reached, disconnect peer=%d\n", pfrom->GetId());
-
- //disconnect node
- pfrom->fDisconnect = true;
- send = false;
- }
- // Pruned nodes may have deleted the block, so check whether
- // it's available before trying to send.
- if (send && (mi->second->nStatus & BLOCK_HAVE_DATA))
- {
- // Send block from disk
- CBlock block;
- if (!ReadBlockFromDisk(block, (*mi).second, consensusParams))
- assert(!"cannot load block from disk");
- if (inv.type == MSG_BLOCK)
- pfrom->PushMessageWithFlag(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, block);
- else if (inv.type == MSG_WITNESS_BLOCK)
- pfrom->PushMessage(NetMsgType::BLOCK, block);
- else if (inv.type == MSG_FILTERED_BLOCK)
- {
- bool sendMerkleBlock = false;
- CMerkleBlock merkleBlock;
- {
- LOCK(pfrom->cs_filter);
- if (pfrom->pfilter) {
- sendMerkleBlock = true;
- merkleBlock = CMerkleBlock(block, *pfrom->pfilter);
- }
- }
- if (sendMerkleBlock) {
- pfrom->PushMessage(NetMsgType::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 didn't 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<unsigned int, uint256> PairType;
- BOOST_FOREACH(PairType& pair, merkleBlock.vMatchedTxn)
- pfrom->PushMessageWithFlag(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, block.vtx[pair.first]);
- }
- // else
- // no response
- }
- else if (inv.type == MSG_CMPCT_BLOCK)
- {
- // If a peer is asking for old blocks, we're almost guaranteed
- // they wont have a useful mempool to match against a compact block,
- // and we don't feel like constructing the object for them, so
- // instead we respond with the full, non-compact block.
- if (mi->second->nHeight >= chainActive.Height() - 10) {
- CBlockHeaderAndShortTxIDs cmpctblock(block);
- pfrom->PushMessageWithFlag(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::CMPCTBLOCK, cmpctblock);
- } else
- pfrom->PushMessageWithFlag(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, block);
- }
-
- // Trigger the peer node 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<CInv> vInv;
- vInv.push_back(CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash()));
- pfrom->PushMessage(NetMsgType::INV, vInv);
- pfrom->hashContinue.SetNull();
- }
- }
- }
- else if (inv.type == MSG_TX || inv.type == MSG_WITNESS_TX)
- {
- // Send stream from relay memory
- bool push = false;
- auto mi = mapRelay.find(inv.hash);
- if (mi != mapRelay.end()) {
- pfrom->PushMessageWithFlag(inv.type == MSG_TX ? SERIALIZE_TRANSACTION_NO_WITNESS : 0, NetMsgType::TX, *mi->second);
- push = true;
- } else if (pfrom->timeLastMempoolReq) {
- auto txinfo = mempool.info(inv.hash);
- // To protect privacy, do not answer getdata using the mempool when
- // that TX couldn't have been INVed in reply to a MEMPOOL request.
- if (txinfo.tx && txinfo.nTime <= pfrom->timeLastMempoolReq) {
- pfrom->PushMessageWithFlag(inv.type == MSG_TX ? SERIALIZE_TRANSACTION_NO_WITNESS : 0, NetMsgType::TX, *txinfo.tx);
- push = true;
- }
- }
- if (!push) {
- vNotFound.push_back(inv);
- }
- }
-
- // Track requests for our stuff.
- GetMainSignals().Inventory(inv.hash);
-
- if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK)
- break;
- }
- }
-
- 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(NetMsgType::NOTFOUND, vNotFound);
- }
+ return VersionBitsStateSinceHeight(chainActive.Tip(), params, pos, versionbitscache);
}
-uint32_t GetFetchFlags(CNode* pfrom, CBlockIndex* pprev, const Consensus::Params& chainparams) {
- uint32_t nFetchFlags = 0;
- if (IsWitnessEnabled(pprev, chainparams) && State(pfrom->GetId())->fHaveWitness) {
- nFetchFlags |= MSG_WITNESS_FLAG;
- }
- return nFetchFlags;
-}
+static const uint64_t MEMPOOL_DUMP_VERSION = 1;
-bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman& connman)
+bool LoadMempool(void)
{
- unsigned int nMaxSendBufferSize = connman.GetSendBufferSize();
-
- LogPrint("net", "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id);
- if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0)
- {
- LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n");
- return true;
- }
-
-
- if (!(pfrom->GetLocalServices() & NODE_BLOOM) &&
- (strCommand == NetMsgType::FILTERLOAD ||
- strCommand == NetMsgType::FILTERADD ||
- strCommand == NetMsgType::FILTERCLEAR))
- {
- if (pfrom->nVersion >= NO_BLOOM_VERSION) {
- LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 100);
- return false;
- } else {
- pfrom->fDisconnect = true;
- return false;
- }
- }
-
-
- if (strCommand == NetMsgType::VERSION)
- {
- // Feeler connections exist only to verify if address is online.
- if (pfrom->fFeeler) {
- assert(pfrom->fInbound == false);
- pfrom->fDisconnect = true;
- }
-
- // Each connection can only send one version message
- if (pfrom->nVersion != 0)
- {
- pfrom->PushMessage(NetMsgType::REJECT, strCommand, REJECT_DUPLICATE, string("Duplicate version message"));
- LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 1);
- return false;
- }
-
- int64_t nTime;
- CAddress addrMe;
- CAddress addrFrom;
- uint64_t nNonce = 1;
- uint64_t nServiceInt;
- vRecv >> pfrom->nVersion >> nServiceInt >> nTime >> addrMe;
- pfrom->nServices = ServiceFlags(nServiceInt);
- if (!pfrom->fInbound)
- {
- connman.SetServices(pfrom->addr, pfrom->nServices);
- }
- if (pfrom->nServicesExpected & ~pfrom->nServices)
- {
- LogPrint("net", "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->id, pfrom->nServices, pfrom->nServicesExpected);
- pfrom->PushMessage(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD,
- strprintf("Expected to offer services %08x", pfrom->nServicesExpected));
- pfrom->fDisconnect = true;
- return false;
- }
-
- if (pfrom->nVersion < MIN_PEER_PROTO_VERSION)
- {
- // disconnect from peers older than this proto version
- LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion);
- pfrom->PushMessage(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE,
- strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION));
- pfrom->fDisconnect = true;
- return false;
- }
-
- if (pfrom->nVersion == 10300)
- pfrom->nVersion = 300;
- if (!vRecv.empty())
- vRecv >> addrFrom >> nNonce;
- if (!vRecv.empty()) {
- vRecv >> LIMITED_STRING(pfrom->strSubVer, MAX_SUBVERSION_LENGTH);
- pfrom->cleanSubVer = SanitizeString(pfrom->strSubVer);
- }
- if (!vRecv.empty()) {
- vRecv >> pfrom->nStartingHeight;
- }
- {
- LOCK(pfrom->cs_filter);
- if (!vRecv.empty())
- vRecv >> pfrom->fRelayTxes; // set to true after we get the first filter* message
- else
- pfrom->fRelayTxes = true;
- }
-
- // Disconnect if we connected to ourself
- if (pfrom->fInbound && !connman.CheckIncomingNonce(nNonce))
- {
- LogPrintf("connected to self at %s, disconnecting\n", pfrom->addr.ToString());
- pfrom->fDisconnect = true;
- return true;
- }
-
- pfrom->addrLocal = addrMe;
- if (pfrom->fInbound && addrMe.IsRoutable())
- {
- SeenLocal(addrMe);
- }
-
- // Be shy and don't send version until we hear
- if (pfrom->fInbound)
- pfrom->PushVersion();
-
- pfrom->fClient = !(pfrom->nServices & NODE_NETWORK);
-
- if((pfrom->nServices & NODE_WITNESS))
- {
- LOCK(cs_main);
- State(pfrom->GetId())->fHaveWitness = true;
- }
-
- // Potentially mark this peer as a preferred download peer.
- {
- LOCK(cs_main);
- UpdatePreferredDownload(pfrom, State(pfrom->GetId()));
- }
-
- // Change version
- pfrom->PushMessage(NetMsgType::VERACK);
- pfrom->ssSend.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION));
-
- if (!pfrom->fInbound)
- {
- // Advertise our address
- if (fListen && !IsInitialBlockDownload())
- {
- CAddress addr = GetLocalAddress(&pfrom->addr, pfrom->GetLocalServices());
- if (addr.IsRoutable())
- {
- LogPrint("net", "ProcessMessages: advertising address %s\n", addr.ToString());
- pfrom->PushAddress(addr);
- } else if (IsPeerAddrLocalGood(pfrom)) {
- addr.SetIP(pfrom->addrLocal);
- LogPrint("net", "ProcessMessages: advertising address %s\n", addr.ToString());
- pfrom->PushAddress(addr);
- }
- }
-
- // Get recent addresses
- if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || connman.GetAddressCount() < 1000)
- {
- pfrom->PushMessage(NetMsgType::GETADDR);
- pfrom->fGetAddr = true;
- }
- connman.MarkAddressGood(pfrom->addr);
- }
-
- pfrom->fSuccessfullyConnected = true;
-
- string remoteAddr;
- if (fLogIPs)
- remoteAddr = ", peeraddr=" + pfrom->addr.ToString();
-
- LogPrintf("receive version message: %s: version %d, blocks=%d, us=%s, peer=%d%s\n",
- pfrom->cleanSubVer, pfrom->nVersion,
- pfrom->nStartingHeight, addrMe.ToString(), pfrom->id,
- remoteAddr);
-
- int64_t nTimeOffset = nTime - GetTime();
- pfrom->nTimeOffset = nTimeOffset;
- AddTimeData(pfrom->addr, nTimeOffset);
- }
-
-
- else if (pfrom->nVersion == 0)
- {
- // Must have a version message before anything else
- LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 1);
+ int64_t nExpiryTimeout = GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60;
+ FILE* filestr = fopen((GetDataDir() / "mempool.dat").string().c_str(), "r");
+ CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
+ if (file.IsNull()) {
+ LogPrintf("Failed to open mempool file from disk. Continuing anyway.\n");
return false;
}
+ int64_t count = 0;
+ int64_t skipped = 0;
+ int64_t failed = 0;
+ int64_t nNow = GetTime();
- else if (strCommand == NetMsgType::VERACK)
- {
- pfrom->SetRecvVersion(min(pfrom->nVersion, PROTOCOL_VERSION));
-
- // Mark this node as currently connected, so we update its timestamp later.
- if (pfrom->fNetworkNode) {
- LOCK(cs_main);
- State(pfrom->GetId())->fCurrentlyConnected = true;
- }
-
- if (pfrom->nVersion >= SENDHEADERS_VERSION) {
- // Tell our peer we prefer to receive headers rather than inv's
- // We send this to non-NODE NETWORK peers as well, because even
- // non-NODE NETWORK peers can announce blocks (such as pruning
- // nodes)
- pfrom->PushMessage(NetMsgType::SENDHEADERS);
- }
- if (pfrom->nVersion >= SHORT_IDS_BLOCKS_VERSION) {
- // Tell our peer we are willing to provide version-1 cmpctblocks
- // However, we do not request new block announcements using
- // cmpctblock messages.
- // We send this to non-NODE NETWORK peers as well, because
- // they may wish to request compact blocks from us
- bool fAnnounceUsingCMPCTBLOCK = false;
- uint64_t nCMPCTBLOCKVersion = 1;
- pfrom->PushMessage(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion);
- }
- }
-
-
- else if (strCommand == NetMsgType::ADDR)
- {
- vector<CAddress> vAddr;
- vRecv >> vAddr;
-
- // Don't want addr from older versions unless seeding
- if (pfrom->nVersion < CADDR_TIME_VERSION && connman.GetAddressCount() > 1000)
- return true;
- if (vAddr.size() > 1000)
- {
- LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 20);
- return error("message addr size() = %u", vAddr.size());
- }
-
- // Store the new addresses
- vector<CAddress> vAddrOk;
- int64_t nNow = GetAdjustedTime();
- int64_t nSince = nNow - 10 * 60;
- BOOST_FOREACH(CAddress& addr, vAddr)
- {
- boost::this_thread::interruption_point();
-
- if ((addr.nServices & REQUIRED_SERVICES) != REQUIRED_SERVICES)
- continue;
-
- 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
- RelayAddress(addr, fReachable, connman);
- }
- // Do not store addresses outside our network
- if (fReachable)
- vAddrOk.push_back(addr);
- }
- connman.AddNewAddresses(vAddrOk, pfrom->addr, 2 * 60 * 60);
- if (vAddr.size() < 1000)
- pfrom->fGetAddr = false;
- if (pfrom->fOneShot)
- pfrom->fDisconnect = true;
- }
-
- else if (strCommand == NetMsgType::SENDHEADERS)
- {
- LOCK(cs_main);
- State(pfrom->GetId())->fPreferHeaders = true;
- }
-
- else if (strCommand == NetMsgType::SENDCMPCT)
- {
- bool fAnnounceUsingCMPCTBLOCK = false;
- uint64_t nCMPCTBLOCKVersion = 1;
- vRecv >> fAnnounceUsingCMPCTBLOCK >> nCMPCTBLOCKVersion;
- if (nCMPCTBLOCKVersion == 1) {
- LOCK(cs_main);
- State(pfrom->GetId())->fProvidesHeaderAndIDs = true;
- State(pfrom->GetId())->fPreferHeaderAndIDs = fAnnounceUsingCMPCTBLOCK;
- }
- }
-
-
- else if (strCommand == NetMsgType::INV)
- {
- vector<CInv> vInv;
- vRecv >> vInv;
- if (vInv.size() > MAX_INV_SZ)
- {
- LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 20);
- return error("message inv size() = %u", vInv.size());
- }
-
- bool fBlocksOnly = !fRelayTxes;
-
- // Allow whitelisted peers to send data other than blocks in blocks only mode if whitelistrelay is true
- if (pfrom->fWhitelisted && GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY))
- fBlocksOnly = false;
-
- LOCK(cs_main);
-
- uint32_t nFetchFlags = GetFetchFlags(pfrom, chainActive.Tip(), chainparams.GetConsensus());
-
- std::vector<CInv> vToFetch;
-
- for (unsigned int nInv = 0; nInv < vInv.size(); nInv++)
- {
- CInv &inv = vInv[nInv];
-
- boost::this_thread::interruption_point();
-
- bool fAlreadyHave = AlreadyHave(inv);
- LogPrint("net", "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->id);
-
- if (inv.type == MSG_TX) {
- inv.type |= nFetchFlags;
- }
-
- if (inv.type == MSG_BLOCK) {
- UpdateBlockAvailability(pfrom->GetId(), inv.hash);
- if (!fAlreadyHave && !fImporting && !fReindex && !mapBlocksInFlight.count(inv.hash)) {
- // First request the headers preceding the announced block. In the normal fully-synced
- // case where a new block is announced that succeeds the current tip (no reorganization),
- // there are no such headers.
- // Secondly, and only when we are close to being synced, we request the announced block directly,
- // to avoid an extra round-trip. Note that we must *first* ask for the headers, so by the
- // time the block arrives, the header chain leading up to it is already validated. Not
- // doing this will result in the received block being rejected as an orphan in case it is
- // not a direct successor.
- pfrom->PushMessage(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), inv.hash);
- CNodeState *nodestate = State(pfrom->GetId());
- if (CanDirectFetch(chainparams.GetConsensus()) &&
- nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER &&
- (!IsWitnessEnabled(chainActive.Tip(), chainparams.GetConsensus()) || State(pfrom->GetId())->fHaveWitness)) {
- inv.type |= nFetchFlags;
- if (nodestate->fProvidesHeaderAndIDs && !(pfrom->GetLocalServices() & NODE_WITNESS))
- vToFetch.push_back(CInv(MSG_CMPCT_BLOCK, inv.hash));
- else
- vToFetch.push_back(inv);
- // Mark block as in flight already, even though the actual "getdata" message only goes out
- // later (within the same cs_main lock, though).
- MarkBlockAsInFlight(pfrom->GetId(), inv.hash, chainparams.GetConsensus());
- }
- LogPrint("net", "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, inv.hash.ToString(), pfrom->id);
- }
- }
- else
- {
- pfrom->AddInventoryKnown(inv);
- if (fBlocksOnly)
- LogPrint("net", "transaction (%s) inv sent in violation of protocol peer=%d\n", inv.hash.ToString(), pfrom->id);
- else if (!fAlreadyHave && !fImporting && !fReindex && !IsInitialBlockDownload())
- pfrom->AskFor(inv);
- }
-
- // Track requests for our stuff
- GetMainSignals().Inventory(inv.hash);
-
- if (pfrom->nSendSize > (nMaxSendBufferSize * 2)) {
- Misbehaving(pfrom->GetId(), 50);
- return error("send buffer size() = %u", pfrom->nSendSize);
- }
- }
-
- if (!vToFetch.empty())
- pfrom->PushMessage(NetMsgType::GETDATA, vToFetch);
- }
-
-
- else if (strCommand == NetMsgType::GETDATA)
- {
- vector<CInv> vInv;
- vRecv >> vInv;
- if (vInv.size() > MAX_INV_SZ)
- {
- LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 20);
- return error("message getdata size() = %u", vInv.size());
- }
-
- if (fDebug || (vInv.size() != 1))
- LogPrint("net", "received getdata (%u invsz) peer=%d\n", vInv.size(), pfrom->id);
-
- if ((fDebug && vInv.size() > 0) || (vInv.size() == 1))
- LogPrint("net", "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom->id);
-
- pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end());
- ProcessGetData(pfrom, chainparams.GetConsensus(), connman);
- }
-
-
- else if (strCommand == NetMsgType::GETBLOCKS)
- {
- CBlockLocator locator;
- uint256 hashStop;
- vRecv >> locator >> hashStop;
-
- LOCK(cs_main);
-
- // Find the last block the caller has in the main chain
- CBlockIndex* pindex = FindForkInGlobalIndex(chainActive, locator);
-
- // Send the rest of the chain
- if (pindex)
- pindex = chainActive.Next(pindex);
- int nLimit = 500;
- LogPrint("net", "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), nLimit, pfrom->id);
- for (; pindex; pindex = chainActive.Next(pindex))
- {
- if (pindex->GetBlockHash() == hashStop)
- {
- LogPrint("net", " getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
- break;
- }
- // If pruning, don't inv blocks unless we have on disk and are likely to still have
- // for some reasonable time window (1 hour) that block relay might require.
- const int nPrunedBlocksLikelyToHave = MIN_BLOCKS_TO_KEEP - 3600 / chainparams.GetConsensus().nPowTargetSpacing;
- if (fPruneMode && (!(pindex->nStatus & BLOCK_HAVE_DATA) || pindex->nHeight <= chainActive.Tip()->nHeight - nPrunedBlocksLikelyToHave))
- {
- LogPrint("net", " getblocks stopping, pruned or too old block at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
- break;
- }
- pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash()));
- if (--nLimit <= 0)
- {
- // When this block is requested, we'll send an inv that'll
- // trigger the peer to getblocks the next batch of inventory.
- LogPrint("net", " getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
- pfrom->hashContinue = pindex->GetBlockHash();
- break;
- }
- }
- }
-
-
- else if (strCommand == NetMsgType::GETBLOCKTXN)
- {
- BlockTransactionsRequest req;
- vRecv >> req;
-
- BlockMap::iterator it = mapBlockIndex.find(req.blockhash);
- if (it == mapBlockIndex.end() || !(it->second->nStatus & BLOCK_HAVE_DATA)) {
- LogPrintf("Peer %d sent us a getblocktxn for a block we don't have", pfrom->id);
- return true;
- }
-
- if (it->second->nHeight < chainActive.Height() - 15) {
- LogPrint("net", "Peer %d sent us a getblocktxn for a block > 15 deep", pfrom->id);
- return true;
- }
-
- CBlock block;
- assert(ReadBlockFromDisk(block, it->second, chainparams.GetConsensus()));
-
- BlockTransactions resp(req);
- for (size_t i = 0; i < req.indexes.size(); i++) {
- if (req.indexes[i] >= block.vtx.size()) {
- Misbehaving(pfrom->GetId(), 100);
- LogPrintf("Peer %d sent us a getblocktxn with out-of-bounds tx indices", pfrom->id);
- return true;
- }
- resp.txn[i] = block.vtx[req.indexes[i]];
- }
- pfrom->PushMessageWithFlag(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCKTXN, resp);
- }
-
-
- else if (strCommand == NetMsgType::GETHEADERS)
- {
- CBlockLocator locator;
- uint256 hashStop;
- vRecv >> locator >> hashStop;
-
- LOCK(cs_main);
- if (IsInitialBlockDownload() && !pfrom->fWhitelisted) {
- LogPrint("net", "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom->id);
- return true;
- }
-
- CNodeState *nodestate = State(pfrom->GetId());
- CBlockIndex* pindex = NULL;
- if (locator.IsNull())
- {
- // If locator is null, return the hashStop block
- BlockMap::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 = FindForkInGlobalIndex(chainActive, locator);
- if (pindex)
- pindex = chainActive.Next(pindex);
- }
-
- // we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end
- vector<CBlock> vHeaders;
- int nLimit = MAX_HEADERS_RESULTS;
- LogPrint("net", "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), pfrom->id);
- for (; pindex; pindex = chainActive.Next(pindex))
- {
- vHeaders.push_back(pindex->GetBlockHeader());
- if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop)
- break;
- }
- // pindex can be NULL either if we sent chainActive.Tip() OR
- // if our peer has chainActive.Tip() (and thus we are sending an empty
- // headers message). In both cases it's safe to update
- // pindexBestHeaderSent to be our tip.
- nodestate->pindexBestHeaderSent = pindex ? pindex : chainActive.Tip();
- pfrom->PushMessage(NetMsgType::HEADERS, vHeaders);
- }
-
-
- else if (strCommand == NetMsgType::TX)
- {
- // Stop processing the transaction early if
- // We are in blocks only mode and peer is either not whitelisted or whitelistrelay is off
- if (!fRelayTxes && (!pfrom->fWhitelisted || !GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)))
- {
- LogPrint("net", "transaction sent in violation of protocol peer=%d\n", pfrom->id);
- return true;
- }
-
- deque<COutPoint> vWorkQueue;
- vector<uint256> vEraseQueue;
- CTransaction tx;
- vRecv >> tx;
-
- CInv inv(MSG_TX, tx.GetHash());
- pfrom->AddInventoryKnown(inv);
-
- LOCK(cs_main);
-
- bool fMissingInputs = false;
- CValidationState state;
-
- pfrom->setAskFor.erase(inv.hash);
- mapAlreadyAskedFor.erase(inv.hash);
-
- if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs)) {
- mempool.check(pcoinsTip);
- RelayTransaction(tx, connman);
- for (unsigned int i = 0; i < tx.vout.size(); i++) {
- vWorkQueue.emplace_back(inv.hash, i);
- }
-
- pfrom->nLastTXTime = GetTime();
-
- LogPrint("mempool", "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n",
- pfrom->id,
- tx.GetHash().ToString(),
- mempool.size(), mempool.DynamicMemoryUsage() / 1000);
-
- // Recursively process any orphan transactions that depended on this one
- set<NodeId> setMisbehaving;
- while (!vWorkQueue.empty()) {
- auto itByPrev = mapOrphanTransactionsByPrev.find(vWorkQueue.front());
- vWorkQueue.pop_front();
- if (itByPrev == mapOrphanTransactionsByPrev.end())
- continue;
- for (auto mi = itByPrev->second.begin();
- mi != itByPrev->second.end();
- ++mi)
- {
- const CTransaction& orphanTx = (*mi)->second.tx;
- const uint256& orphanHash = orphanTx.GetHash();
- NodeId fromPeer = (*mi)->second.fromPeer;
- 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 (setMisbehaving.count(fromPeer))
- continue;
- if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2)) {
- LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString());
- RelayTransaction(orphanTx, connman);
- for (unsigned int i = 0; i < orphanTx.vout.size(); i++) {
- vWorkQueue.emplace_back(orphanHash, i);
- }
- vEraseQueue.push_back(orphanHash);
- }
- else if (!fMissingInputs2)
- {
- int nDos = 0;
- if (stateDummy.IsInvalid(nDos) && nDos > 0)
- {
- // Punish peer that gave us an invalid orphan tx
- Misbehaving(fromPeer, nDos);
- setMisbehaving.insert(fromPeer);
- LogPrint("mempool", " invalid orphan tx %s\n", orphanHash.ToString());
- }
- // Has inputs but not accepted to mempool
- // Probably non-standard or insufficient fee/priority
- LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString());
- vEraseQueue.push_back(orphanHash);
- if (orphanTx.wit.IsNull() && !stateDummy.CorruptionPossible()) {
- // Do not use rejection cache for witness transactions or
- // witness-stripped transactions, as they can have been malleated.
- // See https://github.com/bitcoin/bitcoin/issues/8279 for details.
- assert(recentRejects);
- recentRejects->insert(orphanHash);
- }
- }
- mempool.check(pcoinsTip);
- }
- }
-
- BOOST_FOREACH(uint256 hash, vEraseQueue)
- EraseOrphanTx(hash);
- }
- else if (fMissingInputs)
- {
- bool fRejectedParents = false; // It may be the case that the orphans parents have all been rejected
- BOOST_FOREACH(const CTxIn& txin, tx.vin) {
- if (recentRejects->contains(txin.prevout.hash)) {
- fRejectedParents = true;
- break;
- }
- }
- if (!fRejectedParents) {
- BOOST_FOREACH(const CTxIn& txin, tx.vin) {
- CInv inv(MSG_TX, txin.prevout.hash);
- pfrom->AddInventoryKnown(inv);
- if (!AlreadyHave(inv)) pfrom->AskFor(inv);
- }
- AddOrphanTx(tx, pfrom->GetId());
-
- // DoS prevention: do not allow mapOrphanTransactions to grow unbounded
- unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS));
- unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx);
- if (nEvicted > 0)
- LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted);
- } else {
- LogPrint("mempool", "not keeping orphan with rejected parents %s\n",tx.GetHash().ToString());
- }
- } else {
- if (tx.wit.IsNull() && !state.CorruptionPossible()) {
- // Do not use rejection cache for witness transactions or
- // witness-stripped transactions, as they can have been malleated.
- // See https://github.com/bitcoin/bitcoin/issues/8279 for details.
- assert(recentRejects);
- recentRejects->insert(tx.GetHash());
- }
-
- if (pfrom->fWhitelisted && GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) {
- // Always relay transactions received from whitelisted peers, even
- // if they were already in the mempool or rejected from it due
- // to policy, allowing the node to function as a gateway for
- // nodes hidden behind it.
- //
- // Never relay transactions that we would assign a non-zero DoS
- // score for, as we expect peers to do the same with us in that
- // case.
- int nDoS = 0;
- if (!state.IsInvalid(nDoS) || nDoS == 0) {
- LogPrintf("Force relaying tx %s from whitelisted peer=%d\n", tx.GetHash().ToString(), pfrom->id);
- RelayTransaction(tx, connman);
- } else {
- LogPrintf("Not relaying invalid transaction %s from whitelisted peer=%d (%s)\n", tx.GetHash().ToString(), pfrom->id, FormatStateMessage(state));
- }
- }
- }
- int nDoS = 0;
- if (state.IsInvalid(nDoS))
- {
- LogPrint("mempoolrej", "%s from peer=%d was not accepted: %s\n", tx.GetHash().ToString(),
- pfrom->id,
- FormatStateMessage(state));
- if (state.GetRejectCode() < REJECT_INTERNAL) // Never send AcceptToMemoryPool's internal codes over P2P
- pfrom->PushMessage(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(),
- state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash);
- if (nDoS > 0) {
- Misbehaving(pfrom->GetId(), nDoS);
- }
- }
- FlushStateToDisk(state, FLUSH_STATE_PERIODIC);
- }
-
-
- else if (strCommand == NetMsgType::CMPCTBLOCK && !fImporting && !fReindex) // Ignore blocks received while importing
- {
- CBlockHeaderAndShortTxIDs cmpctblock;
- vRecv >> cmpctblock;
-
- LOCK(cs_main);
-
- if (mapBlockIndex.find(cmpctblock.header.hashPrevBlock) == mapBlockIndex.end()) {
- // Doesn't connect (or is genesis), instead of DoSing in AcceptBlockHeader, request deeper headers
- if (!IsInitialBlockDownload())
- pfrom->PushMessage(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256());
- return true;
- }
-
- CBlockIndex *pindex = NULL;
- CValidationState state;
- if (!AcceptBlockHeader(cmpctblock.header, state, chainparams, &pindex)) {
- int nDoS;
- if (state.IsInvalid(nDoS)) {
- if (nDoS > 0)
- Misbehaving(pfrom->GetId(), nDoS);
- LogPrintf("Peer %d sent us invalid header via cmpctblock\n", pfrom->id);
- return true;
- }
- }
-
- // If AcceptBlockHeader returned true, it set pindex
- assert(pindex);
- UpdateBlockAvailability(pfrom->GetId(), pindex->GetBlockHash());
-
- std::map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator blockInFlightIt = mapBlocksInFlight.find(pindex->GetBlockHash());
- bool fAlreadyInFlight = blockInFlightIt != mapBlocksInFlight.end();
-
- if (pindex->nStatus & BLOCK_HAVE_DATA) // Nothing to do here
- return true;
-
- if (pindex->nChainWork <= chainActive.Tip()->nChainWork || // We know something better
- pindex->nTx != 0) { // We had this block at some point, but pruned it
- if (fAlreadyInFlight) {
- // We requested this block for some reason, but our mempool will probably be useless
- // so we just grab the block via normal getdata
- std::vector<CInv> vInv(1);
- vInv[0] = CInv(MSG_BLOCK, cmpctblock.header.GetHash());
- pfrom->PushMessage(NetMsgType::GETDATA, vInv);
- }
- return true;
- }
-
- // If we're not close to tip yet, give up and let parallel block fetch work its magic
- if (!fAlreadyInFlight && !CanDirectFetch(chainparams.GetConsensus()))
- return true;
-
- CNodeState *nodestate = State(pfrom->GetId());
-
- // We want to be a bit conservative just to be extra careful about DoS
- // possibilities in compact block processing...
- if (pindex->nHeight <= chainActive.Height() + 2) {
- if ((!fAlreadyInFlight && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) ||
- (fAlreadyInFlight && blockInFlightIt->second.first == pfrom->GetId())) {
- list<QueuedBlock>::iterator *queuedBlockIt = NULL;
- if (!MarkBlockAsInFlight(pfrom->GetId(), pindex->GetBlockHash(), chainparams.GetConsensus(), pindex, &queuedBlockIt)) {
- if (!(*queuedBlockIt)->partialBlock)
- (*queuedBlockIt)->partialBlock.reset(new PartiallyDownloadedBlock(&mempool));
- else {
- // The block was already in flight using compact blocks from the same peer
- LogPrint("net", "Peer sent us compact block we were already syncing!\n");
- return true;
- }
- }
-
- PartiallyDownloadedBlock& partialBlock = *(*queuedBlockIt)->partialBlock;
- ReadStatus status = partialBlock.InitData(cmpctblock);
- if (status == READ_STATUS_INVALID) {
- MarkBlockAsReceived(pindex->GetBlockHash()); // Reset in-flight state in case of whitelist
- Misbehaving(pfrom->GetId(), 100);
- LogPrintf("Peer %d sent us invalid compact block\n", pfrom->id);
- return true;
- } else if (status == READ_STATUS_FAILED) {
- // Duplicate txindexes, the block is now in-flight, so just request it
- std::vector<CInv> vInv(1);
- vInv[0] = CInv(MSG_BLOCK, cmpctblock.header.GetHash());
- pfrom->PushMessage(NetMsgType::GETDATA, vInv);
- return true;
- }
-
- BlockTransactionsRequest req;
- for (size_t i = 0; i < cmpctblock.BlockTxCount(); i++) {
- if (!partialBlock.IsTxAvailable(i))
- req.indexes.push_back(i);
- }
- if (req.indexes.empty()) {
- // Dirty hack to jump to BLOCKTXN code (TODO: move message handling into their own functions)
- BlockTransactions txn;
- txn.blockhash = cmpctblock.header.GetHash();
- CDataStream blockTxnMsg(SER_NETWORK, PROTOCOL_VERSION);
- blockTxnMsg << txn;
- return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, nTimeReceived, chainparams, connman);
- } else {
- req.blockhash = pindex->GetBlockHash();
- pfrom->PushMessage(NetMsgType::GETBLOCKTXN, req);
- }
- }
- } else {
- if (fAlreadyInFlight) {
- // We requested this block, but its far into the future, so our
- // mempool will probably be useless - request the block normally
- std::vector<CInv> vInv(1);
- vInv[0] = CInv(MSG_BLOCK, cmpctblock.header.GetHash());
- pfrom->PushMessage(NetMsgType::GETDATA, vInv);
- return true;
- } else {
- // If this was an announce-cmpctblock, we want the same treatment as a header message
- // Dirty hack to process as if it were just a headers message (TODO: move message handling into their own functions)
- std::vector<CBlock> headers;
- headers.push_back(cmpctblock.header);
- CDataStream vHeadersMsg(SER_NETWORK, PROTOCOL_VERSION);
- vHeadersMsg << headers;
- return ProcessMessage(pfrom, NetMsgType::HEADERS, vHeadersMsg, nTimeReceived, chainparams, connman);
- }
- }
-
- CheckBlockIndex(chainparams.GetConsensus());
- }
-
- else if (strCommand == NetMsgType::BLOCKTXN && !fImporting && !fReindex) // Ignore blocks received while importing
- {
- BlockTransactions resp;
- vRecv >> resp;
-
- LOCK(cs_main);
-
- map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator it = mapBlocksInFlight.find(resp.blockhash);
- if (it == mapBlocksInFlight.end() || !it->second.second->partialBlock ||
- it->second.first != pfrom->GetId()) {
- LogPrint("net", "Peer %d sent us block transactions for block we weren't expecting\n", pfrom->id);
- return true;
- }
-
- PartiallyDownloadedBlock& partialBlock = *it->second.second->partialBlock;
- CBlock block;
- ReadStatus status = partialBlock.FillBlock(block, resp.txn);
- if (status == READ_STATUS_INVALID) {
- MarkBlockAsReceived(resp.blockhash); // Reset in-flight state in case of whitelist
- Misbehaving(pfrom->GetId(), 100);
- LogPrintf("Peer %d sent us invalid compact block/non-matching block transactions\n", pfrom->id);
- return true;
- } else if (status == READ_STATUS_FAILED) {
- // Might have collided, fall back to getdata now :(
- std::vector<CInv> invs;
- invs.push_back(CInv(MSG_BLOCK, resp.blockhash));
- pfrom->PushMessage(NetMsgType::GETDATA, invs);
- } else {
- CValidationState state;
- ProcessNewBlock(state, chainparams, pfrom, &block, false, NULL, &connman);
- int nDoS;
- if (state.IsInvalid(nDoS)) {
- assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes
- pfrom->PushMessage(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(),
- state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), block.GetHash());
- if (nDoS > 0) {
- LOCK(cs_main);
- Misbehaving(pfrom->GetId(), nDoS);
- }
- }
- }
- }
-
-
- else if (strCommand == NetMsgType::HEADERS && !fImporting && !fReindex) // Ignore headers received while importing
- {
- std::vector<CBlockHeader> headers;
-
- // Bypass the normal CBlock deserialization, as we don't want to risk deserializing 2000 full blocks.
- unsigned int nCount = ReadCompactSize(vRecv);
- if (nCount > MAX_HEADERS_RESULTS) {
- LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 20);
- return error("headers message size = %u", nCount);
- }
- headers.resize(nCount);
- for (unsigned int n = 0; n < nCount; n++) {
- vRecv >> headers[n];
- ReadCompactSize(vRecv); // ignore tx count; assume it is 0.
- }
-
- {
- LOCK(cs_main);
-
- if (nCount == 0) {
- // Nothing interesting. Stop asking this peers for more headers.
- return true;
+ try {
+ uint64_t version;
+ file >> version;
+ if (version != MEMPOOL_DUMP_VERSION) {
+ return false;
}
-
- CNodeState *nodestate = State(pfrom->GetId());
-
- // If this looks like it could be a block announcement (nCount <
- // MAX_BLOCKS_TO_ANNOUNCE), use special logic for handling headers that
- // don't connect:
- // - Send a getheaders message in response to try to connect the chain.
- // - The peer can send up to MAX_UNCONNECTING_HEADERS in a row that
- // don't connect before giving DoS points
- // - Once a headers message is received that is valid and does connect,
- // nUnconnectingHeaders gets reset back to 0.
- if (mapBlockIndex.find(headers[0].hashPrevBlock) == mapBlockIndex.end() && nCount < MAX_BLOCKS_TO_ANNOUNCE) {
- nodestate->nUnconnectingHeaders++;
- pfrom->PushMessage(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256());
- LogPrint("net", "received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, nUnconnectingHeaders=%d)\n",
- headers[0].GetHash().ToString(),
- headers[0].hashPrevBlock.ToString(),
- pindexBestHeader->nHeight,
- pfrom->id, nodestate->nUnconnectingHeaders);
- // Set hashLastUnknownBlock for this peer, so that if we
- // eventually get the headers - even from a different peer -
- // we can use this peer to download.
- UpdateBlockAvailability(pfrom->GetId(), headers.back().GetHash());
-
- if (nodestate->nUnconnectingHeaders % MAX_UNCONNECTING_HEADERS == 0) {
- Misbehaving(pfrom->GetId(), 20);
+ uint64_t num;
+ file >> num;
+ double prioritydummy = 0;
+ while (num--) {
+ int64_t nTime;
+ int64_t nFeeDelta;
+ CTransaction tx(deserialize, file);
+ file >> nTime;
+ file >> nFeeDelta;
+
+ CAmount amountdelta = nFeeDelta;
+ if (amountdelta) {
+ mempool.PrioritiseTransaction(tx.GetHash(), tx.GetHash().ToString(), prioritydummy, amountdelta);
}
- return true;
- }
-
- CBlockIndex *pindexLast = NULL;
- BOOST_FOREACH(const CBlockHeader& header, headers) {
CValidationState state;
- if (pindexLast != NULL && header.hashPrevBlock != pindexLast->GetBlockHash()) {
- Misbehaving(pfrom->GetId(), 20);
- return error("non-continuous headers sequence");
- }
- if (!AcceptBlockHeader(header, state, chainparams, &pindexLast)) {
- int nDoS;
- if (state.IsInvalid(nDoS)) {
- if (nDoS > 0)
- Misbehaving(pfrom->GetId(), nDoS);
- return error("invalid header received");
- }
- }
- }
-
- if (nodestate->nUnconnectingHeaders > 0) {
- LogPrint("net", "peer=%d: resetting nUnconnectingHeaders (%d -> 0)\n", pfrom->id, nodestate->nUnconnectingHeaders);
- }
- nodestate->nUnconnectingHeaders = 0;
-
- assert(pindexLast);
- UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash());
-
- if (nCount == MAX_HEADERS_RESULTS) {
- // Headers message had its maximum size; the peer may have more headers.
- // TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue
- // from there instead.
- LogPrint("net", "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->id, pfrom->nStartingHeight);
- pfrom->PushMessage(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexLast), uint256());
- }
-
- bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus());
- // If this set of headers is valid and ends in a block with at least as
- // much work as our tip, download as much as possible.
- if (fCanDirectFetch && pindexLast->IsValid(BLOCK_VALID_TREE) && chainActive.Tip()->nChainWork <= pindexLast->nChainWork) {
- vector<CBlockIndex *> vToFetch;
- CBlockIndex *pindexWalk = pindexLast;
- // Calculate all the blocks we'd need to switch to pindexLast, up to a limit.
- while (pindexWalk && !chainActive.Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
- if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) &&
- !mapBlocksInFlight.count(pindexWalk->GetBlockHash()) &&
- (!IsWitnessEnabled(pindexWalk->pprev, chainparams.GetConsensus()) || State(pfrom->GetId())->fHaveWitness)) {
- // We don't have this block, and it's not yet in flight.
- vToFetch.push_back(pindexWalk);
- }
- pindexWalk = pindexWalk->pprev;
- }
- // If pindexWalk still isn't on our main chain, we're looking at a
- // very large reorg at a time we think we're close to caught up to
- // the main chain -- this shouldn't really happen. Bail out on the
- // direct fetch and rely on parallel download instead.
- if (!chainActive.Contains(pindexWalk)) {
- LogPrint("net", "Large reorg, won't direct fetch to %s (%d)\n",
- pindexLast->GetBlockHash().ToString(),
- pindexLast->nHeight);
- } else {
- vector<CInv> vGetData;
- // Download as much as possible, from earliest to latest.
- BOOST_REVERSE_FOREACH(CBlockIndex *pindex, vToFetch) {
- if (nodestate->nBlocksInFlight >= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
- // Can't download any more from this peer
- break;
- }
- uint32_t nFetchFlags = GetFetchFlags(pfrom, pindex->pprev, chainparams.GetConsensus());
- vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
- MarkBlockAsInFlight(pfrom->GetId(), pindex->GetBlockHash(), chainparams.GetConsensus(), pindex);
- LogPrint("net", "Requesting block %s from peer=%d\n",
- pindex->GetBlockHash().ToString(), pfrom->id);
- }
- if (vGetData.size() > 1) {
- LogPrint("net", "Downloading blocks toward %s (%d) via headers direct fetch\n",
- pindexLast->GetBlockHash().ToString(), pindexLast->nHeight);
- }
- if (vGetData.size() > 0) {
- if (nodestate->fProvidesHeaderAndIDs && vGetData.size() == 1 && mapBlocksInFlight.size() == 1 && pindexLast->pprev->IsValid(BLOCK_VALID_CHAIN) && !(pfrom->GetLocalServices() & NODE_WITNESS)) {
- // We seem to be rather well-synced, so it appears pfrom was the first to provide us
- // with this block! Let's get them to announce using compact blocks in the future.
- MaybeSetPeerAsAnnouncingHeaderAndIDs(nodestate, pfrom, connman);
- // In any case, we want to download using a compact block, not a regular one
- vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash);
- }
- pfrom->PushMessage(NetMsgType::GETDATA, vGetData);
- }
- }
- }
-
- CheckBlockIndex(chainparams.GetConsensus());
- }
-
- NotifyHeaderTip();
- }
-
- else if (strCommand == NetMsgType::BLOCK && !fImporting && !fReindex) // Ignore blocks received while importing
- {
- CBlock block;
- vRecv >> block;
-
- LogPrint("net", "received block %s peer=%d\n", block.GetHash().ToString(), pfrom->id);
-
- CValidationState state;
- // Process all blocks from whitelisted peers, even if not requested,
- // unless we're still syncing with the network.
- // Such an unrequested block may still be processed, subject to the
- // conditions in AcceptBlock().
- bool forceProcessing = pfrom->fWhitelisted && !IsInitialBlockDownload();
- ProcessNewBlock(state, chainparams, pfrom, &block, forceProcessing, NULL, &connman);
- int nDoS;
- if (state.IsInvalid(nDoS)) {
- assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes
- pfrom->PushMessage(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(),
- state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), block.GetHash());
- if (nDoS > 0) {
+ if (nTime + nExpiryTimeout > nNow) {
LOCK(cs_main);
- Misbehaving(pfrom->GetId(), nDoS);
- }
- }
-
- }
-
-
- else if (strCommand == NetMsgType::GETADDR)
- {
- // This asymmetric behavior for inbound and outbound connections was introduced
- // to prevent a fingerprinting attack: an attacker can send specific fake addresses
- // to users' AddrMan and later request them by sending getaddr messages.
- // Making nodes which are behind NAT and can only make outgoing connections ignore
- // the getaddr message mitigates the attack.
- if (!pfrom->fInbound) {
- LogPrint("net", "Ignoring \"getaddr\" from outbound connection. peer=%d\n", pfrom->id);
- return true;
- }
-
- // Only send one GetAddr response per connection to reduce resource waste
- // and discourage addr stamping of INV announcements.
- if (pfrom->fSentAddr) {
- LogPrint("net", "Ignoring repeated \"getaddr\". peer=%d\n", pfrom->id);
- return true;
- }
- pfrom->fSentAddr = true;
-
- pfrom->vAddrToSend.clear();
- vector<CAddress> vAddr = connman.GetAddresses();
- BOOST_FOREACH(const CAddress &addr, vAddr)
- pfrom->PushAddress(addr);
- }
-
-
- else if (strCommand == NetMsgType::MEMPOOL)
- {
- if (!(pfrom->GetLocalServices() & NODE_BLOOM) && !pfrom->fWhitelisted)
- {
- LogPrint("net", "mempool request with bloom filters disabled, disconnect peer=%d\n", pfrom->GetId());
- pfrom->fDisconnect = true;
- return true;
- }
-
- if (connman.OutboundTargetReached(false) && !pfrom->fWhitelisted)
- {
- LogPrint("net", "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom->GetId());
- pfrom->fDisconnect = true;
- return true;
- }
-
- LOCK(pfrom->cs_inventory);
- pfrom->fSendMempool = true;
- }
-
-
- else if (strCommand == NetMsgType::PING)
- {
- if (pfrom->nVersion > BIP0031_VERSION)
- {
- uint64_t nonce = 0;
- vRecv >> nonce;
- // Echo the message back with the nonce. This allows for two useful features:
- //
- // 1) A remote node can quickly check if the connection is operational
- // 2) Remote nodes can measure the latency of the network thread. If this node
- // is overloaded it won't respond to pings quickly and the remote node can
- // avoid sending us more work, like chain download requests.
- //
- // The nonce stops the remote getting confused between different pings: without
- // it, if the remote node sends a ping once per second and this node takes 5
- // seconds to respond to each, the 5th ping the remote sends would appear to
- // return very quickly.
- pfrom->PushMessage(NetMsgType::PONG, nonce);
- }
- }
-
-
- else if (strCommand == NetMsgType::PONG)
- {
- int64_t pingUsecEnd = nTimeReceived;
- uint64_t nonce = 0;
- size_t nAvail = vRecv.in_avail();
- bool bPingFinished = false;
- std::string sProblem;
-
- if (nAvail >= sizeof(nonce)) {
- vRecv >> nonce;
-
- // Only process pong message if there is an outstanding ping (old ping without nonce should never pong)
- if (pfrom->nPingNonceSent != 0) {
- if (nonce == pfrom->nPingNonceSent) {
- // Matching pong received, this ping is no longer outstanding
- bPingFinished = true;
- int64_t pingUsecTime = pingUsecEnd - pfrom->nPingUsecStart;
- if (pingUsecTime > 0) {
- // Successful ping time measurement, replace previous
- pfrom->nPingUsecTime = pingUsecTime;
- pfrom->nMinPingUsecTime = std::min(pfrom->nMinPingUsecTime, pingUsecTime);
- } else {
- // This should never happen
- sProblem = "Timing mishap";
- }
+ AcceptToMemoryPoolWithTime(mempool, state, tx, true, NULL, nTime);
+ if (state.IsValid()) {
+ ++count;
} else {
- // Nonce mismatches are normal when pings are overlapping
- sProblem = "Nonce mismatch";
- if (nonce == 0) {
- // This is most likely a bug in another implementation somewhere; cancel this ping
- bPingFinished = true;
- sProblem = "Nonce zero";
- }
+ ++failed;
}
} else {
- sProblem = "Unsolicited pong without ping";
- }
- } else {
- // This is most likely a bug in another implementation somewhere; cancel this ping
- bPingFinished = true;
- sProblem = "Short payload";
- }
-
- if (!(sProblem.empty())) {
- LogPrint("net", "pong peer=%d: %s, %x expected, %x received, %u bytes\n",
- pfrom->id,
- sProblem,
- pfrom->nPingNonceSent,
- nonce,
- nAvail);
- }
- if (bPingFinished) {
- pfrom->nPingNonceSent = 0;
- }
- }
-
-
- else if (strCommand == NetMsgType::FILTERLOAD)
- {
- CBloomFilter filter;
- vRecv >> filter;
-
- if (!filter.IsWithinSizeConstraints())
- {
- // There is no excuse for sending a too-large filter
- LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 100);
- }
- else
- {
- LOCK(pfrom->cs_filter);
- delete pfrom->pfilter;
- pfrom->pfilter = new CBloomFilter(filter);
- pfrom->pfilter->UpdateEmptyFull();
- pfrom->fRelayTxes = true;
- }
- }
-
-
- else if (strCommand == NetMsgType::FILTERADD)
- {
- vector<unsigned char> 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
- bool bad = false;
- if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) {
- bad = true;
- } else {
- LOCK(pfrom->cs_filter);
- if (pfrom->pfilter) {
- pfrom->pfilter->insert(vData);
- } else {
- bad = true;
- }
- }
- if (bad) {
- LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 100);
- }
- }
-
-
- else if (strCommand == NetMsgType::FILTERCLEAR)
- {
- LOCK(pfrom->cs_filter);
- delete pfrom->pfilter;
- pfrom->pfilter = new CBloomFilter();
- pfrom->fRelayTxes = true;
- }
-
-
- else if (strCommand == NetMsgType::REJECT)
- {
- if (fDebug) {
- try {
- string strMsg; unsigned char ccode; string strReason;
- vRecv >> LIMITED_STRING(strMsg, CMessageHeader::COMMAND_SIZE) >> ccode >> LIMITED_STRING(strReason, MAX_REJECT_MESSAGE_LENGTH);
-
- ostringstream ss;
- ss << strMsg << " code " << itostr(ccode) << ": " << strReason;
-
- if (strMsg == NetMsgType::BLOCK || strMsg == NetMsgType::TX)
- {
- uint256 hash;
- vRecv >> hash;
- ss << ": hash " << hash.ToString();
- }
- LogPrint("net", "Reject %s\n", SanitizeString(ss.str()));
- } catch (const std::ios_base::failure&) {
- // Avoid feedback loops by preventing reject messages from triggering a new reject message.
- LogPrint("net", "Unparseable reject message received\n");
+ ++skipped;
}
}
- }
+ std::map<uint256, CAmount> mapDeltas;
+ file >> mapDeltas;
- else if (strCommand == NetMsgType::FEEFILTER) {
- CAmount newFeeFilter = 0;
- vRecv >> newFeeFilter;
- if (MoneyRange(newFeeFilter)) {
- {
- LOCK(pfrom->cs_feeFilter);
- pfrom->minFeeFilter = newFeeFilter;
- }
- LogPrint("net", "received: feefilter of %s from peer=%d\n", CFeeRate(newFeeFilter).ToString(), pfrom->id);
+ for (const auto& i : mapDeltas) {
+ mempool.PrioritiseTransaction(i.first, i.first.ToString(), prioritydummy, i.second);
}
+ } catch (const std::exception& e) {
+ LogPrintf("Failed to deserialize mempool data on disk: %s. Continuing anyway.\n", e.what());
+ return false;
}
- else if (strCommand == NetMsgType::NOTFOUND) {
- // We do not care about the NOTFOUND message, but logging an Unknown Command
- // message would be undesirable as we transmit it ourselves.
- }
-
- else {
- // Ignore unknown commands for extensibility
- LogPrint("net", "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->id);
- }
-
-
-
+ LogPrintf("Imported mempool transactions from disk: %i successes, %i failed, %i expired\n", count, failed, skipped);
return true;
}
-// requires LOCK(cs_vRecvMsg)
-bool ProcessMessages(CNode* pfrom, CConnman& connman)
+void DumpMempool(void)
{
- const CChainParams& chainparams = Params();
- unsigned int nMaxSendBufferSize = connman.GetSendBufferSize();
- //if (fDebug)
- // LogPrintf("%s(%u messages)\n", __func__, 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, chainparams.GetConsensus(), connman);
-
- // this maintains the order of responses
- if (!pfrom->vRecvGetData.empty()) return fOk;
-
- std::deque<CNetMessage>::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 >= nMaxSendBufferSize)
- break;
-
- // get next message
- CNetMessage& msg = *it;
-
- //if (fDebug)
- // LogPrintf("%s(message %u msgsz, %u bytes, complete:%s)\n", __func__,
- // 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, chainparams.MessageStart(), MESSAGE_START_SIZE) != 0) {
- LogPrintf("PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", SanitizeString(msg.hdr.GetCommand()), pfrom->id);
- fOk = false;
- break;
- }
-
- // Read header
- CMessageHeader& hdr = msg.hdr;
- if (!hdr.IsValid(chainparams.MessageStart()))
- {
- LogPrintf("PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(hdr.GetCommand()), pfrom->id);
- continue;
- }
- string strCommand = hdr.GetCommand();
-
- // Message size
- unsigned int nMessageSize = hdr.nMessageSize;
-
- // Checksum
- CDataStream& vRecv = msg.vRecv;
- uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize);
- unsigned int nChecksum = ReadLE32((unsigned char*)&hash);
- if (nChecksum != hdr.nChecksum)
- {
- LogPrintf("%s(%s, %u bytes): CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", __func__,
- SanitizeString(strCommand), nMessageSize, nChecksum, hdr.nChecksum);
- continue;
- }
-
- // Process message
- bool fRet = false;
- try
- {
- fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime, chainparams, connman);
- boost::this_thread::interruption_point();
- }
- catch (const std::ios_base::failure& e)
- {
- pfrom->PushMessage(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, string("error parsing message"));
- if (strstr(e.what(), "end of data"))
- {
- // Allow exceptions from under-length message on vRecv
- LogPrintf("%s(%s, %u bytes): Exception '%s' caught, normally caused by a message being shorter than its stated length\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
- }
- else if (strstr(e.what(), "size too large"))
- {
- // Allow exceptions from over-long size
- LogPrintf("%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
- }
- else if (strstr(e.what(), "non-canonical ReadCompactSize()"))
- {
- // Allow exceptions from non-canonical encoding
- LogPrintf("%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
- }
- else
- {
- PrintExceptionContinue(&e, "ProcessMessages()");
- }
- }
- catch (const boost::thread_interrupted&) {
- throw;
- }
- catch (const std::exception& e) {
- PrintExceptionContinue(&e, "ProcessMessages()");
- } catch (...) {
- PrintExceptionContinue(NULL, "ProcessMessages()");
- }
-
- if (!fRet)
- LogPrintf("%s(%s, %u bytes) FAILED peer=%d\n", __func__, SanitizeString(strCommand), nMessageSize, pfrom->id);
-
- break;
- }
-
- // In case the connection got shut down, its receive buffer was wiped
- if (!pfrom->fDisconnect)
- pfrom->vRecvMsg.erase(pfrom->vRecvMsg.begin(), it);
-
- return fOk;
-}
-
-class CompareInvMempoolOrder
-{
- CTxMemPool *mp;
-public:
- CompareInvMempoolOrder(CTxMemPool *mempool)
- {
- mp = mempool;
- }
+ int64_t start = GetTimeMicros();
- bool operator()(std::set<uint256>::iterator a, std::set<uint256>::iterator b)
- {
- /* As std::make_heap produces a max-heap, we want the entries with the
- * fewest ancestors/highest fee to sort later. */
- return mp->CompareDepthAndScore(*b, *a);
- }
-};
+ std::map<uint256, CAmount> mapDeltas;
+ std::vector<TxMempoolInfo> vinfo;
-bool SendMessages(CNode* pto, CConnman& connman)
-{
- const Consensus::Params& consensusParams = Params().GetConsensus();
{
- // Don't send anything until we get its version message
- if (pto->nVersion == 0)
- return true;
-
- //
- // Message: ping
- //
- bool pingSend = false;
- if (pto->fPingQueued) {
- // RPC ping request by user
- pingSend = true;
- }
- if (pto->nPingNonceSent == 0 && pto->nPingUsecStart + PING_INTERVAL * 1000000 < GetTimeMicros()) {
- // Ping automatically sent as a latency probe & keepalive.
- pingSend = true;
- }
- if (pingSend) {
- uint64_t nonce = 0;
- while (nonce == 0) {
- GetRandBytes((unsigned char*)&nonce, sizeof(nonce));
- }
- pto->fPingQueued = false;
- pto->nPingUsecStart = GetTimeMicros();
- if (pto->nVersion > BIP0031_VERSION) {
- pto->nPingNonceSent = nonce;
- pto->PushMessage(NetMsgType::PING, nonce);
- } else {
- // Peer is too old to support ping command with nonce, pong will never arrive.
- pto->nPingNonceSent = 0;
- pto->PushMessage(NetMsgType::PING);
- }
- }
-
- TRY_LOCK(cs_main, lockMain); // Acquire cs_main for IsInitialBlockDownload() and CNodeState()
- if (!lockMain)
- return true;
-
- // Address refresh broadcast
- int64_t nNow = GetTimeMicros();
- if (!IsInitialBlockDownload() && pto->nNextLocalAddrSend < nNow) {
- AdvertiseLocal(pto);
- pto->nNextLocalAddrSend = PoissonNextSend(nNow, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL);
- }
-
- //
- // Message: addr
- //
- if (pto->nNextAddrSend < nNow) {
- pto->nNextAddrSend = PoissonNextSend(nNow, AVG_ADDRESS_BROADCAST_INTERVAL);
- vector<CAddress> vAddr;
- vAddr.reserve(pto->vAddrToSend.size());
- BOOST_FOREACH(const CAddress& addr, pto->vAddrToSend)
- {
- if (!pto->addrKnown.contains(addr.GetKey()))
- {
- pto->addrKnown.insert(addr.GetKey());
- vAddr.push_back(addr);
- // receiver rejects addr messages larger than 1000
- if (vAddr.size() >= 1000)
- {
- pto->PushMessage(NetMsgType::ADDR, vAddr);
- vAddr.clear();
- }
- }
- }
- pto->vAddrToSend.clear();
- if (!vAddr.empty())
- pto->PushMessage(NetMsgType::ADDR, vAddr);
- // we only send the big addr message once
- if (pto->vAddrToSend.capacity() > 40)
- pto->vAddrToSend.shrink_to_fit();
- }
-
- CNodeState &state = *State(pto->GetId());
- if (state.fShouldBan) {
- if (pto->fWhitelisted)
- LogPrintf("Warning: not punishing whitelisted peer %s!\n", pto->addr.ToString());
- else {
- pto->fDisconnect = true;
- if (pto->addr.IsLocal())
- LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString());
- else
- {
- connman.Ban(pto->addr, BanReasonNodeMisbehaving);
- }
- }
- state.fShouldBan = false;
- }
-
- BOOST_FOREACH(const CBlockReject& reject, state.rejects)
- pto->PushMessage(NetMsgType::REJECT, (string)NetMsgType::BLOCK, reject.chRejectCode, reject.strRejectReason, reject.hashBlock);
- state.rejects.clear();
-
- // Start block sync
- if (pindexBestHeader == NULL)
- pindexBestHeader = chainActive.Tip();
- bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->fOneShot); // Download if this is a nice peer, or we have no nice peers and this one might do.
- if (!state.fSyncStarted && !pto->fClient && !fImporting && !fReindex) {
- // Only actively request headers from a single peer, unless we're close to today.
- if ((nSyncStarted == 0 && fFetch) || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) {
- state.fSyncStarted = true;
- nSyncStarted++;
- const CBlockIndex *pindexStart = pindexBestHeader;
- /* If possible, start at the block preceding the currently
- best known header. This ensures that we always get a
- non-empty list of headers back as long as the peer
- is up-to-date. With a non-empty response, we can initialise
- the peer's known best block. This wouldn't be possible
- if we requested starting at pindexBestHeader and
- got back an empty response. */
- if (pindexStart->pprev)
- pindexStart = pindexStart->pprev;
- LogPrint("net", "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->id, pto->nStartingHeight);
- pto->PushMessage(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexStart), uint256());
- }
+ LOCK(mempool.cs);
+ for (const auto &i : mempool.mapDeltas) {
+ mapDeltas[i.first] = i.second.first;
}
+ vinfo = mempool.infoAll();
+ }
- // 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())
- {
- GetMainSignals().Broadcast(nTimeBestReceived, &connman);
- }
+ int64_t mid = GetTimeMicros();
- //
- // Try sending block announcements via headers
- //
- {
- // If we have less than MAX_BLOCKS_TO_ANNOUNCE in our
- // list of block hashes we're relaying, and our peer wants
- // headers announcements, then find the first header
- // not yet known to our peer but would connect, and send.
- // If no header would connect, or if we have too many
- // blocks, or if the peer doesn't want headers, just
- // add all to the inv queue.
- LOCK(pto->cs_inventory);
- vector<CBlock> vHeaders;
- bool fRevertToInv = ((!state.fPreferHeaders &&
- (!state.fPreferHeaderAndIDs || pto->vBlockHashesToAnnounce.size() > 1)) ||
- pto->vBlockHashesToAnnounce.size() > MAX_BLOCKS_TO_ANNOUNCE);
- CBlockIndex *pBestIndex = NULL; // last header queued for delivery
- ProcessBlockAvailability(pto->id); // ensure pindexBestKnownBlock is up-to-date
-
- if (!fRevertToInv) {
- bool fFoundStartingHeader = false;
- // Try to find first header that our peer doesn't have, and
- // then send all headers past that one. If we come across any
- // headers that aren't on chainActive, give up.
- BOOST_FOREACH(const uint256 &hash, pto->vBlockHashesToAnnounce) {
- BlockMap::iterator mi = mapBlockIndex.find(hash);
- assert(mi != mapBlockIndex.end());
- CBlockIndex *pindex = mi->second;
- if (chainActive[pindex->nHeight] != pindex) {
- // Bail out if we reorged away from this block
- fRevertToInv = true;
- break;
- }
- if (pBestIndex != NULL && pindex->pprev != pBestIndex) {
- // This means that the list of blocks to announce don't
- // connect to each other.
- // This shouldn't really be possible to hit during
- // regular operation (because reorgs should take us to
- // a chain that has some block not on the prior chain,
- // which should be caught by the prior check), but one
- // way this could happen is by using invalidateblock /
- // reconsiderblock repeatedly on the tip, causing it to
- // be added multiple times to vBlockHashesToAnnounce.
- // Robustly deal with this rare situation by reverting
- // to an inv.
- fRevertToInv = true;
- break;
- }
- pBestIndex = pindex;
- if (fFoundStartingHeader) {
- // add this to the headers message
- vHeaders.push_back(pindex->GetBlockHeader());
- } else if (PeerHasHeader(&state, pindex)) {
- continue; // keep looking for the first new block
- } else if (pindex->pprev == NULL || PeerHasHeader(&state, pindex->pprev)) {
- // Peer doesn't have this header but they do have the prior one.
- // Start sending headers.
- fFoundStartingHeader = true;
- vHeaders.push_back(pindex->GetBlockHeader());
- } else {
- // Peer doesn't have this header or the prior one -- nothing will
- // connect, so bail out.
- fRevertToInv = true;
- break;
- }
- }
- }
- if (!fRevertToInv && !vHeaders.empty()) {
- if (vHeaders.size() == 1 && state.fPreferHeaderAndIDs) {
- // We only send up to 1 block as header-and-ids, as otherwise
- // probably means we're doing an initial-ish-sync or they're slow
- LogPrint("net", "%s sending header-and-ids %s to peer %d\n", __func__,
- vHeaders.front().GetHash().ToString(), pto->id);
- //TODO: Shouldn't need to reload block from disk, but requires refactor
- CBlock block;
- assert(ReadBlockFromDisk(block, pBestIndex, consensusParams));
- CBlockHeaderAndShortTxIDs cmpctblock(block);
- pto->PushMessageWithFlag(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::CMPCTBLOCK, cmpctblock);
- state.pindexBestHeaderSent = pBestIndex;
- } else if (state.fPreferHeaders) {
- if (vHeaders.size() > 1) {
- LogPrint("net", "%s: %u headers, range (%s, %s), to peer=%d\n", __func__,
- vHeaders.size(),
- vHeaders.front().GetHash().ToString(),
- vHeaders.back().GetHash().ToString(), pto->id);
- } else {
- LogPrint("net", "%s: sending header %s to peer=%d\n", __func__,
- vHeaders.front().GetHash().ToString(), pto->id);
- }
- pto->PushMessage(NetMsgType::HEADERS, vHeaders);
- state.pindexBestHeaderSent = pBestIndex;
- } else
- fRevertToInv = true;
- }
- if (fRevertToInv) {
- // If falling back to using an inv, just try to inv the tip.
- // The last entry in vBlockHashesToAnnounce was our tip at some point
- // in the past.
- if (!pto->vBlockHashesToAnnounce.empty()) {
- const uint256 &hashToAnnounce = pto->vBlockHashesToAnnounce.back();
- BlockMap::iterator mi = mapBlockIndex.find(hashToAnnounce);
- assert(mi != mapBlockIndex.end());
- CBlockIndex *pindex = mi->second;
-
- // Warn if we're announcing a block that is not on the main chain.
- // This should be very rare and could be optimized out.
- // Just log for now.
- if (chainActive[pindex->nHeight] != pindex) {
- LogPrint("net", "Announcing block %s not on main chain (tip=%s)\n",
- hashToAnnounce.ToString(), chainActive.Tip()->GetBlockHash().ToString());
- }
-
- // If the peer's chain has this block, don't inv it back.
- if (!PeerHasHeader(&state, pindex)) {
- pto->PushInventory(CInv(MSG_BLOCK, hashToAnnounce));
- LogPrint("net", "%s: sending inv peer=%d hash=%s\n", __func__,
- pto->id, hashToAnnounce.ToString());
- }
- }
- }
- pto->vBlockHashesToAnnounce.clear();
+ try {
+ FILE* filestr = fopen((GetDataDir() / "mempool.dat.new").string().c_str(), "w");
+ if (!filestr) {
+ return;
}
- //
- // Message: inventory
- //
- vector<CInv> vInv;
- {
- LOCK(pto->cs_inventory);
- vInv.reserve(std::max<size_t>(pto->vInventoryBlockToSend.size(), INVENTORY_BROADCAST_MAX));
-
- // Add blocks
- BOOST_FOREACH(const uint256& hash, pto->vInventoryBlockToSend) {
- vInv.push_back(CInv(MSG_BLOCK, hash));
- if (vInv.size() == MAX_INV_SZ) {
- pto->PushMessage(NetMsgType::INV, vInv);
- vInv.clear();
- }
- }
- pto->vInventoryBlockToSend.clear();
-
- // Check whether periodic sends should happen
- bool fSendTrickle = pto->fWhitelisted;
- if (pto->nNextInvSend < nNow) {
- fSendTrickle = true;
- // Use half the delay for outbound peers, as there is less privacy concern for them.
- pto->nNextInvSend = PoissonNextSend(nNow, INVENTORY_BROADCAST_INTERVAL >> !pto->fInbound);
- }
-
- // Time to send but the peer has requested we not relay transactions.
- if (fSendTrickle) {
- LOCK(pto->cs_filter);
- if (!pto->fRelayTxes) pto->setInventoryTxToSend.clear();
- }
-
- // Respond to BIP35 mempool requests
- if (fSendTrickle && pto->fSendMempool) {
- auto vtxinfo = mempool.infoAll();
- pto->fSendMempool = false;
- CAmount filterrate = 0;
- {
- LOCK(pto->cs_feeFilter);
- filterrate = pto->minFeeFilter;
- }
-
- LOCK(pto->cs_filter);
+ CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
- for (const auto& txinfo : vtxinfo) {
- const uint256& hash = txinfo.tx->GetHash();
- CInv inv(MSG_TX, hash);
- pto->setInventoryTxToSend.erase(hash);
- if (filterrate) {
- if (txinfo.feeRate.GetFeePerK() < filterrate)
- continue;
- }
- if (pto->pfilter) {
- if (!pto->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
- }
- pto->filterInventoryKnown.insert(hash);
- vInv.push_back(inv);
- if (vInv.size() == MAX_INV_SZ) {
- pto->PushMessage(NetMsgType::INV, vInv);
- vInv.clear();
- }
- }
- pto->timeLastMempoolReq = GetTime();
- }
-
- // Determine transactions to relay
- if (fSendTrickle) {
- // Produce a vector with all candidates for sending
- vector<std::set<uint256>::iterator> vInvTx;
- vInvTx.reserve(pto->setInventoryTxToSend.size());
- for (std::set<uint256>::iterator it = pto->setInventoryTxToSend.begin(); it != pto->setInventoryTxToSend.end(); it++) {
- vInvTx.push_back(it);
- }
- CAmount filterrate = 0;
- {
- LOCK(pto->cs_feeFilter);
- filterrate = pto->minFeeFilter;
- }
- // Topologically and fee-rate sort the inventory we send for privacy and priority reasons.
- // A heap is used so that not all items need sorting if only a few are being sent.
- CompareInvMempoolOrder compareInvMempoolOrder(&mempool);
- std::make_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder);
- // No reason to drain out at many times the network's capacity,
- // especially since we have many peers and some will draw much shorter delays.
- unsigned int nRelayedTransactions = 0;
- LOCK(pto->cs_filter);
- while (!vInvTx.empty() && nRelayedTransactions < INVENTORY_BROADCAST_MAX) {
- // Fetch the top element from the heap
- std::pop_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder);
- std::set<uint256>::iterator it = vInvTx.back();
- vInvTx.pop_back();
- uint256 hash = *it;
- // Remove it from the to-be-sent set
- pto->setInventoryTxToSend.erase(it);
- // Check if not in the filter already
- if (pto->filterInventoryKnown.contains(hash)) {
- continue;
- }
- // Not in the mempool anymore? don't bother sending it.
- auto txinfo = mempool.info(hash);
- if (!txinfo.tx) {
- continue;
- }
- if (filterrate && txinfo.feeRate.GetFeePerK() < filterrate) {
- continue;
- }
- if (pto->pfilter && !pto->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
- // Send
- vInv.push_back(CInv(MSG_TX, hash));
- nRelayedTransactions++;
- {
- // Expire old relay messages
- while (!vRelayExpiration.empty() && vRelayExpiration.front().first < nNow)
- {
- mapRelay.erase(vRelayExpiration.front().second);
- vRelayExpiration.pop_front();
- }
+ uint64_t version = MEMPOOL_DUMP_VERSION;
+ file << version;
- auto ret = mapRelay.insert(std::make_pair(hash, std::move(txinfo.tx)));
- if (ret.second) {
- vRelayExpiration.push_back(std::make_pair(nNow + 15 * 60 * 1000000, ret.first));
- }
- }
- if (vInv.size() == MAX_INV_SZ) {
- pto->PushMessage(NetMsgType::INV, vInv);
- vInv.clear();
- }
- pto->filterInventoryKnown.insert(hash);
- }
- }
- }
- if (!vInv.empty())
- pto->PushMessage(NetMsgType::INV, vInv);
-
- // Detect whether we're stalling
- nNow = GetTimeMicros();
- if (!pto->fDisconnect && state.nStallingSince && state.nStallingSince < nNow - 1000000 * BLOCK_STALLING_TIMEOUT) {
- // Stalling only triggers when the block download window cannot move. During normal steady state,
- // the download window should be much larger than the to-be-downloaded set of blocks, so disconnection
- // should only happen during initial block download.
- LogPrintf("Peer=%d is stalling block download, disconnecting\n", pto->id);
- pto->fDisconnect = true;
- }
- // In case there is a block that has been in flight from this peer for 2 + 0.5 * N times the block interval
- // (with N the number of peers from which we're downloading validated blocks), disconnect due to timeout.
- // We compensate for other peers to prevent killing off peers due to our own downstream link
- // being saturated. We only count validated in-flight blocks so peers can't advertise non-existing block hashes
- // to unreasonably increase our timeout.
- if (!pto->fDisconnect && state.vBlocksInFlight.size() > 0) {
- QueuedBlock &queuedBlock = state.vBlocksInFlight.front();
- int nOtherPeersWithValidatedDownloads = nPeersWithValidatedDownloads - (state.nBlocksInFlightValidHeaders > 0);
- if (nNow > state.nDownloadingSince + consensusParams.nPowTargetSpacing * (BLOCK_DOWNLOAD_TIMEOUT_BASE + BLOCK_DOWNLOAD_TIMEOUT_PER_PEER * nOtherPeersWithValidatedDownloads)) {
- LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", queuedBlock.hash.ToString(), pto->id);
- pto->fDisconnect = true;
- }
+ file << (uint64_t)vinfo.size();
+ for (const auto& i : vinfo) {
+ file << *(i.tx);
+ file << (int64_t)i.nTime;
+ file << (int64_t)i.nFeeDelta;
+ mapDeltas.erase(i.tx->GetHash());
}
- //
- // Message: getdata (blocks)
- //
- vector<CInv> vGetData;
- if (!pto->fDisconnect && !pto->fClient && (fFetch || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
- vector<CBlockIndex*> vToDownload;
- NodeId staller = -1;
- FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller, consensusParams);
- BOOST_FOREACH(CBlockIndex *pindex, vToDownload) {
- uint32_t nFetchFlags = GetFetchFlags(pto, pindex->pprev, consensusParams);
- vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
- MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), consensusParams, pindex);
- LogPrint("net", "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(),
- pindex->nHeight, pto->id);
- }
- if (state.nBlocksInFlight == 0 && staller != -1) {
- if (State(staller)->nStallingSince == 0) {
- State(staller)->nStallingSince = nNow;
- LogPrint("net", "Stall started peer=%d\n", staller);
- }
- }
- }
-
- //
- // Message: getdata (non-blocks)
- //
- while (!pto->fDisconnect && !pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow)
- {
- const CInv& inv = (*pto->mapAskFor.begin()).second;
- if (!AlreadyHave(inv))
- {
- if (fDebug)
- LogPrint("net", "Requesting %s peer=%d\n", inv.ToString(), pto->id);
- vGetData.push_back(inv);
- if (vGetData.size() >= 1000)
- {
- pto->PushMessage(NetMsgType::GETDATA, vGetData);
- vGetData.clear();
- }
- } else {
- //If we're not going to ask, don't expect a response.
- pto->setAskFor.erase(inv.hash);
- }
- pto->mapAskFor.erase(pto->mapAskFor.begin());
- }
- if (!vGetData.empty())
- pto->PushMessage(NetMsgType::GETDATA, vGetData);
-
- //
- // Message: feefilter
- //
- // We don't want white listed peers to filter txs to us if we have -whitelistforcerelay
- if (pto->nVersion >= FEEFILTER_VERSION && GetBoolArg("-feefilter", DEFAULT_FEEFILTER) &&
- !(pto->fWhitelisted && GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY))) {
- CAmount currentFilter = mempool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK();
- int64_t timeNow = GetTimeMicros();
- if (timeNow > pto->nextSendTimeFeeFilter) {
- CAmount filterToSend = filterRounder.round(currentFilter);
- if (filterToSend != pto->lastSentFeeFilter) {
- pto->PushMessage(NetMsgType::FEEFILTER, filterToSend);
- pto->lastSentFeeFilter = filterToSend;
- }
- pto->nextSendTimeFeeFilter = PoissonNextSend(timeNow, AVG_FEEFILTER_BROADCAST_INTERVAL);
- }
- // If the fee filter has changed substantially and it's still more than MAX_FEEFILTER_CHANGE_DELAY
- // until scheduled broadcast, then move the broadcast to within MAX_FEEFILTER_CHANGE_DELAY.
- else if (timeNow + MAX_FEEFILTER_CHANGE_DELAY * 1000000 < pto->nextSendTimeFeeFilter &&
- (currentFilter < 3 * pto->lastSentFeeFilter / 4 || currentFilter > 4 * pto->lastSentFeeFilter / 3)) {
- pto->nextSendTimeFeeFilter = timeNow + (insecure_rand() % MAX_FEEFILTER_CHANGE_DELAY) * 1000000;
- }
- }
+ file << mapDeltas;
+ FileCommit(file.Get());
+ file.fclose();
+ RenameOver(GetDataDir() / "mempool.dat.new", GetDataDir() / "mempool.dat");
+ int64_t last = GetTimeMicros();
+ LogPrintf("Dumped mempool: %gs to copy, %gs to dump\n", (mid-start)*0.000001, (last-mid)*0.000001);
+ } catch (const std::exception& e) {
+ LogPrintf("Failed to dump mempool: %s. Continuing anyway.\n", e.what());
}
- return true;
-}
-
- std::string CBlockFileInfo::ToString() const {
- return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst), DateTimeStrFormat("%Y-%m-%d", nTimeLast));
- }
-
-ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos)
-{
- LOCK(cs_main);
- return VersionBitsState(chainActive.Tip(), params, pos, versionbitscache);
}
class CMainCleanup
@@ -6833,9 +4150,5 @@ public:
for (; it1 != mapBlockIndex.end(); it1++)
delete (*it1).second;
mapBlockIndex.clear();
-
- // orphan transactions
- mapOrphanTransactions.clear();
- mapOrphanTransactionsByPrev.clear();
}
} instance_of_cmaincleanup;
diff --git a/src/main.h b/src/validation.h
index 2646d8f86b..f0f54786c7 100644
--- a/src/main.h
+++ b/src/validation.h
@@ -3,8 +3,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_MAIN_H
-#define BITCOIN_MAIN_H
+#ifndef BITCOIN_VALIDATION_H
+#define BITCOIN_VALIDATION_H
#if defined(HAVE_CONFIG_H)
#include "config/bitcoin-config.h"
@@ -13,7 +13,7 @@
#include "amount.h"
#include "chain.h"
#include "coins.h"
-#include "net.h"
+#include "protocol.h" // For CMessageHeader::MessageStartChars
#include "script/script_error.h"
#include "sync.h"
#include "versionbits.h"
@@ -27,7 +27,10 @@
#include <utility>
#include <vector>
+#include <atomic>
+
#include <boost/unordered_map.hpp>
+#include <boost/filesystem/path.hpp>
class CBlockIndex;
class CBlockTreeDB;
@@ -41,7 +44,6 @@ class CValidationInterface;
class CValidationState;
struct PrecomputedTransactionData;
-struct CNodeStateStats;
struct LockPoints;
/** Default for DEFAULT_WHITELISTRELAY. */
@@ -90,6 +92,11 @@ static const unsigned int BLOCK_STALLING_TIMEOUT = 2;
/** Number of headers sent in one getheaders result. We rely on the assumption that if a peer sends
* less than this number, we reached its tip. Changing this value is a protocol upgrade. */
static const unsigned int MAX_HEADERS_RESULTS = 2000;
+/** Maximum depth of blocks we're willing to serve as compact blocks to peers
+ * when requested. For older blocks, a regular BLOCK response will be sent. */
+static const int MAX_CMPCTBLOCK_DEPTH = 5;
+/** Maximum depth of blocks we're willing to respond to GETBLOCKTXN requests for. */
+static const int MAX_BLOCKTXN_DEPTH = 10;
/** Size of the "block download window": how far ahead of our current height do we fetch?
* Larger windows tolerate larger download speed differences between peer, but increase the potential
* degree of disordering of blocks on disk (which make reindexing and in the future perhaps pruning
@@ -120,7 +127,7 @@ static const int64_t BLOCK_DOWNLOAD_TIMEOUT_BASE = 1000000;
/** Additional block download timeout per parallel downloading peer (i.e. 5 min) */
static const int64_t BLOCK_DOWNLOAD_TIMEOUT_PER_PEER = 500000;
-static const unsigned int DEFAULT_LIMITFREERELAY = 15;
+static const unsigned int DEFAULT_LIMITFREERELAY = 0;
static const bool DEFAULT_RELAYPRIORITY = true;
static const int64_t DEFAULT_MAX_TIP_AGE = 24 * 60 * 60;
@@ -130,7 +137,6 @@ static const bool DEFAULT_CHECKPOINTS_ENABLED = true;
static const bool DEFAULT_TXINDEX = false;
static const unsigned int DEFAULT_BANSCORE_THRESHOLD = 100;
-static const bool DEFAULT_TESTSAFEMODE = false;
/** Default for -mempoolreplacement */
static const bool DEFAULT_ENABLE_REPLACEMENT = true;
/** Default for using fee filter */
@@ -160,7 +166,7 @@ extern uint64_t nLastBlockWeight;
extern const std::string strMessageMagic;
extern CWaitableCriticalSection csBestBlock;
extern CConditionVariable cvBlockChange;
-extern bool fImporting;
+extern std::atomic_bool fImporting;
extern bool fReindex;
extern int nScriptCheckThreads;
extern bool fTxIndex;
@@ -206,24 +212,39 @@ static const unsigned int DEFAULT_CHECKLEVEL = 3;
// Setting the target to > than 550MB will make it likely we can respect the target.
static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024;
-/** Register with a network node to receive its signals */
-void RegisterNodeSignals(CNodeSignals& nodeSignals);
-/** Unregister a network node */
-void UnregisterNodeSignals(CNodeSignals& nodeSignals);
-
/**
* Process an incoming block. This only returns after the best known valid
* block is made active. Note that it does not, however, guarantee that the
* specific block passed to it has been checked for validity!
+ *
+ * If you want to *possibly* get feedback on whether pblock is valid, you must
+ * install a CValidationInterface (see validationinterface.h) - this will have
+ * its BlockChecked method called whenever *any* block completes validation.
+ *
+ * Note that we guarantee that either the proof-of-work is valid on pblock, or
+ * (and possibly also) BlockChecked will have been called.
*
- * @param[out] state This may be set to an Error state if any error occurred processing it, including during validation/connection/etc of otherwise unrelated blocks during reorganization; or it may be set to an Invalid state if pblock is itself invalid (but this is not guaranteed even when the block is checked). If you want to *possibly* get feedback on whether pblock is valid, you must also install a CValidationInterface (see validationinterface.h) - this will have its BlockChecked method called whenever *any* block completes validation.
- * @param[in] pfrom The node which we are receiving the block from; it is added to mapBlockSource and may be penalised if the block is invalid.
+ * Call without cs_main held.
+ *
* @param[in] pblock The block we want to process.
* @param[in] fForceProcessing Process this block even if unrequested; used for non-network block sources and whitelisted peers.
- * @param[out] dbp The already known disk position of pblock, or NULL if not yet stored.
+ * @param[out] fNewBlock A boolean which is set to indicate if the block was first received via this call
* @return True if state.IsValid()
*/
-bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp, CConnman* connman);
+bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock> pblock, bool fForceProcessing, bool* fNewBlock);
+
+/**
+ * Process incoming block headers.
+ *
+ * Call without cs_main held.
+ *
+ * @param[in] block The block headers themselves
+ * @param[out] state This may be set to an Error state if any error occurred processing them
+ * @param[in] chainparams The params for the chain we want to connect to
+ * @param[out] ppindex If set, the pointer will be set to point to the last new block index object for the given headers
+ */
+bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex=NULL);
+
/** Check whether enough disk space is available for an incoming block */
bool CheckDiskSpace(uint64_t nAdditionalBytes = 0);
/** Open a block file (blk?????.dat) */
@@ -237,18 +258,9 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB
/** Initialize a new block tree database + block data on disk */
bool InitBlockIndex(const CChainParams& chainparams);
/** Load the block tree and coins database from disk */
-bool LoadBlockIndex();
+bool LoadBlockIndex(const CChainParams& chainparams);
/** Unload database information */
void UnloadBlockIndex();
-/** Process protocol messages received from a given node */
-bool ProcessMessages(CNode* pfrom, CConnman& connman);
-/**
- * Send queued protocol messages to be sent to a give node.
- *
- * @param[in] pto The node which we are sending messages to.
- * @param[in] connman The connection manager for that node.
- */
-bool SendMessages(CNode* pto, CConnman& connman);
/** Run an instance of the script checking thread */
void ThreadScriptCheck();
/** Check whether we are doing an initial block download (synchronizing from disk or network) */
@@ -262,9 +274,9 @@ bool IsInitialBlockDownload();
*/
std::string GetWarnings(const std::string& strFor);
/** Retrieve a transaction (from memory pool, or from disk, if possible) */
-bool GetTransaction(const uint256 &hash, CTransaction &tx, const Consensus::Params& params, uint256 &hashBlock, bool fAllowSlow = false);
+bool GetTransaction(const uint256 &hash, CTransactionRef &tx, const Consensus::Params& params, uint256 &hashBlock, bool fAllowSlow = false);
/** Find the best known block, and make it the tip of the block chain */
-bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, const CBlock* pblock = NULL, CConnman* connman = NULL);
+bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock = std::shared_ptr<const CBlock>());
CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams);
/**
@@ -291,10 +303,6 @@ void UnlinkPrunedFiles(std::set<int>& setFilesToPrune);
/** Create a new block index entry for a given block hash */
CBlockIndex * InsertBlockIndex(uint256 hash);
-/** Get statistics from node state */
-bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats);
-/** Increase a node's misbehavior score. */
-void Misbehaving(NodeId nodeid, int howmuch);
/** Flush all state, indexes and buffers to disk. */
void FlushStateToDisk();
/** Prune block files and flush state to disk. */
@@ -304,20 +312,18 @@ void PruneAndFlush();
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
bool* pfMissingInputs, bool fOverrideMempoolLimit=false, const CAmount nAbsurdFee=0);
+/** (try to) add transaction to memory pool with a specified acceptance time **/
+bool AcceptToMemoryPoolWithTime(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
+ bool* pfMissingInputs, int64_t nAcceptTime, bool fOverrideMempoolLimit=false, const CAmount nAbsurdFee=0);
+
/** Convert CValidationState to a human-readable message for logging */
std::string FormatStateMessage(const CValidationState &state);
/** Get the BIP9 state for a given deployment at the current tip. */
ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos);
-struct CNodeStateStats {
- int nMisbehavior;
- int nSyncHeight;
- int nCommonHeight;
- std::vector<int> vHeightInFlight;
-};
-
-
+/** Get the block height at which the BIP9 deployment switched into the state for the block building on the current tip. */
+int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::DeploymentPos pos);
/**
* Count ECDSA signature operations the old-fashioned (pre-0.6) way
@@ -358,7 +364,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight);
/** Transaction validation functions */
/** Context-independent validity checks */
-bool CheckTransaction(const CTransaction& tx, CValidationState& state);
+bool CheckTransaction(const CTransaction& tx, CValidationState& state, bool fCheckDuplicateInputs=true);
namespace Consensus {
@@ -504,6 +510,9 @@ public:
/** Find the last common block between the parameter chain and a locator. */
CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator);
+/** Mark a block as precious and reorganize. */
+bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex);
+
/** Mark a block as invalid. */
bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex);
@@ -545,4 +554,10 @@ static const unsigned int REJECT_ALREADY_KNOWN = 0x101;
/** Transaction conflicts with a transaction already known */
static const unsigned int REJECT_CONFLICT = 0x102;
-#endif // BITCOIN_MAIN_H
+/** Dump the mempool to disk. */
+void DumpMempool();
+
+/** Load the mempool from disk. */
+bool LoadMempool();
+
+#endif // BITCOIN_VALIDATION_H
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index 6ddf37658d..085c336ccf 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -13,7 +13,7 @@ CMainSignals& GetMainSignals()
}
void RegisterValidationInterface(CValidationInterface* pwalletIn) {
- g_signals.UpdatedBlockTip.connect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1));
+ g_signals.UpdatedBlockTip.connect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3));
g_signals.SyncTransaction.connect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2, _3));
g_signals.UpdatedTransaction.connect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1));
g_signals.SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
@@ -33,7 +33,7 @@ void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
g_signals.SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
g_signals.UpdatedTransaction.disconnect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1));
g_signals.SyncTransaction.disconnect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2, _3));
- g_signals.UpdatedBlockTip.disconnect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1));
+ g_signals.UpdatedBlockTip.disconnect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3));
}
void UnregisterAllValidationInterfaces() {
@@ -47,7 +47,3 @@ void UnregisterAllValidationInterfaces() {
g_signals.SyncTransaction.disconnect_all_slots();
g_signals.UpdatedBlockTip.disconnect_all_slots();
}
-
-void SyncWithWallets(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock) {
- g_signals.SyncTransaction(tx, pindex, posInBlock);
-}
diff --git a/src/validationinterface.h b/src/validationinterface.h
index 0c91ec8308..a29859999b 100644
--- a/src/validationinterface.h
+++ b/src/validationinterface.h
@@ -28,12 +28,10 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn);
void UnregisterValidationInterface(CValidationInterface* pwalletIn);
/** Unregister all wallets from core */
void UnregisterAllValidationInterfaces();
-/** Push an updated transaction to all registered wallets */
-void SyncWithWallets(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock = -1);
class CValidationInterface {
protected:
- virtual void UpdatedBlockTip(const CBlockIndex *pindex) {}
+ virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {}
virtual void SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock) {}
virtual void SetBestChain(const CBlockLocator &locator) {}
virtual void UpdatedTransaction(const uint256 &hash) {}
@@ -49,7 +47,9 @@ protected:
struct CMainSignals {
/** Notifies listeners of updated block chain tip */
- boost::signals2::signal<void (const CBlockIndex *)> UpdatedBlockTip;
+ boost::signals2::signal<void (const CBlockIndex *, const CBlockIndex *, bool fInitialDownload)> UpdatedBlockTip;
+ /** A posInBlock value for SyncTransaction which indicates the transaction was conflicted, disconnected, or not in a block */
+ static const int SYNC_TRANSACTION_NOT_IN_BLOCK = -1;
/** Notifies listeners of updated transaction data (transaction, and optionally the block it is found in. */
boost::signals2::signal<void (const CTransaction &, const CBlockIndex *pindex, int posInBlock)> SyncTransaction;
/** Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible). */
diff --git a/src/version.h b/src/version.h
index 68ccd6d378..87fb1a3a75 100644
--- a/src/version.h
+++ b/src/version.h
@@ -9,7 +9,7 @@
* network protocol versioning
*/
-static const int PROTOCOL_VERSION = 70014;
+static const int PROTOCOL_VERSION = 70015;
//! initial proto version, to be increased after version/verack negotiation
static const int INIT_PROTO_VERSION = 209;
@@ -39,7 +39,10 @@ static const int SENDHEADERS_VERSION = 70012;
//! "feefilter" tells peers to filter invs to you by fee starts with this version
static const int FEEFILTER_VERSION = 70013;
-//! shord-id-based block download starts with this version
+//! short-id-based block download starts with this version
static const int SHORT_IDS_BLOCKS_VERSION = 70014;
+//! not banning for invalid compact blocks starts with this version
+static const int INVALID_CB_NO_BAN_VERSION = 70015;
+
#endif // BITCOIN_VERSION_H
diff --git a/src/versionbits.cpp b/src/versionbits.cpp
index bf32ae6627..d73f340510 100644
--- a/src/versionbits.cpp
+++ b/src/versionbits.cpp
@@ -105,6 +105,36 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
return state;
}
+int AbstractThresholdConditionChecker::GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const
+{
+ const ThresholdState initialState = GetStateFor(pindexPrev, params, cache);
+
+ // BIP 9 about state DEFINED: "The genesis block is by definition in this state for each deployment."
+ if (initialState == THRESHOLD_DEFINED) {
+ return 0;
+ }
+
+ const int nPeriod = Period(params);
+
+ // A block's state is always the same as that of the first of its period, so it is computed based on a pindexPrev whose height equals a multiple of nPeriod - 1.
+ // To ease understanding of the following height calculation, it helps to remember that
+ // right now pindexPrev points to the block prior to the block that we are computing for, thus:
+ // if we are computing for the last block of a period, then pindexPrev points to the second to last block of the period, and
+ // if we are computing for the first block of a period, then pindexPrev points to the last block of the previous period.
+ // The parent of the genesis block is represented by NULL.
+ pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod));
+
+ const CBlockIndex* previousPeriodParent = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod);
+
+ while (previousPeriodParent != NULL && GetStateFor(previousPeriodParent, params, cache) == initialState) {
+ pindexPrev = previousPeriodParent;
+ previousPeriodParent = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod);
+ }
+
+ // Adjust the result because right now we point to the parent block.
+ return pindexPrev->nHeight + 1;
+}
+
namespace
{
/**
@@ -137,6 +167,11 @@ ThresholdState VersionBitsState(const CBlockIndex* pindexPrev, const Consensus::
return VersionBitsConditionChecker(pos).GetStateFor(pindexPrev, params, cache.caches[pos]);
}
+int VersionBitsStateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache)
+{
+ return VersionBitsConditionChecker(pos).GetStateSinceHeightFor(pindexPrev, params, cache.caches[pos]);
+}
+
uint32_t VersionBitsMask(const Consensus::Params& params, Consensus::DeploymentPos pos)
{
return VersionBitsConditionChecker(pos).Mask(params);
diff --git a/src/versionbits.h b/src/versionbits.h
index ede2dcdda8..7a929266aa 100644
--- a/src/versionbits.h
+++ b/src/versionbits.h
@@ -51,8 +51,9 @@ protected:
virtual int Threshold(const Consensus::Params& params) const =0;
public:
- // Note that the function below takes a pindexPrev as input: they compute information for block B based on its parent.
+ // Note that the functions below take a pindexPrev as input: they compute information for block B based on its parent.
ThresholdState GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const;
+ int GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const;
};
struct VersionBitsCache
@@ -63,6 +64,7 @@ struct VersionBitsCache
};
ThresholdState VersionBitsState(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache);
+int VersionBitsStateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache);
uint32_t VersionBitsMask(const Consensus::Params& params, Consensus::DeploymentPos pos);
#endif
diff --git a/src/coincontrol.h b/src/wallet/coincontrol.h
index e33adc4d2b..08d23688ff 100644
--- a/src/coincontrol.h
+++ b/src/wallet/coincontrol.h
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_COINCONTROL_H
-#define BITCOIN_COINCONTROL_H
+#ifndef BITCOIN_WALLET_COINCONTROL_H
+#define BITCOIN_WALLET_COINCONTROL_H
#include "primitives/transaction.h"
@@ -22,6 +22,8 @@ public:
bool fOverrideFeeRate;
//! Feerate to use if overrideFeeRate is true
CFeeRate nFeeRate;
+ //! Override the default confirmation target, 0 = use default
+ int nConfirmTarget;
CCoinControl()
{
@@ -37,6 +39,7 @@ public:
nMinimumTotalFee = 0;
nFeeRate = CFeeRate(0);
fOverrideFeeRate = false;
+ nConfirmTarget = 0;
}
bool HasSelected() const
@@ -73,4 +76,4 @@ private:
std::set<COutPoint> setSelected;
};
-#endif // BITCOIN_COINCONTROL_H
+#endif // BITCOIN_WALLET_COINCONTROL_H
diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp
index 190f8ecf2a..31ee060677 100644
--- a/src/wallet/crypter.cpp
+++ b/src/wallet/crypter.cpp
@@ -48,12 +48,12 @@ bool CCrypter::SetKeyFromPassphrase(const SecureString& strKeyData, const std::v
int i = 0;
if (nDerivationMethod == 0)
- i = BytesToKeySHA512AES(chSalt, strKeyData, nRounds, chKey, chIV);
+ i = BytesToKeySHA512AES(chSalt, strKeyData, nRounds, vchKey.data(), vchIV.data());
if (i != (int)WALLET_CRYPTO_KEY_SIZE)
{
- memory_cleanse(chKey, sizeof(chKey));
- memory_cleanse(chIV, sizeof(chIV));
+ memory_cleanse(vchKey.data(), vchKey.size());
+ memory_cleanse(vchIV.data(), vchIV.size());
return false;
}
@@ -66,8 +66,8 @@ bool CCrypter::SetKey(const CKeyingMaterial& chNewKey, const std::vector<unsigne
if (chNewKey.size() != WALLET_CRYPTO_KEY_SIZE || chNewIV.size() != WALLET_CRYPTO_IV_SIZE)
return false;
- memcpy(&chKey[0], &chNewKey[0], sizeof chKey);
- memcpy(&chIV[0], &chNewIV[0], sizeof chIV);
+ memcpy(vchKey.data(), chNewKey.data(), chNewKey.size());
+ memcpy(vchIV.data(), chNewIV.data(), chNewIV.size());
fKeySet = true;
return true;
@@ -82,7 +82,7 @@ bool CCrypter::Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned
// n + AES_BLOCKSIZE bytes
vchCiphertext.resize(vchPlaintext.size() + AES_BLOCKSIZE);
- AES256CBCEncrypt enc(chKey, chIV, true);
+ AES256CBCEncrypt enc(vchKey.data(), vchIV.data(), true);
size_t nLen = enc.Encrypt(&vchPlaintext[0], vchPlaintext.size(), &vchCiphertext[0]);
if(nLen < vchPlaintext.size())
return false;
@@ -101,7 +101,7 @@ bool CCrypter::Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingM
vchPlaintext.resize(nLen);
- AES256CBCDecrypt dec(chKey, chIV, true);
+ AES256CBCDecrypt dec(vchKey.data(), vchIV.data(), true);
nLen = dec.Decrypt(&vchCiphertext[0], vchCiphertext.size(), &vchPlaintext[0]);
if(nLen == 0)
return false;
diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h
index 5d0a4a3305..e89c15b5d4 100644
--- a/src/wallet/crypter.h
+++ b/src/wallet/crypter.h
@@ -47,7 +47,7 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(vchCryptedKey);
READWRITE(vchSalt);
READWRITE(nDerivationMethod);
@@ -77,8 +77,8 @@ class CCrypter
{
friend class wallet_crypto::TestCrypter; // for test access to chKey/chIV
private:
- unsigned char chKey[WALLET_CRYPTO_KEY_SIZE];
- unsigned char chIV[WALLET_CRYPTO_IV_SIZE];
+ std::vector<unsigned char, secure_allocator<unsigned char>> vchKey;
+ std::vector<unsigned char, secure_allocator<unsigned char>> vchIV;
bool fKeySet;
int BytesToKeySHA512AES(const std::vector<unsigned char>& chSalt, const SecureString& strKeyData, int count, unsigned char *key,unsigned char *iv) const;
@@ -91,28 +91,21 @@ public:
void CleanKey()
{
- memory_cleanse(chKey, sizeof(chKey));
- memory_cleanse(chIV, sizeof(chIV));
+ memory_cleanse(vchKey.data(), vchKey.size());
+ memory_cleanse(vchIV.data(), vchIV.size());
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);
+ vchKey.resize(WALLET_CRYPTO_KEY_SIZE);
+ vchIV.resize(WALLET_CRYPTO_IV_SIZE);
}
~CCrypter()
{
CleanKey();
-
- LockedPageManager::Instance().UnlockRange(&chKey[0], sizeof chKey);
- LockedPageManager::Instance().UnlockRange(&chIV[0], sizeof chIV);
}
};
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 42ebdb9b9b..008a4ece19 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -6,7 +6,7 @@
#include "chain.h"
#include "rpc/server.h"
#include "init.h"
-#include "main.h"
+#include "validation.h"
#include "script/script.h"
#include "script/standard.h"
#include "sync.h"
@@ -24,6 +24,7 @@
#include <univalue.h>
+#include <boost/assign/list_of.hpp>
#include <boost/foreach.hpp>
using namespace std;
@@ -74,12 +75,12 @@ std::string DecodeDumpString(const std::string &str) {
return ret.str();
}
-UniValue importprivkey(const UniValue& params, bool fHelp)
+UniValue importprivkey(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() < 1 || params.size() > 3)
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
throw runtime_error(
"importprivkey \"bitcoinprivkey\" ( \"label\" rescan )\n"
"\nAdds a private key (as returned by dumpprivkey) to your wallet.\n"
@@ -104,15 +105,15 @@ UniValue importprivkey(const UniValue& params, bool fHelp)
EnsureWalletIsUnlocked();
- string strSecret = params[0].get_str();
+ string strSecret = request.params[0].get_str();
string strLabel = "";
- if (params.size() > 1)
- strLabel = params[1].get_str();
+ if (request.params.size() > 1)
+ strLabel = request.params[1].get_str();
// Whether to perform rescan after import
bool fRescan = true;
- if (params.size() > 2)
- fRescan = params[2].get_bool();
+ if (request.params.size() > 2)
+ fRescan = request.params[2].get_bool();
if (fRescan && fPruneMode)
throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode");
@@ -184,12 +185,12 @@ void ImportAddress(const CBitcoinAddress& address, const string& strLabel)
pwalletMain->SetAddressBook(address.Get(), strLabel, "receive");
}
-UniValue importaddress(const UniValue& params, bool fHelp)
+UniValue importaddress(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() < 1 || params.size() > 4)
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 4)
throw runtime_error(
"importaddress \"address\" ( \"label\" rescan p2sh )\n"
"\nAdds a script (in hex) or address that can be watched as if it were in your wallet but cannot be used to spend.\n"
@@ -213,31 +214,31 @@ UniValue importaddress(const UniValue& params, bool fHelp)
string strLabel = "";
- if (params.size() > 1)
- strLabel = params[1].get_str();
+ if (request.params.size() > 1)
+ strLabel = request.params[1].get_str();
// Whether to perform rescan after import
bool fRescan = true;
- if (params.size() > 2)
- fRescan = params[2].get_bool();
+ if (request.params.size() > 2)
+ fRescan = request.params[2].get_bool();
if (fRescan && fPruneMode)
throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode");
// Whether to import a p2sh version, too
bool fP2SH = false;
- if (params.size() > 3)
- fP2SH = params[3].get_bool();
+ if (request.params.size() > 3)
+ fP2SH = request.params[3].get_bool();
LOCK2(cs_main, pwalletMain->cs_wallet);
- CBitcoinAddress address(params[0].get_str());
+ CBitcoinAddress address(request.params[0].get_str());
if (address.IsValid()) {
if (fP2SH)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot use the p2sh flag with an address - use a script instead");
ImportAddress(address, strLabel);
- } else if (IsHex(params[0].get_str())) {
- std::vector<unsigned char> data(ParseHex(params[0].get_str()));
+ } else if (IsHex(request.params[0].get_str())) {
+ std::vector<unsigned char> data(ParseHex(request.params[0].get_str()));
ImportScript(CScript(data.begin(), data.end()), strLabel, fP2SH);
} else {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address or script");
@@ -252,12 +253,12 @@ UniValue importaddress(const UniValue& params, bool fHelp)
return NullUniValue;
}
-UniValue importprunedfunds(const UniValue& params, bool fHelp)
+UniValue importprunedfunds(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() != 2)
+ if (request.fHelp || request.params.size() != 2)
throw runtime_error(
"importprunedfunds\n"
"\nImports funds without rescan. Corresponding address or script must previously be included in wallet. Aimed towards pruned wallets. The end-user is responsible to import additional transactions that subsequently spend the imported outputs or rescan after the point in the blockchain the transaction is included.\n"
@@ -266,13 +267,13 @@ UniValue importprunedfunds(const UniValue& params, bool fHelp)
"2. \"txoutproof\" (string, required) The hex output from gettxoutproof that contains the transaction\n"
);
- CTransaction tx;
- if (!DecodeHexTx(tx, params[0].get_str()))
+ CMutableTransaction tx;
+ if (!DecodeHexTx(tx, request.params[0].get_str()))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
uint256 hashTx = tx.GetHash();
- CWalletTx wtx(pwalletMain,tx);
+ CWalletTx wtx(pwalletMain, MakeTransactionRef(std::move(tx)));
- CDataStream ssMB(ParseHexV(params[1], "proof"), SER_NETWORK, PROTOCOL_VERSION);
+ CDataStream ssMB(ParseHexV(request.params[1], "proof"), SER_NETWORK, PROTOCOL_VERSION);
CMerkleBlock merkleBlock;
ssMB >> merkleBlock;
@@ -303,7 +304,7 @@ UniValue importprunedfunds(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
- if (pwalletMain->IsMine(tx)) {
+ if (pwalletMain->IsMine(wtx)) {
pwalletMain->AddToWallet(wtx, false);
return NullUniValue;
}
@@ -311,12 +312,12 @@ UniValue importprunedfunds(const UniValue& params, bool fHelp)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No addresses in wallet correspond to included transaction");
}
-UniValue removeprunedfunds(const UniValue& params, bool fHelp)
+UniValue removeprunedfunds(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() != 1)
+ if (request.fHelp || request.params.size() != 1)
throw runtime_error(
"removeprunedfunds \"txid\"\n"
"\nDeletes the specified transaction from the wallet. Meant for use with pruned wallets and as a companion to importprunedfunds. This will effect wallet balances.\n"
@@ -331,7 +332,7 @@ UniValue removeprunedfunds(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
uint256 hash;
- hash.SetHex(params[0].get_str());
+ hash.SetHex(request.params[0].get_str());
vector<uint256> vHash;
vHash.push_back(hash);
vector<uint256> vHashOut;
@@ -344,17 +345,15 @@ UniValue removeprunedfunds(const UniValue& params, bool fHelp)
throw JSONRPCError(RPC_INTERNAL_ERROR, "Transaction does not exist in wallet.");
}
- ThreadFlushWalletDB(pwalletMain->strWalletFile);
-
return NullUniValue;
}
-UniValue importpubkey(const UniValue& params, bool fHelp)
+UniValue importpubkey(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() < 1 || params.size() > 4)
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 4)
throw runtime_error(
"importpubkey \"pubkey\" ( \"label\" rescan )\n"
"\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend.\n"
@@ -374,20 +373,20 @@ UniValue importpubkey(const UniValue& params, bool fHelp)
string strLabel = "";
- if (params.size() > 1)
- strLabel = params[1].get_str();
+ if (request.params.size() > 1)
+ strLabel = request.params[1].get_str();
// Whether to perform rescan after import
bool fRescan = true;
- if (params.size() > 2)
- fRescan = params[2].get_bool();
+ if (request.params.size() > 2)
+ fRescan = request.params[2].get_bool();
if (fRescan && fPruneMode)
throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode");
- if (!IsHex(params[0].get_str()))
+ if (!IsHex(request.params[0].get_str()))
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey must be a hex string");
- std::vector<unsigned char> data(ParseHex(params[0].get_str()));
+ std::vector<unsigned char> data(ParseHex(request.params[0].get_str()));
CPubKey pubKey(data.begin(), data.end());
if (!pubKey.IsFullyValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key");
@@ -407,12 +406,12 @@ UniValue importpubkey(const UniValue& params, bool fHelp)
}
-UniValue importwallet(const UniValue& params, bool fHelp)
+UniValue importwallet(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() != 1)
+ if (request.fHelp || request.params.size() != 1)
throw runtime_error(
"importwallet \"filename\"\n"
"\nImports keys from a wallet dump file (see dumpwallet).\n"
@@ -435,7 +434,7 @@ UniValue importwallet(const UniValue& params, bool fHelp)
EnsureWalletIsUnlocked();
ifstream file;
- file.open(params[0].get_str().c_str(), std::ios::in | std::ios::ate);
+ file.open(request.params[0].get_str().c_str(), std::ios::in | std::ios::ate);
if (!file.is_open())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
@@ -514,12 +513,12 @@ UniValue importwallet(const UniValue& params, bool fHelp)
return NullUniValue;
}
-UniValue dumpprivkey(const UniValue& params, bool fHelp)
+UniValue dumpprivkey(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() != 1)
+ if (request.fHelp || request.params.size() != 1)
throw runtime_error(
"dumpprivkey \"bitcoinaddress\"\n"
"\nReveals the private key corresponding to 'bitcoinaddress'.\n"
@@ -538,7 +537,7 @@ UniValue dumpprivkey(const UniValue& params, bool fHelp)
EnsureWalletIsUnlocked();
- string strAddress = params[0].get_str();
+ string strAddress = request.params[0].get_str();
CBitcoinAddress address;
if (!address.SetString(strAddress))
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
@@ -552,12 +551,12 @@ UniValue dumpprivkey(const UniValue& params, bool fHelp)
}
-UniValue dumpwallet(const UniValue& params, bool fHelp)
+UniValue dumpwallet(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() != 1)
+ if (request.fHelp || request.params.size() != 1)
throw runtime_error(
"dumpwallet \"filename\"\n"
"\nDumps all wallet keys in a human-readable format.\n"
@@ -573,7 +572,7 @@ UniValue dumpwallet(const UniValue& params, bool fHelp)
EnsureWalletIsUnlocked();
ofstream file;
- file.open(params[0].get_str().c_str());
+ file.open(request.params[0].get_str().c_str());
if (!file.is_open())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
@@ -639,3 +638,424 @@ UniValue dumpwallet(const UniValue& params, bool fHelp)
file.close();
return NullUniValue;
}
+
+
+UniValue processImport(const UniValue& data) {
+ try {
+ bool success = false;
+
+ // Required fields.
+ const UniValue& scriptPubKey = data["scriptPubKey"];
+
+ // Should have script or JSON with "address".
+ if (!(scriptPubKey.getType() == UniValue::VOBJ && scriptPubKey.exists("address")) && !(scriptPubKey.getType() == UniValue::VSTR)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid scriptPubKey");
+ }
+
+ // Optional fields.
+ const string& strRedeemScript = data.exists("redeemscript") ? data["redeemscript"].get_str() : "";
+ const UniValue& pubKeys = data.exists("pubkeys") ? data["pubkeys"].get_array() : UniValue();
+ const UniValue& keys = data.exists("keys") ? data["keys"].get_array() : UniValue();
+ const bool& internal = data.exists("internal") ? data["internal"].get_bool() : false;
+ const bool& watchOnly = data.exists("watchonly") ? data["watchonly"].get_bool() : false;
+ const string& label = data.exists("label") && !internal ? data["label"].get_str() : "";
+ const int64_t& timestamp = data.exists("timestamp") && data["timestamp"].get_int64() > 1 ? data["timestamp"].get_int64() : 1;
+
+ bool isScript = scriptPubKey.getType() == UniValue::VSTR;
+ bool isP2SH = strRedeemScript.length() > 0;
+ const string& output = isScript ? scriptPubKey.get_str() : scriptPubKey["address"].get_str();
+
+ // Parse the output.
+ CScript script;
+ CBitcoinAddress address;
+
+ if (!isScript) {
+ address = CBitcoinAddress(output);
+ script = GetScriptForDestination(address.Get());
+ } else {
+ if (!IsHex(output)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid scriptPubKey");
+ }
+
+ std::vector<unsigned char> vData(ParseHex(output));
+ script = CScript(vData.begin(), vData.end());
+ }
+
+ // Watchonly and private keys
+ if (watchOnly && keys.size()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Incompatibility found between watchonly and keys");
+ }
+
+ // Internal + Label
+ if (internal && data.exists("label")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Incompatibility found between internal and label");
+ }
+
+ // Not having Internal + Script
+ if (!internal && isScript) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal must be set for hex scriptPubKey");
+ }
+
+ // Keys / PubKeys size check.
+ if (!isP2SH && (keys.size() > 1 || pubKeys.size() > 1)) { // Address / scriptPubKey
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "More than private key given for one address");
+ }
+
+ // Invalid P2SH redeemScript
+ if (isP2SH && !IsHex(strRedeemScript)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid redeem script");
+ }
+
+ // Process. //
+
+ // P2SH
+ if (isP2SH) {
+ // Import redeem script.
+ std::vector<unsigned char> vData(ParseHex(strRedeemScript));
+ CScript redeemScript = CScript(vData.begin(), vData.end());
+
+ // Invalid P2SH address
+ if (!script.IsPayToScriptHash()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid P2SH address / script");
+ }
+
+ pwalletMain->MarkDirty();
+
+ if (!pwalletMain->HaveWatchOnly(redeemScript) && !pwalletMain->AddWatchOnly(redeemScript)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
+ }
+
+ if (!pwalletMain->HaveCScript(redeemScript) && !pwalletMain->AddCScript(redeemScript)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet");
+ }
+
+ CBitcoinAddress redeemAddress = CBitcoinAddress(CScriptID(redeemScript));
+ CScript redeemDestination = GetScriptForDestination(redeemAddress.Get());
+
+ if (::IsMine(*pwalletMain, redeemDestination) == ISMINE_SPENDABLE) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
+ }
+
+ pwalletMain->MarkDirty();
+
+ if (!pwalletMain->HaveWatchOnly(redeemDestination) && !pwalletMain->AddWatchOnly(redeemDestination)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
+ }
+
+ // add to address book or update label
+ if (address.IsValid()) {
+ pwalletMain->SetAddressBook(address.Get(), label, "receive");
+ }
+
+ // Import private keys.
+ if (keys.size()) {
+ for (size_t i = 0; i < keys.size(); i++) {
+ const string& privkey = keys[i].get_str();
+
+ CBitcoinSecret vchSecret;
+ bool fGood = vchSecret.SetString(privkey);
+
+ if (!fGood) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
+ }
+
+ CKey key = vchSecret.GetKey();
+
+ if (!key.IsValid()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range");
+ }
+
+ CPubKey pubkey = key.GetPubKey();
+ assert(key.VerifyPubKey(pubkey));
+
+ CKeyID vchAddress = pubkey.GetID();
+ pwalletMain->MarkDirty();
+ pwalletMain->SetAddressBook(vchAddress, label, "receive");
+
+ if (pwalletMain->HaveKey(vchAddress)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key");
+ }
+
+ pwalletMain->mapKeyMetadata[vchAddress].nCreateTime = timestamp;
+
+ if (!pwalletMain->AddKeyPubKey(key, pubkey)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
+ }
+
+ if (timestamp < pwalletMain->nTimeFirstKey) {
+ pwalletMain->nTimeFirstKey = timestamp;
+ }
+ }
+ }
+
+ success = true;
+ } else {
+ // Import public keys.
+ if (pubKeys.size() && keys.size() == 0) {
+ const string& strPubKey = pubKeys[0].get_str();
+
+ if (!IsHex(strPubKey)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey must be a hex string");
+ }
+
+ std::vector<unsigned char> vData(ParseHex(strPubKey));
+ CPubKey pubKey(vData.begin(), vData.end());
+
+ if (!pubKey.IsFullyValid()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key");
+ }
+
+ CBitcoinAddress pubKeyAddress = CBitcoinAddress(pubKey.GetID());
+
+ // Consistency check.
+ if (!isScript && !(pubKeyAddress.Get() == address.Get())) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
+ }
+
+ // Consistency check.
+ if (isScript) {
+ CBitcoinAddress scriptAddress;
+ CTxDestination destination;
+
+ if (ExtractDestination(script, destination)) {
+ scriptAddress = CBitcoinAddress(destination);
+ if (!(scriptAddress.Get() == pubKeyAddress.Get())) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
+ }
+ }
+ }
+
+ CScript pubKeyScript = GetScriptForDestination(pubKeyAddress.Get());
+
+ if (::IsMine(*pwalletMain, pubKeyScript) == ISMINE_SPENDABLE) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
+ }
+
+ pwalletMain->MarkDirty();
+
+ if (!pwalletMain->HaveWatchOnly(pubKeyScript) && !pwalletMain->AddWatchOnly(pubKeyScript)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
+ }
+
+ // add to address book or update label
+ if (pubKeyAddress.IsValid()) {
+ pwalletMain->SetAddressBook(pubKeyAddress.Get(), label, "receive");
+ }
+
+ // TODO Is this necessary?
+ CScript scriptRawPubKey = GetScriptForRawPubKey(pubKey);
+
+ if (::IsMine(*pwalletMain, scriptRawPubKey) == ISMINE_SPENDABLE) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
+ }
+
+ pwalletMain->MarkDirty();
+
+ if (!pwalletMain->HaveWatchOnly(scriptRawPubKey) && !pwalletMain->AddWatchOnly(scriptRawPubKey)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
+ }
+
+ success = true;
+ }
+
+ // Import private keys.
+ if (keys.size()) {
+ const string& strPrivkey = keys[0].get_str();
+
+ // Checks.
+ CBitcoinSecret vchSecret;
+ bool fGood = vchSecret.SetString(strPrivkey);
+
+ if (!fGood) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
+ }
+
+ CKey key = vchSecret.GetKey();
+ if (!key.IsValid()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range");
+ }
+
+ CPubKey pubKey = key.GetPubKey();
+ assert(key.VerifyPubKey(pubKey));
+
+ CBitcoinAddress pubKeyAddress = CBitcoinAddress(pubKey.GetID());
+
+ // Consistency check.
+ if (!isScript && !(pubKeyAddress.Get() == address.Get())) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
+ }
+
+ // Consistency check.
+ if (isScript) {
+ CBitcoinAddress scriptAddress;
+ CTxDestination destination;
+
+ if (ExtractDestination(script, destination)) {
+ scriptAddress = CBitcoinAddress(destination);
+ if (!(scriptAddress.Get() == pubKeyAddress.Get())) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
+ }
+ }
+ }
+
+ CKeyID vchAddress = pubKey.GetID();
+ pwalletMain->MarkDirty();
+ pwalletMain->SetAddressBook(vchAddress, label, "receive");
+
+ if (pwalletMain->HaveKey(vchAddress)) {
+ return false;
+ }
+
+ pwalletMain->mapKeyMetadata[vchAddress].nCreateTime = timestamp;
+
+ if (!pwalletMain->AddKeyPubKey(key, pubKey)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
+ }
+
+ if (timestamp < pwalletMain->nTimeFirstKey) {
+ pwalletMain->nTimeFirstKey = timestamp;
+ }
+
+ success = true;
+ }
+
+ // Import scriptPubKey only.
+ if (pubKeys.size() == 0 && keys.size() == 0) {
+ if (::IsMine(*pwalletMain, script) == ISMINE_SPENDABLE) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
+ }
+
+ pwalletMain->MarkDirty();
+
+ if (!pwalletMain->HaveWatchOnly(script) && !pwalletMain->AddWatchOnly(script)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
+ }
+
+ if (scriptPubKey.getType() == UniValue::VOBJ) {
+ // add to address book or update label
+ if (address.IsValid()) {
+ pwalletMain->SetAddressBook(address.Get(), label, "receive");
+ }
+ }
+
+ success = true;
+ }
+ }
+
+ UniValue result = UniValue(UniValue::VOBJ);
+ result.pushKV("success", UniValue(success));
+ return result;
+ } catch (const UniValue& e) {
+ UniValue result = UniValue(UniValue::VOBJ);
+ result.pushKV("success", UniValue(false));
+ result.pushKV("error", e);
+ return result;
+ } catch (...) {
+ UniValue result = UniValue(UniValue::VOBJ);
+ result.pushKV("success", UniValue(false));
+ result.pushKV("error", JSONRPCError(RPC_MISC_ERROR, "Missing required fields"));
+ return result;
+ }
+}
+
+UniValue importmulti(const JSONRPCRequest& mainRequest)
+{
+ // clang-format off
+ if (mainRequest.fHelp || mainRequest.params.size() < 1 || mainRequest.params.size() > 2)
+ throw runtime_error(
+ "importmulti '[<json import requests>]' '<json options>' \n\n"
+ "Import addresses/scripts (with private or public keys, redeem script (P2SH)), rescanning all addresses in one-shot-only (rescan can be disabled via options).\n\n"
+ "Arguments:\n"
+ "1. request array (array, required) Data to be imported\n"
+ " [ (array of json objects)\n"
+ " {\n"
+ " \"scriptPubKey\": \"<script>\" | { \"address\":\"<address>\" }, (string / json, required) Type of scriptPubKey (string for script, json for address)\n"
+ " \"redeemscript\": \"<script>\" , (string, optional) Allowed only if the scriptPubKey is a P2SH address or a P2SH scriptPubKey\n"
+ " \"pubkeys\": [\"<pubKey>\", ... ] , (array, optional) Array of strings giving pubkeys that must occur in the output or redeemscript\n"
+ " \"keys\": [\"<key>\", ... ] , (array, optional) Array of strings giving private keys whose corresponding public keys must occur in the output or redeemscript\n"
+ " \"internal\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be be treated as not incoming payments\n"
+ " \"watchonly\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be considered watched even when they're not spendable, only allowed if keys are empty\n"
+ " \"label\": <label> , (string, optional, default: '') Label to assign to the address (aka account name, for now), only allowed with internal=false\n"
+ " \"timestamp\": 1454686740, (integer, optional, default now) Timestamp\n"
+ " }\n"
+ " ,...\n"
+ " ]\n"
+ "2. json options (json, optional)\n"
+ " {\n"
+ " \"rescan\": <false>, (boolean, optional, default: true) Stating if should rescan the blockchain after all imports\n"
+ " }\n"
+ "\nExamples:\n" +
+ HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }, "
+ "{ \"scriptPubKey\": { \"address\": \"<my 2nd address>\" }, \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
+ HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }]' '{ \"rescan\": false}'") +
+
+ "\nResponse is an array with the same size as the input that has the execution result :\n"
+ " [{ \"success\": true } , { \"success\": false, \"error\": { \"code\": -1, \"message\": \"Internal Server Error\"} }, ... ]\n");
+
+ // clang-format on
+ if (!EnsureWalletIsAvailable(mainRequest.fHelp)) {
+ return NullUniValue;
+ }
+
+ RPCTypeCheck(mainRequest.params, boost::assign::list_of(UniValue::VARR)(UniValue::VOBJ));
+
+ const UniValue& requests = mainRequest.params[0];
+
+ //Default options
+ bool fRescan = true;
+
+ if (mainRequest.params.size() > 1) {
+ const UniValue& options = mainRequest.params[1];
+
+ if (options.exists("rescan")) {
+ fRescan = options["rescan"].get_bool();
+ }
+ }
+
+ LOCK2(cs_main, pwalletMain->cs_wallet);
+ EnsureWalletIsUnlocked();
+
+ bool fRunScan = false;
+ const int64_t minimumTimestamp = 1;
+ int64_t nLowestTimestamp = 0;
+
+ if (fRescan && chainActive.Tip()) {
+ nLowestTimestamp = chainActive.Tip()->GetBlockTime();
+ } else {
+ fRescan = false;
+ }
+
+ UniValue response(UniValue::VARR);
+
+ BOOST_FOREACH (const UniValue& data, requests.getValues()) {
+ const UniValue result = processImport(data);
+ response.push_back(result);
+
+ if (!fRescan) {
+ continue;
+ }
+
+ // If at least one request was successful then allow rescan.
+ if (result["success"].get_bool()) {
+ fRunScan = true;
+ }
+
+ // Get the lowest timestamp.
+ const int64_t& timestamp = data.exists("timestamp") && data["timestamp"].get_int64() > minimumTimestamp ? data["timestamp"].get_int64() : minimumTimestamp;
+
+ if (timestamp < nLowestTimestamp) {
+ nLowestTimestamp = timestamp;
+ }
+ }
+
+ if (fRescan && fRunScan && requests.size() && nLowestTimestamp <= chainActive.Tip()->GetBlockTime()) {
+ CBlockIndex* pindex = nLowestTimestamp > minimumTimestamp ? chainActive.FindLatestBefore(nLowestTimestamp) : chainActive.Genesis();
+
+ if (pindex) {
+ pwalletMain->ScanForWalletTransactions(pindex, true);
+ pwalletMain->ReacceptWalletTransactions();
+ }
+ }
+
+ return response;
+}
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 993caad400..5a4fcc743c 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -1,14 +1,15 @@
// Copyright (c) 2010 Satoshi Nakamoto
-// Copyright (c) 2009-2015 The Bitcoin Core developers
+// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "amount.h"
#include "base58.h"
#include "chain.h"
+#include "consensus/validation.h"
#include "core_io.h"
#include "init.h"
-#include "main.h"
+#include "validation.h"
#include "net.h"
#include "policy/rbf.h"
#include "rpc/server.h"
@@ -101,12 +102,12 @@ string AccountFromValue(const UniValue& value)
return strAccount;
}
-UniValue getnewaddress(const UniValue& params, bool fHelp)
+UniValue getnewaddress(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() > 1)
+ if (request.fHelp || request.params.size() > 1)
throw runtime_error(
"getnewaddress ( \"account\" )\n"
"\nReturns a new Bitcoin address for receiving payments.\n"
@@ -125,8 +126,8 @@ UniValue getnewaddress(const UniValue& params, bool fHelp)
// Parse the account first so we don't generate a key if there's an error
string strAccount;
- if (params.size() > 0)
- strAccount = AccountFromValue(params[0]);
+ if (request.params.size() > 0)
+ strAccount = AccountFromValue(request.params[0]);
if (!pwalletMain->IsLocked())
pwalletMain->TopUpKeyPool();
@@ -153,12 +154,12 @@ CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
return CBitcoinAddress(pubKey.GetID());
}
-UniValue getaccountaddress(const UniValue& params, bool fHelp)
+UniValue getaccountaddress(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() != 1)
+ if (request.fHelp || request.params.size() != 1)
throw runtime_error(
"getaccountaddress \"account\"\n"
"\nDEPRECATED. Returns the current Bitcoin address for receiving payments to this account.\n"
@@ -176,7 +177,7 @@ UniValue getaccountaddress(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
// Parse the account first so we don't generate a key if there's an error
- string strAccount = AccountFromValue(params[0]);
+ string strAccount = AccountFromValue(request.params[0]);
UniValue ret(UniValue::VSTR);
@@ -185,12 +186,12 @@ UniValue getaccountaddress(const UniValue& params, bool fHelp)
}
-UniValue getrawchangeaddress(const UniValue& params, bool fHelp)
+UniValue getrawchangeaddress(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() > 1)
+ if (request.fHelp || request.params.size() > 1)
throw runtime_error(
"getrawchangeaddress\n"
"\nReturns a new Bitcoin address, for receiving change.\n"
@@ -220,12 +221,12 @@ UniValue getrawchangeaddress(const UniValue& params, bool fHelp)
}
-UniValue setaccount(const UniValue& params, bool fHelp)
+UniValue setaccount(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() < 1 || params.size() > 2)
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw runtime_error(
"setaccount \"bitcoinaddress\" \"account\"\n"
"\nDEPRECATED. Sets the account associated with the given address.\n"
@@ -239,13 +240,13 @@ UniValue setaccount(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
- CBitcoinAddress address(params[0].get_str());
+ CBitcoinAddress address(request.params[0].get_str());
if (!address.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
string strAccount;
- if (params.size() > 1)
- strAccount = AccountFromValue(params[1]);
+ if (request.params.size() > 1)
+ strAccount = AccountFromValue(request.params[1]);
// Only add the account if the address is yours.
if (IsMine(*pwalletMain, address.Get()))
@@ -266,12 +267,12 @@ UniValue setaccount(const UniValue& params, bool fHelp)
}
-UniValue getaccount(const UniValue& params, bool fHelp)
+UniValue getaccount(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() != 1)
+ if (request.fHelp || request.params.size() != 1)
throw runtime_error(
"getaccount \"bitcoinaddress\"\n"
"\nDEPRECATED. Returns the account associated with the given address.\n"
@@ -286,7 +287,7 @@ UniValue getaccount(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
- CBitcoinAddress address(params[0].get_str());
+ CBitcoinAddress address(request.params[0].get_str());
if (!address.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
@@ -298,12 +299,12 @@ UniValue getaccount(const UniValue& params, bool fHelp)
}
-UniValue getaddressesbyaccount(const UniValue& params, bool fHelp)
+UniValue getaddressesbyaccount(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() != 1)
+ if (request.fHelp || request.params.size() != 1)
throw runtime_error(
"getaddressesbyaccount \"account\"\n"
"\nDEPRECATED. Returns the list of addresses for the given account.\n"
@@ -321,7 +322,7 @@ UniValue getaddressesbyaccount(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
- string strAccount = AccountFromValue(params[0]);
+ string strAccount = AccountFromValue(request.params[0]);
// Find all addresses that have the given account
UniValue ret(UniValue::VARR);
@@ -361,20 +362,23 @@ static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtr
CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount};
vecSend.push_back(recipient);
if (!pwalletMain->CreateTransaction(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) {
- if (!fSubtractFeeFromAmount && nValue + nFeeRequired > pwalletMain->GetBalance())
- strError = strprintf("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds!", FormatMoney(nFeeRequired));
+ if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance)
+ strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired));
+ throw JSONRPCError(RPC_WALLET_ERROR, strError);
+ }
+ CValidationState state;
+ if (!pwalletMain->CommitTransaction(wtxNew, reservekey, g_connman.get(), state)) {
+ strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason());
throw JSONRPCError(RPC_WALLET_ERROR, strError);
}
- if (!pwalletMain->CommitTransaction(wtxNew, reservekey, g_connman.get()))
- throw JSONRPCError(RPC_WALLET_ERROR, "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 the wallet and coins were spent in the copy but not marked as spent here.");
}
-UniValue sendtoaddress(const UniValue& params, bool fHelp)
+UniValue sendtoaddress(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() < 2 || params.size() > 5)
+ if (request.fHelp || request.params.size() < 2 || request.params.size() > 5)
throw runtime_error(
"sendtoaddress \"bitcoinaddress\" amount ( \"comment\" \"comment-to\" subtractfeefromamount )\n"
"\nSend an amount to a given address.\n"
@@ -400,25 +404,25 @@ UniValue sendtoaddress(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
- CBitcoinAddress address(params[0].get_str());
+ CBitcoinAddress address(request.params[0].get_str());
if (!address.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
// Amount
- CAmount nAmount = AmountFromValue(params[1]);
+ CAmount nAmount = AmountFromValue(request.params[1]);
if (nAmount <= 0)
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
// Wallet comments
CWalletTx wtx;
- if (params.size() > 2 && !params[2].isNull() && !params[2].get_str().empty())
- wtx.mapValue["comment"] = params[2].get_str();
- if (params.size() > 3 && !params[3].isNull() && !params[3].get_str().empty())
- wtx.mapValue["to"] = params[3].get_str();
+ if (request.params.size() > 2 && !request.params[2].isNull() && !request.params[2].get_str().empty())
+ wtx.mapValue["comment"] = request.params[2].get_str();
+ if (request.params.size() > 3 && !request.params[3].isNull() && !request.params[3].get_str().empty())
+ wtx.mapValue["to"] = request.params[3].get_str();
bool fSubtractFeeFromAmount = false;
- if (params.size() > 4)
- fSubtractFeeFromAmount = params[4].get_bool();
+ if (request.params.size() > 4)
+ fSubtractFeeFromAmount = request.params[4].get_bool();
EnsureWalletIsUnlocked();
@@ -427,12 +431,12 @@ UniValue sendtoaddress(const UniValue& params, bool fHelp)
return wtx.GetHash().GetHex();
}
-UniValue listaddressgroupings(const UniValue& params, bool fHelp)
+UniValue listaddressgroupings(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp)
+ if (request.fHelp)
throw runtime_error(
"listaddressgroupings\n"
"\nLists groups of addresses which have had their common ownership\n"
@@ -478,12 +482,12 @@ UniValue listaddressgroupings(const UniValue& params, bool fHelp)
return jsonGroupings;
}
-UniValue signmessage(const UniValue& params, bool fHelp)
+UniValue signmessage(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() != 2)
+ if (request.fHelp || request.params.size() != 2)
throw runtime_error(
"signmessage \"bitcoinaddress\" \"message\"\n"
"\nSign a message with the private key of an address"
@@ -508,8 +512,8 @@ UniValue signmessage(const UniValue& params, bool fHelp)
EnsureWalletIsUnlocked();
- string strAddress = params[0].get_str();
- string strMessage = params[1].get_str();
+ string strAddress = request.params[0].get_str();
+ string strMessage = request.params[1].get_str();
CBitcoinAddress addr(strAddress);
if (!addr.IsValid())
@@ -534,12 +538,12 @@ UniValue signmessage(const UniValue& params, bool fHelp)
return EncodeBase64(&vchSig[0], vchSig.size());
}
-UniValue getreceivedbyaddress(const UniValue& params, bool fHelp)
+UniValue getreceivedbyaddress(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() < 1 || params.size() > 2)
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw runtime_error(
"getreceivedbyaddress \"bitcoinaddress\" ( minconf )\n"
"\nReturns the total amount received by the given bitcoinaddress in transactions with at least minconf confirmations.\n"
@@ -562,7 +566,7 @@ UniValue getreceivedbyaddress(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
// Bitcoin address
- CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
+ CBitcoinAddress address = CBitcoinAddress(request.params[0].get_str());
if (!address.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
CScript scriptPubKey = GetScriptForDestination(address.Get());
@@ -571,18 +575,18 @@ UniValue getreceivedbyaddress(const UniValue& params, bool fHelp)
// Minimum confirmations
int nMinDepth = 1;
- if (params.size() > 1)
- nMinDepth = params[1].get_int();
+ if (request.params.size() > 1)
+ nMinDepth = request.params[1].get_int();
// Tally
CAmount nAmount = 0;
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
{
const CWalletTx& wtx = (*it).second;
- if (wtx.IsCoinBase() || !CheckFinalTx(wtx))
+ if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx))
continue;
- BOOST_FOREACH(const CTxOut& txout, wtx.vout)
+ BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout)
if (txout.scriptPubKey == scriptPubKey)
if (wtx.GetDepthInMainChain() >= nMinDepth)
nAmount += txout.nValue;
@@ -592,12 +596,12 @@ UniValue getreceivedbyaddress(const UniValue& params, bool fHelp)
}
-UniValue getreceivedbyaccount(const UniValue& params, bool fHelp)
+UniValue getreceivedbyaccount(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() < 1 || params.size() > 2)
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw runtime_error(
"getreceivedbyaccount \"account\" ( minconf )\n"
"\nDEPRECATED. Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.\n"
@@ -621,11 +625,11 @@ UniValue getreceivedbyaccount(const UniValue& params, bool fHelp)
// Minimum confirmations
int nMinDepth = 1;
- if (params.size() > 1)
- nMinDepth = params[1].get_int();
+ if (request.params.size() > 1)
+ nMinDepth = request.params[1].get_int();
// Get the set of pub keys assigned to account
- string strAccount = AccountFromValue(params[0]);
+ string strAccount = AccountFromValue(request.params[0]);
set<CTxDestination> setAddress = pwalletMain->GetAccountAddresses(strAccount);
// Tally
@@ -633,10 +637,10 @@ UniValue getreceivedbyaccount(const UniValue& params, bool fHelp)
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
{
const CWalletTx& wtx = (*it).second;
- if (wtx.IsCoinBase() || !CheckFinalTx(wtx))
+ if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx))
continue;
- BOOST_FOREACH(const CTxOut& txout, wtx.vout)
+ BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout)
{
CTxDestination address;
if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address))
@@ -649,12 +653,12 @@ UniValue getreceivedbyaccount(const UniValue& params, bool fHelp)
}
-UniValue getbalance(const UniValue& params, bool fHelp)
+UniValue getbalance(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() > 3)
+ if (request.fHelp || request.params.size() > 3)
throw runtime_error(
"getbalance ( \"account\" minconf includeWatchonly )\n"
"\nIf account is not specified, returns the server's total available balance.\n"
@@ -678,18 +682,18 @@ UniValue getbalance(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
- if (params.size() == 0)
+ if (request.params.size() == 0)
return ValueFromAmount(pwalletMain->GetBalance());
int nMinDepth = 1;
- if (params.size() > 1)
- nMinDepth = params[1].get_int();
+ if (request.params.size() > 1)
+ nMinDepth = request.params[1].get_int();
isminefilter filter = ISMINE_SPENDABLE;
- if(params.size() > 2)
- if(params[2].get_bool())
+ if(request.params.size() > 2)
+ if(request.params[2].get_bool())
filter = filter | ISMINE_WATCH_ONLY;
- if (params[0].get_str() == "*") {
+ if (request.params[0].get_str() == "*") {
// Calculate total balance a different way from GetBalance()
// (GetBalance() sums up all unspent TxOuts)
// getbalance and "getbalance * 1 true" should return the same number
@@ -717,19 +721,19 @@ UniValue getbalance(const UniValue& params, bool fHelp)
return ValueFromAmount(nBalance);
}
- string strAccount = AccountFromValue(params[0]);
+ string strAccount = AccountFromValue(request.params[0]);
CAmount nBalance = pwalletMain->GetAccountBalance(strAccount, nMinDepth, filter);
return ValueFromAmount(nBalance);
}
-UniValue getunconfirmedbalance(const UniValue &params, bool fHelp)
+UniValue getunconfirmedbalance(const JSONRPCRequest &request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() > 0)
+ if (request.fHelp || request.params.size() > 0)
throw runtime_error(
"getunconfirmedbalance\n"
"Returns the server's total unconfirmed balance\n");
@@ -740,12 +744,12 @@ UniValue getunconfirmedbalance(const UniValue &params, bool fHelp)
}
-UniValue movecmd(const UniValue& params, bool fHelp)
+UniValue movecmd(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() < 3 || params.size() > 5)
+ if (request.fHelp || request.params.size() < 3 || request.params.size() > 5)
throw runtime_error(
"move \"fromaccount\" \"toaccount\" amount ( minconf \"comment\" )\n"
"\nDEPRECATED. Move a specified amount from one account in your wallet to another.\n"
@@ -768,17 +772,17 @@ UniValue movecmd(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
- string strFrom = AccountFromValue(params[0]);
- string strTo = AccountFromValue(params[1]);
- CAmount nAmount = AmountFromValue(params[2]);
+ string strFrom = AccountFromValue(request.params[0]);
+ string strTo = AccountFromValue(request.params[1]);
+ CAmount nAmount = AmountFromValue(request.params[2]);
if (nAmount <= 0)
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
- if (params.size() > 3)
+ if (request.params.size() > 3)
// unused parameter, used to be nMinDepth, keep type-checking it though
- (void)params[3].get_int();
+ (void)request.params[3].get_int();
string strComment;
- if (params.size() > 4)
- strComment = params[4].get_str();
+ if (request.params.size() > 4)
+ strComment = request.params[4].get_str();
if (!pwalletMain->AccountMove(strFrom, strTo, nAmount, strComment))
throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
@@ -787,12 +791,12 @@ UniValue movecmd(const UniValue& params, bool fHelp)
}
-UniValue sendfrom(const UniValue& params, bool fHelp)
+UniValue sendfrom(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() < 3 || params.size() > 6)
+ if (request.fHelp || request.params.size() < 3 || request.params.size() > 6)
throw runtime_error(
"sendfrom \"fromaccount\" \"tobitcoinaddress\" amount ( minconf \"comment\" \"comment-to\" )\n"
"\nDEPRECATED (use sendtoaddress). Sent an amount from an account to a bitcoin address."
@@ -820,23 +824,23 @@ UniValue sendfrom(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
- string strAccount = AccountFromValue(params[0]);
- CBitcoinAddress address(params[1].get_str());
+ string strAccount = AccountFromValue(request.params[0]);
+ CBitcoinAddress address(request.params[1].get_str());
if (!address.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
- CAmount nAmount = AmountFromValue(params[2]);
+ CAmount nAmount = AmountFromValue(request.params[2]);
if (nAmount <= 0)
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
int nMinDepth = 1;
- if (params.size() > 3)
- nMinDepth = params[3].get_int();
+ if (request.params.size() > 3)
+ nMinDepth = request.params[3].get_int();
CWalletTx wtx;
wtx.strFromAccount = strAccount;
- if (params.size() > 4 && !params[4].isNull() && !params[4].get_str().empty())
- wtx.mapValue["comment"] = params[4].get_str();
- if (params.size() > 5 && !params[5].isNull() && !params[5].get_str().empty())
- wtx.mapValue["to"] = params[5].get_str();
+ if (request.params.size() > 4 && !request.params[4].isNull() && !request.params[4].get_str().empty())
+ wtx.mapValue["comment"] = request.params[4].get_str();
+ if (request.params.size() > 5 && !request.params[5].isNull() && !request.params[5].get_str().empty())
+ wtx.mapValue["to"] = request.params[5].get_str();
EnsureWalletIsUnlocked();
@@ -851,12 +855,12 @@ UniValue sendfrom(const UniValue& params, bool fHelp)
}
-UniValue sendmany(const UniValue& params, bool fHelp)
+UniValue sendmany(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() < 2 || params.size() > 5)
+ if (request.fHelp || request.params.size() < 2 || request.params.size() > 5)
throw runtime_error(
"sendmany \"fromaccount\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] )\n"
"\nSend multiple times. Amounts are double-precision floating point numbers."
@@ -897,20 +901,20 @@ UniValue sendmany(const UniValue& params, bool fHelp)
if (pwalletMain->GetBroadcastTransactions() && !g_connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
- string strAccount = AccountFromValue(params[0]);
- UniValue sendTo = params[1].get_obj();
+ string strAccount = AccountFromValue(request.params[0]);
+ UniValue sendTo = request.params[1].get_obj();
int nMinDepth = 1;
- if (params.size() > 2)
- nMinDepth = params[2].get_int();
+ if (request.params.size() > 2)
+ nMinDepth = request.params[2].get_int();
CWalletTx wtx;
wtx.strFromAccount = strAccount;
- if (params.size() > 3 && !params[3].isNull() && !params[3].get_str().empty())
- wtx.mapValue["comment"] = params[3].get_str();
+ if (request.params.size() > 3 && !request.params[3].isNull() && !request.params[3].get_str().empty())
+ wtx.mapValue["comment"] = request.params[3].get_str();
UniValue subtractFeeFromAmount(UniValue::VARR);
- if (params.size() > 4)
- subtractFeeFromAmount = params[4].get_array();
+ if (request.params.size() > 4)
+ subtractFeeFromAmount = request.params[4].get_array();
set<CBitcoinAddress> setAddress;
vector<CRecipient> vecSend;
@@ -959,8 +963,11 @@ UniValue sendmany(const UniValue& params, bool fHelp)
bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, nChangePosRet, strFailReason);
if (!fCreated)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
- if (!pwalletMain->CommitTransaction(wtx, keyChange, g_connman.get()))
- throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed");
+ CValidationState state;
+ if (!pwalletMain->CommitTransaction(wtx, keyChange, g_connman.get(), state)) {
+ strFailReason = strprintf("Transaction commit failed:: %s", state.GetRejectReason());
+ throw JSONRPCError(RPC_WALLET_ERROR, strFailReason);
+ }
return wtx.GetHash().GetHex();
}
@@ -968,12 +975,12 @@ UniValue sendmany(const UniValue& params, bool fHelp)
// Defined in rpc/misc.cpp
extern CScript _createmultisig_redeemScript(const UniValue& params);
-UniValue addmultisigaddress(const UniValue& params, bool fHelp)
+UniValue addmultisigaddress(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() < 2 || params.size() > 3)
+ if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
{
string msg = "addmultisigaddress nrequired [\"key\",...] ( \"account\" )\n"
"\nAdd a nrequired-to-sign multisignature address to the wallet.\n"
@@ -1004,11 +1011,11 @@ UniValue addmultisigaddress(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
string strAccount;
- if (params.size() > 2)
- strAccount = AccountFromValue(params[2]);
+ if (request.params.size() > 2)
+ strAccount = AccountFromValue(request.params[2]);
// Construct using pay-to-script-hash:
- CScript inner = _createmultisig_redeemScript(params);
+ CScript inner = _createmultisig_redeemScript(request.params);
CScriptID innerID(inner);
pwalletMain->AddCScript(inner);
@@ -1025,9 +1032,12 @@ public:
bool operator()(const CKeyID &keyID) {
CPubKey pubkey;
- if (pwalletMain && pwalletMain->GetPubKey(keyID, pubkey)) {
- CScript basescript;
- basescript << ToByteVector(pubkey) << OP_CHECKSIG;
+ if (pwalletMain) {
+ CScript basescript = GetScriptForDestination(keyID);
+ isminetype typ;
+ typ = IsMine(*pwalletMain, basescript, SIGVERSION_WITNESS_V0);
+ if (typ != ISMINE_SPENDABLE && typ != ISMINE_WATCH_SOLVABLE)
+ return false;
CScript witscript = GetScriptForWitness(basescript);
pwalletMain->AddCScript(witscript);
result = CScriptID(witscript);
@@ -1045,6 +1055,10 @@ public:
result = scriptID;
return true;
}
+ isminetype typ;
+ typ = IsMine(*pwalletMain, subscript, SIGVERSION_WITNESS_V0);
+ if (typ != ISMINE_SPENDABLE && typ != ISMINE_WATCH_SOLVABLE)
+ return false;
CScript witscript = GetScriptForWitness(subscript);
pwalletMain->AddCScript(witscript);
result = CScriptID(witscript);
@@ -1054,12 +1068,12 @@ public:
}
};
-UniValue addwitnessaddress(const UniValue& params, bool fHelp)
+UniValue addwitnessaddress(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() < 1 || params.size() > 1)
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 1)
{
string msg = "addwitnessaddress \"address\"\n"
"\nAdd a witness address for a script (with pubkey or redeemscript known).\n"
@@ -1082,7 +1096,7 @@ UniValue addwitnessaddress(const UniValue& params, bool fHelp)
}
}
- CBitcoinAddress address(params[0].get_str());
+ CBitcoinAddress address(request.params[0].get_str());
if (!address.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
@@ -1090,7 +1104,7 @@ UniValue addwitnessaddress(const UniValue& params, bool fHelp)
CTxDestination dest = address.Get();
bool ret = boost::apply_visitor(w, dest);
if (!ret) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Public key or redeemscript not known to wallet");
+ throw JSONRPCError(RPC_WALLET_ERROR, "Public key or redeemscript not known to wallet, or the key is uncompressed");
}
pwalletMain->SetAddressBook(w.result, "", "receive");
@@ -1135,14 +1149,14 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts)
{
const CWalletTx& wtx = (*it).second;
- if (wtx.IsCoinBase() || !CheckFinalTx(wtx))
+ if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx))
continue;
int nDepth = wtx.GetDepthInMainChain();
if (nDepth < nMinDepth)
continue;
- BOOST_FOREACH(const CTxOut& txout, wtx.vout)
+ BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout)
{
CTxDestination address;
if (!ExtractDestination(txout.scriptPubKey, address))
@@ -1232,12 +1246,12 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts)
return ret;
}
-UniValue listreceivedbyaddress(const UniValue& params, bool fHelp)
+UniValue listreceivedbyaddress(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() > 3)
+ if (request.fHelp || request.params.size() > 3)
throw runtime_error(
"listreceivedbyaddress ( minconf includeempty includeWatchonly)\n"
"\nList balances by receiving address.\n"
@@ -1267,15 +1281,15 @@ UniValue listreceivedbyaddress(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
- return ListReceived(params, false);
+ return ListReceived(request.params, false);
}
-UniValue listreceivedbyaccount(const UniValue& params, bool fHelp)
+UniValue listreceivedbyaccount(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() > 3)
+ if (request.fHelp || request.params.size() > 3)
throw runtime_error(
"listreceivedbyaccount ( minconf includeempty includeWatchonly)\n"
"\nDEPRECATED. List balances by account.\n"
@@ -1304,7 +1318,7 @@ UniValue listreceivedbyaccount(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
- return ListReceived(params, true);
+ return ListReceived(request.params, true);
}
static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
@@ -1406,12 +1420,12 @@ void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Un
}
}
-UniValue listtransactions(const UniValue& params, bool fHelp)
+UniValue listtransactions(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() > 4)
+ if (request.fHelp || request.params.size() > 4)
throw runtime_error(
"listtransactions ( \"account\" count from includeWatchonly)\n"
"\nReturns up to 'count' most recent transactions skipping the first 'from' transactions for account 'account'.\n"
@@ -1473,17 +1487,17 @@ UniValue listtransactions(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
string strAccount = "*";
- if (params.size() > 0)
- strAccount = params[0].get_str();
+ if (request.params.size() > 0)
+ strAccount = request.params[0].get_str();
int nCount = 10;
- if (params.size() > 1)
- nCount = params[1].get_int();
+ if (request.params.size() > 1)
+ nCount = request.params[1].get_int();
int nFrom = 0;
- if (params.size() > 2)
- nFrom = params[2].get_int();
+ if (request.params.size() > 2)
+ nFrom = request.params[2].get_int();
isminefilter filter = ISMINE_SPENDABLE;
- if(params.size() > 3)
- if(params[3].get_bool())
+ if(request.params.size() > 3)
+ if(request.params[3].get_bool())
filter = filter | ISMINE_WATCH_ONLY;
if (nCount < 0)
@@ -1533,12 +1547,12 @@ UniValue listtransactions(const UniValue& params, bool fHelp)
return ret;
}
-UniValue listaccounts(const UniValue& params, bool fHelp)
+UniValue listaccounts(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() > 2)
+ if (request.fHelp || request.params.size() > 2)
throw runtime_error(
"listaccounts ( minconf includeWatchonly)\n"
"\nDEPRECATED. Returns Object that has account names as keys, account balances as values.\n"
@@ -1564,11 +1578,11 @@ UniValue listaccounts(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
int nMinDepth = 1;
- if (params.size() > 0)
- nMinDepth = params[0].get_int();
+ if (request.params.size() > 0)
+ nMinDepth = request.params[0].get_int();
isminefilter includeWatchonly = ISMINE_SPENDABLE;
- if(params.size() > 1)
- if(params[1].get_bool())
+ if(request.params.size() > 1)
+ if(request.params[1].get_bool())
includeWatchonly = includeWatchonly | ISMINE_WATCH_ONLY;
map<string, CAmount> mapAccountBalances;
@@ -1612,12 +1626,12 @@ UniValue listaccounts(const UniValue& params, bool fHelp)
return ret;
}
-UniValue listsinceblock(const UniValue& params, bool fHelp)
+UniValue listsinceblock(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp)
+ if (request.fHelp)
throw runtime_error(
"listsinceblock ( \"blockhash\" target-confirmations includeWatchonly)\n"
"\nGet all transactions in blocks since block [blockhash], or all transactions if omitted\n"
@@ -1660,26 +1674,26 @@ UniValue listsinceblock(const UniValue& params, bool fHelp)
int target_confirms = 1;
isminefilter filter = ISMINE_SPENDABLE;
- if (params.size() > 0)
+ if (request.params.size() > 0)
{
uint256 blockId;
- blockId.SetHex(params[0].get_str());
+ blockId.SetHex(request.params[0].get_str());
BlockMap::iterator it = mapBlockIndex.find(blockId);
if (it != mapBlockIndex.end())
pindex = it->second;
}
- if (params.size() > 1)
+ if (request.params.size() > 1)
{
- target_confirms = params[1].get_int();
+ target_confirms = request.params[1].get_int();
if (target_confirms < 1)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
}
- if(params.size() > 2)
- if(params[2].get_bool())
+ if(request.params.size() > 2)
+ if(request.params[2].get_bool())
filter = filter | ISMINE_WATCH_ONLY;
int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1;
@@ -1704,12 +1718,12 @@ UniValue listsinceblock(const UniValue& params, bool fHelp)
return ret;
}
-UniValue gettransaction(const UniValue& params, bool fHelp)
+UniValue gettransaction(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() < 1 || params.size() > 2)
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw runtime_error(
"gettransaction \"txid\" ( includeWatchonly )\n"
"\nGet detailed information about in-wallet transaction <txid>\n"
@@ -1751,11 +1765,11 @@ UniValue gettransaction(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
uint256 hash;
- hash.SetHex(params[0].get_str());
+ hash.SetHex(request.params[0].get_str());
isminefilter filter = ISMINE_SPENDABLE;
- if(params.size() > 1)
- if(params[1].get_bool())
+ if(request.params.size() > 1)
+ if(request.params[1].get_bool())
filter = filter | ISMINE_WATCH_ONLY;
UniValue entry(UniValue::VOBJ);
@@ -1766,7 +1780,7 @@ UniValue gettransaction(const UniValue& params, bool fHelp)
CAmount nCredit = wtx.GetCredit(filter);
CAmount nDebit = wtx.GetDebit(filter);
CAmount nNet = nCredit - nDebit;
- CAmount nFee = (wtx.IsFromMe(filter) ? wtx.GetValueOut() - nDebit : 0);
+ CAmount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut() - nDebit : 0);
entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
if (wtx.IsFromMe(filter))
@@ -1778,18 +1792,18 @@ UniValue gettransaction(const UniValue& params, bool fHelp)
ListTransactions(wtx, "*", 0, false, details, filter);
entry.push_back(Pair("details", details));
- string strHex = EncodeHexTx(static_cast<CTransaction>(wtx));
+ string strHex = EncodeHexTx(static_cast<CTransaction>(wtx), RPCSerializationFlags());
entry.push_back(Pair("hex", strHex));
return entry;
}
-UniValue abandontransaction(const UniValue& params, bool fHelp)
+UniValue abandontransaction(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() != 1)
+ if (request.fHelp || request.params.size() != 1)
throw runtime_error(
"abandontransaction \"txid\"\n"
"\nMark in-wallet transaction <txid> as abandoned\n"
@@ -1808,7 +1822,7 @@ UniValue abandontransaction(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
uint256 hash;
- hash.SetHex(params[0].get_str());
+ hash.SetHex(request.params[0].get_str());
if (!pwalletMain->mapWallet.count(hash))
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id");
@@ -1819,12 +1833,12 @@ UniValue abandontransaction(const UniValue& params, bool fHelp)
}
-UniValue backupwallet(const UniValue& params, bool fHelp)
+UniValue backupwallet(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() != 1)
+ if (request.fHelp || request.params.size() != 1)
throw runtime_error(
"backupwallet \"destination\"\n"
"\nSafely copies current wallet file to destination, which can be a directory or a path with filename.\n"
@@ -1837,7 +1851,7 @@ UniValue backupwallet(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
- string strDest = params[0].get_str();
+ string strDest = request.params[0].get_str();
if (!pwalletMain->BackupWallet(strDest))
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
@@ -1845,12 +1859,12 @@ UniValue backupwallet(const UniValue& params, bool fHelp)
}
-UniValue keypoolrefill(const UniValue& params, bool fHelp)
+UniValue keypoolrefill(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() > 1)
+ if (request.fHelp || request.params.size() > 1)
throw runtime_error(
"keypoolrefill ( newsize )\n"
"\nFills the keypool."
@@ -1866,10 +1880,10 @@ UniValue keypoolrefill(const UniValue& params, bool fHelp)
// 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool
unsigned int kpSize = 0;
- if (params.size() > 0) {
- if (params[0].get_int() < 0)
+ if (request.params.size() > 0) {
+ if (request.params[0].get_int() < 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size.");
- kpSize = (unsigned int)params[0].get_int();
+ kpSize = (unsigned int)request.params[0].get_int();
}
EnsureWalletIsUnlocked();
@@ -1889,12 +1903,12 @@ static void LockWallet(CWallet* pWallet)
pWallet->Lock();
}
-UniValue walletpassphrase(const UniValue& params, bool fHelp)
+UniValue walletpassphrase(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
+ if (pwalletMain->IsCrypted() && (request.fHelp || request.params.size() != 2))
throw runtime_error(
"walletpassphrase \"passphrase\" timeout\n"
"\nStores the wallet decryption key in memory for 'timeout' seconds.\n"
@@ -1916,17 +1930,17 @@ UniValue walletpassphrase(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
- if (fHelp)
+ if (request.fHelp)
return true;
if (!pwalletMain->IsCrypted())
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
- // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
+ // Note that the walletpassphrase is stored in request.params[0] which is not mlock()ed
SecureString strWalletPass;
strWalletPass.reserve(100);
// TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
- // Alternately, find a way to make params[0] mlock()'d to begin with.
- strWalletPass = params[0].get_str().c_str();
+ // Alternately, find a way to make request.params[0] mlock()'d to begin with.
+ strWalletPass = request.params[0].get_str().c_str();
if (strWalletPass.length() > 0)
{
@@ -1940,7 +1954,7 @@ UniValue walletpassphrase(const UniValue& params, bool fHelp)
pwalletMain->TopUpKeyPool();
- int64_t nSleepTime = params[1].get_int64();
+ int64_t nSleepTime = request.params[1].get_int64();
LOCK(cs_nWalletUnlockTime);
nWalletUnlockTime = GetTime() + nSleepTime;
RPCRunLater("lockwallet", boost::bind(LockWallet, pwalletMain), nSleepTime);
@@ -1949,12 +1963,12 @@ UniValue walletpassphrase(const UniValue& params, bool fHelp)
}
-UniValue walletpassphrasechange(const UniValue& params, bool fHelp)
+UniValue walletpassphrasechange(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
+ if (pwalletMain->IsCrypted() && (request.fHelp || request.params.size() != 2))
throw runtime_error(
"walletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\n"
"\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n"
@@ -1968,20 +1982,20 @@ UniValue walletpassphrasechange(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
- if (fHelp)
+ if (request.fHelp)
return true;
if (!pwalletMain->IsCrypted())
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
// TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
- // Alternately, find a way to make params[0] mlock()'d to begin with.
+ // Alternately, find a way to make request.params[0] mlock()'d to begin with.
SecureString strOldWalletPass;
strOldWalletPass.reserve(100);
- strOldWalletPass = params[0].get_str().c_str();
+ strOldWalletPass = request.params[0].get_str().c_str();
SecureString strNewWalletPass;
strNewWalletPass.reserve(100);
- strNewWalletPass = params[1].get_str().c_str();
+ strNewWalletPass = request.params[1].get_str().c_str();
if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
throw runtime_error(
@@ -1995,12 +2009,12 @@ UniValue walletpassphrasechange(const UniValue& params, bool fHelp)
}
-UniValue walletlock(const UniValue& params, bool fHelp)
+UniValue walletlock(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
+ if (pwalletMain->IsCrypted() && (request.fHelp || request.params.size() != 0))
throw runtime_error(
"walletlock\n"
"\nRemoves the wallet encryption key from memory, locking the wallet.\n"
@@ -2019,7 +2033,7 @@ UniValue walletlock(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
- if (fHelp)
+ if (request.fHelp)
return true;
if (!pwalletMain->IsCrypted())
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
@@ -2034,12 +2048,12 @@ UniValue walletlock(const UniValue& params, bool fHelp)
}
-UniValue encryptwallet(const UniValue& params, bool fHelp)
+UniValue encryptwallet(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
+ if (!pwalletMain->IsCrypted() && (request.fHelp || request.params.size() != 1))
throw runtime_error(
"encryptwallet \"passphrase\"\n"
"\nEncrypts the wallet with 'passphrase'. This is for first time encryption.\n"
@@ -2065,16 +2079,16 @@ UniValue encryptwallet(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
- if (fHelp)
+ if (request.fHelp)
return true;
if (pwalletMain->IsCrypted())
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
// TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
- // Alternately, find a way to make params[0] mlock()'d to begin with.
+ // Alternately, find a way to make request.params[0] mlock()'d to begin with.
SecureString strWalletPass;
strWalletPass.reserve(100);
- strWalletPass = params[0].get_str().c_str();
+ strWalletPass = request.params[0].get_str().c_str();
if (strWalletPass.length() < 1)
throw runtime_error(
@@ -2091,12 +2105,12 @@ UniValue encryptwallet(const UniValue& params, bool fHelp)
return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed and a new HD seed was generated (if you are using HD). You need to make a new backup.";
}
-UniValue lockunspent(const UniValue& params, bool fHelp)
+UniValue lockunspent(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() < 1 || params.size() > 2)
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw runtime_error(
"lockunspent unlock ([{\"txid\":\"txid\",\"vout\":n},...])\n"
"\nUpdates list of temporarily unspendable outputs.\n"
@@ -2135,20 +2149,20 @@ UniValue lockunspent(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
- if (params.size() == 1)
- RPCTypeCheck(params, boost::assign::list_of(UniValue::VBOOL));
+ if (request.params.size() == 1)
+ RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VBOOL));
else
- RPCTypeCheck(params, boost::assign::list_of(UniValue::VBOOL)(UniValue::VARR));
+ RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VBOOL)(UniValue::VARR));
- bool fUnlock = params[0].get_bool();
+ bool fUnlock = request.params[0].get_bool();
- if (params.size() == 1) {
+ if (request.params.size() == 1) {
if (fUnlock)
pwalletMain->UnlockAllCoins();
return true;
}
- UniValue outputs = params[1].get_array();
+ UniValue outputs = request.params[1].get_array();
for (unsigned int idx = 0; idx < outputs.size(); idx++) {
const UniValue& output = outputs[idx];
if (!output.isObject())
@@ -2180,12 +2194,12 @@ UniValue lockunspent(const UniValue& params, bool fHelp)
return true;
}
-UniValue listlockunspent(const UniValue& params, bool fHelp)
+UniValue listlockunspent(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() > 0)
+ if (request.fHelp || request.params.size() > 0)
throw runtime_error(
"listlockunspent\n"
"\nReturns list of temporarily unspendable outputs.\n"
@@ -2229,12 +2243,12 @@ UniValue listlockunspent(const UniValue& params, bool fHelp)
return ret;
}
-UniValue settxfee(const UniValue& params, bool fHelp)
+UniValue settxfee(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() < 1 || params.size() > 1)
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 1)
throw runtime_error(
"settxfee amount\n"
"\nSet the transaction fee per kB. Overwrites the paytxfee parameter.\n"
@@ -2250,18 +2264,18 @@ UniValue settxfee(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
// Amount
- CAmount nAmount = AmountFromValue(params[0]);
+ CAmount nAmount = AmountFromValue(request.params[0]);
payTxFee = CFeeRate(nAmount, 1000);
return true;
}
-UniValue getwalletinfo(const UniValue& params, bool fHelp)
+UniValue getwalletinfo(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() != 0)
+ if (request.fHelp || request.params.size() != 0)
throw runtime_error(
"getwalletinfo\n"
"Returns an object containing various wallet state info.\n"
@@ -2272,7 +2286,7 @@ UniValue getwalletinfo(const UniValue& params, bool fHelp)
" \"unconfirmed_balance\": xxx, (numeric) the total unconfirmed balance of the wallet in " + CURRENCY_UNIT + "\n"
" \"immature_balance\": xxxxxx, (numeric) the total immature balance of the wallet in " + CURRENCY_UNIT + "\n"
" \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n"
- " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n"
+ " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since Unix epoch) of the oldest pre-generated key in the key pool\n"
" \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n"
" \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n"
" \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n"
@@ -2302,12 +2316,12 @@ UniValue getwalletinfo(const UniValue& params, bool fHelp)
return obj;
}
-UniValue resendwallettransactions(const UniValue& params, bool fHelp)
+UniValue resendwallettransactions(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() != 0)
+ if (request.fHelp || request.params.size() != 0)
throw runtime_error(
"resendwallettransactions\n"
"Immediately re-broadcast unconfirmed wallet transactions to all peers.\n"
@@ -2330,12 +2344,12 @@ UniValue resendwallettransactions(const UniValue& params, bool fHelp)
return result;
}
-UniValue listunspent(const UniValue& params, bool fHelp)
+UniValue listunspent(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() > 3)
+ if (request.fHelp || request.params.size() > 3)
throw runtime_error(
"listunspent ( minconf maxconf [\"address\",...] )\n"
"\nReturns array of unspent transaction outputs\n"
@@ -2372,19 +2386,19 @@ UniValue listunspent(const UniValue& params, bool fHelp)
+ HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"")
);
- RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)(UniValue::VNUM)(UniValue::VARR));
+ RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VNUM)(UniValue::VNUM)(UniValue::VARR));
int nMinDepth = 1;
- if (params.size() > 0)
- nMinDepth = params[0].get_int();
+ if (request.params.size() > 0)
+ nMinDepth = request.params[0].get_int();
int nMaxDepth = 9999999;
- if (params.size() > 1)
- nMaxDepth = params[1].get_int();
+ if (request.params.size() > 1)
+ nMaxDepth = request.params[1].get_int();
set<CBitcoinAddress> setAddress;
- if (params.size() > 2) {
- UniValue inputs = params[2].get_array();
+ if (request.params.size() > 2) {
+ UniValue inputs = request.params[2].get_array();
for (unsigned int idx = 0; idx < inputs.size(); idx++) {
const UniValue& input = inputs[idx];
CBitcoinAddress address(input.get_str());
@@ -2406,7 +2420,7 @@ UniValue listunspent(const UniValue& params, bool fHelp)
continue;
CTxDestination address;
- const CScript& scriptPubKey = out.tx->vout[out.i].scriptPubKey;
+ const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey;
bool fValidAddress = ExtractDestination(scriptPubKey, address);
if (setAddress.size() && (!fValidAddress || !setAddress.count(address)))
@@ -2431,7 +2445,7 @@ UniValue listunspent(const UniValue& params, bool fHelp)
}
entry.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end())));
- entry.push_back(Pair("amount", ValueFromAmount(out.tx->vout[out.i].nValue)));
+ entry.push_back(Pair("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue)));
entry.push_back(Pair("confirmations", out.nDepth));
entry.push_back(Pair("spendable", out.fSpendable));
entry.push_back(Pair("solvable", out.fSolvable));
@@ -2441,12 +2455,12 @@ UniValue listunspent(const UniValue& params, bool fHelp)
return results;
}
-UniValue fundrawtransaction(const UniValue& params, bool fHelp)
+UniValue fundrawtransaction(const JSONRPCRequest& request)
{
- if (!EnsureWalletIsAvailable(fHelp))
+ if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (fHelp || params.size() < 1 || params.size() > 2)
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw runtime_error(
"fundrawtransaction \"hexstring\" ( options )\n"
"\nAdd inputs to a transaction until it has enough in value to meet its out value.\n"
@@ -2487,7 +2501,7 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp)
+ HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"")
);
- RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR));
+ RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR));
CTxDestination changeAddress = CNoDestination();
int changePosition = -1;
@@ -2496,15 +2510,15 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp)
CFeeRate feeRate = CFeeRate(0);
bool overrideEstimatedFeerate = false;
- if (params.size() > 1) {
- if (params[1].type() == UniValue::VBOOL) {
+ if (request.params.size() > 1) {
+ if (request.params[1].type() == UniValue::VBOOL) {
// backward compatibility bool only fallback
- includeWatching = params[1].get_bool();
+ includeWatching = request.params[1].get_bool();
}
else {
- RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VOBJ));
+ RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VOBJ));
- UniValue options = params[1];
+ UniValue options = request.params[1];
RPCTypeCheckObj(options,
{
@@ -2543,17 +2557,16 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp)
}
// parse hex string from parameter
- CTransaction origTx;
- if (!DecodeHexTx(origTx, params[0].get_str(), true))
+ CMutableTransaction tx;
+ if (!DecodeHexTx(tx, request.params[0].get_str(), true))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
- if (origTx.vout.size() == 0)
+ if (tx.vout.size() == 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "TX must have at least one output");
- if (changePosition != -1 && (changePosition < 0 || (unsigned int)changePosition > origTx.vout.size()))
+ if (changePosition != -1 && (changePosition < 0 || (unsigned int)changePosition > tx.vout.size()))
throw JSONRPCError(RPC_INVALID_PARAMETER, "changePosition out of bounds");
- CMutableTransaction tx(origTx);
CAmount nFeeOut;
string strFailReason;
@@ -2568,14 +2581,15 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp)
return result;
}
-extern UniValue dumpprivkey(const UniValue& params, bool fHelp); // in rpcdump.cpp
-extern UniValue importprivkey(const UniValue& params, bool fHelp);
-extern UniValue importaddress(const UniValue& params, bool fHelp);
-extern UniValue importpubkey(const UniValue& params, bool fHelp);
-extern UniValue dumpwallet(const UniValue& params, bool fHelp);
-extern UniValue importwallet(const UniValue& params, bool fHelp);
-extern UniValue importprunedfunds(const UniValue& params, bool fHelp);
-extern UniValue removeprunedfunds(const UniValue& params, bool fHelp);
+extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
+extern UniValue importprivkey(const JSONRPCRequest& request);
+extern UniValue importaddress(const JSONRPCRequest& request);
+extern UniValue importpubkey(const JSONRPCRequest& request);
+extern UniValue dumpwallet(const JSONRPCRequest& request);
+extern UniValue importwallet(const JSONRPCRequest& request);
+extern UniValue importprunedfunds(const JSONRPCRequest& request);
+extern UniValue removeprunedfunds(const JSONRPCRequest& request);
+extern UniValue importmulti(const JSONRPCRequest& request);
static const CRPCCommand commands[] =
{ // category name actor (function) okSafeMode
@@ -2600,6 +2614,7 @@ static const CRPCCommand commands[] =
{ "wallet", "gettransaction", &gettransaction, false },
{ "wallet", "getunconfirmedbalance", &getunconfirmedbalance, false },
{ "wallet", "getwalletinfo", &getwalletinfo, false },
+ { "wallet", "importmulti", &importmulti, true },
{ "wallet", "importprivkey", &importprivkey, true },
{ "wallet", "importwallet", &importwallet, true },
{ "wallet", "importaddress", &importaddress, true },
@@ -2630,6 +2645,9 @@ static const CRPCCommand commands[] =
void RegisterWalletRPCCommands(CRPCTable &t)
{
+ if (GetBoolArg("-disablewallet", false))
+ return;
+
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
t.appendCommand(commands[vcidx].name, &commands[vcidx]);
}
diff --git a/src/wallet/test/accounting_tests.cpp b/src/wallet/test/accounting_tests.cpp
index a6cada46a2..eb14d176bd 100644
--- a/src/wallet/test/accounting_tests.cpp
+++ b/src/wallet/test/accounting_tests.cpp
@@ -3,7 +3,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "wallet/wallet.h"
-#include "wallet/walletdb.h"
#include "wallet/test/wallet_test_fixture.h"
@@ -17,13 +16,13 @@ extern CWallet* pwalletMain;
BOOST_FIXTURE_TEST_SUITE(accounting_tests, WalletTestingSetup)
static void
-GetResults(CWalletDB& walletdb, std::map<CAmount, CAccountingEntry>& results)
+GetResults(std::map<CAmount, CAccountingEntry>& results)
{
std::list<CAccountingEntry> aes;
results.clear();
- BOOST_CHECK(walletdb.ReorderTransactions(pwalletMain) == DB_LOAD_OK);
- walletdb.ListAccountCreditDebit("", aes);
+ BOOST_CHECK(pwalletMain->ReorderTransactions() == DB_LOAD_OK);
+ pwalletMain->ListAccountCreditDebit("", aes);
BOOST_FOREACH(CAccountingEntry& ae, aes)
{
results[ae.nOrderPos] = ae;
@@ -32,7 +31,6 @@ GetResults(CWalletDB& walletdb, std::map<CAmount, CAccountingEntry>& results)
BOOST_AUTO_TEST_CASE(acc_orderupgrade)
{
- CWalletDB walletdb(pwalletMain->strWalletFile);
std::vector<CWalletTx*> vpwtx;
CWalletTx wtx;
CAccountingEntry ae;
@@ -45,7 +43,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
ae.nTime = 1333333333;
ae.strOtherAccount = "b";
ae.strComment = "";
- pwalletMain->AddAccountingEntry(ae, walletdb);
+ pwalletMain->AddAccountingEntry(ae);
wtx.mapValue["comment"] = "z";
pwalletMain->AddToWallet(wtx);
@@ -55,9 +53,9 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
ae.nTime = 1333333336;
ae.strOtherAccount = "c";
- pwalletMain->AddAccountingEntry(ae, walletdb);
+ pwalletMain->AddAccountingEntry(ae);
- GetResults(walletdb, results);
+ GetResults(results);
BOOST_CHECK(pwalletMain->nOrderPosNext == 3);
BOOST_CHECK(2 == results.size());
@@ -71,9 +69,9 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
ae.nTime = 1333333330;
ae.strOtherAccount = "d";
ae.nOrderPos = pwalletMain->IncOrderPosNext();
- pwalletMain->AddAccountingEntry(ae, walletdb);
+ pwalletMain->AddAccountingEntry(ae);
- GetResults(walletdb, results);
+ GetResults(results);
BOOST_CHECK(results.size() == 3);
BOOST_CHECK(pwalletMain->nOrderPosNext == 4);
@@ -88,7 +86,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
{
CMutableTransaction tx(wtx);
--tx.nLockTime; // Just to change the hash :)
- *static_cast<CTransaction*>(&wtx) = CTransaction(tx);
+ wtx.SetTx(MakeTransactionRef(std::move(tx)));
}
pwalletMain->AddToWallet(wtx);
vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]);
@@ -98,14 +96,14 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
{
CMutableTransaction tx(wtx);
--tx.nLockTime; // Just to change the hash :)
- *static_cast<CTransaction*>(&wtx) = CTransaction(tx);
+ wtx.SetTx(MakeTransactionRef(std::move(tx)));
}
pwalletMain->AddToWallet(wtx);
vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]);
vpwtx[2]->nTimeReceived = (unsigned int)1333333329;
vpwtx[2]->nOrderPos = -1;
- GetResults(walletdb, results);
+ GetResults(results);
BOOST_CHECK(results.size() == 3);
BOOST_CHECK(pwalletMain->nOrderPosNext == 6);
@@ -121,9 +119,9 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
ae.nTime = 1333333334;
ae.strOtherAccount = "e";
ae.nOrderPos = -1;
- pwalletMain->AddAccountingEntry(ae, walletdb);
+ pwalletMain->AddAccountingEntry(ae);
- GetResults(walletdb, results);
+ GetResults(results);
BOOST_CHECK(results.size() == 4);
BOOST_CHECK(pwalletMain->nOrderPosNext == 7);
diff --git a/src/wallet/test/crypto_tests.cpp b/src/wallet/test/crypto_tests.cpp
index 05387f5f2b..c64c76244e 100644
--- a/src/wallet/test/crypto_tests.cpp
+++ b/src/wallet/test/crypto_tests.cpp
@@ -2,7 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "random.h"
+#include "test/test_random.h"
#include "utilstrencodings.h"
#include "test/test_bitcoin.h"
#include "wallet/crypter.h"
@@ -42,15 +42,19 @@ bool OldEncrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char>
int nCLen = nLen + AES_BLOCK_SIZE, nFLen = 0;
vchCiphertext = std::vector<unsigned char> (nCLen);
- EVP_CIPHER_CTX ctx;
+ EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
+
+ if (!ctx) return false;
bool fOk = true;
- EVP_CIPHER_CTX_init(&ctx);
- if (fOk) fOk = EVP_EncryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV) != 0;
- if (fOk) fOk = EVP_EncryptUpdate(&ctx, &vchCiphertext[0], &nCLen, &vchPlaintext[0], nLen) != 0;
- if (fOk) fOk = EVP_EncryptFinal_ex(&ctx, (&vchCiphertext[0]) + nCLen, &nFLen) != 0;
- EVP_CIPHER_CTX_cleanup(&ctx);
+ EVP_CIPHER_CTX_init(ctx);
+ if (fOk) fOk = EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, chKey, chIV) != 0;
+ if (fOk) fOk = EVP_EncryptUpdate(ctx, &vchCiphertext[0], &nCLen, &vchPlaintext[0], nLen) != 0;
+ if (fOk) fOk = EVP_EncryptFinal_ex(ctx, (&vchCiphertext[0]) + nCLen, &nFLen) != 0;
+ EVP_CIPHER_CTX_cleanup(ctx);
+
+ EVP_CIPHER_CTX_free(ctx);
if (!fOk) return false;
@@ -66,15 +70,19 @@ bool OldDecrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingMaterial
vchPlaintext = CKeyingMaterial(nPLen);
- EVP_CIPHER_CTX ctx;
+ EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
+
+ if (!ctx) return false;
bool fOk = true;
- EVP_CIPHER_CTX_init(&ctx);
- if (fOk) fOk = EVP_DecryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV) != 0;
- if (fOk) fOk = EVP_DecryptUpdate(&ctx, &vchPlaintext[0], &nPLen, &vchCiphertext[0], nLen) != 0;
- if (fOk) fOk = EVP_DecryptFinal_ex(&ctx, (&vchPlaintext[0]) + nPLen, &nFLen) != 0;
- EVP_CIPHER_CTX_cleanup(&ctx);
+ EVP_CIPHER_CTX_init(ctx);
+ if (fOk) fOk = EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, chKey, chIV) != 0;
+ if (fOk) fOk = EVP_DecryptUpdate(ctx, &vchPlaintext[0], &nPLen, &vchCiphertext[0], nLen) != 0;
+ if (fOk) fOk = EVP_DecryptFinal_ex(ctx, (&vchPlaintext[0]) + nPLen, &nFLen) != 0;
+ EVP_CIPHER_CTX_cleanup(ctx);
+
+ EVP_CIPHER_CTX_free(ctx);
if (!fOk) return false;
@@ -97,10 +105,10 @@ static void TestPassphraseSingle(const std::vector<unsigned char>& vchSalt, cons
OldSetKeyFromPassphrase(passphrase, vchSalt, rounds, 0, chKey, chIV);
- BOOST_CHECK_MESSAGE(memcmp(chKey, crypt.chKey, sizeof(chKey)) == 0, \
- HexStr(chKey, chKey+sizeof(chKey)) + std::string(" != ") + HexStr(crypt.chKey, crypt.chKey + (sizeof crypt.chKey)));
- BOOST_CHECK_MESSAGE(memcmp(chIV, crypt.chIV, sizeof(chIV)) == 0, \
- HexStr(chIV, chIV+sizeof(chIV)) + std::string(" != ") + HexStr(crypt.chIV, crypt.chIV + (sizeof crypt.chIV)));
+ BOOST_CHECK_MESSAGE(memcmp(chKey, crypt.vchKey.data(), crypt.vchKey.size()) == 0, \
+ HexStr(chKey, chKey+sizeof(chKey)) + std::string(" != ") + HexStr(crypt.vchKey));
+ BOOST_CHECK_MESSAGE(memcmp(chIV, crypt.vchIV.data(), crypt.vchIV.size()) == 0, \
+ HexStr(chIV, chIV+sizeof(chIV)) + std::string(" != ") + HexStr(crypt.vchIV));
if(!correctKey.empty())
BOOST_CHECK_MESSAGE(memcmp(chKey, &correctKey[0], sizeof(chKey)) == 0, \
@@ -127,7 +135,7 @@ static void TestDecrypt(const CCrypter& crypt, const std::vector<unsigned char>&
CKeyingMaterial vchDecrypted2;
int result1, result2;
result1 = crypt.Decrypt(vchCiphertext, vchDecrypted1);
- result2 = OldDecrypt(vchCiphertext, vchDecrypted2, crypt.chKey, crypt.chIV);
+ result2 = OldDecrypt(vchCiphertext, vchDecrypted2, crypt.vchKey.data(), crypt.vchIV.data());
BOOST_CHECK(result1 == result2);
// These two should be equal. However, OpenSSL 1.0.1j introduced a change
@@ -152,7 +160,7 @@ static void TestEncryptSingle(const CCrypter& crypt, const CKeyingMaterial& vchP
std::vector<unsigned char> vchCiphertext2;
int result1 = crypt.Encrypt(vchPlaintext, vchCiphertext1);
- int result2 = OldEncrypt(vchPlaintext, vchCiphertext2, crypt.chKey, crypt.chIV);
+ int result2 = OldEncrypt(vchPlaintext, vchCiphertext2, crypt.vchKey.data(), crypt.vchIV.data());
BOOST_CHECK(result1 == result2);
BOOST_CHECK(vchCiphertext1 == vchCiphertext2);
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index acf980c784..9465d88067 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -42,7 +42,7 @@ static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = fa
// so stop vin being empty, and cache a non-zero Debit to fake out IsFromMe()
tx.vin.resize(1);
}
- CWalletTx* wtx = new CWalletTx(&wallet, tx);
+ CWalletTx* wtx = new CWalletTx(&wallet, MakeTransactionRef(std::move(tx)));
if (fIsFromMe)
{
wtx->fDebitCached = true;
@@ -78,24 +78,24 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
empty_wallet();
// with an empty wallet we can't even pay one cent
- BOOST_CHECK(!wallet.SelectCoinsMinConf( 1 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK(!wallet.SelectCoinsMinConf( 1 * CENT, 1, 6, 0, vCoins, setCoinsRet, nValueRet));
add_coin(1*CENT, 4); // add a new 1 cent coin
// with a new 1 cent coin, we still can't find a mature 1 cent
- BOOST_CHECK(!wallet.SelectCoinsMinConf( 1 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK(!wallet.SelectCoinsMinConf( 1 * CENT, 1, 6, 0, vCoins, setCoinsRet, nValueRet));
// but we can find a new 1 cent
- BOOST_CHECK( wallet.SelectCoinsMinConf( 1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf( 1 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1 * CENT);
add_coin(2*CENT); // add a mature 2 cent coin
// we can't make 3 cents of mature coins
- BOOST_CHECK(!wallet.SelectCoinsMinConf( 3 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK(!wallet.SelectCoinsMinConf( 3 * CENT, 1, 6, 0, vCoins, setCoinsRet, nValueRet));
// we can make 3 cents of new coins
- BOOST_CHECK( wallet.SelectCoinsMinConf( 3 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf( 3 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 3 * CENT);
add_coin(5*CENT); // add a mature 5 cent coin,
@@ -105,33 +105,33 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
// now we have new: 1+10=11 (of which 10 was self-sent), and mature: 2+5+20=27. total = 38
// we can't make 38 cents only if we disallow new coins:
- BOOST_CHECK(!wallet.SelectCoinsMinConf(38 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK(!wallet.SelectCoinsMinConf(38 * CENT, 1, 6, 0, vCoins, setCoinsRet, nValueRet));
// we can't even make 37 cents if we don't allow new coins even if they're from us
- BOOST_CHECK(!wallet.SelectCoinsMinConf(38 * CENT, 6, 6, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK(!wallet.SelectCoinsMinConf(38 * CENT, 6, 6, 0, vCoins, setCoinsRet, nValueRet));
// but we can make 37 cents if we accept new coins from ourself
- BOOST_CHECK( wallet.SelectCoinsMinConf(37 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(37 * CENT, 1, 6, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 37 * CENT);
// and we can make 38 cents if we accept all new coins
- BOOST_CHECK( wallet.SelectCoinsMinConf(38 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(38 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 38 * CENT);
// try making 34 cents from 1,2,5,10,20 - we can't do it exactly
- BOOST_CHECK( wallet.SelectCoinsMinConf(34 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(34 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 35 * CENT); // but 35 cents is closest
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); // the best should be 20+10+5. it's incredibly unlikely the 1 or 2 got included (but possible)
// when we try making 7 cents, the smaller coins (1,2,5) are enough. We should see just 2+5
- BOOST_CHECK( wallet.SelectCoinsMinConf( 7 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf( 7 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 7 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
// when we try making 8 cents, the smaller coins (1,2,5) are exactly enough.
- BOOST_CHECK( wallet.SelectCoinsMinConf( 8 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf( 8 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK(nValueRet == 8 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
// when we try making 9 cents, no subset of smaller coins is enough, and we get the next bigger coin (10)
- BOOST_CHECK( wallet.SelectCoinsMinConf( 9 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf( 9 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 10 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
@@ -145,30 +145,30 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
add_coin(30*CENT); // now we have 6+7+8+20+30 = 71 cents total
// check that we have 71 and not 72
- BOOST_CHECK( wallet.SelectCoinsMinConf(71 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
- BOOST_CHECK(!wallet.SelectCoinsMinConf(72 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(71 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK(!wallet.SelectCoinsMinConf(72 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
// now try making 16 cents. the best smaller coins can do is 6+7+8 = 21; not as good at the next biggest coin, 20
- BOOST_CHECK( wallet.SelectCoinsMinConf(16 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(16 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 20 * CENT); // we should get 20 in one coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
add_coin( 5*CENT); // now we have 5+6+7+8+20+30 = 75 cents total
// now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, better than the next biggest coin, 20
- BOOST_CHECK( wallet.SelectCoinsMinConf(16 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(16 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 3 coins
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
add_coin( 18*CENT); // now we have 5+6+7+8+18+20+30
// and now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, the same as the next biggest coin, 18
- BOOST_CHECK( wallet.SelectCoinsMinConf(16 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(16 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 1 coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); // because in the event of a tie, the biggest coin wins
// now try making 11 cents. we should get 5+6
- BOOST_CHECK( wallet.SelectCoinsMinConf(11 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(11 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 11 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
@@ -177,11 +177,11 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
add_coin( 2*COIN);
add_coin( 3*COIN);
add_coin( 4*COIN); // now we have 5+6+7+8+18+20+30+100+200+300+400 = 1094 cents
- BOOST_CHECK( wallet.SelectCoinsMinConf(95 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(95 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1 * COIN); // we should get 1 BTC in 1 coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
- BOOST_CHECK( wallet.SelectCoinsMinConf(195 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(195 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 2 * COIN); // we should get 2 BTC in 1 coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
@@ -196,14 +196,14 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
// try making 1 * MIN_CHANGE from the 1.5 * MIN_CHANGE
// we'll get change smaller than MIN_CHANGE whatever happens, so can expect MIN_CHANGE exactly
- BOOST_CHECK( wallet.SelectCoinsMinConf(MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(MIN_CHANGE, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE);
// but if we add a bigger coin, small change is avoided
add_coin(1111*MIN_CHANGE);
// try making 1 from 0.1 + 0.2 + 0.3 + 0.4 + 0.5 + 1111 = 1112.5
- BOOST_CHECK( wallet.SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount
// if we add more small coins:
@@ -211,7 +211,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
add_coin(MIN_CHANGE * 7 / 10);
// and try again to make 1.0 * MIN_CHANGE
- BOOST_CHECK( wallet.SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount
// run the 'mtgox' test (see http://blockexplorer.com/tx/29a3efd3ef04f9153d47a990bd7b048a4b2d213daaa5fb8ed670fb85f13bdbcf)
@@ -220,7 +220,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
for (int j = 0; j < 20; j++)
add_coin(50000 * COIN);
- BOOST_CHECK( wallet.SelectCoinsMinConf(500000 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(500000 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 500000 * COIN); // we should get the exact amount
BOOST_CHECK_EQUAL(setCoinsRet.size(), 10U); // in ten coins
@@ -233,7 +233,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
add_coin(MIN_CHANGE * 6 / 10);
add_coin(MIN_CHANGE * 7 / 10);
add_coin(1111 * MIN_CHANGE);
- BOOST_CHECK( wallet.SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1111 * MIN_CHANGE); // we get the bigger coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
@@ -243,7 +243,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
add_coin(MIN_CHANGE * 6 / 10);
add_coin(MIN_CHANGE * 8 / 10);
add_coin(1111 * MIN_CHANGE);
- BOOST_CHECK( wallet.SelectCoinsMinConf(MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(MIN_CHANGE, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE); // we should get the exact amount
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); // in two coins 0.4+0.6
@@ -254,22 +254,22 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
add_coin(MIN_CHANGE * 100);
// trying to make 100.01 from these three coins
- BOOST_CHECK(wallet.SelectCoinsMinConf(MIN_CHANGE * 10001 / 100, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK(wallet.SelectCoinsMinConf(MIN_CHANGE * 10001 / 100, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE * 10105 / 100); // we should get all coins
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
// but if we try to make 99.9, we should take the bigger of the two small coins to avoid small change
- BOOST_CHECK(wallet.SelectCoinsMinConf(MIN_CHANGE * 9990 / 100, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK(wallet.SelectCoinsMinConf(MIN_CHANGE * 9990 / 100, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 101 * MIN_CHANGE);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
// test with many inputs
for (CAmount amt=1500; amt < COIN; amt*=10) {
empty_wallet();
- // Create 676 inputs (= MAX_STANDARD_TX_SIZE / 148 bytes per input)
+ // Create 676 inputs (= (old MAX_STANDARD_TX_SIZE == 100000) / 148 bytes per input)
for (uint16_t j = 0; j < 676; j++)
add_coin(amt);
- BOOST_CHECK(wallet.SelectCoinsMinConf(2000, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK(wallet.SelectCoinsMinConf(2000, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
if (amt - 2000 < MIN_CHANGE) {
// needs more than one input:
uint16_t returnSize = std::ceil((2000.0 + MIN_CHANGE)/amt);
@@ -291,8 +291,8 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
// picking 50 from 100 coins doesn't depend on the shuffle,
// but does depend on randomness in the stochastic approximation code
- BOOST_CHECK(wallet.SelectCoinsMinConf(50 * COIN, 1, 6, vCoins, setCoinsRet , nValueRet));
- BOOST_CHECK(wallet.SelectCoinsMinConf(50 * COIN, 1, 6, vCoins, setCoinsRet2, nValueRet));
+ BOOST_CHECK(wallet.SelectCoinsMinConf(50 * COIN, 1, 6, 0, vCoins, setCoinsRet , nValueRet));
+ BOOST_CHECK(wallet.SelectCoinsMinConf(50 * COIN, 1, 6, 0, vCoins, setCoinsRet2, nValueRet));
BOOST_CHECK(!equal_sets(setCoinsRet, setCoinsRet2));
int fails = 0;
@@ -300,8 +300,8 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
{
// selecting 1 from 100 identical coins depends on the shuffle; this test will fail 1% of the time
// run the test RANDOM_REPEATS times and only complain if all of them fail
- BOOST_CHECK(wallet.SelectCoinsMinConf(COIN, 1, 6, vCoins, setCoinsRet , nValueRet));
- BOOST_CHECK(wallet.SelectCoinsMinConf(COIN, 1, 6, vCoins, setCoinsRet2, nValueRet));
+ BOOST_CHECK(wallet.SelectCoinsMinConf(COIN, 1, 6, 0, vCoins, setCoinsRet , nValueRet));
+ BOOST_CHECK(wallet.SelectCoinsMinConf(COIN, 1, 6, 0, vCoins, setCoinsRet2, nValueRet));
if (equal_sets(setCoinsRet, setCoinsRet2))
fails++;
}
@@ -321,8 +321,8 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
{
// selecting 1 from 100 identical coins depends on the shuffle; this test will fail 1% of the time
// run the test RANDOM_REPEATS times and only complain if all of them fail
- BOOST_CHECK(wallet.SelectCoinsMinConf(90*CENT, 1, 6, vCoins, setCoinsRet , nValueRet));
- BOOST_CHECK(wallet.SelectCoinsMinConf(90*CENT, 1, 6, vCoins, setCoinsRet2, nValueRet));
+ BOOST_CHECK(wallet.SelectCoinsMinConf(90*CENT, 1, 6, 0, vCoins, setCoinsRet , nValueRet));
+ BOOST_CHECK(wallet.SelectCoinsMinConf(90*CENT, 1, 6, 0, vCoins, setCoinsRet2, nValueRet));
if (equal_sets(setCoinsRet, setCoinsRet2))
fails++;
}
@@ -346,7 +346,7 @@ BOOST_AUTO_TEST_CASE(ApproximateBestSubset)
add_coin(1000 * COIN);
add_coin(3 * COIN);
- BOOST_CHECK(wallet.SelectCoinsMinConf(1003 * COIN, 1, 6, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK(wallet.SelectCoinsMinConf(1003 * COIN, 1, 6, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1003 * COIN);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
}
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 4c10ea0edb..c4051d2ac6 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -8,12 +8,12 @@
#include "base58.h"
#include "checkpoints.h"
#include "chain.h"
-#include "coincontrol.h"
+#include "wallet/coincontrol.h"
#include "consensus/consensus.h"
#include "consensus/validation.h"
#include "key.h"
#include "keystore.h"
-#include "main.h"
+#include "validation.h"
#include "net.h"
#include "policy/policy.h"
#include "primitives/block.h"
@@ -75,7 +75,7 @@ struct CompareValueOnly
std::string COutput::ToString() const
{
- return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->vout[i].nValue));
+ return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue));
}
const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
@@ -100,43 +100,7 @@ CPubKey CWallet::GenerateNewKey()
// use HD key derivation if HD was enabled during wallet creation
if (IsHDEnabled()) {
- // for now we use a fixed keypath scheme of m/0'/0'/k
- CKey key; //master key seed (256bit)
- CExtKey masterKey; //hd master key
- CExtKey accountKey; //key at m/0'
- CExtKey externalChainChildKey; //key at m/0'/0'
- CExtKey childKey; //key at m/0'/0'/<n>'
-
- // try to get the master key
- if (!GetKey(hdChain.masterKeyID, key))
- throw std::runtime_error(std::string(__func__) + ": Master key not found");
-
- masterKey.SetMaster(key.begin(), key.size());
-
- // derive m/0'
- // use hardened derivation (child keys >= 0x80000000 are hardened after bip32)
- masterKey.Derive(accountKey, BIP32_HARDENED_KEY_LIMIT);
-
- // derive m/0'/0'
- accountKey.Derive(externalChainChildKey, BIP32_HARDENED_KEY_LIMIT);
-
- // derive child key at next index, skip keys already known to the wallet
- do
- {
- // always derive hardened keys
- // childIndex | BIP32_HARDENED_KEY_LIMIT = derive childIndex in hardened child-index-range
- // example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649
- externalChainChildKey.Derive(childKey, hdChain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
- metadata.hdKeypath = "m/0'/0'/"+std::to_string(hdChain.nExternalChainCounter)+"'";
- metadata.hdMasterKeyID = hdChain.masterKeyID;
- // increment childkey index
- hdChain.nExternalChainCounter++;
- } while(HaveKey(childKey.key.GetPubKey().GetID()));
- secret = childKey.key;
-
- // update the chain model in the database
- if (!CWalletDB(strWalletFile).WriteHDChain(hdChain))
- throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed");
+ DeriveNewChildKey(metadata, secret);
} else {
secret.MakeNewKey(fCompressed);
}
@@ -157,6 +121,46 @@ CPubKey CWallet::GenerateNewKey()
return pubkey;
}
+void CWallet::DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret)
+{
+ // for now we use a fixed keypath scheme of m/0'/0'/k
+ CKey key; //master key seed (256bit)
+ CExtKey masterKey; //hd master key
+ CExtKey accountKey; //key at m/0'
+ CExtKey externalChainChildKey; //key at m/0'/0'
+ CExtKey childKey; //key at m/0'/0'/<n>'
+
+ // try to get the master key
+ if (!GetKey(hdChain.masterKeyID, key))
+ throw std::runtime_error(std::string(__func__) + ": Master key not found");
+
+ masterKey.SetMaster(key.begin(), key.size());
+
+ // derive m/0'
+ // use hardened derivation (child keys >= 0x80000000 are hardened after bip32)
+ masterKey.Derive(accountKey, BIP32_HARDENED_KEY_LIMIT);
+
+ // derive m/0'/0'
+ accountKey.Derive(externalChainChildKey, BIP32_HARDENED_KEY_LIMIT);
+
+ // derive child key at next index, skip keys already known to the wallet
+ do {
+ // always derive hardened keys
+ // childIndex | BIP32_HARDENED_KEY_LIMIT = derive childIndex in hardened child-index-range
+ // example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649
+ externalChainChildKey.Derive(childKey, hdChain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
+ metadata.hdKeypath = "m/0'/0'/" + std::to_string(hdChain.nExternalChainCounter) + "'";
+ metadata.hdMasterKeyID = hdChain.masterKeyID;
+ // increment childkey index
+ hdChain.nExternalChainCounter++;
+ } while (HaveKey(childKey.key.GetPubKey().GetID()));
+ secret = childKey.key;
+
+ // update the chain model in the database
+ if (!CWalletDB(strWalletFile).WriteHDChain(hdChain))
+ throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed");
+}
+
bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey)
{
AssertLockHeld(cs_wallet); // mapKeyMetadata
@@ -396,13 +400,13 @@ set<uint256> CWallet::GetConflicts(const uint256& txid) const
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range;
- BOOST_FOREACH(const CTxIn& txin, wtx.vin)
+ BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin)
{
if (mapTxSpends.count(txin.prevout) <= 1)
continue; // No conflict if zero or one spends
range = mapTxSpends.equal_range(txin.prevout);
- for (TxSpends::const_iterator it = range.first; it != range.second; ++it)
- result.insert(it->second);
+ for (TxSpends::const_iterator _it = range.first; _it != range.second; ++_it)
+ result.insert(_it->second);
}
return result;
}
@@ -414,6 +418,9 @@ void CWallet::Flush(bool shutdown)
bool CWallet::Verify()
{
+ if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET))
+ return true;
+
LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0));
std::string walletFile = GetArg("-wallet", DEFAULT_WALLET_DAT);
@@ -545,7 +552,7 @@ void CWallet::AddToSpends(const uint256& wtxid)
if (thisTx.IsCoinBase()) // Coinbases don't spend anything!
return;
- BOOST_FOREACH(const CTxIn& txin, thisTx.vin)
+ BOOST_FOREACH(const CTxIn& txin, thisTx.tx->vin)
AddToSpends(txin.prevout, wtxid);
}
@@ -649,6 +656,83 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
return true;
}
+DBErrors CWallet::ReorderTransactions()
+{
+ LOCK(cs_wallet);
+ CWalletDB walletdb(strWalletFile);
+
+ // Old wallets didn't have any defined order for transactions
+ // Probably a bad idea to change the output of this
+
+ // First: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap.
+ typedef pair<CWalletTx*, CAccountingEntry*> TxPair;
+ typedef multimap<int64_t, TxPair > TxItems;
+ TxItems txByTime;
+
+ for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ {
+ CWalletTx* wtx = &((*it).second);
+ txByTime.insert(make_pair(wtx->nTimeReceived, TxPair(wtx, (CAccountingEntry*)0)));
+ }
+ list<CAccountingEntry> acentries;
+ walletdb.ListAccountCreditDebit("", acentries);
+ BOOST_FOREACH(CAccountingEntry& entry, acentries)
+ {
+ txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry)));
+ }
+
+ nOrderPosNext = 0;
+ std::vector<int64_t> nOrderPosOffsets;
+ for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it)
+ {
+ CWalletTx *const pwtx = (*it).second.first;
+ CAccountingEntry *const pacentry = (*it).second.second;
+ int64_t& nOrderPos = (pwtx != 0) ? pwtx->nOrderPos : pacentry->nOrderPos;
+
+ if (nOrderPos == -1)
+ {
+ nOrderPos = nOrderPosNext++;
+ nOrderPosOffsets.push_back(nOrderPos);
+
+ if (pwtx)
+ {
+ if (!walletdb.WriteTx(*pwtx))
+ return DB_LOAD_FAIL;
+ }
+ else
+ if (!walletdb.WriteAccountingEntry(pacentry->nEntryNo, *pacentry))
+ return DB_LOAD_FAIL;
+ }
+ else
+ {
+ int64_t nOrderPosOff = 0;
+ BOOST_FOREACH(const int64_t& nOffsetStart, nOrderPosOffsets)
+ {
+ if (nOrderPos >= nOffsetStart)
+ ++nOrderPosOff;
+ }
+ nOrderPos += nOrderPosOff;
+ nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1);
+
+ if (!nOrderPosOff)
+ continue;
+
+ // Since we're changing the order, write it back
+ if (pwtx)
+ {
+ if (!walletdb.WriteTx(*pwtx))
+ return DB_LOAD_FAIL;
+ }
+ else
+ if (!walletdb.WriteAccountingEntry(pacentry->nEntryNo, *pacentry))
+ return DB_LOAD_FAIL;
+ }
+ }
+ walletdb.WriteOrderPosNext(nOrderPosNext);
+
+ return DB_LOAD_OK;
+}
+
int64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb)
{
AssertLockHeld(cs_wallet); // nOrderPosNext
@@ -677,7 +761,7 @@ bool CWallet::AccountMove(std::string strFrom, std::string strTo, CAmount nAmoun
debit.nTime = nNow;
debit.strOtherAccount = strTo;
debit.strComment = strComment;
- AddAccountingEntry(debit, walletdb);
+ AddAccountingEntry(debit, &walletdb);
// Credit
CAccountingEntry credit;
@@ -687,7 +771,7 @@ bool CWallet::AccountMove(std::string strFrom, std::string strTo, CAmount nAmoun
credit.nTime = nNow;
credit.strOtherAccount = strFrom;
credit.strComment = strComment;
- AddAccountingEntry(credit, walletdb);
+ AddAccountingEntry(credit, &walletdb);
if (!walletdb.TxnCommit())
return false;
@@ -711,7 +795,7 @@ bool CWallet::GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bFo
for (map<uint256, CWalletTx>::iterator it = mapWallet.begin();
it != mapWallet.end() && account.vchPubKey.IsValid();
++it)
- BOOST_FOREACH(const CTxOut& txout, (*it).second.vout)
+ BOOST_FOREACH(const CTxOut& txout, (*it).second.tx->vout)
if (txout.scriptPubKey == scriptPubKey) {
bForceNew = true;
break;
@@ -870,7 +954,7 @@ bool CWallet::LoadToWallet(const CWalletTx& wtxIn)
wtx.BindWallet(this);
wtxOrdered.insert(make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0)));
AddToSpends(hash);
- BOOST_FOREACH(const CTxIn& txin, wtx.vin) {
+ BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) {
if (mapWallet.count(txin.prevout.hash)) {
CWalletTx& prevtx = mapWallet[txin.prevout.hash];
if (prevtx.nIndex == -1 && !prevtx.hashUnset()) {
@@ -909,7 +993,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlockIndex
if (fExisted && !fUpdate) return false;
if (fExisted || IsMine(tx) || IsFromMe(tx))
{
- CWalletTx wtx(this,tx);
+ CWalletTx wtx(this, MakeTransactionRef(tx));
// Get merkle branch if transaction was found in a block
if (posInBlock != -1)
@@ -925,8 +1009,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
{
LOCK2(cs_main, cs_wallet);
- // Do not flush the wallet here for performance reasons
- CWalletDB walletdb(strWalletFile, "r+", false);
+ CWalletDB walletdb(strWalletFile, "r+");
std::set<uint256> todo;
std::set<uint256> done;
@@ -968,7 +1051,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
}
// If a transaction changes 'conflicted' state, that changes the balance
// available of the outputs it spends. So force those to be recomputed
- BOOST_FOREACH(const CTxIn& txin, wtx.vin)
+ BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin)
{
if (mapWallet.count(txin.prevout.hash))
mapWallet[txin.prevout.hash].MarkDirty();
@@ -1029,7 +1112,7 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
}
// If a transaction changes 'conflicted' state, that changes the balance
// available of the outputs it spends. So force those to be recomputed
- BOOST_FOREACH(const CTxIn& txin, wtx.vin)
+ BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin)
{
if (mapWallet.count(txin.prevout.hash))
mapWallet[txin.prevout.hash].MarkDirty();
@@ -1064,8 +1147,8 @@ isminetype CWallet::IsMine(const CTxIn &txin) const
if (mi != mapWallet.end())
{
const CWalletTx& prev = (*mi).second;
- if (txin.prevout.n < prev.vout.size())
- return IsMine(prev.vout[txin.prevout.n]);
+ if (txin.prevout.n < prev.tx->vout.size())
+ return IsMine(prev.tx->vout[txin.prevout.n]);
}
}
return ISMINE_NO;
@@ -1079,9 +1162,9 @@ CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const
if (mi != mapWallet.end())
{
const CWalletTx& prev = (*mi).second;
- if (txin.prevout.n < prev.vout.size())
- if (IsMine(prev.vout[txin.prevout.n]) & filter)
- return prev.vout[txin.prevout.n].nValue;
+ if (txin.prevout.n < prev.tx->vout.size())
+ if (IsMine(prev.tx->vout[txin.prevout.n]) & filter)
+ return prev.tx->vout[txin.prevout.n].nValue;
}
}
return 0;
@@ -1272,9 +1355,9 @@ int CWalletTx::GetRequestCount() const
// How about the block it's in?
if (nRequests == 0 && !hashUnset())
{
- map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(hashBlock);
- if (mi != pwallet->mapRequestCount.end())
- nRequests = (*mi).second;
+ map<uint256, int>::const_iterator _mi = pwallet->mapRequestCount.find(hashBlock);
+ if (_mi != pwallet->mapRequestCount.end())
+ nRequests = (*_mi).second;
else
nRequests = 1; // If it's in someone else's block it must have got out
}
@@ -1296,14 +1379,14 @@ void CWalletTx::GetAmounts(list<COutputEntry>& listReceived,
CAmount nDebit = GetDebit(filter);
if (nDebit > 0) // debit>0 means we signed/sent this transaction
{
- CAmount nValueOut = GetValueOut();
+ CAmount nValueOut = tx->GetValueOut();
nFee = nDebit - nValueOut;
}
// Sent/received.
- for (unsigned int i = 0; i < vout.size(); ++i)
+ for (unsigned int i = 0; i < tx->vout.size(); ++i)
{
- const CTxOut& txout = vout[i];
+ const CTxOut& txout = tx->vout[i];
isminetype fIsMine = pwallet->IsMine(txout);
// Only need to handle txouts if AT LEAST one of these is true:
// 1) they debit from us (sent)
@@ -1408,7 +1491,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
int posInBlock;
for (posInBlock = 0; posInBlock < (int)block.vtx.size(); posInBlock++)
{
- if (AddToWalletIfInvolvingMe(block.vtx[posInBlock], pindex, posInBlock, fUpdate))
+ if (AddToWalletIfInvolvingMe(*block.vtx[posInBlock], pindex, posInBlock, fUpdate))
ret++;
}
pindex = chainActive.Next(pindex);
@@ -1450,16 +1533,19 @@ void CWallet::ReacceptWalletTransactions()
CWalletTx& wtx = *(item.second);
LOCK(mempool.cs);
- wtx.AcceptToMemoryPool(false, maxTxFee);
+ CValidationState state;
+ wtx.AcceptToMemoryPool(maxTxFee, state);
}
}
bool CWalletTx::RelayWalletTransaction(CConnman* connman)
{
assert(pwallet->GetBroadcastTransactions());
- if (!IsCoinBase())
+ if (!IsCoinBase() && !isAbandoned() && GetDepthInMainChain() == 0)
{
- if (GetDepthInMainChain() == 0 && !isAbandoned() && InMempool()) {
+ CValidationState state;
+ /* GetDepthInMainChain already catches known conflicts. */
+ if (InMempool() || AcceptToMemoryPool(maxTxFee, state)) {
LogPrintf("Relaying wtx %s\n", GetHash().ToString());
if (connman) {
CInv inv(MSG_TX, GetHash());
@@ -1488,7 +1574,7 @@ set<uint256> CWalletTx::GetConflicts() const
CAmount CWalletTx::GetDebit(const isminefilter& filter) const
{
- if (vin.empty())
+ if (tx->vin.empty())
return 0;
CAmount debit = 0;
@@ -1523,7 +1609,7 @@ CAmount CWalletTx::GetCredit(const isminefilter& filter) const
if (IsCoinBase() && GetBlocksToMaturity() > 0)
return 0;
- int64_t credit = 0;
+ CAmount credit = 0;
if (filter & ISMINE_SPENDABLE)
{
// GetBalance can assume transactions in mapWallet won't change
@@ -1578,11 +1664,11 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const
CAmount nCredit = 0;
uint256 hashTx = GetHash();
- for (unsigned int i = 0; i < vout.size(); i++)
+ for (unsigned int i = 0; i < tx->vout.size(); i++)
{
if (!pwallet->IsSpent(hashTx, i))
{
- const CTxOut &txout = vout[i];
+ const CTxOut &txout = tx->vout[i];
nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE);
if (!MoneyRange(nCredit))
throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range");
@@ -1621,11 +1707,11 @@ CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool& fUseCache) const
return nAvailableWatchCreditCached;
CAmount nCredit = 0;
- for (unsigned int i = 0; i < vout.size(); i++)
+ for (unsigned int i = 0; i < tx->vout.size(); i++)
{
if (!pwallet->IsSpent(GetHash(), i))
{
- const CTxOut &txout = vout[i];
+ const CTxOut &txout = tx->vout[i];
nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY);
if (!MoneyRange(nCredit))
throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range");
@@ -1673,23 +1759,23 @@ bool CWalletTx::IsTrusted() const
return false;
// Trusted if all inputs are from us and are in the mempool:
- BOOST_FOREACH(const CTxIn& txin, vin)
+ BOOST_FOREACH(const CTxIn& txin, tx->vin)
{
// Transactions not sent by us: not trusted
const CWalletTx* parent = pwallet->GetWalletTx(txin.prevout.hash);
if (parent == NULL)
return false;
- const CTxOut& parentOut = parent->vout[txin.prevout.n];
+ const CTxOut& parentOut = parent->tx->vout[txin.prevout.n];
if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE)
return false;
}
return true;
}
-bool CWalletTx::IsEquivalentTo(const CWalletTx& tx) const
+bool CWalletTx::IsEquivalentTo(const CWalletTx& _tx) const
{
- CMutableTransaction tx1 = *this;
- CMutableTransaction tx2 = tx;
+ CMutableTransaction tx1 = *this->tx;
+ CMutableTransaction tx2 = *_tx.tx;
for (unsigned int i = 0; i < tx1.vin.size(); i++) tx1.vin[i].scriptSig = CScript();
for (unsigned int i = 0; i < tx2.vin.size(); i++) tx2.vin[i].scriptSig = CScript();
return CTransaction(tx1) == CTransaction(tx2);
@@ -1872,10 +1958,10 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const
if (nDepth == 0 && !pcoin->InMempool())
continue;
- for (unsigned int i = 0; i < pcoin->vout.size(); i++) {
- isminetype mine = IsMine(pcoin->vout[i]);
+ for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) {
+ isminetype mine = IsMine(pcoin->tx->vout[i]);
if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO &&
- !IsLockedCoin((*it).first, i) && (pcoin->vout[i].nValue > 0 || fIncludeZeroValue) &&
+ !IsLockedCoin((*it).first, i) && (pcoin->tx->vout[i].nValue > 0 || fIncludeZeroValue) &&
(!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected(COutPoint((*it).first, i))))
vCoins.push_back(COutput(pcoin, i, nDepth,
((mine & ISMINE_SPENDABLE) != ISMINE_NO) ||
@@ -1894,7 +1980,7 @@ static void ApproximateBestSubset(vector<pair<CAmount, pair<const CWalletTx*,uns
vfBest.assign(vValue.size(), true);
nBest = nTotalLower;
- seed_insecure_rand();
+ FastRandomContext insecure_rand;
for (int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++)
{
@@ -1911,7 +1997,7 @@ static void ApproximateBestSubset(vector<pair<CAmount, pair<const CWalletTx*,uns
//that the rng is fast. We do not use a constant random sequence,
//because there may be some privacy improvement by making
//the selection random.
- if (nPass == 0 ? insecure_rand()&1 : !vfIncluded[i])
+ if (nPass == 0 ? insecure_rand.rand32()&1 : !vfIncluded[i])
{
nTotal += vValue[i].first;
vfIncluded[i] = true;
@@ -1932,7 +2018,7 @@ static void ApproximateBestSubset(vector<pair<CAmount, pair<const CWalletTx*,uns
}
}
-bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, vector<COutput> vCoins,
+bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMine, const int nConfTheirs, const uint64_t nMaxAncestors, vector<COutput> vCoins,
set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet) const
{
setCoinsRet.clear();
@@ -1957,8 +2043,11 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int
if (output.nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? nConfMine : nConfTheirs))
continue;
+ if (!mempool.TransactionWithinChainLimit(pcoin->GetHash(), nMaxAncestors))
+ continue;
+
int i = output.i;
- CAmount n = pcoin->vout[i].nValue;
+ CAmount n = pcoin->tx->vout[i].nValue;
pair<CAmount,pair<const CWalletTx*,unsigned int> > coin = make_pair(n,make_pair(pcoin, i));
@@ -2045,7 +2134,7 @@ bool CWallet::SelectCoins(const vector<COutput>& vAvailableCoins, const CAmount&
{
if (!out.fSpendable)
continue;
- nValueRet += out.tx->vout[out.i].nValue;
+ nValueRet += out.tx->tx->vout[out.i].nValue;
setCoinsRet.insert(make_pair(out.tx, out.i));
}
return (nValueRet >= nTargetValue);
@@ -2065,9 +2154,9 @@ bool CWallet::SelectCoins(const vector<COutput>& vAvailableCoins, const CAmount&
{
const CWalletTx* pcoin = &it->second;
// Clearly invalid input, fail
- if (pcoin->vout.size() <= outpoint.n)
+ if (pcoin->tx->vout.size() <= outpoint.n)
return false;
- nValueFromPresetInputs += pcoin->vout[outpoint.n].nValue;
+ nValueFromPresetInputs += pcoin->tx->vout[outpoint.n].nValue;
setPresetCoins.insert(make_pair(pcoin, outpoint.n));
} else
return false; // TODO: Allow non-wallet inputs
@@ -2082,10 +2171,17 @@ bool CWallet::SelectCoins(const vector<COutput>& vAvailableCoins, const CAmount&
++it;
}
+ size_t nMaxChainLength = std::min(GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT), GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT));
+ bool fRejectLongChains = GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS);
+
bool res = nTargetValue <= nValueFromPresetInputs ||
- SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 6, vCoins, setCoinsRet, nValueRet) ||
- SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 1, vCoins, setCoinsRet, nValueRet) ||
- (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, vCoins, setCoinsRet, nValueRet));
+ SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 6, 0, vCoins, setCoinsRet, nValueRet) ||
+ SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 1, 0, vCoins, setCoinsRet, nValueRet) ||
+ (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, 2, vCoins, setCoinsRet, nValueRet)) ||
+ (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, std::min((size_t)4, nMaxChainLength/3), vCoins, setCoinsRet, nValueRet)) ||
+ (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, nMaxChainLength/2, vCoins, setCoinsRet, nValueRet)) ||
+ (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, nMaxChainLength, vCoins, setCoinsRet, nValueRet)) ||
+ (bSpendZeroConfChange && !fRejectLongChains && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, std::numeric_limits<uint64_t>::max(), vCoins, setCoinsRet, nValueRet));
// because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset
setCoinsRet.insert(setPresetCoins.begin(), setPresetCoins.end());
@@ -2123,10 +2219,10 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool ov
return false;
if (nChangePosInOut != -1)
- tx.vout.insert(tx.vout.begin() + nChangePosInOut, wtx.vout[nChangePosInOut]);
+ tx.vout.insert(tx.vout.begin() + nChangePosInOut, wtx.tx->vout[nChangePosInOut]);
// Add new txins (keeping original txin scriptSig/order)
- BOOST_FOREACH(const CTxIn& txin, wtx.vin)
+ BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin)
{
if (!coinControl.IsSelected(txin.prevout))
{
@@ -2153,7 +2249,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
{
if (nValue < 0 || recipient.nAmount < 0)
{
- strFailReason = _("Transaction amounts must be positive");
+ strFailReason = _("Transaction amounts must not be negative");
return false;
}
nValue += recipient.nAmount;
@@ -2161,9 +2257,9 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
if (recipient.fSubtractFeeFromAmount)
nSubtractFeeFromAmount++;
}
- if (vecSend.empty() || nValue < 0)
+ if (vecSend.empty())
{
- strFailReason = _("Transaction amounts must be positive");
+ strFailReason = _("Transaction must have at least one recipient");
return false;
}
@@ -2266,7 +2362,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
}
BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
{
- CAmount nCredit = pcoin.first->vout[pcoin.second].nValue;
+ CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue;
//The coin age after the next block (depth+1) is used instead of the current,
//reflecting an assumption the user would accept a bit more delay for
//a chance at a free transaction.
@@ -2304,7 +2400,11 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
CPubKey vchPubKey;
bool ret;
ret = reservekey.GetReservedKey(vchPubKey);
- assert(ret); // should never fail, as we just unlocked
+ if (!ret)
+ {
+ strFailReason = _("Keypool ran out, please call keypoolrefill first");
+ return false;
+ }
scriptChange = GetScriptForDestination(vchPubKey.GetID());
}
@@ -2381,10 +2481,10 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins)
{
bool signSuccess;
- const CScript& scriptPubKey = coin.first->vout[coin.second].scriptPubKey;
+ const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey;
SignatureData sigdata;
if (sign)
- signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata);
+ signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata);
else
signSuccess = ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata);
@@ -2409,28 +2509,33 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
}
// Embed the constructed transaction data in wtxNew.
- *static_cast<CTransaction*>(&wtxNew) = CTransaction(txNew);
+ wtxNew.SetTx(MakeTransactionRef(std::move(txNew)));
// Limit size
- if (GetTransactionWeight(txNew) >= MAX_STANDARD_TX_WEIGHT)
+ if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT)
{
strFailReason = _("Transaction too large");
return false;
}
- dPriority = wtxNew.ComputePriority(dPriority, nBytes);
+ dPriority = wtxNew.tx->ComputePriority(dPriority, nBytes);
+
+ // Allow to override the default confirmation target over the CoinControl instance
+ int currentConfirmationTarget = nTxConfirmTarget;
+ if (coinControl && coinControl->nConfirmTarget > 0)
+ currentConfirmationTarget = coinControl->nConfirmTarget;
// Can we complete this as a free transaction?
if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE)
{
// Not enough fee: enough priority?
- double dPriorityNeeded = mempool.estimateSmartPriority(nTxConfirmTarget);
+ double dPriorityNeeded = mempool.estimateSmartPriority(currentConfirmationTarget);
// Require at least hard-coded AllowFree.
if (dPriority >= dPriorityNeeded && AllowFree(dPriority))
break;
}
- CAmount nFeeNeeded = GetMinimumFee(nBytes, nTxConfirmTarget, mempool);
+ CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool);
if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) {
nFeeNeeded = coinControl->nMinimumTotalFee;
}
@@ -2455,17 +2560,32 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
}
}
+ if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
+ // Lastly, ensure this tx will pass the mempool's chain limits
+ LockPoints lp;
+ CTxMemPoolEntry entry(txNew, 0, 0, 0, 0, false, 0, false, 0, lp);
+ CTxMemPool::setEntries setAncestors;
+ size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
+ size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000;
+ size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);
+ size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000;
+ std::string errString;
+ if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) {
+ strFailReason = _("Transaction has too long of a mempool chain");
+ return false;
+ }
+ }
return true;
}
/**
* Call after CreateTransaction unless you want to abort
*/
-bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman)
+bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman, CValidationState& state)
{
{
LOCK2(cs_main, cs_wallet);
- LogPrintf("CommitTransaction:\n%s", wtxNew.ToString());
+ LogPrintf("CommitTransaction:\n%s", wtxNew.tx->ToString());
{
// Take key pair from key pool so it won't be used again
reservekey.KeepKey();
@@ -2475,7 +2595,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CCon
AddToWallet(wtxNew);
// Notify that old coins are spent
- BOOST_FOREACH(const CTxIn& txin, wtxNew.vin)
+ BOOST_FOREACH(const CTxIn& txin, wtxNew.tx->vin)
{
CWalletTx &coin = mapWallet[txin.prevout.hash];
coin.BindWallet(this);
@@ -2489,21 +2609,32 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CCon
if (fBroadcastTransactions)
{
// Broadcast
- if (!wtxNew.AcceptToMemoryPool(false, maxTxFee))
- {
- // This must not fail. The transaction has already been signed and recorded.
- LogPrintf("CommitTransaction(): Error: Transaction not valid\n");
- return false;
+ if (!wtxNew.AcceptToMemoryPool(maxTxFee, state)) {
+ LogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", state.GetRejectReason());
+ // TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure.
+ } else {
+ wtxNew.RelayWalletTransaction(connman);
}
- wtxNew.RelayWalletTransaction(connman);
}
}
return true;
}
-bool CWallet::AddAccountingEntry(const CAccountingEntry& acentry, CWalletDB & pwalletdb)
+void CWallet::ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& entries) {
+ CWalletDB walletdb(strWalletFile);
+ return walletdb.ListAccountCreditDebit(strAccount, entries);
+}
+
+bool CWallet::AddAccountingEntry(const CAccountingEntry& acentry)
{
- if (!pwalletdb.WriteAccountingEntry_Backend(acentry))
+ CWalletDB walletdb(strWalletFile);
+
+ return AddAccountingEntry(acentry, &walletdb);
+}
+
+bool CWallet::AddAccountingEntry(const CAccountingEntry& acentry, CWalletDB *pwalletdb)
+{
+ if (!pwalletdb->WriteAccountingEntry_Backend(acentry))
return false;
laccentries.push_back(acentry);
@@ -2828,7 +2959,7 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances()
{
CWalletTx *pcoin = &walletEntry.second;
- if (!CheckFinalTx(*pcoin) || !pcoin->IsTrusted())
+ if (!pcoin->IsTrusted())
continue;
if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0)
@@ -2838,15 +2969,15 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances()
if (nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1))
continue;
- for (unsigned int i = 0; i < pcoin->vout.size(); i++)
+ for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++)
{
CTxDestination addr;
- if (!IsMine(pcoin->vout[i]))
+ if (!IsMine(pcoin->tx->vout[i]))
continue;
- if(!ExtractDestination(pcoin->vout[i].scriptPubKey, addr))
+ if(!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, addr))
continue;
- CAmount n = IsSpent(walletEntry.first, i) ? 0 : pcoin->vout[i].nValue;
+ CAmount n = IsSpent(walletEntry.first, i) ? 0 : pcoin->tx->vout[i].nValue;
if (!balances.count(addr))
balances[addr] = 0;
@@ -2868,16 +2999,16 @@ set< set<CTxDestination> > CWallet::GetAddressGroupings()
{
CWalletTx *pcoin = &walletEntry.second;
- if (pcoin->vin.size() > 0)
+ if (pcoin->tx->vin.size() > 0)
{
bool any_mine = false;
// group all input addresses with each other
- BOOST_FOREACH(CTxIn txin, pcoin->vin)
+ BOOST_FOREACH(CTxIn txin, pcoin->tx->vin)
{
CTxDestination address;
if(!IsMine(txin)) /* If this input isn't mine, ignore it */
continue;
- if(!ExtractDestination(mapWallet[txin.prevout.hash].vout[txin.prevout.n].scriptPubKey, address))
+ if(!ExtractDestination(mapWallet[txin.prevout.hash].tx->vout[txin.prevout.n].scriptPubKey, address))
continue;
grouping.insert(address);
any_mine = true;
@@ -2886,7 +3017,7 @@ set< set<CTxDestination> > CWallet::GetAddressGroupings()
// group change with input addresses
if (any_mine)
{
- BOOST_FOREACH(CTxOut txout, pcoin->vout)
+ BOOST_FOREACH(CTxOut txout, pcoin->tx->vout)
if (IsChange(txout))
{
CTxDestination txoutAddr;
@@ -2903,11 +3034,11 @@ set< set<CTxDestination> > CWallet::GetAddressGroupings()
}
// group lone addrs by themselves
- for (unsigned int i = 0; i < pcoin->vout.size(); i++)
- if (IsMine(pcoin->vout[i]))
+ for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++)
+ if (IsMine(pcoin->tx->vout[i]))
{
CTxDestination address;
- if(!ExtractDestination(pcoin->vout[i].scriptPubKey, address))
+ if(!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, address))
continue;
grouping.insert(address);
groupings.insert(grouping);
@@ -2917,17 +3048,17 @@ set< set<CTxDestination> > CWallet::GetAddressGroupings()
set< set<CTxDestination>* > uniqueGroupings; // a set of pointers to groups of addresses
map< CTxDestination, set<CTxDestination>* > setmap; // map addresses to the unique group containing it
- BOOST_FOREACH(set<CTxDestination> grouping, groupings)
+ BOOST_FOREACH(set<CTxDestination> _grouping, groupings)
{
// make a set of all the groups hit by this new group
set< set<CTxDestination>* > hits;
map< CTxDestination, set<CTxDestination>* >::iterator it;
- BOOST_FOREACH(CTxDestination address, grouping)
+ BOOST_FOREACH(CTxDestination address, _grouping)
if ((it = setmap.find(address)) != setmap.end())
hits.insert((*it).second);
// merge all hit groups into a new single group and delete old groups
- set<CTxDestination>* merged = new set<CTxDestination>(grouping);
+ set<CTxDestination>* merged = new set<CTxDestination>(_grouping);
BOOST_FOREACH(set<CTxDestination>* hit, hits)
{
merged->insert(hit->begin(), hit->end());
@@ -3174,7 +3305,7 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const {
if (blit != mapBlockIndex.end() && chainActive.Contains(blit->second)) {
// ... which are already in a block
int nHeight = blit->second->nHeight;
- BOOST_FOREACH(const CTxOut &txout, wtx.vout) {
+ BOOST_FOREACH(const CTxOut &txout, wtx.tx->vout) {
// iterate over all their outputs
CAffectedKeysVisitor(*this, vAffected).Process(txout.scriptPubKey);
BOOST_FOREACH(const CKeyID &keyid, vAffected) {
@@ -3268,6 +3399,7 @@ std::string CWallet::GetWalletHelpString(bool showDebug)
strUsage += HelpMessageOpt("-dblogsize=<n>", strprintf("Flush wallet database activity from memory to disk log every <n> megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE));
strUsage += HelpMessageOpt("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET));
strUsage += HelpMessageOpt("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB));
+ strUsage += HelpMessageOpt("-walletrejectlongchains", strprintf(_("Wallet will not create transactions that violate mempool chain limits (default: %u"), DEFAULT_WALLET_REJECT_LONG_CHAINS));
}
return strUsage;
@@ -3275,6 +3407,12 @@ std::string CWallet::GetWalletHelpString(bool showDebug)
bool CWallet::InitLoadWallet()
{
+ if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
+ pwalletMain = NULL;
+ LogPrintf("Wallet disabled!\n");
+ return true;
+ }
+
std::string walletFile = GetArg("-wallet", DEFAULT_WALLET_DAT);
// needed to restore wallet transaction meta data after -zapwallettxes
@@ -3436,23 +3574,59 @@ bool CWallet::InitLoadWallet()
LogPrintf("mapWallet.size() = %u\n", walletInstance->mapWallet.size());
LogPrintf("mapAddressBook.size() = %u\n", walletInstance->mapAddressBook.size());
}
- // Add wallet transactions that aren't already in a block to mapTransactions
- walletInstance->ReacceptWalletTransactions();
pwalletMain = walletInstance;
return true;
}
+void CWallet::postInitProcess(boost::thread_group& threadGroup)
+{
+ // Add wallet transactions that aren't already in a block to mempool
+ // Do this here as mempool requires genesis block to be loaded
+ ReacceptWalletTransactions();
+
+ // Run a thread to flush wallet periodically
+ threadGroup.create_thread(boost::bind(&ThreadFlushWalletDB, boost::ref(this->strWalletFile)));
+}
+
bool CWallet::ParameterInteraction()
{
+ if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET))
+ return true;
+
+ if (GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && SoftSetBoolArg("-walletbroadcast", false)) {
+ LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__);
+ }
+
+ if (GetBoolArg("-salvagewallet", false) && SoftSetBoolArg("-rescan", true)) {
+ // Rewrite just private keys: rescan to find transactions
+ LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting -rescan=1\n", __func__);
+ }
+
+ // -zapwallettx implies a rescan
+ if (GetBoolArg("-zapwallettxes", false) && SoftSetBoolArg("-rescan", true)) {
+ LogPrintf("%s: parameter interaction: -zapwallettxes=<mode> -> setting -rescan=1\n", __func__);
+ }
+
+ if (GetBoolArg("-sysperms", false))
+ return InitError("-sysperms is not allowed in combination with enabled wallet functionality");
+ if (GetArg("-prune", 0) && GetBoolArg("-rescan", false))
+ return InitError(_("Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again."));
+
+ if (::minRelayTxFee.GetFeePerK() > HIGH_TX_FEE_PER_KB)
+ InitWarning(AmountHighWarn("-minrelaytxfee") + " " +
+ _("The wallet will avoid paying less than the minimum relay fee."));
+
if (mapArgs.count("-mintxfee"))
{
CAmount n = 0;
- if (ParseMoney(mapArgs["-mintxfee"], n) && n > 0)
- CWallet::minTxFee = CFeeRate(n);
- else
+ if (!ParseMoney(mapArgs["-mintxfee"], n) || 0 == n)
return InitError(AmountErrMsg("mintxfee", mapArgs["-mintxfee"]));
+ if (n > HIGH_TX_FEE_PER_KB)
+ InitWarning(AmountHighWarn("-mintxfee") + " " +
+ _("This is the minimum transaction fee you pay on every transaction."));
+ CWallet::minTxFee = CFeeRate(n);
}
if (mapArgs.count("-fallbackfee"))
{
@@ -3460,7 +3634,8 @@ bool CWallet::ParameterInteraction()
if (!ParseMoney(mapArgs["-fallbackfee"], nFeePerK))
return InitError(strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"), mapArgs["-fallbackfee"]));
if (nFeePerK > HIGH_TX_FEE_PER_KB)
- InitWarning(_("-fallbackfee is set very high! This is the transaction fee you may pay when fee estimates are not available."));
+ InitWarning(AmountHighWarn("-fallbackfee") + " " +
+ _("This is the transaction fee you may pay when fee estimates are not available."));
CWallet::fallbackFee = CFeeRate(nFeePerK);
}
if (mapArgs.count("-paytxfee"))
@@ -3469,7 +3644,9 @@ bool CWallet::ParameterInteraction()
if (!ParseMoney(mapArgs["-paytxfee"], nFeePerK))
return InitError(AmountErrMsg("paytxfee", mapArgs["-paytxfee"]));
if (nFeePerK > HIGH_TX_FEE_PER_KB)
- InitWarning(_("-paytxfee is set very high! This is the transaction fee you will pay if you send a transaction."));
+ InitWarning(AmountHighWarn("-paytxfee") + " " +
+ _("This is the transaction fee you will pay if you send a transaction."));
+
payTxFee = CFeeRate(nFeePerK, 1000);
if (payTxFee < ::minRelayTxFee)
{
@@ -3496,6 +3673,9 @@ bool CWallet::ParameterInteraction()
fSendFreeTransactions = GetBoolArg("-sendfreetransactions", DEFAULT_SEND_FREE_TRANSACTIONS);
fWalletRbf = GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF);
+ if (fSendFreeTransactions && GetArg("-limitfreerelay", DEFAULT_LIMITFREERELAY) <= 0)
+ return InitError("Creation of free transactions with their relay disabled is not supported.");
+
return true;
}
@@ -3600,8 +3780,7 @@ int CMerkleTx::GetBlocksToMaturity() const
}
-bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, CAmount nAbsurdFee)
+bool CMerkleTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state)
{
- CValidationState state;
- return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, false, nAbsurdFee);
+ return ::AcceptToMemoryPool(mempool, state, *this, true, NULL, false, nAbsurdFee);
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 0d096488f6..72c1239cc4 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -27,6 +27,7 @@
#include <vector>
#include <boost/shared_ptr.hpp>
+#include <boost/thread.hpp>
extern CWallet* pwalletMain;
@@ -52,14 +53,16 @@ static const CAmount MIN_CHANGE = CENT;
static const bool DEFAULT_SPEND_ZEROCONF_CHANGE = true;
//! Default for -sendfreetransactions
static const bool DEFAULT_SEND_FREE_TRANSACTIONS = false;
+//! Default for -walletrejectlongchains
+static const bool DEFAULT_WALLET_REJECT_LONG_CHAINS = false;
//! -txconfirmtarget default
-static const unsigned int DEFAULT_TX_CONFIRM_TARGET = 2;
+static const unsigned int DEFAULT_TX_CONFIRM_TARGET = 6;
//! -walletrbf default
static const bool DEFAULT_WALLET_RBF = false;
//! Largest (in bytes) free transaction we're willing to create
static const unsigned int MAX_FREE_TRANSACTION_CREATE_SIZE = 1000;
static const bool DEFAULT_WALLETBROADCAST = true;
-
+static const bool DEFAULT_DISABLE_WALLET = false;
//! if set, all keys will be derived by using BIP32
static const bool DEFAULT_USE_HD_WALLET = true;
@@ -99,8 +102,9 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
- if (!(nType & SER_GETHASH))
+ inline void SerializationOp(Stream& s, Operation ser_action) {
+ int nVersion = s.GetVersion();
+ if (!(s.GetType() & SER_GETHASH))
READWRITE(nVersion);
READWRITE(nTime);
READWRITE(vchPubKey);
@@ -133,7 +137,7 @@ struct CRecipient
typedef std::map<std::string, std::string> mapValue_t;
-static void ReadOrderPos(int64_t& nOrderPos, mapValue_t& mapValue)
+static inline void ReadOrderPos(int64_t& nOrderPos, mapValue_t& mapValue)
{
if (!mapValue.count("n"))
{
@@ -144,7 +148,7 @@ static void ReadOrderPos(int64_t& nOrderPos, mapValue_t& mapValue)
}
-static void WriteOrderPos(const int64_t& nOrderPos, mapValue_t& mapValue)
+static inline void WriteOrderPos(const int64_t& nOrderPos, mapValue_t& mapValue)
{
if (nOrderPos == -1)
return;
@@ -159,13 +163,14 @@ struct COutputEntry
};
/** A transaction with a merkle branch linking it to the block chain. */
-class CMerkleTx : public CTransaction
+class CMerkleTx
{
private:
/** Constant used in hashBlock to indicate tx has been abandoned */
static const uint256 ABANDON_HASH;
public:
+ CTransactionRef tx;
uint256 hashBlock;
/* An nIndex == -1 means that hashBlock (in nonzero) refers to the earliest
@@ -177,27 +182,37 @@ public:
CMerkleTx()
{
+ SetTx(MakeTransactionRef());
Init();
}
- CMerkleTx(const CTransaction& txIn) : CTransaction(txIn)
+ CMerkleTx(CTransactionRef arg)
{
+ SetTx(std::move(arg));
Init();
}
+ /** Helper conversion operator to allow passing CMerkleTx where CTransaction is expected.
+ * TODO: adapt callers and remove this operator. */
+ operator const CTransaction&() const { return *tx; }
+
void Init()
{
hashBlock = uint256();
nIndex = -1;
}
+ void SetTx(CTransactionRef arg)
+ {
+ tx = std::move(arg);
+ }
+
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
std::vector<uint256> vMerkleBranch; // For compatibility with older versions.
- READWRITE(*(CTransaction*)this);
- nVersion = this->nVersion;
+ READWRITE(tx);
READWRITE(hashBlock);
READWRITE(vMerkleBranch);
READWRITE(nIndex);
@@ -216,10 +231,13 @@ public:
bool IsInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet) > 0; }
int GetBlocksToMaturity() const;
/** Pass this transaction to the mempool. Fails if absolute fee exceeds absurd fee. */
- bool AcceptToMemoryPool(bool fLimitFree, const CAmount nAbsurdFee);
+ bool AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state);
bool hashUnset() const { return (hashBlock.IsNull() || hashBlock == ABANDON_HASH); }
bool isAbandoned() const { return (hashBlock == ABANDON_HASH); }
void setAbandoned() { hashBlock = ABANDON_HASH; }
+
+ const uint256& GetHash() const { return tx->GetHash(); }
+ bool IsCoinBase() const { return tx->IsCoinBase(); }
};
/**
@@ -266,17 +284,7 @@ public:
Init(NULL);
}
- CWalletTx(const CWallet* pwalletIn)
- {
- Init(pwalletIn);
- }
-
- CWalletTx(const CWallet* pwalletIn, const CMerkleTx& txIn) : CMerkleTx(txIn)
- {
- Init(pwalletIn);
- }
-
- CWalletTx(const CWallet* pwalletIn, const CTransaction& txIn) : CMerkleTx(txIn)
+ CWalletTx(const CWallet* pwalletIn, CTransactionRef arg) : CMerkleTx(std::move(arg))
{
Init(pwalletIn);
}
@@ -315,7 +323,7 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
if (ser_action.ForRead())
Init(NULL);
char fSpent = false;
@@ -449,8 +457,9 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
- if (!(nType & SER_GETHASH))
+ inline void SerializationOp(Stream& s, Operation ser_action) {
+ int nVersion = s.GetVersion();
+ if (!(s.GetType() & SER_GETHASH))
READWRITE(nVersion);
READWRITE(vchPrivKey);
READWRITE(nTimeCreated);
@@ -494,8 +503,9 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
- if (!(nType & SER_GETHASH))
+ inline void SerializationOp(Stream& s, Operation ser_action) {
+ int nVersion = s.GetVersion();
+ if (!(s.GetType() & SER_GETHASH))
READWRITE(nVersion);
//! Note: strAccount is serialized as part of the key, not here.
READWRITE(nCreditDebit);
@@ -508,7 +518,7 @@ public:
if (!(mapValue.empty() && _ssExtra.empty()))
{
- CDataStream ss(nType, nVersion);
+ CDataStream ss(s.GetType(), s.GetVersion());
ss.insert(ss.begin(), '\0');
ss << mapValue;
ss.insert(ss.end(), _ssExtra.begin(), _ssExtra.end());
@@ -524,7 +534,7 @@ public:
mapValue.clear();
if (std::string::npos != nSepPos)
{
- CDataStream ss(std::vector<char>(strComment.begin() + nSepPos + 1, strComment.end()), nType, nVersion);
+ CDataStream ss(std::vector<char>(strComment.begin() + nSepPos + 1, strComment.end()), s.GetType(), s.GetVersion());
ss >> mapValue;
_ssExtra = std::vector<char>(ss.begin(), ss.end());
}
@@ -598,7 +608,7 @@ public:
*/
mutable CCriticalSection cs_wallet;
- std::string strWalletFile;
+ const std::string strWalletFile;
void LoadKeyPool(int nIndex, const CKeyPool &keypool)
{
@@ -623,11 +633,9 @@ public:
SetNull();
}
- CWallet(const std::string& strWalletFileIn)
+ CWallet(const std::string& strWalletFileIn) : strWalletFile(strWalletFileIn)
{
SetNull();
-
- strWalletFile = strWalletFileIn;
fFileBacked = true;
}
@@ -685,7 +693,7 @@ public:
* completion the coin set and corresponding actual target value is
* assembled
*/
- bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, std::vector<COutput> vCoins, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet) const;
+ bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, uint64_t nMaxAncestors, std::vector<COutput> vCoins, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet) const;
bool IsSpent(const uint256& hash, unsigned int n) const;
@@ -700,6 +708,7 @@ public:
* Generate a new key
*/
CPubKey GenerateNewKey();
+ void DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret);
//! Adds a key to the store, and saves it to disk.
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey);
//! Adds a key to the store, without saving it to disk (used by LoadWallet)
@@ -742,6 +751,7 @@ public:
* @return next transaction order id
*/
int64_t IncOrderPosNext(CWalletDB *pwalletdb = NULL);
+ DBErrors ReorderTransactions();
bool AccountMove(std::string strFrom, std::string strTo, CAmount nAmount, std::string strComment = "");
bool GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bForceNew = false);
@@ -774,9 +784,11 @@ public:
*/
bool CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut,
std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true);
- bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman);
+ bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman, CValidationState& state);
- bool AddAccountingEntry(const CAccountingEntry&, CWalletDB & pwalletdb);
+ void ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& entries);
+ bool AddAccountingEntry(const CAccountingEntry&);
+ bool AddAccountingEntry(const CAccountingEntry&, CWalletDB *pwalletdb);
static CFeeRate minTxFee;
static CFeeRate fallbackFee;
@@ -910,6 +922,12 @@ public:
/* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */
static bool InitLoadWallet();
+ /**
+ * Wallet post-init setup
+ * Gives the wallet a chance to register repetitive tasks and complete post-init tasks
+ */
+ void postInitProcess(boost::thread_group& threadGroup);
+
/* Wallets parameter interaction */
static bool ParameterInteraction();
@@ -977,8 +995,9 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
- if (!(nType & SER_GETHASH))
+ inline void SerializationOp(Stream& s, Operation ser_action) {
+ int nVersion = s.GetVersion();
+ if (!(s.GetType() & SER_GETHASH))
READWRITE(nVersion);
READWRITE(vchPubKey);
}
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 80bfe8255d..0395e4a072 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -7,7 +7,7 @@
#include "base58.h"
#include "consensus/validation.h"
-#include "main.h" // For CheckTransaction
+#include "validation.h" // For CheckTransaction
#include "protocol.h"
#include "serialize.h"
#include "sync.h"
@@ -251,82 +251,6 @@ void CWalletDB::ListAccountCreditDebit(const string& strAccount, list<CAccountin
pcursor->close();
}
-DBErrors CWalletDB::ReorderTransactions(CWallet* pwallet)
-{
- LOCK(pwallet->cs_wallet);
- // Old wallets didn't have any defined order for transactions
- // Probably a bad idea to change the output of this
-
- // First: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap.
- typedef pair<CWalletTx*, CAccountingEntry*> TxPair;
- typedef multimap<int64_t, TxPair > TxItems;
- TxItems txByTime;
-
- for (map<uint256, CWalletTx>::iterator it = pwallet->mapWallet.begin(); it != pwallet->mapWallet.end(); ++it)
- {
- CWalletTx* wtx = &((*it).second);
- txByTime.insert(make_pair(wtx->nTimeReceived, TxPair(wtx, (CAccountingEntry*)0)));
- }
- list<CAccountingEntry> acentries;
- ListAccountCreditDebit("", acentries);
- BOOST_FOREACH(CAccountingEntry& entry, acentries)
- {
- txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry)));
- }
-
- int64_t& nOrderPosNext = pwallet->nOrderPosNext;
- nOrderPosNext = 0;
- std::vector<int64_t> nOrderPosOffsets;
- for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it)
- {
- CWalletTx *const pwtx = (*it).second.first;
- CAccountingEntry *const pacentry = (*it).second.second;
- int64_t& nOrderPos = (pwtx != 0) ? pwtx->nOrderPos : pacentry->nOrderPos;
-
- if (nOrderPos == -1)
- {
- nOrderPos = nOrderPosNext++;
- nOrderPosOffsets.push_back(nOrderPos);
-
- if (pwtx)
- {
- if (!WriteTx(*pwtx))
- return DB_LOAD_FAIL;
- }
- else
- if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry))
- return DB_LOAD_FAIL;
- }
- else
- {
- int64_t nOrderPosOff = 0;
- BOOST_FOREACH(const int64_t& nOffsetStart, nOrderPosOffsets)
- {
- if (nOrderPos >= nOffsetStart)
- ++nOrderPosOff;
- }
- nOrderPos += nOrderPosOff;
- nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1);
-
- if (!nOrderPosOff)
- continue;
-
- // Since we're changing the order, write it back
- if (pwtx)
- {
- if (!WriteTx(*pwtx))
- return DB_LOAD_FAIL;
- }
- else
- if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry))
- return DB_LOAD_FAIL;
- }
- }
- WriteOrderPosNext(nOrderPosNext);
-
- return DB_LOAD_OK;
-}
-
class CWalletScanState {
public:
unsigned int nKeys;
@@ -711,7 +635,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
WriteVersion(CLIENT_VERSION);
if (wss.fAnyUnordered)
- result = ReorderTransactions(pwallet);
+ result = pwallet->ReorderTransactions();
pwallet->laccentries.clear();
ListAccountCreditDebit("*", pwallet->laccentries);
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index 5addd5c5c0..eb25ac613d 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -54,10 +54,9 @@ public:
CHDChain() { SetNull(); }
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
+ inline void SerializationOp(Stream& s, Operation ser_action)
{
READWRITE(this->nVersion);
- nVersion = this->nVersion;
READWRITE(nExternalChainCounter);
READWRITE(masterKeyID);
}
@@ -94,9 +93,8 @@ public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(this->nVersion);
- nVersion = this->nVersion;
READWRITE(nCreateTime);
if (this->nVersion >= VERSION_WITH_HDDATA)
{
@@ -155,6 +153,7 @@ public:
/// This writes directly to the database, and will not update the CWallet's cached accounting entries!
/// Use wallet.AddAccountingEntry instead, to write *and* update its caches.
+ bool WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry);
bool WriteAccountingEntry_Backend(const CAccountingEntry& acentry);
bool ReadAccount(const std::string& strAccount, CAccount& account);
bool WriteAccount(const std::string& strAccount, const CAccount& account);
@@ -167,7 +166,6 @@ public:
CAmount GetAccountCreditDebit(const std::string& strAccount);
void ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& acentries);
- DBErrors ReorderTransactions(CWallet* pwallet);
DBErrors LoadWallet(CWallet* pwallet);
DBErrors FindWalletTx(CWallet* pwallet, std::vector<uint256>& vTxHash, std::vector<CWalletTx>& vWtx);
DBErrors ZapWalletTx(CWallet* pwallet, std::vector<CWalletTx>& vWtx);
@@ -182,7 +180,6 @@ private:
CWalletDB(const CWalletDB&);
void operator=(const CWalletDB&);
- bool WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry);
};
void ThreadFlushWalletDB(const std::string& strFile);
diff --git a/src/warnings.cpp b/src/warnings.cpp
new file mode 100644
index 0000000000..2c1b1b0e12
--- /dev/null
+++ b/src/warnings.cpp
@@ -0,0 +1,89 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2016 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "sync.h"
+#include "clientversion.h"
+#include "util.h"
+#include "warnings.h"
+
+CCriticalSection cs_warnings;
+std::string strMiscWarning;
+bool fLargeWorkForkFound = false;
+bool fLargeWorkInvalidChainFound = false;
+
+void SetMiscWarning(const std::string& strWarning)
+{
+ LOCK(cs_warnings);
+ strMiscWarning = strWarning;
+}
+
+void SetfLargeWorkForkFound(bool flag)
+{
+ LOCK(cs_warnings);
+ fLargeWorkForkFound = flag;
+}
+
+bool GetfLargeWorkForkFound()
+{
+ LOCK(cs_warnings);
+ return fLargeWorkForkFound;
+}
+
+void SetfLargeWorkInvalidChainFound(bool flag)
+{
+ LOCK(cs_warnings);
+ fLargeWorkInvalidChainFound = flag;
+}
+
+bool GetfLargeWorkInvalidChainFound()
+{
+ LOCK(cs_warnings);
+ return fLargeWorkInvalidChainFound;
+}
+
+std::string GetWarnings(const std::string& strFor)
+{
+ std::string strStatusBar;
+ std::string strRPC;
+ std::string strGUI;
+ const std::string uiAlertSeperator = "<hr />";
+
+ LOCK(cs_warnings);
+
+ 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";
+ strGUI = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications");
+ }
+
+ if (GetBoolArg("-testsafemode", DEFAULT_TESTSAFEMODE))
+ strStatusBar = strRPC = strGUI = "testsafemode enabled";
+
+ // Misc warnings like out of disk space and clock is wrong
+ if (strMiscWarning != "")
+ {
+ strStatusBar = strMiscWarning;
+ strGUI += (strGUI.empty() ? "" : uiAlertSeperator) + strMiscWarning;
+ }
+
+ if (fLargeWorkForkFound)
+ {
+ strStatusBar = strRPC = "Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.";
+ strGUI += (strGUI.empty() ? "" : uiAlertSeperator) + _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.");
+ }
+ else if (fLargeWorkInvalidChainFound)
+ {
+ strStatusBar = strRPC = "Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.";
+ strGUI += (strGUI.empty() ? "" : uiAlertSeperator) + _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.");
+ }
+
+ if (strFor == "gui")
+ return strGUI;
+ else if (strFor == "statusbar")
+ return strStatusBar;
+ else if (strFor == "rpc")
+ return strRPC;
+ assert(!"GetWarnings(): invalid parameter");
+ return "error";
+}
diff --git a/src/warnings.h b/src/warnings.h
new file mode 100644
index 0000000000..a7aa657426
--- /dev/null
+++ b/src/warnings.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2016 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_WARNINGS_H
+#define BITCOIN_WARNINGS_H
+
+#include <stdlib.h>
+#include <string>
+
+void SetMiscWarning(const std::string& strWarning);
+void SetfLargeWorkForkFound(bool flag);
+bool GetfLargeWorkForkFound();
+void SetfLargeWorkInvalidChainFound(bool flag);
+bool GetfLargeWorkInvalidChainFound();
+std::string GetWarnings(const std::string& strFor);
+
+static const bool DEFAULT_TESTSAFEMODE = false;
+
+#endif // BITCOIN_WARNINGS_H
diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp
index 376e7dec59..a7e20a8356 100644
--- a/src/zmq/zmqnotificationinterface.cpp
+++ b/src/zmq/zmqnotificationinterface.cpp
@@ -6,7 +6,7 @@
#include "zmqpublishnotifier.h"
#include "version.h"
-#include "main.h"
+#include "validation.h"
#include "streams.h"
#include "util.h"
@@ -124,12 +124,15 @@ void CZMQNotificationInterface::Shutdown()
}
}
-void CZMQNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindex)
+void CZMQNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload)
{
+ if (fInitialDownload || pindexNew == pindexFork) // In IBD or blocks were disconnected without any new ones
+ return;
+
for (std::list<CZMQAbstractNotifier*>::iterator i = notifiers.begin(); i!=notifiers.end(); )
{
CZMQAbstractNotifier *notifier = *i;
- if (notifier->NotifyBlock(pindex))
+ if (notifier->NotifyBlock(pindexNew))
{
i++;
}
diff --git a/src/zmq/zmqnotificationinterface.h b/src/zmq/zmqnotificationinterface.h
index a853447267..037470ec17 100644
--- a/src/zmq/zmqnotificationinterface.h
+++ b/src/zmq/zmqnotificationinterface.h
@@ -25,7 +25,7 @@ protected:
// CValidationInterface
void SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock);
- void UpdatedBlockTip(const CBlockIndex *pindex);
+ void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload);
private:
CZMQNotificationInterface();
diff --git a/src/zmq/zmqpublishnotifier.cpp b/src/zmq/zmqpublishnotifier.cpp
index b6c907980f..a11256dfd5 100644
--- a/src/zmq/zmqpublishnotifier.cpp
+++ b/src/zmq/zmqpublishnotifier.cpp
@@ -3,9 +3,11 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "chainparams.h"
+#include "streams.h"
#include "zmqpublishnotifier.h"
-#include "main.h"
+#include "validation.h"
#include "util.h"
+#include "rpc/server.h"
static std::multimap<std::string, CZMQAbstractPublishNotifier*> mapPublishNotifiers;
@@ -165,7 +167,7 @@ bool CZMQPublishRawBlockNotifier::NotifyBlock(const CBlockIndex *pindex)
LogPrint("zmq", "zmq: Publish rawblock %s\n", pindex->GetBlockHash().GetHex());
const Consensus::Params& consensusParams = Params().GetConsensus();
- CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
+ CDataStream ss(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
{
LOCK(cs_main);
CBlock block;
@@ -185,7 +187,7 @@ bool CZMQPublishRawTransactionNotifier::NotifyTransaction(const CTransaction &tr
{
uint256 hash = transaction.GetHash();
LogPrint("zmq", "zmq: Publish rawtx %s\n", hash.GetHex());
- CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
+ CDataStream ss(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
ss << transaction;
return SendMessage(MSG_RAWTX, &(*ss.begin()), ss.size());
}